1. thread_local! 导致跨线程缓存不可见(主要问题)

kline_py.rs 中 BSP_CACHE、KLINE_IDENTITY、BAR_IDENTITY 使用了 thread_local!。主线程调用 识别买卖点() →
  买卖点信息.add() 写入的是主线程的 PySet,backtrader 策略线程读取的是自己线程独立的空 PySet。

  修复:将三个缓存从 thread_local! 改为全局 static + std::sync::LazyLock<RwLock<HashMap<...>>>

  影响分析

  这些身份缓存的影响与 KLINE_IDENTITY/BAR_IDENTITY 不同——它们只影响 Python 对象身份(is
  比较),不直接影响数据内容(因为 Rust 数据通过 Arc 共享,读写都是同一份)。

  具体后果:
  - 不同线程访问同一个 Rust Arc 会得到不同的 Python wrapper 对象
  - a is b 跨线程比较返回 False,哪怕它们包装同一个底层 Rust 对象
  - 每个线程维护一份独立缓存,内存浪费(不过 wrapper 很小)
This commit is contained in:
YuWuKunCheng
2026-05-30 22:15:09 +08:00
parent 15dc44e8e0
commit b7c4e60420
12 changed files with 327 additions and 309 deletions
+82 -85
View File
@@ -31,26 +31,28 @@ use std::sync::atomic::Ordering;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::RwLock;
thread_local! { // 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性
static HUB_IDENTITY: RwLock<HashMap<usize, Py<Py>>> = RwLock::new(HashMap::new()); static HUB_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<Py>>>> =
} std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
pub(crate) fn hub_to_py( pub(crate) fn hub_to_py(
py: Python<'_>, inner: Arc<chanlun::algorithm::hub::> py: Python<'_>, inner: Arc<chanlun::algorithm::hub::>
) -> Py<Py> { ) -> Py<Py> {
let key = Arc::as_ptr(&inner) as usize; let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = if let Some(cached) = HUB_IDENTITY
HUB_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) .read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{ {
return cached; return cached;
} }
HUB_IDENTITY.with(|c| { HUB_IDENTITY
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); .write()
}); .unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, Py { inner }).unwrap(); let obj = Py::new(py, Py { inner }).unwrap();
HUB_IDENTITY.with(|c| { HUB_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
c.write().unwrap().insert(key, obj.clone_ref(py));
});
obj obj
} }
@@ -362,13 +364,13 @@ impl 笔Py {
: Vec<Py<线Py>>, : Vec<Py<线Py>>,
: &Bound<'_, Py>, : &Bound<'_, Py>,
py: Python<'_>, py: Python<'_>,
) -> Option<线Py> { ) -> Option<Py<线Py>> {
let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> = let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter() .iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner)) .map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect(); .collect();
chanlun::algorithm::bi::::(&bi_list, &.borrow().inner) chanlun::algorithm::bi::::(&bi_list, &.borrow().inner)
.map(|inner| 线Py { inner }) .map(|inner| dashed_to_py(py, inner))
} }
#[classmethod] #[classmethod]
@@ -378,13 +380,13 @@ impl 笔Py {
: Vec<Py<线Py>>, : Vec<Py<线Py>>,
: &Bound<'_, Py>, : &Bound<'_, Py>,
py: Python<'_>, py: Python<'_>,
) -> Option<线Py> { ) -> Option<Py<线Py>> {
let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> = let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter() .iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner)) .map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect(); .collect();
chanlun::algorithm::bi::::(&bi_list, &.borrow().inner) chanlun::algorithm::bi::::(&bi_list, &.borrow().inner)
.map(|inner| 线Py { inner }) .map(|inner| dashed_to_py(py, inner))
} }
#[classmethod] #[classmethod]
@@ -396,13 +398,13 @@ impl 笔Py {
K: &Bound<'_, crate::kline_py::K线Py>, K: &Bound<'_, crate::kline_py::K线Py>,
: i64, : i64,
py: Python<'_>, py: Python<'_>,
) -> Option<线Py> { ) -> Option<Py<线Py>> {
let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> = let bi_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter() .iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner)) .map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect(); .collect();
chanlun::algorithm::bi::::K找笔(&bi_list, &K.borrow().inner, ) chanlun::algorithm::bi::::K找笔(&bi_list, &K.borrow().inner, )
.map(|inner| 线Py { inner }) .map(|inner| dashed_to_py(py, inner))
} }
#[classmethod] #[classmethod]
@@ -501,7 +503,7 @@ impl 笔Py {
) -> bool { ) -> bool {
let obs = .borrow(); let obs = .borrow();
let obs_ref = obs.obs(); let obs_ref = obs.obs();
chanlun::algorithm::bi::::(&.borrow().inner, &*obs_ref) chanlun::algorithm::bi::::(&.borrow().inner, &obs_ref)
} }
#[classmethod] #[classmethod]
@@ -510,12 +512,13 @@ impl 笔Py {
_cls: &Bound<'_, PyType>, _cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>, : &Bound<'_, 线Py>,
: &Bound<'_, Py>, : &Bound<'_, Py>,
) -> Vec<线Py> { py: Python<'_>,
) -> Vec<Py<线Py>> {
let obs = .borrow(); let obs = .borrow();
let obs_ref = obs.obs(); let obs_ref = obs.obs();
chanlun::algorithm::bi::::(&.borrow().inner, &*obs_ref) chanlun::algorithm::bi::::(&.borrow().inner, &obs_ref)
.into_iter() .into_iter()
.map(|d| 线Py { inner: Arc::new(d) }) .map(|d| dashed_to_py(py, Arc::new(d)))
.collect() .collect()
} }
@@ -529,7 +532,7 @@ impl 笔Py {
) -> Vec<Py<K线Py>> { ) -> Vec<Py<K线Py>> {
let obs = .borrow(); let obs = .borrow();
let obs_ref = obs.obs(); let obs_ref = obs.obs();
chanlun::algorithm::bi::::(&.borrow().inner, &*obs_ref) chanlun::algorithm::bi::::(&.borrow().inner, &obs_ref)
.into_iter() .into_iter()
.map(|ck| chan_kline_to_py(py, ck)) .map(|ck| chan_kline_to_py(py, ck))
.collect() .collect()
@@ -569,8 +572,8 @@ impl 线段Py {
: &Bound<'_, 线Py>, : &Bound<'_, 线Py>,
) -> PyResult<()> { ) -> PyResult<()> {
let bi_rc = Arc::clone(&.borrow().inner); let bi_rc = Arc::clone(&.borrow().inner);
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::线(&mut ref_mut.inner, bi_rc); chanlun::algorithm::segment::线::线(&ref_mut.inner, bi_rc);
Ok(()) Ok(())
} }
@@ -582,9 +585,9 @@ impl 线段Py {
: &Bound<'_, Py>, : &Bound<'_, Py>,
: u32, : u32,
) -> PyResult<()> { ) -> PyResult<()> {
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::( chanlun::algorithm::segment::线::(
&mut ref_mut.inner, &ref_mut.inner,
&Arc::clone(&.borrow().inner), &Arc::clone(&.borrow().inner),
, ,
); );
@@ -594,8 +597,8 @@ impl 线段Py {
#[classmethod] #[classmethod]
/// 武终 /// 武终
fn (_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>, : u32) -> PyResult<()> { fn (_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>, : u32) -> PyResult<()> {
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, ); chanlun::algorithm::segment::线::(&ref_mut.inner, );
Ok(()) Ok(())
} }
@@ -607,12 +610,12 @@ impl 线段Py {
: Vec<Py<线Py>>, : Vec<Py<线Py>>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<()> { ) -> PyResult<()> {
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> = let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter() .iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner)) .map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect(); .collect();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, &rc_list); chanlun::algorithm::segment::线::(&ref_mut.inner, &rc_list);
Ok(()) Ok(())
} }
@@ -624,12 +627,12 @@ impl 线段Py {
: Vec<Py<线Py>>, : Vec<Py<线Py>>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<()> { ) -> PyResult<()> {
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> = let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> =
.iter() .iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner)) .map(|d| Arc::clone(&d.bind(py).borrow().inner))
.collect(); .collect();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, &rc_list); chanlun::algorithm::segment::线::(&ref_mut.inner, &rc_list);
Ok(()) Ok(())
} }
@@ -692,7 +695,7 @@ impl 线段Py {
let seq: Vec<Option<Arc<chanlun::structure::segment_feat::线>>> = if .is_none() let seq: Vec<Option<Arc<chanlun::structure::segment_feat::线>>> = if .is_none()
{ {
vec![] vec![]
} else if let Ok(list) = .downcast::<pyo3::types::PyList>() { } else if let Ok(list) = .cast::<pyo3::types::PyList>() {
let mut result = Vec::with_capacity(list.len()); let mut result = Vec::with_capacity(list.len());
for item in list.iter() { for item in list.iter() {
if item.is_none() { if item.is_none() {
@@ -708,8 +711,8 @@ impl 线段Py {
"序列 必须是 list 或 None", "序列 必须是 list 或 None",
)); ));
}; };
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
chanlun::algorithm::segment::线::(&mut ref_mut.inner, seq, ); chanlun::algorithm::segment::线::(&ref_mut.inner, seq, );
Ok(()) Ok(())
} }
@@ -721,22 +724,26 @@ impl 线段Py {
: &Bound<'_, Py>, : &Bound<'_, Py>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<()> { ) -> PyResult<()> {
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
let config = .borrow().to_rust_config(py)?; let config = .borrow().to_rust_config(py)?;
chanlun::algorithm::segment::线::(&mut ref_mut.inner, &config); chanlun::algorithm::segment::线::(&ref_mut.inner, &config);
Ok(()) Ok(())
} }
#[classmethod] #[classmethod]
/// 查找贯穿伤 /// 查找贯穿伤
fn 穿(_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>) -> Option<线Py> { fn 穿(
_cls: &Bound<'_, PyType>, : &Bound<'_, 线Py>
) -> Option<Py<线Py>> {
let py = _cls.py();
chanlun::algorithm::segment::线::穿(&.borrow().inner) chanlun::algorithm::segment::线::穿(&.borrow().inner)
.map(|inner| 线Py { inner }) .map(|inner| dashed_to_py(py, inner))
} }
#[classmethod] #[classmethod]
#[pyo3(signature = (段, 所属中枢 = None))] #[pyo3(signature = (段, 所属中枢 = None))]
/// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分 /// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分
#[allow(clippy::type_complexity)]
fn ( fn (
_cls: &Bound<'_, PyType>, _cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>, : &Bound<'_, 线Py>,
@@ -761,19 +768,10 @@ impl 线段Py {
} else { } else {
chanlun::algorithm::segment::线::(&borrowed.inner, None) chanlun::algorithm::segment::线::(&borrowed.inner, None)
}; };
let wrap = |v: Vec<Arc<chanlun::structure::dash_line::线>>| -> PyResult<Vec<Py<线Py>>> { let wrap = |v: Vec<Arc<chanlun::structure::dash_line::线>>| -> Vec<Py<线Py>> {
let mut result = Vec::new(); v.into_iter().map(|x| dashed_to_py(py, x)).collect()
for x in v {
result.push(Py::new(py, 线Py { inner: x })?);
}
Ok(result)
}; };
Ok(( Ok((wrap(a), wrap(b), wrap(c), d.map(|x| dashed_to_py(py, x))))
wrap(a)?,
wrap(b)?,
wrap(c)?,
d.map(|x| Py::new(py, 线Py { inner: x })).transpose()?,
))
} }
#[classmethod] #[classmethod]
@@ -784,9 +782,9 @@ impl 线段Py {
: &Bound<'_, Py>, : &Bound<'_, Py>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<()> { ) -> PyResult<()> {
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
let config = .borrow().to_rust_config(py)?; let config = .borrow().to_rust_config(py)?;
chanlun::algorithm::segment::线::(&mut ref_mut.inner, &config); chanlun::algorithm::segment::线::(&ref_mut.inner, &config);
Ok(()) Ok(())
} }
@@ -798,16 +796,14 @@ impl 线段Py {
: &Bound<'_, Py>, : &Bound<'_, Py>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<(Py<PyAny>, Py<PyAny>, Py<PyAny>)> { ) -> PyResult<(Py<PyAny>, Py<PyAny>, Py<PyAny>)> {
let mut ref_mut = .borrow_mut(); let ref_mut = .borrow_mut();
let config = .borrow().to_rust_config(py)?; let config = .borrow().to_rust_config(py)?;
let (a, b, c) = chanlun::algorithm::segment::线::( let (a, b, c) =
&mut ref_mut.inner, chanlun::algorithm::segment::线::(&ref_mut.inner, &config);
&config,
);
let pk_list = |v: Vec<Arc<chanlun::algorithm::hub::>>| -> PyResult<Py<PyAny>> { let pk_list = |v: Vec<Arc<chanlun::algorithm::hub::>>| -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py); let list = pyo3::types::PyList::empty(py);
for h in v { for h in v {
list.append(Py::new(py, Py { inner: h })?)?; list.append(hub_to_py(py, h))?;
} }
Ok(list.into()) Ok(list.into())
}; };
@@ -818,7 +814,7 @@ impl 线段Py {
/// 内部方法:向线段序列添加新线段 /// 内部方法:向线段序列添加新线段
fn _添加线段( fn _添加线段(
_cls: &Bound<'_, PyType>, _cls: &Bound<'_, PyType>,
线: &Bound<'_, PyAny>, _线段序列: &Bound<'_, PyAny>,
线: &Bound<'_, 线Py>, 线: &Bound<'_, 线Py>,
: &Bound<'_, Py>, : &Bound<'_, Py>,
: String, : String,
@@ -839,12 +835,12 @@ impl 线段Py {
/// 内部方法:从线段序列弹出最后一个线段 /// 内部方法:从线段序列弹出最后一个线段
fn _弹出线段( fn _弹出线段(
_cls: &Bound<'_, PyType>, _cls: &Bound<'_, PyType>,
线: &Bound<'_, PyAny>, _线段序列: &Bound<'_, PyAny>,
线: &Bound<'_, 线Py>, 线: &Bound<'_, 线Py>,
: &Bound<'_, Py>, : &Bound<'_, Py>,
: String, : String,
py: Python<'_>, py: Python<'_>,
) -> PyResult<Option<线Py>> { ) -> PyResult<Option<Py<线Py>>> {
let config = .borrow().to_rust_config(py)?; let config = .borrow().to_rust_config(py)?;
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::线>> = vec![]; let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::线>> = vec![];
let result = chanlun::algorithm::segment::线::_弹出线段( let result = chanlun::algorithm::segment::线::_弹出线段(
@@ -853,7 +849,7 @@ impl 线段Py {
&config, &config,
, ,
); );
Ok(result.map(|inner| 线Py { inner })) Ok(result.map(|inner| dashed_to_py(py, inner)))
} }
#[classmethod] #[classmethod]
@@ -1007,7 +1003,7 @@ impl 线段Py {
线: &Bound<'_, 线Py>, 线: &Bound<'_, 线Py>,
: u32, : u32,
py: Python<'_>, py: Python<'_>,
) -> PyResult<Option<线Py>> { ) -> PyResult<Option<Py<线Py>>> {
let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::线>> = 线 let mut seg_seq: Vec<Arc<chanlun::structure::dash_line::线>> = 线
.iter() .iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner)) .map(|d| Arc::clone(&d.bind(py).borrow().inner))
@@ -1017,7 +1013,7 @@ impl 线段Py {
&Arc::clone(&线.borrow().inner), &Arc::clone(&线.borrow().inner),
, ,
); );
Ok(result.map(|inner| 线Py { inner })) Ok(result.map(|inner| dashed_to_py(py, inner)))
} }
#[classmethod] #[classmethod]
@@ -1053,7 +1049,7 @@ impl 线段Py {
let obs_ref = obs.obs(); let obs_ref = obs.obs();
chanlun::algorithm::segment::线::线( chanlun::algorithm::segment::线::线(
&.borrow().inner, &.borrow().inner,
&*obs_ref, &obs_ref,
) )
} }
@@ -1063,12 +1059,13 @@ impl 线段Py {
_cls: &Bound<'_, PyType>, _cls: &Bound<'_, PyType>,
: &Bound<'_, 线Py>, : &Bound<'_, 线Py>,
: &Bound<'_, Py>, : &Bound<'_, Py>,
) -> Vec<线Py> { py: Python<'_>,
) -> Vec<Py<线Py>> {
let obs = .borrow(); let obs = .borrow();
let obs_ref = obs.obs(); let obs_ref = obs.obs();
chanlun::algorithm::segment::线::(&.borrow().inner, &*obs_ref) chanlun::algorithm::segment::线::(&.borrow().inner, &obs_ref)
.into_iter() .into_iter()
.map(|d| 线Py { inner: Arc::new(d) }) .map(|d| dashed_to_py(py, Arc::new(d)))
.collect() .collect()
} }
@@ -1082,7 +1079,7 @@ impl 线段Py {
) -> Vec<Py<K线Py>> { ) -> Vec<Py<K线Py>> {
let obs = .borrow(); let obs = .borrow();
let obs_ref = obs.obs(); let obs_ref = obs.obs();
chanlun::algorithm::segment::线::(&.borrow().inner, &*obs_ref) chanlun::algorithm::segment::线::(&.borrow().inner, &obs_ref)
.into_iter() .into_iter()
.map(|ck| chan_kline_to_py(py, ck)) .map(|ck| chan_kline_to_py(py, ck))
.collect() .collect()
@@ -1264,7 +1261,7 @@ impl 中枢Py {
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> { fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py); let list = pyo3::types::PyList::empty(py);
for d in self.inner.() { for d in self.inner.() {
list.append(线Py { inner: d })?; list.append(dashed_to_py(py, d))?;
} }
Ok(list.into()) Ok(list.into())
} }
@@ -1381,16 +1378,15 @@ impl 中枢Py {
: &Bound<'_, 线Py>, : &Bound<'_, 线Py>,
: i64, : i64,
: &str, : &str,
) -> Self { ) -> Py<Py> {
Self { let inner = Arc::new(chanlun::algorithm::hub::::(
inner: Arc::new(chanlun::algorithm::hub::::( Arc::clone(&.borrow().inner),
Arc::clone(&.borrow().inner), Arc::clone(&.borrow().inner),
Arc::clone(&.borrow().inner), Arc::clone(&.borrow().inner),
Arc::clone(&.borrow().inner), ,
, ,
, ));
)), hub_to_py(_cls.py(), inner)
}
} }
#[classmethod] #[classmethod]
@@ -1401,7 +1397,7 @@ impl 中枢Py {
: &Bound<'_, Py>, : &Bound<'_, Py>,
: &str, : &str,
py: Python<'_>, py: Python<'_>,
) -> Option<Self> { ) -> Option<Py<Py>> {
let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> = 线 let rc_list: Vec<Arc<chanlun::structure::dash_line::线>> = 线
.iter() .iter()
.map(|d| Arc::clone(&d.bind(py).borrow().inner)) .map(|d| Arc::clone(&d.bind(py).borrow().inner))
@@ -1411,7 +1407,7 @@ impl 中枢Py {
.borrow().inner, .borrow().inner,
, ,
) )
.map(|inner| Self { inner }) .map(|inner| hub_to_py(py, inner))
} }
#[classmethod] #[classmethod]
@@ -1421,8 +1417,9 @@ impl 中枢Py {
: &Bound<'_, PyAny>, : &Bound<'_, PyAny>,
: &Bound<'_, Self>, : &Bound<'_, Self>,
) -> PyResult<()> { ) -> PyResult<()> {
let py = .py();
let inner = Arc::clone(&.borrow().inner); let inner = Arc::clone(&.borrow().inner);
let wrapper = Py::new(.py(), Self { inner })?; let wrapper = hub_to_py(py, inner);
.call_method1("append", (wrapper,))?; .call_method1("append", (wrapper,))?;
Ok(()) Ok(())
} }
@@ -1432,7 +1429,7 @@ impl 中枢Py {
fn ( fn (
_cls: &Bound<'_, PyType>, _cls: &Bound<'_, PyType>,
: &Bound<'_, PyAny>, : &Bound<'_, PyAny>,
: &Bound<'_, Self>, _待弹出中枢: &Bound<'_, Self>,
) -> PyResult<Option<Self>> { ) -> PyResult<Option<Self>> {
let result = .call_method1("pop", ())?; let result = .call_method1("pop", ())?;
if result.is_none() { if result.is_none() {
+18 -28
View File
@@ -28,14 +28,12 @@ use std::sync::RwLock;
use crate::algorithm_py::hub_to_py; use crate::algorithm_py::hub_to_py;
use crate::kline_py::bar_to_py; use crate::kline_py::bar_to_py;
use crate::structure_py::{dashed_to_py, fractal_to_py}; use crate::structure_py::{dashed_to_py, fractal_to_py, Py};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use crate::algorithm_py::Py;
use crate::config_py::Py; use crate::config_py::Py;
use crate::kline_py::{K线Py, K线Py}; use crate::kline_py::{K线Py, K线Py};
use crate::structure_py::{Py, 线Py};
use crate::types_py::Py; use crate::types_py::Py;
// ========== 基础买卖点 ========== // ========== 基础买卖点 ==========
@@ -100,10 +98,8 @@ impl 基础买卖点Py {
} }
#[getter] #[getter]
fn (&self) -> Py { fn (&self, py: Python<'_>) -> Py<Py> {
Py { fractal_to_py(py, Arc::clone(&self.inner.))
inner: Arc::clone(&self.inner.),
}
} }
#[getter] #[getter]
@@ -576,13 +572,13 @@ impl 观察者Py {
let obs = me.obs(); let obs = me.obs();
(obs..clone(), obs.) (obs..clone(), obs.)
}; };
let kline = K线Py { let kline = bar_to_py(
inner: Arc::new(chanlun::kline::bar::K线::K( slf.py(),
Arc::new(chanlun::kline::bar::K线::K(
&, , , , , , , 0, , &, , , , , , , 0, ,
)), )),
}; );
let kline_py = Py::new(slf.py(), kline)?; slf.call_method1("增加原始K线", (kline,))?;
slf.call_method1("增加原始K线", (kline_py,))?;
Ok(()) Ok(())
} }
@@ -860,11 +856,11 @@ impl K线合成器Py {
&mut self, &mut self,
K: &Bound<'_, K线Py>, K: &Bound<'_, K线Py>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<Vec<(i64, K线Py)>> { ) -> PyResult<Vec<(i64, Py<K线Py>)>> {
let results = self.inner.K线((*K.borrow().inner).clone()); let results = self.inner.K线((*K.borrow().inner).clone());
Ok(results Ok(results
.into_iter() .into_iter()
.map(|(, k)| (, K线Py { inner: Arc::new(k) })) .map(|(, k)| (, bar_to_py(py, Arc::new(k))))
.collect()) .collect())
} }
@@ -877,7 +873,8 @@ impl K线合成器Py {
: f64, : f64,
: f64, : f64,
: f64, : f64,
) -> Vec<(i64, K线Py)> { py: Python<'_>,
) -> Vec<(i64, Py<K线Py>)> {
let min_cycle = self.inner..iter().copied().min().unwrap_or(1); let min_cycle = self.inner..iter().copied().min().unwrap_or(1);
let k = chanlun::kline::bar::K线::K( let k = chanlun::kline::bar::K线::K(
&self.inner., &self.inner.,
@@ -893,22 +890,15 @@ impl K线合成器Py {
let results = self.inner.K线(k); let results = self.inner.K线(k);
results results
.into_iter() .into_iter()
.map(|(, k2)| { .map(|(, k2)| (, bar_to_py(py, Arc::new(k2))))
(
,
K线Py {
inner: Arc::new(k2),
},
)
})
.collect() .collect()
} }
/// 获取指定周期当前正在合成的K线 /// 获取指定周期当前正在合成的K线
fn K线(&self, : i64) -> Option<K线Py> { fn K线(&self, : i64, py: Python<'_>) -> Option<Py<K线Py>> {
self.inner.K线().map(|k| K线Py { self.inner
inner: Arc::new(k.clone()), .K线()
}) .map(|k| bar_to_py(py, Arc::new(k.clone())))
} }
#[getter] #[getter]
@@ -957,7 +947,7 @@ impl 立体分析器Py {
}; };
let cfg_map: Option<HashMap<i64, chanlun::config::>> = match { let cfg_map: Option<HashMap<i64, chanlun::config::>> = match {
Some(dict_any) => { Some(dict_any) => {
let dict = dict_any.downcast::<pyo3::types::PyDict>()?; let dict = dict_any.cast::<pyo3::types::PyDict>()?;
let mut map = HashMap::new(); let mut map = HashMap::new();
for (key, value) in dict.iter() { for (key, value) in dict.iter() {
let period: i64 = key.extract()?; let period: i64 = key.extract()?;
+6 -2
View File
@@ -239,7 +239,7 @@ impl 缠论配置Py {
) -> PyResult<Py<PyDict>> { ) -> PyResult<Py<PyDict>> {
let py = .py(); let py = .py();
let result = PyDict::new(py); let result = PyDict::new(py);
if let Ok(default_dict) = .downcast::<PyDict>() { if let Ok(default_dict) = .cast::<PyDict>() {
for (key, value) in default_dict.iter() { for (key, value) in default_dict.iter() {
if .contains(&key)? { if .contains(&key)? {
result.set_item(key.clone(), .get_item(&key)?)?; result.set_item(key.clone(), .get_item(&key)?)?;
@@ -252,6 +252,7 @@ impl 缠论配置Py {
} }
/// 比较当前配置与另一个配置的差异 /// 比较当前配置与另一个配置的差异
#[allow(clippy::type_complexity)]
fn ( fn (
&self, &self,
py: Python<'_>, py: Python<'_>,
@@ -290,7 +291,10 @@ impl 缠论配置Py {
Ok(Self { fields }) Ok(Self { fields })
} }
pub(crate) fn to_rust_config(&self, py: Python<'_>) -> PyResult<chanlun::config::> { pub(crate) fn to_rust_config(
&self,
_py: Python<'_>,
) -> PyResult<chanlun::config::> {
dict_to_rust_config(&self.fields) dict_to_rust_config(&self.fields)
} }
+40 -31
View File
@@ -355,17 +355,18 @@ impl 缠论K线Py {
} }
} }
thread_local! { /// 对象标识缓存:Arc 地址 → 规范 Python 对象
/// 对象标识缓存:Rc 地址 → 规范 Python 对象 /// 确保同一底层 Arc 指针在 Python 侧始终映射到同一 PyObject
/// 确保同一底层 Rc 指针在 Python 侧始终映射到同一 PyObject /// 使用全局 static 而非 thread_local!,保证跨线程对象标识和买卖点信息一致性
static BAR_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<K线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static BAR_IDENTITY: RwLock<HashMap<usize, Py<K线Py>>> = RwLock::new(HashMap::new()); static KLINE_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<K线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static KLINE_IDENTITY: RwLock<HashMap<usize, Py<K线Py>>> = RwLock::new(HashMap::new()); /// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet
static BSP_CACHE: std::sync::LazyLock<RwLock<HashMap<usize, Py<pyo3::types::PySet>>>> =
/// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static BSP_CACHE: RwLock<HashMap<usize, Py<pyo3::types::PySet>>> = RwLock::new(HashMap::new());
}
/// 将 Rc<K线> 转为 Py<K线Py>,确保同一 Rc 地址总是返回同一 Python 对象 /// 将 Rc<K线> 转为 Py<K线Py>,确保同一 Rc 地址总是返回同一 Python 对象
pub(crate) fn bar_to_py( pub(crate) fn bar_to_py(
@@ -373,15 +374,16 @@ pub(crate) fn bar_to_py(
inner: std::sync::Arc<chanlun::kline::bar::K线>, inner: std::sync::Arc<chanlun::kline::bar::K线>,
) -> Py<K线Py> { ) -> Py<K线Py> {
let key = Arc::as_ptr(&inner) as usize; let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = if let Some(cached) = BAR_IDENTITY
BAR_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) .read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{ {
return cached; return cached;
} }
let obj = Py::new(py, K线Py { inner }).unwrap(); let obj = Py::new(py, K线Py { inner }).unwrap();
BAR_IDENTITY.with(|c| { BAR_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py));
c.write().unwrap().insert(key, obj.clone_ref(py));
});
obj obj
} }
@@ -391,15 +393,19 @@ pub(crate) fn chan_kline_to_py(
inner: std::sync::Arc<chanlun::kline::chan_kline::K线>, inner: std::sync::Arc<chanlun::kline::chan_kline::K线>,
) -> Py<K线Py> { ) -> Py<K线Py> {
let key = Arc::as_ptr(&inner) as usize; let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = if let Some(cached) = KLINE_IDENTITY
KLINE_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) .read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{ {
return cached; return cached;
} }
let obj = Py::new(py, K线Py::from_rc(inner)).unwrap(); let obj = Py::new(py, K线Py::from_rc(inner)).unwrap();
KLINE_IDENTITY.with(|c| { KLINE_IDENTITY
c.write().unwrap().insert(key, obj.clone_ref(py)); .write()
}); .unwrap()
.insert(key, obj.clone_ref(py));
obj obj
} }
@@ -534,17 +540,18 @@ impl 缠论K线Py {
// 复制买卖点信息到镜像 // 复制买卖点信息到镜像
let src_key = Arc::as_ptr(&self.inner) as usize; let src_key = Arc::as_ptr(&self.inner) as usize;
let dst_key = Arc::as_ptr(&mirror.inner) as usize; let dst_key = Arc::as_ptr(&mirror.inner) as usize;
let cached_src = let cached_src = BSP_CACHE
BSP_CACHE.with(|c| c.read().unwrap().get(&src_key).map(|p| p.clone_ref(py))); .read()
.unwrap()
.get(&src_key)
.map(|p| p.clone_ref(py));
if let Some(cached_src) = cached_src { if let Some(cached_src) = cached_src {
if let Ok(new_set) = pyo3::types::PySet::empty(py) { if let Ok(new_set) = pyo3::types::PySet::empty(py) {
for item in cached_src.bind(py).iter() { for item in cached_src.bind(py).iter() {
let _ = new_set.add(item); let _ = new_set.add(item);
} }
let py_set: Py<pyo3::types::PySet> = new_set.into(); let py_set: Py<pyo3::types::PySet> = new_set.into();
BSP_CACHE.with(|c| { BSP_CACHE.write().unwrap().insert(dst_key, py_set);
c.write().unwrap().insert(dst_key, py_set);
});
} }
} }
mirror mirror
@@ -572,18 +579,20 @@ impl 缠论K线Py {
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> { fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let key = Arc::as_ptr(&self.inner) as usize; let key = Arc::as_ptr(&self.inner) as usize;
// 检查全局缓存 // 检查全局缓存
let cached = BSP_CACHE.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))); let cached = BSP_CACHE.read().unwrap().get(&key).map(|p| p.clone_ref(py));
if let Some(set) = cached { if let Some(set) = cached {
return Ok(set.into_any()); return Ok(set.into_any());
} }
// 创建新的 PySet 并存入全局缓存 // 创建新的 PySet 并存入全局缓存
let set = pyo3::types::PySet::empty(py)?; let set = pyo3::types::PySet::empty(py)?;
BSP_CACHE.with(|c| { BSP_CACHE.write().unwrap().insert(key, set.into());
c.write().unwrap().insert(key, set.into());
});
// 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读) // 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读)
Ok(BSP_CACHE Ok(BSP_CACHE
.with(|c| c.read().unwrap().get(&key).unwrap().clone_ref(py)) .read()
.unwrap()
.get(&key)
.unwrap()
.clone_ref(py)
.into_any()) .into_any())
} }
@@ -642,13 +651,13 @@ impl 缠论K线Py {
: &Bound<'_, Py>, : &Bound<'_, Py>,
py: Python<'_>, py: Python<'_>,
) -> PyResult<(Option<Py<Self>>, Option<String>)> { ) -> PyResult<(Option<Py<Self>>, Option<String>)> {
let mut ck_inner = (*K.borrow().inner).clone(); let ck_inner = (*K.borrow().inner).clone();
let config = .borrow().to_rust_config(py)?; let config = .borrow().to_rust_config(py)?;
let prev_ref = K.map(|prev| prev.borrow()); let prev_ref = K.map(|prev| prev.borrow());
let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref()); let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref());
let (result, mode) = chanlun::kline::chan_kline::K线::( let (result, mode) = chanlun::kline::chan_kline::K线::(
prev_inner, prev_inner,
&mut ck_inner, &ck_inner,
&K.borrow().inner, &K.borrow().inner,
&config, &config,
); );
+2
View File
@@ -22,6 +22,8 @@
* SOFTWARE. * SOFTWARE.
*/ */
#![allow(non_snake_case, clippy::too_many_arguments)]
use pyo3::prelude::*; use pyo3::prelude::*;
use std::sync::atomic::Ordering; use std::sync::atomic::Ordering;
+73 -63
View File
@@ -29,37 +29,45 @@ use std::sync::atomic::Ordering;
use std::sync::Arc; use std::sync::Arc;
use std::sync::RwLock; use std::sync::RwLock;
use crate::algorithm_py::{hub_to_py, Py}; use crate::algorithm_py::hub_to_py;
use crate::config_py::Py; use crate::config_py::Py;
use crate::kline_py::{K线Py, K线Py}; use crate::kline_py::{bar_to_py, K线Py, K线Py};
// ---- 身份缓存 (弱引用:通过 refcnt 检测存活,仅缓存持有则视为过期) ---- // ---- 身份缓存 (弱引用:通过 refcnt 检测存活,仅缓存持有则视为过期) ----
thread_local! { // 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性
static FRACTAL_IDENTITY: RwLock<HashMap<usize, Py<Py>>> = RwLock::new(HashMap::new()); static FRACTAL_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<Py>>>> =
static DASHED_IDENTITY: RwLock<HashMap<usize, Py<线Py>>> = RwLock::new(HashMap::new()); std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static SEGFEAT_IDENTITY: RwLock<HashMap<usize, Py<线Py>>> = RwLock::new(HashMap::new()); static DASHED_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<线Py>>>> =
static FEATFRAC_IDENTITY: RwLock<HashMap<usize, Py<Py>>> = RwLock::new(HashMap::new()); std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
} static SEGFEAT_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<线Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
static FEATFRAC_IDENTITY: std::sync::LazyLock<RwLock<HashMap<usize, Py<Py>>>> =
std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
pub(crate) fn fractal_to_py( pub(crate) fn fractal_to_py(
py: Python<'_>, py: Python<'_>,
inner: Arc<chanlun::structure::fractal_obj::>, inner: Arc<chanlun::structure::fractal_obj::>,
) -> Py<Py> { ) -> Py<Py> {
let key = Arc::as_ptr(&inner) as usize; let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = if let Some(cached) = FRACTAL_IDENTITY
FRACTAL_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) .read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{ {
return cached; return cached;
} }
// 清理 refcnt==1 的过期条目(仅缓存持有,Python 侧已无引用) // 清理 refcnt==1 的过期条目(仅缓存持有,Python 侧已无引用)
FRACTAL_IDENTITY.with(|c| { FRACTAL_IDENTITY
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); .write()
}); .unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, Py { inner }).unwrap(); let obj = Py::new(py, Py { inner }).unwrap();
FRACTAL_IDENTITY.with(|c| { FRACTAL_IDENTITY
c.write().unwrap().insert(key, obj.clone_ref(py)); .write()
}); .unwrap()
.insert(key, obj.clone_ref(py));
obj obj
} }
@@ -68,18 +76,23 @@ pub(crate) fn dashed_to_py(
inner: Arc<chanlun::structure::dash_line::线>, inner: Arc<chanlun::structure::dash_line::线>,
) -> Py<线Py> { ) -> Py<线Py> {
let key = Arc::as_ptr(&inner) as usize; let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = if let Some(cached) = DASHED_IDENTITY
DASHED_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) .read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{ {
return cached; return cached;
} }
DASHED_IDENTITY.with(|c| { DASHED_IDENTITY
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); .write()
}); .unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, 线Py { inner }).unwrap(); let obj = Py::new(py, 线Py { inner }).unwrap();
DASHED_IDENTITY.with(|c| { DASHED_IDENTITY
c.write().unwrap().insert(key, obj.clone_ref(py)); .write()
}); .unwrap()
.insert(key, obj.clone_ref(py));
obj obj
} }
@@ -88,18 +101,23 @@ pub(crate) fn segfeat_to_py(
inner: Arc<chanlun::structure::segment_feat::线>, inner: Arc<chanlun::structure::segment_feat::线>,
) -> Py<线Py> { ) -> Py<线Py> {
let key = Arc::as_ptr(&inner) as usize; let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = if let Some(cached) = SEGFEAT_IDENTITY
SEGFEAT_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) .read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{ {
return cached; return cached;
} }
SEGFEAT_IDENTITY.with(|c| { SEGFEAT_IDENTITY
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); .write()
}); .unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, 线Py { inner }).unwrap(); let obj = Py::new(py, 线Py { inner }).unwrap();
SEGFEAT_IDENTITY.with(|c| { SEGFEAT_IDENTITY
c.write().unwrap().insert(key, obj.clone_ref(py)); .write()
}); .unwrap()
.insert(key, obj.clone_ref(py));
obj obj
} }
@@ -108,18 +126,23 @@ pub(crate) fn featfrac_to_py(
inner: Arc<chanlun::structure::feat_fractal::>, inner: Arc<chanlun::structure::feat_fractal::>,
) -> Py<Py> { ) -> Py<Py> {
let key = Arc::as_ptr(&inner) as usize; let key = Arc::as_ptr(&inner) as usize;
if let Some(cached) = if let Some(cached) = FEATFRAC_IDENTITY
FEATFRAC_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) .read()
.unwrap()
.get(&key)
.map(|p| p.clone_ref(py))
{ {
return cached; return cached;
} }
FEATFRAC_IDENTITY.with(|c| { FEATFRAC_IDENTITY
c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); .write()
}); .unwrap()
.retain(|_, v| v.get_refcnt(py) > 1);
let obj = Py::new(py, Py { inner }).unwrap(); let obj = Py::new(py, Py { inner }).unwrap();
FEATFRAC_IDENTITY.with(|c| { FEATFRAC_IDENTITY
c.write().unwrap().insert(key, obj.clone_ref(py)); .write()
}); .unwrap()
.insert(key, obj.clone_ref(py));
obj obj
} }
use crate::types_py::{Py, Py, Py}; use crate::types_py::{Py, Py, Py};
@@ -469,15 +492,13 @@ impl 虚线Py {
} }
#[getter] #[getter]
fn (&self) -> Option<Self> { fn (&self, py: Python<'_>) -> Option<Py<线Py>> {
self.inner self.inner
. .
.read() .read()
.unwrap() .unwrap()
.as_ref() .as_ref()
.map(|d| Self { .map(|d| dashed_to_py(py, Arc::clone(d)))
inner: Arc::clone(d),
})
} }
// ---- 序列 getters ---- // ---- 序列 getters ----
@@ -486,12 +507,7 @@ impl 虚线Py {
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> { fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py); let list = pyo3::types::PyList::empty(py);
for d in self.inner..read().unwrap().iter() { for d in self.inner..read().unwrap().iter() {
list.append(Py::new( list.append(dashed_to_py(py, Arc::clone(d)))?;
py,
Self {
inner: Arc::clone(d),
},
)?)?;
} }
Ok(list.into()) Ok(list.into())
} }
@@ -530,12 +546,7 @@ impl 虚线Py {
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> { fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py); let list = pyo3::types::PyList::empty(py);
for d in self.inner..read().unwrap().iter() { for d in self.inner..read().unwrap().iter() {
list.append(Py::new( list.append(dashed_to_py(py, Arc::clone(d)))?;
py,
Self {
inner: Arc::clone(d),
},
)?)?;
} }
Ok(list.into()) Ok(list.into())
} }
@@ -582,11 +593,10 @@ impl 虚线Py {
let obs_ref = .borrow(); let obs_ref = .borrow();
let observer_inner = obs_ref.obs(); let observer_inner = obs_ref.obs();
let result = self.inner.K序列(&observer_inner.K线序列); let result = self.inner.K序列(&observer_inner.K线序列);
let list = pyo3::types::PyList::empty(.py()); let py = .py();
let list = pyo3::types::PyList::empty(py);
for k in &result { for k in &result {
list.append(K线Py { list.append(bar_to_py(py, Arc::clone(k)))?;
inner: Arc::clone(k),
})?;
} }
Ok(list.into()) Ok(list.into())
} }
@@ -973,7 +983,7 @@ impl 虚线Py {
) -> (bool, String) { ) -> (bool, String) {
let obs = .borrow(); let obs = .borrow();
let obs_ref = obs.obs(); let obs_ref = obs.obs();
chanlun::structure::dash_line::线::(&线.borrow().inner, &*obs_ref) chanlun::structure::dash_line::线::(&线.borrow().inner, &obs_ref)
} }
} }
@@ -1148,7 +1158,7 @@ impl 线段特征Py {
let inner = Arc::make_mut(&mut self.inner); let inner = Arc::make_mut(&mut self.inner);
inner inner
.(Arc::clone(&线.borrow().inner)) .(Arc::clone(&线.borrow().inner))
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e)) .map_err(pyo3::exceptions::PyValueError::new_err)
} }
/// :param 待删除虚线: 待删除的虚线 /// :param 待删除虚线: 待删除的虚线
@@ -1156,7 +1166,7 @@ impl 线段特征Py {
let inner = Arc::make_mut(&mut self.inner); let inner = Arc::make_mut(&mut self.inner);
inner inner
.(&Arc::clone(&线.borrow().inner)) .(&Arc::clone(&线.borrow().inner))
.map_err(|e| pyo3::exceptions::PyValueError::new_err(e)) .map_err(pyo3::exceptions::PyValueError::new_err)
} }
// ---- classmethods ---- // ---- classmethods ----
+6 -6
View File
@@ -545,7 +545,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
// 买卖点类型 class attributes (singleton instances) // 买卖点类型 class attributes (singleton instances)
let py = m.py(); let py = m.py();
let bsp_class = m.getattr("买卖点类型")?; let bsp_class = m.getattr("买卖点类型")?;
let bsp_class = bsp_class.downcast_into::<PyType>()?; let bsp_class = bsp_class.cast_into::<PyType>()?;
let variants: &[(&str, chanlun::types::)] = &[ let variants: &[(&str, chanlun::types::)] = &[
("一买", chanlun::types::::), ("一买", chanlun::types::::),
@@ -568,7 +568,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("T3B卖", chanlun::types::::T3B卖), ("T3B卖", chanlun::types::::T3B卖),
]; ];
let mut bsp_members = PyDict::new(py); let bsp_members = PyDict::new(py);
for (name, value) in variants { for (name, value) in variants {
let instance = Py::new(py, Py { inner: *value })?; let instance = Py::new(py, Py { inner: *value })?;
bsp_class.setattr(*name, instance.clone_ref(py))?; bsp_class.setattr(*name, instance.clone_ref(py))?;
@@ -577,7 +577,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
bsp_class.setattr("__members__", bsp_members)?; bsp_class.setattr("__members__", bsp_members)?;
// 相对方向 class attributes // 相对方向 class attributes
let dir_class = m.getattr("相对方向")?.downcast_into::<PyType>()?.clone(); let dir_class = m.getattr("相对方向")?.cast_into::<PyType>()?.clone();
let dir_variants: &[(&str, chanlun::types::)] = &[ let dir_variants: &[(&str, chanlun::types::)] = &[
("向上", chanlun::types::::), ("向上", chanlun::types::::),
("向下", chanlun::types::::), ("向下", chanlun::types::::),
@@ -590,7 +590,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("", chanlun::types::::), ("", chanlun::types::::),
]; ];
let mut dir_members = PyDict::new(py); let dir_members = PyDict::new(py);
for (name, value) in dir_variants { for (name, value) in dir_variants {
let instance = Py::new(py, Py { inner: *value })?; let instance = Py::new(py, Py { inner: *value })?;
dir_class.setattr(*name, instance.clone_ref(py))?; dir_class.setattr(*name, instance.clone_ref(py))?;
@@ -599,7 +599,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
dir_class.setattr("__members__", dir_members)?; dir_class.setattr("__members__", dir_members)?;
// 分型结构 class attributes // 分型结构 class attributes
let frac_class = m.getattr("分型结构")?.downcast_into::<PyType>()?.clone(); let frac_class = m.getattr("分型结构")?.cast_into::<PyType>()?.clone();
let frac_variants: &[(&str, chanlun::types::)] = &[ let frac_variants: &[(&str, chanlun::types::)] = &[
("", chanlun::types::::), ("", chanlun::types::::),
("", chanlun::types::::), ("", chanlun::types::::),
@@ -608,7 +608,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
("", chanlun::types::::), ("", chanlun::types::::),
]; ];
let mut frac_members = PyDict::new(py); let frac_members = PyDict::new(py);
for (name, value) in frac_variants { for (name, value) in frac_variants {
let instance = Py::new(py, Py { inner: *value })?; let instance = Py::new(py, Py { inner: *value })?;
frac_class.setattr(*name, instance.clone_ref(py))?; frac_class.setattr(*name, instance.clone_ref(py))?;
+33 -32
View File
@@ -566,6 +566,30 @@ impl 中枢 {
} }
} }
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let _str = self
.
.read()
.unwrap()
.iter()
.map(|d| format!("{}", d))
.collect::<Vec<_>>()
.join(", ");
write!(
f,
"{}({}, {}, 元素数量: {}, [{}], {} ===>>> {})",
self..read().unwrap(),
crate::utils::format_f64_g(self.()),
crate::utils::format_f64_g(self.()),
self..read().unwrap().len(),
_str,
self.(),
self.(),
)
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -575,13 +599,14 @@ mod tests {
use crate::types::; use crate::types::;
fn _创建K线(: i64, : f64, : f64, : f64, : f64) -> K线 { fn _创建K线(: i64, : f64, : f64, : f64, : f64) -> K线 {
let mut k = K线::default(); K线 {
k. = ; ,
k. = ; ,
k. = ; ,
k. = ; : ,
k. = ; : ,
k ..Default::default()
}
} }
fn _创建缠K( fn _创建缠K(
@@ -710,7 +735,7 @@ mod tests {
.线(Arc::clone(&1)); .线(Arc::clone(&1));
assert!(.线.read().unwrap().is_some()); assert!(.线.read().unwrap().is_some());
assert_eq!( assert_eq!(
Arc::as_ptr(&*.线.read().unwrap().as_ref().unwrap()), Arc::as_ptr(.线.read().unwrap().as_ref().unwrap()),
Arc::as_ptr(&1) Arc::as_ptr(&1)
); );
@@ -870,27 +895,3 @@ mod tests {
); );
} }
} }
impl std::fmt::Display for {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let _str = self
.
.read()
.unwrap()
.iter()
.map(|d| format!("{}", d))
.collect::<Vec<_>>()
.join(", ");
write!(
f,
"{}({}, {}, 元素数量: {}, [{}], {} ===>>> {})",
self..read().unwrap(),
crate::utils::format_f64_g(self.()),
crate::utils::format_f64_g(self.()),
self..read().unwrap().len(),
_str,
self.(),
self.(),
)
}
}
+3 -3
View File
@@ -590,7 +590,7 @@ mod tests {
let config = ::default(); let config = ::default();
let obs_ref = ::new("btcusd".into(), 300, config); let obs_ref = ::new("btcusd".into(), 300, config);
let data = std::fs::read(&test_data_path()).unwrap(); let data = std::fs::read(test_data_path()).unwrap();
let size = 48; let size = 48;
for i in 0..data.len() / size { for i in 0..data.len() / size {
@@ -720,7 +720,7 @@ mod tests {
#[test] #[test]
fn test_重复计算后结果一致() { fn test_重复计算后结果一致() {
let data = std::fs::read(&test_data_path()).unwrap(); let data = std::fs::read(test_data_path()).unwrap();
let size = 48; let size = 48;
let = || { let = || {
@@ -764,7 +764,7 @@ mod tests {
let config = ::default(); let config = ::default();
let obs_ref = ::new("btcusd".into(), 300, config); let obs_ref = ::new("btcusd".into(), 300, config);
let data = std::fs::read(&test_data_path()).unwrap(); let data = std::fs::read(test_data_path()).unwrap();
let size = 48; let size = 48;
for i in 0..data.len() / size { for i in 0..data.len() / size {
+51 -50
View File
@@ -960,6 +960,49 @@ impl 虚线 {
} }
} }
impl std::fmt::Display for 线 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if *self..read().unwrap() == "" {
write!(
f,
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
self..load(Ordering::Relaxed),
self.(),
self.,
self..read().unwrap(),
self...,
self..read().unwrap()...load(Ordering::Relaxed)
- self....load(Ordering::Relaxed)
+ 1
)
} else {
let = crate::algorithm::segment::线::(self);
let = crate::algorithm::segment::线::(self);
let _str = match {
Some(g) => format!("{}", g),
None => "None".to_string(),
};
let K线_str = match &*self.K线.read().unwrap() {
Some(k) => format!("{}", k),
None => "None".to_string(),
};
write!(
f,
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
self..read().unwrap(),
self..load(Ordering::Relaxed),
,
self.(),
self.,
self..read().unwrap(),
self..read().unwrap().len(),
_str,
K线_str,
)
}
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -969,13 +1012,14 @@ mod tests {
/// 辅助:创建一根最小化的原始K线 /// 辅助:创建一根最小化的原始K线
fn _创建K线(: i64, : f64, : f64, : f64, : f64) -> K线 { fn _创建K线(: i64, : f64, : f64, : f64, : f64) -> K线 {
let mut k = K线::default(); K线 {
k. = ; ,
k. = ; ,
k. = ; ,
k. = ; : ,
k. = ; : ,
k ..Default::default()
}
} }
/// 辅助:创建一根缠论K线 /// 辅助:创建一根缠论K线
@@ -1239,46 +1283,3 @@ mod tests {
assert_eq!(, 300); assert_eq!(, 300);
} }
} }
impl std::fmt::Display for 线 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if *self..read().unwrap() == "" {
write!(
f,
"笔({}, {}, {}, {}, 周期: {}, 数量: {})",
self..load(Ordering::Relaxed),
self.(),
self.,
self..read().unwrap(),
self...,
self..read().unwrap()...load(Ordering::Relaxed)
- self....load(Ordering::Relaxed)
+ 1
)
} else {
let = crate::algorithm::segment::线::(self);
let = crate::algorithm::segment::线::(self);
let _str = match {
Some(g) => format!("{}", g),
None => "None".to_string(),
};
let K线_str = match &*self.K线.read().unwrap() {
Some(k) => format!("{}", k),
None => "None".to_string(),
};
write!(
f,
"{}<{}, {}, {}, {}, {}, 数量: {}, 缺口: {}, {}>",
self..read().unwrap(),
self..load(Ordering::Relaxed),
,
self.(),
self.,
self..read().unwrap(),
self..read().unwrap().len(),
_str,
K线_str,
)
}
}
}
+8 -7
View File
@@ -320,13 +320,14 @@ mod tests {
use crate::types::; use crate::types::;
fn _创建K线(: i64, : f64, : f64, : f64, : f64) -> K线 { fn _创建K线(: i64, : f64, : f64, : f64, : f64) -> K线 {
let mut k = K线::default(); K线 {
k. = ; ,
k. = ; ,
k. = ; ,
k. = ; : ,
k. = ; : ,
k ..Default::default()
}
} }
fn _创建缠K( fn _创建缠K(
+5 -2
View File
@@ -770,7 +770,7 @@ class 高级策略基类(bt.Strategy):
def 日志(self, 文本, 时间=None): def 日志(self, 文本, 时间=None):
时间 = 时间 or self.datas[0].datetime.datetime(0) 时间 = 时间 or self.datas[0].datetime.datetime(0)
print(f"{时间} {文本}") print(f"{时间} {self.p.观察员.__class__.__name__}: {文本}")
def 计算目标数量(self, 价格): def 计算目标数量(self, 价格):
"""根据资金类型和仓位比例计算目标数量""" """根据资金类型和仓位比例计算目标数量"""
@@ -971,6 +971,7 @@ class 回测(高级策略基类):
return None return None
def next(self): def next(self):
# self.日志(f"{self.p.观察员.__class__.__name__} called next ")
# 1. 更新移动止损(基类方法) # 1. 更新移动止损(基类方法)
if self.position: if self.position:
self.更新止损订单(self.position.size > 0, self.data.close[0]) self.更新止损订单(self.position.size > 0, self.data.close[0])
@@ -1000,6 +1001,7 @@ class 回测(高级策略基类):
def 检查买信号(self): def 检查买信号(self):
if self.p.观察员.笔序列: if self.p.观察员.笔序列:
k线 = self.p.观察员.缠论K线序列[-1] k线 = self.p.观察员.缠论K线序列[-1]
self.日志(f"检查买信号 当前笔 {self.p.观察员.笔序列[-1]}")
if k线.买卖点信息: if k线.买卖点信息:
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息) print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
= True if k线.买卖点信息 and "" in next(iter(k线.买卖点信息)) else False = True if k线.买卖点信息 and "" in next(iter(k线.买卖点信息)) else False
@@ -1022,6 +1024,7 @@ class 回测(高级策略基类):
def 检查卖信号(self): def 检查卖信号(self):
if self.p.观察员.笔序列: if self.p.观察员.笔序列:
self.日志(f"检查卖信号 当前笔 {self.p.观察员.笔序列[-1]}")
k线 = self.p.观察员.缠论K线序列[-1] k线 = self.p.观察员.缠论K线序列[-1]
if k线.买卖点信息: if k线.买卖点信息:
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息) print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)
@@ -1045,7 +1048,7 @@ class 回测(高级策略基类):
def log(self, 文本, dt=None): def log(self, 文本, dt=None):
dt = dt or bt.num2date(self.data.datetime[0]) dt = dt or bt.num2date(self.data.datetime[0])
print(f"[{dt.strftime('%Y-%m-%d %H:%M')}] {self.p.符号} | {文本}") print(f"[{dt.strftime('%Y-%m-%d %H:%M')}] {self.p.观察员.__class__.__name__}: {self.p.符号} | {文本}")
# ==================== 回测运行入口 ==================== # ==================== 回测运行入口 ====================