From b7c4e604208946d2b4caa9ae7f41729d5c107ffb Mon Sep 17 00:00:00 2001 From: YuWuKunCheng Date: Sat, 30 May 2026 21:24:46 +0800 Subject: [PATCH] =?UTF-8?q?1.=20thread=5Flocal!=20=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E8=B7=A8=E7=BA=BF=E7=A8=8B=E7=BC=93=E5=AD=98=E4=B8=8D=E5=8F=AF?= =?UTF-8?q?=E8=A7=81=EF=BC=88=E4=B8=BB=E8=A6=81=E9=97=AE=E9=A2=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kline_py.rs 中 BSP_CACHE、KLINE_IDENTITY、BAR_IDENTITY 使用了 thread_local!。主线程调用 识别买卖点() → 买卖点信息.add() 写入的是主线程的 PySet,backtrader 策略线程读取的是自己线程独立的空 PySet。 修复:将三个缓存从 thread_local! 改为全局 static + std::sync::LazyLock>> 影响分析 这些身份缓存的影响与 KLINE_IDENTITY/BAR_IDENTITY 不同——它们只影响 Python 对象身份(is 比较),不直接影响数据内容(因为 Rust 数据通过 Arc 共享,读写都是同一份)。 具体后果: - 不同线程访问同一个 Rust Arc 会得到不同的 Python wrapper 对象 - a is b 跨线程比较返回 False,哪怕它们包装同一个底层 Rust 对象 - 每个线程维护一份独立缓存,内存浪费(不过 wrapper 很小) --- chanlun-py/src/algorithm_py.rs | 167 +++++++++++++------------- chanlun-py/src/business_py.rs | 46 +++---- chanlun-py/src/config_py.rs | 8 +- chanlun-py/src/kline_py.rs | 71 ++++++----- chanlun-py/src/lib.rs | 2 + chanlun-py/src/structure_py.rs | 136 +++++++++++---------- chanlun-py/src/types_py.rs | 12 +- chanlun/src/algorithm/hub.rs | 65 +++++----- chanlun/src/business/observer.rs | 6 +- chanlun/src/structure/dash_line.rs | 101 ++++++++-------- chanlun/src/structure/segment_feat.rs | 15 +-- strategies.py | 7 +- 12 files changed, 327 insertions(+), 309 deletions(-) diff --git a/chanlun-py/src/algorithm_py.rs b/chanlun-py/src/algorithm_py.rs index bcaba57..47f8cb6 100644 --- a/chanlun-py/src/algorithm_py.rs +++ b/chanlun-py/src/algorithm_py.rs @@ -31,26 +31,28 @@ use std::sync::atomic::Ordering; use std::sync::Arc; use std::sync::RwLock; -thread_local! { - static HUB_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); -} +// 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性 +static HUB_IDENTITY: std::sync::LazyLock>>> = + std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); pub(crate) fn hub_to_py( py: Python<'_>, inner: Arc ) -> Py<中枢Py> { let key = Arc::as_ptr(&inner) as usize; - if let Some(cached) = - HUB_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) + if let Some(cached) = HUB_IDENTITY + .read() + .unwrap() + .get(&key) + .map(|p| p.clone_ref(py)) { return cached; } - HUB_IDENTITY.with(|c| { - c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); - }); + HUB_IDENTITY + .write() + .unwrap() + .retain(|_, v| v.get_refcnt(py) > 1); let obj = Py::new(py, 中枢Py { inner }).unwrap(); - HUB_IDENTITY.with(|c| { - c.write().unwrap().insert(key, obj.clone_ref(py)); - }); + HUB_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py)); obj } @@ -362,13 +364,13 @@ impl 笔Py { 笔序列: Vec>, 文: &Bound<'_, 分型Py>, py: Python<'_>, - ) -> Option<虚线Py> { + ) -> Option> { let bi_list: Vec> = 笔序列 .iter() .map(|d| Arc::clone(&d.bind(py).borrow().inner)) .collect(); chanlun::algorithm::bi::笔::以文会友(&bi_list, &文.borrow().inner) - .map(|inner| 虚线Py { inner }) + .map(|inner| dashed_to_py(py, inner)) } #[classmethod] @@ -378,13 +380,13 @@ impl 笔Py { 笔序列: Vec>, 武: &Bound<'_, 分型Py>, py: Python<'_>, - ) -> Option<虚线Py> { + ) -> Option> { let bi_list: Vec> = 笔序列 .iter() .map(|d| Arc::clone(&d.bind(py).borrow().inner)) .collect(); chanlun::algorithm::bi::笔::以武会友(&bi_list, &武.borrow().inner) - .map(|inner| 虚线Py { inner }) + .map(|inner| dashed_to_py(py, inner)) } #[classmethod] @@ -396,13 +398,13 @@ impl 笔Py { 缠K: &Bound<'_, crate::kline_py::缠论K线Py>, 偏移: i64, py: Python<'_>, - ) -> Option<虚线Py> { + ) -> Option> { let bi_list: Vec> = 笔序列 .iter() .map(|d| Arc::clone(&d.bind(py).borrow().inner)) .collect(); chanlun::algorithm::bi::笔::根据缠K找笔(&bi_list, &缠K.borrow().inner, 偏移) - .map(|inner| 虚线Py { inner }) + .map(|inner| dashed_to_py(py, inner)) } #[classmethod] @@ -501,7 +503,7 @@ impl 笔Py { ) -> bool { let obs = 观察员.borrow(); let obs_ref = obs.obs(); - chanlun::algorithm::bi::笔::自检(&筆.borrow().inner, &*obs_ref) + chanlun::algorithm::bi::笔::自检(&筆.borrow().inner, &obs_ref) } #[classmethod] @@ -510,12 +512,13 @@ impl 笔Py { _cls: &Bound<'_, PyType>, 筆: &Bound<'_, 虚线Py>, 观察员: &Bound<'_, 观察者Py>, - ) -> Vec<虚线Py> { + py: Python<'_>, + ) -> Vec> { let obs = 观察员.borrow(); let obs_ref = obs.obs(); - chanlun::algorithm::bi::笔::获取所有停顿位置(&筆.borrow().inner, &*obs_ref) + chanlun::algorithm::bi::笔::获取所有停顿位置(&筆.borrow().inner, &obs_ref) .into_iter() - .map(|d| 虚线Py { inner: Arc::new(d) }) + .map(|d| dashed_to_py(py, Arc::new(d))) .collect() } @@ -529,7 +532,7 @@ impl 笔Py { ) -> Vec> { let obs = 观察员.borrow(); let obs_ref = obs.obs(); - chanlun::algorithm::bi::笔::是否背驰过(&当前筆.borrow().inner, &*obs_ref) + chanlun::algorithm::bi::笔::是否背驰过(&当前筆.borrow().inner, &obs_ref) .into_iter() .map(|ck| chan_kline_to_py(py, ck)) .collect() @@ -569,8 +572,8 @@ impl 线段Py { 筆: &Bound<'_, 虚线Py>, ) -> PyResult<()> { let bi_rc = Arc::clone(&筆.borrow().inner); - let mut ref_mut = 段.borrow_mut(); - chanlun::algorithm::segment::线段::添加虚线(&mut ref_mut.inner, bi_rc); + let ref_mut = 段.borrow_mut(); + chanlun::algorithm::segment::线段::添加虚线(&ref_mut.inner, bi_rc); Ok(()) } @@ -582,9 +585,9 @@ impl 线段Py { 武: &Bound<'_, 分型Py>, 行号: u32, ) -> PyResult<()> { - let mut ref_mut = 段.borrow_mut(); + let ref_mut = 段.borrow_mut(); chanlun::algorithm::segment::线段::武斗( - &mut ref_mut.inner, + &ref_mut.inner, &Arc::clone(&武.borrow().inner), 行号, ); @@ -594,8 +597,8 @@ impl 线段Py { #[classmethod] /// 武终 fn 武终(_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>, 行号: u32) -> PyResult<()> { - let mut ref_mut = 段.borrow_mut(); - chanlun::algorithm::segment::线段::武终(&mut ref_mut.inner, 行号); + let ref_mut = 段.borrow_mut(); + chanlun::algorithm::segment::线段::武终(&ref_mut.inner, 行号); Ok(()) } @@ -607,12 +610,12 @@ impl 线段Py { 序列: Vec>, py: Python<'_>, ) -> PyResult<()> { - let mut ref_mut = 段.borrow_mut(); + let ref_mut = 段.borrow_mut(); let rc_list: Vec> = 序列 .iter() .map(|d| Arc::clone(&d.bind(py).borrow().inner)) .collect(); - chanlun::algorithm::segment::线段::验证序列(&mut ref_mut.inner, &rc_list); + chanlun::algorithm::segment::线段::验证序列(&ref_mut.inner, &rc_list); Ok(()) } @@ -624,12 +627,12 @@ impl 线段Py { 序列: Vec>, py: Python<'_>, ) -> PyResult<()> { - let mut ref_mut = 段.borrow_mut(); + let ref_mut = 段.borrow_mut(); let rc_list: Vec> = 序列 .iter() .map(|d| Arc::clone(&d.bind(py).borrow().inner)) .collect(); - chanlun::algorithm::segment::线段::序列重置(&mut ref_mut.inner, &rc_list); + chanlun::algorithm::segment::线段::序列重置(&ref_mut.inner, &rc_list); Ok(()) } @@ -692,7 +695,7 @@ impl 线段Py { let seq: Vec>> = if 序列.is_none() { vec![] - } else if let Ok(list) = 序列.downcast::() { + } else if let Ok(list) = 序列.cast::() { let mut result = Vec::with_capacity(list.len()); for item in list.iter() { if item.is_none() { @@ -708,8 +711,8 @@ impl 线段Py { "序列 必须是 list 或 None", )); }; - let mut ref_mut = 段.borrow_mut(); - chanlun::algorithm::segment::线段::设置特征序列(&mut ref_mut.inner, seq, 行号); + let ref_mut = 段.borrow_mut(); + chanlun::algorithm::segment::线段::设置特征序列(&ref_mut.inner, seq, 行号); Ok(()) } @@ -721,22 +724,26 @@ impl 线段Py { 配置: &Bound<'_, 缠论配置Py>, py: Python<'_>, ) -> PyResult<()> { - let mut ref_mut = 段.borrow_mut(); + let ref_mut = 段.borrow_mut(); let config = 配置.borrow().to_rust_config(py)?; - chanlun::algorithm::segment::线段::刷新特征序列(&mut ref_mut.inner, &config); + chanlun::algorithm::segment::线段::刷新特征序列(&ref_mut.inner, &config); Ok(()) } #[classmethod] /// 查找贯穿伤 - fn 查找贯穿伤(_cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>) -> Option<虚线Py> { + fn 查找贯穿伤( + _cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py> + ) -> Option> { + let py = _cls.py(); chanlun::algorithm::segment::线段::查找贯穿伤(&段.borrow().inner) - .map(|inner| 虚线Py { inner }) + .map(|inner| dashed_to_py(py, inner)) } #[classmethod] #[pyo3(signature = (段, 所属中枢 = None))] /// 将线段基础序列分割为前/后/第三买卖/贯穿伤四部分 + #[allow(clippy::type_complexity)] fn 分割序列( _cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>, @@ -761,19 +768,10 @@ impl 线段Py { } else { chanlun::algorithm::segment::线段::分割序列(&borrowed.inner, None) }; - let wrap = |v: Vec>| -> PyResult>> { - let mut result = Vec::new(); - for x in v { - result.push(Py::new(py, 虚线Py { inner: x })?); - } - Ok(result) + let wrap = |v: Vec>| -> Vec> { + v.into_iter().map(|x| dashed_to_py(py, x)).collect() }; - Ok(( - wrap(a)?, - wrap(b)?, - wrap(c)?, - d.map(|x| Py::new(py, 虚线Py { inner: x })).transpose()?, - )) + Ok((wrap(a), wrap(b), wrap(c), d.map(|x| dashed_to_py(py, x)))) } #[classmethod] @@ -784,9 +782,9 @@ impl 线段Py { 配置: &Bound<'_, 缠论配置Py>, py: Python<'_>, ) -> PyResult<()> { - let mut ref_mut = 段.borrow_mut(); + let ref_mut = 段.borrow_mut(); let config = 配置.borrow().to_rust_config(py)?; - chanlun::algorithm::segment::线段::刷新(&mut ref_mut.inner, &config); + chanlun::algorithm::segment::线段::刷新(&ref_mut.inner, &config); Ok(()) } @@ -798,16 +796,14 @@ impl 线段Py { 配置: &Bound<'_, 缠论配置Py>, py: Python<'_>, ) -> PyResult<(Py, Py, Py)> { - let mut ref_mut = 段.borrow_mut(); + let ref_mut = 段.borrow_mut(); let config = 配置.borrow().to_rust_config(py)?; - let (a, b, c) = chanlun::algorithm::segment::线段::获取内部中枢序列( - &mut ref_mut.inner, - &config, - ); + let (a, b, c) = + chanlun::algorithm::segment::线段::获取内部中枢序列(&ref_mut.inner, &config); let pk_list = |v: Vec>| -> PyResult> { let list = pyo3::types::PyList::empty(py); for h in v { - list.append(Py::new(py, 中枢Py { inner: h })?)?; + list.append(hub_to_py(py, h))?; } Ok(list.into()) }; @@ -818,7 +814,7 @@ impl 线段Py { /// 内部方法:向线段序列添加新线段 fn _添加线段( _cls: &Bound<'_, PyType>, - 线段序列: &Bound<'_, PyAny>, + _线段序列: &Bound<'_, PyAny>, 待添加线段: &Bound<'_, 虚线Py>, 配置: &Bound<'_, 缠论配置Py>, 行号: String, @@ -839,12 +835,12 @@ impl 线段Py { /// 内部方法:从线段序列弹出最后一个线段 fn _弹出线段( _cls: &Bound<'_, PyType>, - 线段序列: &Bound<'_, PyAny>, + _线段序列: &Bound<'_, PyAny>, 待弹出线段: &Bound<'_, 虚线Py>, 配置: &Bound<'_, 缠论配置Py>, 行号: String, py: Python<'_>, - ) -> PyResult> { + ) -> PyResult>> { let config = 配置.borrow().to_rust_config(py)?; let mut seg_seq: Vec> = vec![]; let result = chanlun::algorithm::segment::线段::_弹出线段( @@ -853,7 +849,7 @@ impl 线段Py { &config, 行号, ); - Ok(result.map(|inner| 虚线Py { inner })) + Ok(result.map(|inner| dashed_to_py(py, inner))) } #[classmethod] @@ -1007,7 +1003,7 @@ impl 线段Py { 待弹出线段: &Bound<'_, 虚线Py>, 行号: u32, py: Python<'_>, - ) -> PyResult> { + ) -> PyResult>> { let mut seg_seq: Vec> = 线段序列 .iter() .map(|d| Arc::clone(&d.bind(py).borrow().inner)) @@ -1017,7 +1013,7 @@ impl 线段Py { &Arc::clone(&待弹出线段.borrow().inner), 行号, ); - Ok(result.map(|inner| 虚线Py { inner })) + Ok(result.map(|inner| dashed_to_py(py, inner))) } #[classmethod] @@ -1053,7 +1049,7 @@ impl 线段Py { let obs_ref = obs.obs(); chanlun::algorithm::segment::线段::判断线段内部是否背驰( &当前段.borrow().inner, - &*obs_ref, + &obs_ref, ) } @@ -1063,12 +1059,13 @@ impl 线段Py { _cls: &Bound<'_, PyType>, 段: &Bound<'_, 虚线Py>, 观察员: &Bound<'_, 观察者Py>, - ) -> Vec<虚线Py> { + py: Python<'_>, + ) -> Vec> { let obs = 观察员.borrow(); let obs_ref = obs.obs(); - chanlun::algorithm::segment::线段::获取所有停顿位置(&段.borrow().inner, &*obs_ref) + chanlun::algorithm::segment::线段::获取所有停顿位置(&段.borrow().inner, &obs_ref) .into_iter() - .map(|d| 虚线Py { inner: Arc::new(d) }) + .map(|d| dashed_to_py(py, Arc::new(d))) .collect() } @@ -1082,7 +1079,7 @@ impl 线段Py { ) -> Vec> { let obs = 观察员.borrow(); let obs_ref = obs.obs(); - chanlun::algorithm::segment::线段::是否背驰过(&当前段.borrow().inner, &*obs_ref) + chanlun::algorithm::segment::线段::是否背驰过(&当前段.borrow().inner, &obs_ref) .into_iter() .map(|ck| chan_kline_to_py(py, ck)) .collect() @@ -1264,7 +1261,7 @@ impl 中枢Py { fn 获取序列(&self, py: Python<'_>) -> PyResult> { let list = pyo3::types::PyList::empty(py); for d in self.inner.获取序列() { - list.append(虚线Py { inner: d })?; + list.append(dashed_to_py(py, d))?; } Ok(list.into()) } @@ -1381,16 +1378,15 @@ impl 中枢Py { 右: &Bound<'_, 虚线Py>, 级别: i64, 标识: &str, - ) -> Self { - Self { - inner: Arc::new(chanlun::algorithm::hub::中枢::创建( - Arc::clone(&左.borrow().inner), - Arc::clone(&中.borrow().inner), - Arc::clone(&右.borrow().inner), - 级别, - 标识, - )), - } + ) -> Py<中枢Py> { + let inner = Arc::new(chanlun::algorithm::hub::中枢::创建( + Arc::clone(&左.borrow().inner), + Arc::clone(&中.borrow().inner), + Arc::clone(&右.borrow().inner), + 级别, + 标识, + )); + hub_to_py(_cls.py(), inner) } #[classmethod] @@ -1401,7 +1397,7 @@ impl 中枢Py { 起始方向: &Bound<'_, 相对方向Py>, 标识: &str, py: Python<'_>, - ) -> Option { + ) -> Option> { let rc_list: Vec> = 虚线序列 .iter() .map(|d| Arc::clone(&d.bind(py).borrow().inner)) @@ -1411,7 +1407,7 @@ impl 中枢Py { 起始方向.borrow().inner, 标识, ) - .map(|inner| Self { inner }) + .map(|inner| hub_to_py(py, inner)) } #[classmethod] @@ -1421,8 +1417,9 @@ impl 中枢Py { 中枢序列: &Bound<'_, PyAny>, 待添加中枢: &Bound<'_, Self>, ) -> PyResult<()> { + let py = 中枢序列.py(); let inner = Arc::clone(&待添加中枢.borrow().inner); - let wrapper = Py::new(中枢序列.py(), Self { inner })?; + let wrapper = hub_to_py(py, inner); 中枢序列.call_method1("append", (wrapper,))?; Ok(()) } @@ -1432,7 +1429,7 @@ impl 中枢Py { fn 从中枢序列尾部弹出( _cls: &Bound<'_, PyType>, 中枢序列: &Bound<'_, PyAny>, - 待弹出中枢: &Bound<'_, Self>, + _待弹出中枢: &Bound<'_, Self>, ) -> PyResult> { let result = 中枢序列.call_method1("pop", ())?; if result.is_none() { diff --git a/chanlun-py/src/business_py.rs b/chanlun-py/src/business_py.rs index d7b4012..2081f5e 100644 --- a/chanlun-py/src/business_py.rs +++ b/chanlun-py/src/business_py.rs @@ -28,14 +28,12 @@ use std::sync::RwLock; use crate::algorithm_py::hub_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::sync::Arc; -use crate::algorithm_py::中枢Py; use crate::config_py::缠论配置Py; use crate::kline_py::{缠论K线Py, K线Py}; -use crate::structure_py::{分型Py, 虚线Py}; use crate::types_py::买卖点类型Py; // ========== 基础买卖点 ========== @@ -100,10 +98,8 @@ impl 基础买卖点Py { } #[getter] - fn 买卖点分型(&self) -> 分型Py { - 分型Py { - inner: Arc::clone(&self.inner.买卖点分型), - } + fn 买卖点分型(&self, py: Python<'_>) -> Py<分型Py> { + fractal_to_py(py, Arc::clone(&self.inner.买卖点分型)) } #[getter] @@ -576,13 +572,13 @@ impl 观察者Py { let obs = me.obs(); (obs.符号.clone(), obs.周期) }; - let kline = K线Py { - inner: Arc::new(chanlun::kline::bar::K线::创建普K( + let kline = bar_to_py( + slf.py(), + Arc::new(chanlun::kline::bar::K线::创建普K( &符号, 时间戳, 开, 高, 低, 收, 量, 0, 周期, )), - }; - let kline_py = Py::new(slf.py(), kline)?; - slf.call_method1("增加原始K线", (kline_py,))?; + ); + slf.call_method1("增加原始K线", (kline,))?; Ok(()) } @@ -860,11 +856,11 @@ impl K线合成器Py { &mut self, 普K: &Bound<'_, K线Py>, py: Python<'_>, - ) -> PyResult> { + ) -> PyResult)>> { let results = self.inner.投喂K线((*普K.borrow().inner).clone()); Ok(results .into_iter() - .map(|(周期, k)| (周期, K线Py { inner: Arc::new(k) })) + .map(|(周期, k)| (周期, bar_to_py(py, Arc::new(k)))) .collect()) } @@ -877,7 +873,8 @@ impl K线合成器Py { 低: f64, 收: f64, 量: f64, - ) -> Vec<(i64, K线Py)> { + py: Python<'_>, + ) -> Vec<(i64, Py)> { let min_cycle = self.inner.周期组.iter().copied().min().unwrap_or(1); let k = chanlun::kline::bar::K线::创建普K( &self.inner.标识, @@ -893,22 +890,15 @@ impl K线合成器Py { let results = self.inner.投喂K线(k); results .into_iter() - .map(|(周期, k2)| { - ( - 周期, - K线Py { - inner: Arc::new(k2), - }, - ) - }) + .map(|(周期, k2)| (周期, bar_to_py(py, Arc::new(k2)))) .collect() } /// 获取指定周期当前正在合成的K线 - fn 获取当前K线(&self, 周期: i64) -> Option { - self.inner.获取当前K线(周期).map(|k| K线Py { - inner: Arc::new(k.clone()), - }) + fn 获取当前K线(&self, 周期: i64, py: Python<'_>) -> Option> { + self.inner + .获取当前K线(周期) + .map(|k| bar_to_py(py, Arc::new(k.clone()))) } #[getter] @@ -957,7 +947,7 @@ impl 立体分析器Py { }; let cfg_map: Option> = match 配置组 { Some(dict_any) => { - let dict = dict_any.downcast::()?; + let dict = dict_any.cast::()?; let mut map = HashMap::new(); for (key, value) in dict.iter() { let period: i64 = key.extract()?; diff --git a/chanlun-py/src/config_py.rs b/chanlun-py/src/config_py.rs index 20f7d2b..b56344c 100644 --- a/chanlun-py/src/config_py.rs +++ b/chanlun-py/src/config_py.rs @@ -239,7 +239,7 @@ impl 缠论配置Py { ) -> PyResult> { let py = 原始字典.py(); let result = PyDict::new(py); - if let Ok(default_dict) = 默认配置.downcast::() { + if let Ok(default_dict) = 默认配置.cast::() { for (key, value) in default_dict.iter() { if 原始字典.contains(&key)? { result.set_item(key.clone(), 原始字典.get_item(&key)?)?; @@ -252,6 +252,7 @@ impl 缠论配置Py { } /// 比较当前配置与另一个配置的差异 + #[allow(clippy::type_complexity)] fn 对比( &self, py: Python<'_>, @@ -290,7 +291,10 @@ impl 缠论配置Py { Ok(Self { fields }) } - pub(crate) fn to_rust_config(&self, py: Python<'_>) -> PyResult { + pub(crate) fn to_rust_config( + &self, + _py: Python<'_>, + ) -> PyResult { dict_to_rust_config(&self.fields) } diff --git a/chanlun-py/src/kline_py.rs b/chanlun-py/src/kline_py.rs index 0f8faff..146a8be 100644 --- a/chanlun-py/src/kline_py.rs +++ b/chanlun-py/src/kline_py.rs @@ -355,17 +355,18 @@ impl 缠论K线Py { } } -thread_local! { - /// 对象标识缓存:Rc 地址 → 规范 Python 对象 - /// 确保同一底层 Rc 指针在 Python 侧始终映射到同一 PyObject +/// 对象标识缓存:Arc 地址 → 规范 Python 对象 +/// 确保同一底层 Arc 指针在 Python 侧始终映射到同一 PyObject +/// 使用全局 static 而非 thread_local!,保证跨线程对象标识和买卖点信息一致性 +static BAR_IDENTITY: std::sync::LazyLock>>> = + std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); - static BAR_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); +static KLINE_IDENTITY: std::sync::LazyLock>>> = + std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); - static KLINE_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); - - /// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet - static BSP_CACHE: RwLock>> = RwLock::new(HashMap::new()); -} +/// 买卖点信息缓存 — 按 Arc 指针全局共享,确保所有 wrapper 看到同一 PySet +static BSP_CACHE: std::sync::LazyLock>>> = + std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); /// 将 Rc 转为 Py,确保同一 Rc 地址总是返回同一 Python 对象 pub(crate) fn bar_to_py( @@ -373,15 +374,16 @@ pub(crate) fn bar_to_py( inner: std::sync::Arc, ) -> Py { let key = Arc::as_ptr(&inner) as usize; - if let Some(cached) = - BAR_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) + if let Some(cached) = BAR_IDENTITY + .read() + .unwrap() + .get(&key) + .map(|p| p.clone_ref(py)) { return cached; } let obj = Py::new(py, K线Py { inner }).unwrap(); - BAR_IDENTITY.with(|c| { - c.write().unwrap().insert(key, obj.clone_ref(py)); - }); + BAR_IDENTITY.write().unwrap().insert(key, obj.clone_ref(py)); obj } @@ -391,15 +393,19 @@ pub(crate) fn chan_kline_to_py( inner: std::sync::Arc, ) -> Py<缠论K线Py> { let key = Arc::as_ptr(&inner) as usize; - if let Some(cached) = - KLINE_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) + if let Some(cached) = KLINE_IDENTITY + .read() + .unwrap() + .get(&key) + .map(|p| p.clone_ref(py)) { return cached; } let obj = Py::new(py, 缠论K线Py::from_rc(inner)).unwrap(); - KLINE_IDENTITY.with(|c| { - c.write().unwrap().insert(key, obj.clone_ref(py)); - }); + KLINE_IDENTITY + .write() + .unwrap() + .insert(key, obj.clone_ref(py)); obj } @@ -534,17 +540,18 @@ impl 缠论K线Py { // 复制买卖点信息到镜像 let src_key = Arc::as_ptr(&self.inner) as usize; let dst_key = Arc::as_ptr(&mirror.inner) as usize; - let cached_src = - BSP_CACHE.with(|c| c.read().unwrap().get(&src_key).map(|p| p.clone_ref(py))); + let cached_src = BSP_CACHE + .read() + .unwrap() + .get(&src_key) + .map(|p| p.clone_ref(py)); if let Some(cached_src) = cached_src { if let Ok(new_set) = pyo3::types::PySet::empty(py) { for item in cached_src.bind(py).iter() { let _ = new_set.add(item); } let py_set: Py = new_set.into(); - BSP_CACHE.with(|c| { - c.write().unwrap().insert(dst_key, py_set); - }); + BSP_CACHE.write().unwrap().insert(dst_key, py_set); } } mirror @@ -572,18 +579,20 @@ impl 缠论K线Py { fn 买卖点信息(&self, py: Python<'_>) -> PyResult> { 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 { return Ok(set.into_any()); } // 创建新的 PySet 并存入全局缓存 let set = pyo3::types::PySet::empty(py)?; - BSP_CACHE.with(|c| { - c.write().unwrap().insert(key, set.into()); - }); + BSP_CACHE.write().unwrap().insert(key, set.into()); // 重新读取并返回(无法从 insert 获取 Py 引用,需要重新读) Ok(BSP_CACHE - .with(|c| c.read().unwrap().get(&key).unwrap().clone_ref(py)) + .read() + .unwrap() + .get(&key) + .unwrap() + .clone_ref(py) .into_any()) } @@ -642,13 +651,13 @@ impl 缠论K线Py { 配置: &Bound<'_, 缠论配置Py>, py: Python<'_>, ) -> PyResult<(Option>, Option)> { - let mut ck_inner = (*当前缠K.borrow().inner).clone(); + let ck_inner = (*当前缠K.borrow().inner).clone(); let config = 配置.borrow().to_rust_config(py)?; let prev_ref = 之前缠K.map(|prev| prev.borrow()); let prev_inner = prev_ref.as_ref().map(|r| r.inner.as_ref()); let (result, mode) = chanlun::kline::chan_kline::缠论K线::兼并( prev_inner, - &mut ck_inner, + &ck_inner, &当前普K.borrow().inner, &config, ); diff --git a/chanlun-py/src/lib.rs b/chanlun-py/src/lib.rs index 89174ef..bb85a29 100644 --- a/chanlun-py/src/lib.rs +++ b/chanlun-py/src/lib.rs @@ -22,6 +22,8 @@ * SOFTWARE. */ +#![allow(non_snake_case, clippy::too_many_arguments)] + use pyo3::prelude::*; use std::sync::atomic::Ordering; diff --git a/chanlun-py/src/structure_py.rs b/chanlun-py/src/structure_py.rs index de96b52..c1346ef 100644 --- a/chanlun-py/src/structure_py.rs +++ b/chanlun-py/src/structure_py.rs @@ -29,37 +29,45 @@ use std::sync::atomic::Ordering; use std::sync::Arc; 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::kline_py::{缠论K线Py, K线Py}; +use crate::kline_py::{bar_to_py, 缠论K线Py, K线Py}; // ---- 身份缓存 (弱引用:通过 refcnt 检测存活,仅缓存持有则视为过期) ---- -thread_local! { - static FRACTAL_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); - static DASHED_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); - static SEGFEAT_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); - static FEATFRAC_IDENTITY: RwLock>> = RwLock::new(HashMap::new()); -} +// 使用全局 static 而非 thread_local!,保证跨线程对象标识一致性 +static FRACTAL_IDENTITY: std::sync::LazyLock>>> = + std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); +static DASHED_IDENTITY: std::sync::LazyLock>>> = + std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); +static SEGFEAT_IDENTITY: std::sync::LazyLock>>> = + std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); +static FEATFRAC_IDENTITY: std::sync::LazyLock>>> = + std::sync::LazyLock::new(|| RwLock::new(HashMap::new())); pub(crate) fn fractal_to_py( py: Python<'_>, inner: Arc, ) -> Py<分型Py> { let key = Arc::as_ptr(&inner) as usize; - if let Some(cached) = - FRACTAL_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) + if let Some(cached) = FRACTAL_IDENTITY + .read() + .unwrap() + .get(&key) + .map(|p| p.clone_ref(py)) { return cached; } // 清理 refcnt==1 的过期条目(仅缓存持有,Python 侧已无引用) - FRACTAL_IDENTITY.with(|c| { - c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); - }); + FRACTAL_IDENTITY + .write() + .unwrap() + .retain(|_, v| v.get_refcnt(py) > 1); let obj = Py::new(py, 分型Py { inner }).unwrap(); - FRACTAL_IDENTITY.with(|c| { - c.write().unwrap().insert(key, obj.clone_ref(py)); - }); + FRACTAL_IDENTITY + .write() + .unwrap() + .insert(key, obj.clone_ref(py)); obj } @@ -68,18 +76,23 @@ pub(crate) fn dashed_to_py( inner: Arc, ) -> Py<虚线Py> { let key = Arc::as_ptr(&inner) as usize; - if let Some(cached) = - DASHED_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) + if let Some(cached) = DASHED_IDENTITY + .read() + .unwrap() + .get(&key) + .map(|p| p.clone_ref(py)) { return cached; } - DASHED_IDENTITY.with(|c| { - c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); - }); + DASHED_IDENTITY + .write() + .unwrap() + .retain(|_, v| v.get_refcnt(py) > 1); let obj = Py::new(py, 虚线Py { inner }).unwrap(); - DASHED_IDENTITY.with(|c| { - c.write().unwrap().insert(key, obj.clone_ref(py)); - }); + DASHED_IDENTITY + .write() + .unwrap() + .insert(key, obj.clone_ref(py)); obj } @@ -88,18 +101,23 @@ pub(crate) fn segfeat_to_py( inner: Arc, ) -> Py<线段特征Py> { let key = Arc::as_ptr(&inner) as usize; - if let Some(cached) = - SEGFEAT_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) + if let Some(cached) = SEGFEAT_IDENTITY + .read() + .unwrap() + .get(&key) + .map(|p| p.clone_ref(py)) { return cached; } - SEGFEAT_IDENTITY.with(|c| { - c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); - }); + SEGFEAT_IDENTITY + .write() + .unwrap() + .retain(|_, v| v.get_refcnt(py) > 1); let obj = Py::new(py, 线段特征Py { inner }).unwrap(); - SEGFEAT_IDENTITY.with(|c| { - c.write().unwrap().insert(key, obj.clone_ref(py)); - }); + SEGFEAT_IDENTITY + .write() + .unwrap() + .insert(key, obj.clone_ref(py)); obj } @@ -108,18 +126,23 @@ pub(crate) fn featfrac_to_py( inner: Arc, ) -> Py<特征分型Py> { let key = Arc::as_ptr(&inner) as usize; - if let Some(cached) = - FEATFRAC_IDENTITY.with(|c| c.read().unwrap().get(&key).map(|p| p.clone_ref(py))) + if let Some(cached) = FEATFRAC_IDENTITY + .read() + .unwrap() + .get(&key) + .map(|p| p.clone_ref(py)) { return cached; } - FEATFRAC_IDENTITY.with(|c| { - c.write().unwrap().retain(|_, v| v.get_refcnt(py) > 1); - }); + FEATFRAC_IDENTITY + .write() + .unwrap() + .retain(|_, v| v.get_refcnt(py) > 1); let obj = Py::new(py, 特征分型Py { inner }).unwrap(); - FEATFRAC_IDENTITY.with(|c| { - c.write().unwrap().insert(key, obj.clone_ref(py)); - }); + FEATFRAC_IDENTITY + .write() + .unwrap() + .insert(key, obj.clone_ref(py)); obj } use crate::types_py::{分型结构Py, 相对方向Py, 缺口Py}; @@ -469,15 +492,13 @@ impl 虚线Py { } #[getter] - fn 前一结束位置(&self) -> Option { + fn 前一结束位置(&self, py: Python<'_>) -> Option> { self.inner .前一结束位置 .read() .unwrap() .as_ref() - .map(|d| Self { - inner: Arc::clone(d), - }) + .map(|d| dashed_to_py(py, Arc::clone(d))) } // ---- 序列 getters ---- @@ -486,12 +507,7 @@ impl 虚线Py { fn 基础序列(&self, py: Python<'_>) -> PyResult> { let list = pyo3::types::PyList::empty(py); for d in self.inner.基础序列.read().unwrap().iter() { - list.append(Py::new( - py, - Self { - inner: Arc::clone(d), - }, - )?)?; + list.append(dashed_to_py(py, Arc::clone(d)))?; } Ok(list.into()) } @@ -530,12 +546,7 @@ impl 虚线Py { fn 笔序列(&self, py: Python<'_>) -> PyResult> { let list = pyo3::types::PyList::empty(py); for d in self.inner.基础序列.read().unwrap().iter() { - list.append(Py::new( - py, - Self { - inner: Arc::clone(d), - }, - )?)?; + list.append(dashed_to_py(py, Arc::clone(d)))?; } Ok(list.into()) } @@ -582,11 +593,10 @@ impl 虚线Py { let obs_ref = 观察员.borrow(); let observer_inner = obs_ref.obs(); 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 { - list.append(K线Py { - inner: Arc::clone(k), - })?; + list.append(bar_to_py(py, Arc::clone(k)))?; } Ok(list.into()) } @@ -973,7 +983,7 @@ impl 虚线Py { ) -> (bool, String) { let obs = 观察员.borrow(); 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); inner .添加(Arc::clone(&待添加虚线.borrow().inner)) - .map_err(|e| pyo3::exceptions::PyValueError::new_err(e)) + .map_err(pyo3::exceptions::PyValueError::new_err) } /// :param 待删除虚线: 待删除的虚线 @@ -1156,7 +1166,7 @@ impl 线段特征Py { let inner = Arc::make_mut(&mut self.inner); inner .删除(&Arc::clone(&待删除虚线.borrow().inner)) - .map_err(|e| pyo3::exceptions::PyValueError::new_err(e)) + .map_err(pyo3::exceptions::PyValueError::new_err) } // ---- classmethods ---- diff --git a/chanlun-py/src/types_py.rs b/chanlun-py/src/types_py.rs index 550c6aa..ffb2cbc 100644 --- a/chanlun-py/src/types_py.rs +++ b/chanlun-py/src/types_py.rs @@ -545,7 +545,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { // 买卖点类型 class attributes (singleton instances) let py = m.py(); let bsp_class = m.getattr("买卖点类型")?; - let bsp_class = bsp_class.downcast_into::()?; + let bsp_class = bsp_class.cast_into::()?; let variants: &[(&str, chanlun::types::买卖点类型)] = &[ ("一买", chanlun::types::买卖点类型::一买), @@ -568,7 +568,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { ("T3B卖", chanlun::types::买卖点类型::T3B卖), ]; - let mut bsp_members = PyDict::new(py); + let bsp_members = PyDict::new(py); for (name, value) in variants { let instance = Py::new(py, 买卖点类型Py { inner: *value })?; 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)?; // 相对方向 class attributes - let dir_class = m.getattr("相对方向")?.downcast_into::()?.clone(); + let dir_class = m.getattr("相对方向")?.cast_into::()?.clone(); let dir_variants: &[(&str, chanlun::types::相对方向)] = &[ ("向上", chanlun::types::相对方向::向上), ("向下", chanlun::types::相对方向::向下), @@ -590,7 +590,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { ("同", chanlun::types::相对方向::同), ]; - let mut dir_members = PyDict::new(py); + let dir_members = PyDict::new(py); for (name, value) in dir_variants { let instance = Py::new(py, 相对方向Py { inner: *value })?; 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)?; // 分型结构 class attributes - let frac_class = m.getattr("分型结构")?.downcast_into::()?.clone(); + let frac_class = m.getattr("分型结构")?.cast_into::()?.clone(); let frac_variants: &[(&str, chanlun::types::分型结构)] = &[ ("上", chanlun::types::分型结构::上), ("下", chanlun::types::分型结构::下), @@ -608,7 +608,7 @@ pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> { ("散", chanlun::types::分型结构::散), ]; - let mut frac_members = PyDict::new(py); + let frac_members = PyDict::new(py); for (name, value) in frac_variants { let instance = Py::new(py, 分型结构Py { inner: *value })?; frac_class.setattr(*name, instance.clone_ref(py))?; diff --git a/chanlun/src/algorithm/hub.rs b/chanlun/src/algorithm/hub.rs index be2a8e8..c728d4d 100644 --- a/chanlun/src/algorithm/hub.rs +++ b/chanlun/src/algorithm/hub.rs @@ -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::>() + .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)] mod tests { use super::*; @@ -575,13 +599,14 @@ mod tests { use crate::types::分型结构; fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 { - let mut k = K线::default(); - k.时间戳 = 时间戳; - k.高 = 高; - k.低 = 低; - k.开盘价 = 开; - k.收盘价 = 收; - k + K线 { + 时间戳, + 高, + 低, + 开盘价: 开, + 收盘价: 收, + ..Default::default() + } } fn 辅助_创建缠K( @@ -710,7 +735,7 @@ mod tests { 中枢.设置第三买卖线(Arc::clone(&笔1)); assert!(中枢.第三买卖线.read().unwrap().is_some()); assert_eq!( - Arc::as_ptr(&*中枢.第三买卖线.read().unwrap().as_ref().unwrap()), + Arc::as_ptr(中枢.第三买卖线.read().unwrap().as_ref().unwrap()), 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::>() - .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.武(), - ) - } -} diff --git a/chanlun/src/business/observer.rs b/chanlun/src/business/observer.rs index ed818a9..5b417b8 100644 --- a/chanlun/src/business/observer.rs +++ b/chanlun/src/business/observer.rs @@ -590,7 +590,7 @@ mod tests { let config = 缠论配置::default(); 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; for i in 0..data.len() / size { @@ -720,7 +720,7 @@ mod tests { #[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 计算 = || { @@ -764,7 +764,7 @@ mod tests { let config = 缠论配置::default(); 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; for i in 0..data.len() / size { diff --git a/chanlun/src/structure/dash_line.rs b/chanlun/src/structure/dash_line.rs index 6a5839a..25b5e5d 100644 --- a/chanlun/src/structure/dash_line.rs +++ b/chanlun/src/structure/dash_line.rs @@ -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)] mod tests { use super::*; @@ -969,13 +1012,14 @@ mod tests { /// 辅助:创建一根最小化的原始K线 fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 { - let mut k = K线::default(); - k.时间戳 = 时间戳; - k.高 = 高; - k.低 = 低; - k.开盘价 = 开; - k.收盘价 = 收; - k + K线 { + 时间戳, + 高, + 低, + 开盘价: 开, + 收盘价: 收, + ..Default::default() + } } /// 辅助:创建一根缠论K线 @@ -1239,46 +1283,3 @@ mod tests { 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, - ) - } - } -} diff --git a/chanlun/src/structure/segment_feat.rs b/chanlun/src/structure/segment_feat.rs index a8359ed..3f53f71 100644 --- a/chanlun/src/structure/segment_feat.rs +++ b/chanlun/src/structure/segment_feat.rs @@ -320,13 +320,14 @@ mod tests { use crate::types::分型结构; fn 辅助_创建K线(时间戳: i64, 高: f64, 低: f64, 开: f64, 收: f64) -> K线 { - let mut k = K线::default(); - k.时间戳 = 时间戳; - k.高 = 高; - k.低 = 低; - k.开盘价 = 开; - k.收盘价 = 收; - k + K线 { + 时间戳, + 高, + 低, + 开盘价: 开, + 收盘价: 收, + ..Default::default() + } } fn 辅助_创建缠K( diff --git a/strategies.py b/strategies.py index 5d49d1f..d4e0f3b 100644 --- a/strategies.py +++ b/strategies.py @@ -770,7 +770,7 @@ class 高级策略基类(bt.Strategy): def 日志(self, 文本, 时间=None): 时间 = 时间 or self.datas[0].datetime.datetime(0) - print(f"{时间} {文本}") + print(f"{时间} {self.p.观察员.__class__.__name__}: {文本}") def 计算目标数量(self, 价格): """根据资金类型和仓位比例计算目标数量""" @@ -971,6 +971,7 @@ class 回测(高级策略基类): return None def next(self): + # self.日志(f"{self.p.观察员.__class__.__name__} called next ") # 1. 更新移动止损(基类方法) if self.position: self.更新止损订单(self.position.size > 0, self.data.close[0]) @@ -1000,6 +1001,7 @@ class 回测(高级策略基类): def 检查买信号(self): if self.p.观察员.笔序列: k线 = self.p.观察员.缠论K线序列[-1] + self.日志(f"检查买信号 当前笔 {self.p.观察员.笔序列[-1]}") if k线.买卖点信息: print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息) 首 = True if k线.买卖点信息 and "买" in next(iter(k线.买卖点信息)) else False @@ -1022,6 +1024,7 @@ class 回测(高级策略基类): def 检查卖信号(self): if self.p.观察员.笔序列: + self.日志(f"检查卖信号 当前笔 {self.p.观察员.笔序列[-1]}") k线 = self.p.观察员.缠论K线序列[-1] if k线.买卖点信息: print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息) @@ -1045,7 +1048,7 @@ class 回测(高级策略基类): def log(self, 文本, dt=None): 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.符号} | {文本}") # ==================== 回测运行入口 ====================