From da5222d9603a03df119599c872dcfe8897085b31 Mon Sep 17 00:00:00 2001 From: YuWuKunCheng Date: Wed, 10 Jun 2026 23:21:26 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20=E5=85=A8=E5=B1=80?= =?UTF-8?q?=E5=8F=98=E9=87=8F=E2=80=9C=E6=89=A9=E5=B1=95=E7=BA=BF=E6=AE=B5?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F=E2=80=9D=20=E9=BB=98=E8=AE=A4=E4=B8=BA=20Tru?= =?UTF-8?q?e=20=E7=BA=BF=E6=AE=B5=20=E6=89=A9=E5=B1=95=E5=88=86=E6=9E=90?= =?UTF-8?q?=E9=87=87=E7=94=A8=E2=80=9C=E7=AB=AF=E7=82=B9=E9=AB=98,=20?= =?UTF-8?q?=E7=AB=AF=E7=82=B9=E4=BD=8E=E2=80=9D=20=E4=BD=9C=E4=B8=BA?= =?UTF-8?q?=E9=AB=98=E4=BD=8E=E5=8F=96=E5=80=BC=E3=80=82=E4=B8=AD=E6=9E=A2?= =?UTF-8?q?=E5=88=86=E6=9E=90=E6=97=B6=E9=AB=98=E4=BD=8E=E5=8F=96=E5=80=BC?= =?UTF-8?q?=E4=B8=8D=E5=8F=98=EF=BC=8C=E6=AD=A4=E4=B8=BE=E5=9C=A8=E4=B8=AD?= =?UTF-8?q?=E6=9E=A2<=E6=89=A9=E5=B1=95**>=20=E4=B8=AD=E6=9B=B4=E8=B4=B4?= =?UTF-8?q?=E5=90=88=E5=8E=9F=E6=96=87=E5=90=8C=E7=BA=A7=E5=88=AB=E5=88=86?= =?UTF-8?q?=E7=BA=A7=E6=97=B6=E7=9A=84=E4=B8=AD=E6=9E=A2=E9=AB=98=E4=BD=8E?= =?UTF-8?q?=E5=8F=96=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- chan.py | 37 ++++++++++++---- chanlun-py/Cargo.toml | 2 +- chanlun-py/chanlun/__init__.py | 2 + chanlun-py/chanlun/__init__.pyi | 2 + chanlun-py/chanlun/chan.py | 41 +++++++++++++----- chanlun-py/src/lib.rs | 14 ++++++ chanlun/src/algorithm/segment.rs | 10 +++-- chanlun/src/config.rs | 4 +- chanlun/src/structure/dash_line.rs | 68 +++++++++++++++++++++++++++--- 9 files changed, 146 insertions(+), 34 deletions(-) diff --git a/chan.py b/chan.py index 265f5d9..4855af9 100644 --- a/chan.py +++ b/chan.py @@ -3488,6 +3488,9 @@ class 分型: 分型序列.append(当前分型) +扩展线段模式 = True # TODO 虚线高低取值 暂定,此举将符合同级别分解时正确的高低取值涉及中枢等问题 + + class 虚线: """笔/线段的通用数据结构,持有一组分型端点(文=起点分型, 武=终点分型)。 @@ -3615,15 +3618,29 @@ class 虚线: case _: raise RuntimeError("无法识别的方向", self.文.结构, self.武.结构) + @property + def 端点高(self) -> float: + if self.方向 is 相对方向.向上: + return self.武.中.高 + return self.文.中.高 + + @property + def 端点低(self) -> float: + if self.方向 is 相对方向.向下: + return self.武.中.低 + return self.文.中.低 + @property def 高(self) -> float: """虚线区间的最高价。 :return: 向上虚线取武.中.高,向下虚线取文.中.高 """ - if self.方向 is 相对方向.向上: - return self.武.中.高 - return self.文.中.高 + if 扩展线段模式 and self.模式 != "文武" and self.标识 != "笔" and "扩展" in self.标识: # 扩展线段 + 端点序列 = [筆.文 for 筆 in self.基础序列] + 端点序列.append(self.基础序列[-1].武) + return max(端点序列, key=lambda o: o.中.高).中.高 + return self.端点高 @property def 低(self) -> float: @@ -3631,9 +3648,11 @@ class 虚线: :return: 向下虚线取武.中.低,向上虚线取文.中.低 """ - if self.方向 is 相对方向.向下: - return self.武.中.低 - return self.文.中.低 + if 扩展线段模式 and self.模式 != "文武" and self.标识 != "笔" and "扩展" in self.标识: # 扩展线段 + 端点序列 = [筆.文 for 筆 in self.基础序列] + 端点序列.append(self.基础序列[-1].武) + return min(端点序列, key=lambda o: o.中.低).中.低 + return self.端点低 def 之前是(self, 之前: 虚线) -> bool: """ @@ -5626,7 +5645,7 @@ class 线段: if not 线段序列: for i in range(1, len(虚线序列) - 1): 左, 中, 右 = 虚线序列[i - 1], 虚线序列[i], 虚线序列[i + 1] - 关系 = 相对方向.分析(左.高, 左.低, 右.高, 右.低) + 关系 = 相对方向.分析(左.端点高, 左.端点低, 右.端点高, 右.端点低) if 关系 not in (相对方向.向下, 相对方向.向上, 相对方向.顺, 相对方向.逆, 相对方向.同): # FIXME 此处为首个线段 continue @@ -5646,7 +5665,7 @@ class 线段: if not 配置.扩展线段_当下分析: 左, 中, 右 = 当前线段.基础序列[:3] - if not 相对方向.分析(左.高, 左.低, 右.高, 右.低).是否缺口(): + if not 相对方向.分析(左.端点高, 左.端点低, 右.端点高, 右.端点低).是否缺口(): 当前线段.基础序列[:] = 当前线段.基础序列[:3] 线段._武终(当前线段, sys._getframe().f_lineno) else: @@ -5663,7 +5682,7 @@ class 线段: for i in range(序号 + 1, len(虚线序列) - 1): 左, 中, 右 = 虚线序列[i - 1], 虚线序列[i], 虚线序列[i + 1] - 相对关系 = 相对方向.分析(左.高, 左.低, 右.高, 右.低) + 相对关系 = 相对方向.分析(左.端点高, 左.端点低, 右.端点高, 右.端点低) if 相对关系.是否缺口(): 线段._添加虚线(当前线段, 左) 线段._添加虚线(当前线段, 中) diff --git a/chanlun-py/Cargo.toml b/chanlun-py/Cargo.toml index 13c81e8..b39ff43 100644 --- a/chanlun-py/Cargo.toml +++ b/chanlun-py/Cargo.toml @@ -12,7 +12,7 @@ crate-type = ["cdylib"] name = "chanlun" [dependencies] -chanlun = "26.6.3" # { path = "../chanlun" } +chanlun = { path = "../chanlun" } lru = "0.18" pyo3 = { version = "0.28", features = ["experimental-inspect"] } serde_json = "1" diff --git a/chanlun-py/chanlun/__init__.py b/chanlun-py/chanlun/__init__.py index 7e6ec1f..9327f60 100644 --- a/chanlun-py/chanlun/__init__.py +++ b/chanlun-py/chanlun/__init__.py @@ -32,6 +32,8 @@ __all__ = [ "布林带", "get_分型模式", "set_分型模式", + "get_扩展线段模式", + "set_扩展线段模式", "get_log_level", "set_log_level", "get_rs_log_level", diff --git a/chanlun-py/chanlun/__init__.pyi b/chanlun-py/chanlun/__init__.pyi index d2725be..d3a8f39 100644 --- a/chanlun-py/chanlun/__init__.pyi +++ b/chanlun-py/chanlun/__init__.pyi @@ -11,6 +11,8 @@ def get_log_level() -> str: ... def set_log_level(level: str) -> None: ... def get_分型模式() -> bool: ... def set_分型模式(value: bool) -> None: ... +def get_扩展线段模式() -> bool: ... +def set_扩展线段模式(value: bool) -> None: ... def K线相等(A: K线, B: K线, 浮点容差: float = 1e-9) -> Tuple[bool, str]: ... def 缠论K线相等(A: 缠论K线, B: 缠论K线, 浮点容差: float = 1e-9) -> Tuple[bool, str]: ... def 分型相等(A: 分型, B: 分型, 浮点容差: float = 1e-9) -> Tuple[bool, str]: ... diff --git a/chanlun-py/chanlun/chan.py b/chanlun-py/chanlun/chan.py index c76d2cd..62de866 100644 --- a/chanlun-py/chanlun/chan.py +++ b/chanlun-py/chanlun/chan.py @@ -3488,6 +3488,9 @@ class 分型: 分型序列.append(当前分型) +扩展线段模式 = True # TODO 虚线高低取值 暂定,此举将符合同级别分解时正确的高低取值涉及中枢等问题 + + class 虚线: """笔/线段的通用数据结构,持有一组分型端点(文=起点分型, 武=终点分型)。 @@ -3615,15 +3618,29 @@ class 虚线: case _: raise RuntimeError("无法识别的方向", self.文.结构, self.武.结构) + @property + def 端点高(self) -> float: + if self.方向 is 相对方向.向上: + return self.武.中.高 + return self.文.中.高 + + @property + def 端点低(self) -> float: + if self.方向 is 相对方向.向下: + return self.武.中.低 + return self.文.中.低 + @property def 高(self) -> float: """虚线区间的最高价。 :return: 向上虚线取武.中.高,向下虚线取文.中.高 """ - if self.方向 is 相对方向.向上: - return self.武.中.高 - return self.文.中.高 + if 扩展线段模式 and self.模式 != "文武" and self.标识 != "笔" and "扩展" in self.标识: # 扩展线段 + 端点序列 = [筆.文 for 筆 in self.基础序列] + 端点序列.append(self.基础序列[-1].武) + return max(端点序列, key=lambda o: o.中.高).中.高 + return self.端点高 @property def 低(self) -> float: @@ -3631,9 +3648,11 @@ class 虚线: :return: 向下虚线取武.中.低,向上虚线取文.中.低 """ - if self.方向 is 相对方向.向下: - return self.武.中.低 - return self.文.中.低 + if 扩展线段模式 and self.模式 != "文武" and self.标识 != "笔" and "扩展" in self.标识: # 扩展线段 + 端点序列 = [筆.文 for 筆 in self.基础序列] + 端点序列.append(self.基础序列[-1].武) + return min(端点序列, key=lambda o: o.中.低).中.低 + return self.端点低 def 之前是(self, 之前: 虚线) -> bool: """ @@ -5626,7 +5645,7 @@ class 线段: if not 线段序列: for i in range(1, len(虚线序列) - 1): 左, 中, 右 = 虚线序列[i - 1], 虚线序列[i], 虚线序列[i + 1] - 关系 = 相对方向.分析(左.高, 左.低, 右.高, 右.低) + 关系 = 相对方向.分析(左.端点高, 左.端点低, 右.端点高, 右.端点低) if 关系 not in (相对方向.向下, 相对方向.向上, 相对方向.顺, 相对方向.逆, 相对方向.同): # FIXME 此处为首个线段 continue @@ -5646,7 +5665,7 @@ class 线段: if not 配置.扩展线段_当下分析: 左, 中, 右 = 当前线段.基础序列[:3] - if not 相对方向.分析(左.高, 左.低, 右.高, 右.低).是否缺口(): + if not 相对方向.分析(左.端点高, 左.端点低, 右.端点高, 右.端点低).是否缺口(): 当前线段.基础序列[:] = 当前线段.基础序列[:3] 线段._武终(当前线段, sys._getframe().f_lineno) else: @@ -5663,7 +5682,7 @@ class 线段: for i in range(序号 + 1, len(虚线序列) - 1): 左, 中, 右 = 虚线序列[i - 1], 虚线序列[i], 虚线序列[i + 1] - 相对关系 = 相对方向.分析(左.高, 左.低, 右.高, 右.低) + 相对关系 = 相对方向.分析(左.端点高, 左.端点低, 右.端点高, 右.端点低) if 相对关系.是否缺口(): 线段._添加虚线(当前线段, 左) 线段._添加虚线(当前线段, 中) @@ -6926,6 +6945,6 @@ if __name__ == "__main__": 当前配置 = 缠论配置.不推送() 当前配置.加载文件路径 = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "tests", "btcusd-300-1761327300-1776327900.nb") with tempfile.TemporaryDirectory() as tmpdir: - # 测试_读取数据(观察者("", 0, 当前配置), 当前配置)().测试_保存数据(tmpdir) + 测试_读取数据(观察者("", 0, 当前配置), 当前配置)().测试_保存数据(tmpdir) # 测试_周期合成(当前配置)().测试_保存数据(tmpdir) - 测试_指标挂载(当前配置)().测试_保存数据(tmpdir) + # 测试_指标挂载(当前配置)().测试_保存数据(tmpdir) diff --git a/chanlun-py/src/lib.rs b/chanlun-py/src/lib.rs index 101439f..f5d9235 100644 --- a/chanlun-py/src/lib.rs +++ b/chanlun-py/src/lib.rs @@ -121,6 +121,18 @@ fn set_分型模式(value: bool) { chanlun::structure::fractal_obj::分型模式.store(value, Ordering::Relaxed); } +/// 扩展线段模式 — 控制虚线高低取值方式,默认 False +#[pyfunction] +fn get_扩展线段模式() -> bool { + chanlun::structure::dash_line::扩展线段模式.load(Ordering::Relaxed) +} + +/// 设置 扩展线段模式 +#[pyfunction] +fn set_扩展线段模式(value: bool) { + chanlun::structure::dash_line::扩展线段模式.store(value, Ordering::Relaxed); +} + /// 获取当前日志级别 ("trace" / "debug" / "info" / "warn" / "error" / "off") #[pyfunction] fn get_log_level() -> &'static str { @@ -160,6 +172,8 @@ fn _chanlun(_py: Python, m: &Bound<'_, PyModule>) -> PyResult<()> { init_tracing(); m.add_function(wrap_pyfunction!(get_分型模式, m)?)?; m.add_function(wrap_pyfunction!(set_分型模式, m)?)?; + m.add_function(wrap_pyfunction!(get_扩展线段模式, m)?)?; + m.add_function(wrap_pyfunction!(set_扩展线段模式, m)?)?; m.add_function(wrap_pyfunction!(get_log_level, m)?)?; m.add_function(wrap_pyfunction!(set_log_level, m)?)?; // 阶段 1: 枚举和基础类型 diff --git a/chanlun/src/algorithm/segment.rs b/chanlun/src/algorithm/segment.rs index b2e9bdc..fc88ce1 100644 --- a/chanlun/src/algorithm/segment.rs +++ b/chanlun/src/algorithm/segment.rs @@ -1537,7 +1537,8 @@ impl 线段 { let 左 = &虚线序列[i - 1]; let 中 = &虚线序列[i]; let 右 = &虚线序列[i + 1]; - let 关系 = 相对方向::分析(左.高(), 左.低(), 右.高(), 右.低()); + let 关系 = + 相对方向::分析(左.端点高(), 左.端点低(), 右.端点高(), 右.端点低()); if !matches!( 关系, 相对方向::向下 @@ -1579,7 +1580,7 @@ impl 线段 { let 左 = Arc::clone(&当前线段.基础序列.read().unwrap()[0]); let 右 = Arc::clone(&当前线段.基础序列.read().unwrap()[2]); - if !相对方向::分析(左.高(), 左.低(), 右.高(), 右.低()).是否缺口() + if !相对方向::分析(左.端点高(), 左.端点低(), 右.端点高(), 右.端点低()).是否缺口() { { let cur = Arc::make_mut(线段序列.last_mut().unwrap()); @@ -1634,7 +1635,8 @@ impl 线段 { let 左 = &虚线序列[i - 1]; let 中 = &虚线序列[i]; let 右 = &虚线序列[i + 1]; - let 相对关系 = 相对方向::分析(左.高(), 左.低(), 右.高(), 右.低()); + let 相对关系 = + 相对方向::分析(左.端点高(), 左.端点低(), 右.端点高(), 右.端点低()); if 相对关系.是否缺口() { let 段_rc = Arc::clone(线段序列.last().unwrap()); @@ -2071,7 +2073,7 @@ mod tests { let 文 = 辅助_创建底分型(100, 90.0); let 武 = 辅助_创建顶分型(200, 110.0); let 段 = 辅助_创建笔(文, 武); - let (前, 后, 第三买卖线, 贯穿伤) = 线段::分割序列(&段, None); + let (_前, 后, 第三买卖线, 贯穿伤) = 线段::分割序列(&段, None); // 独立笔无基础序列时,前也为空 assert!(后.is_empty()); assert!(第三买卖线.is_empty()); diff --git a/chanlun/src/config.rs b/chanlun/src/config.rs index eefe08a..c8b5c9f 100644 --- a/chanlun/src/config.rs +++ b/chanlun/src/config.rs @@ -795,7 +795,7 @@ mod tests { #[test] fn test_对比_有差异() { - let mut a = 缠论配置::default(); + let a = 缠论配置::default(); let mut b = 缠论配置::default(); b.标识 = "changed".into(); b.笔内元素数量 = 99; @@ -854,7 +854,7 @@ mod tests { #[test] fn test_对比_boolean_difference() { - let mut a = 缠论配置::default(); + let a = 缠论配置::default(); let mut b = 缠论配置::default(); b.推送K线 = false; b.图表展示 = false; diff --git a/chanlun/src/structure/dash_line.rs b/chanlun/src/structure/dash_line.rs index a2cc6da..8092ae2 100644 --- a/chanlun/src/structure/dash_line.rs +++ b/chanlun/src/structure/dash_line.rs @@ -34,6 +34,12 @@ use std::sync::atomic::{AtomicBool, AtomicI64, Ordering}; use std::sync::{Arc, LazyLock, Mutex, RwLock}; use tracing::warn; +/// 扩展线段模式 — 控制虚线高低取值方式 +/// +/// 默认 `true`:扩展线段取基础序列中所有子段端点的全局极值(与原文54课一致) +/// 设为 `true`:使用文/武分型端点 +pub static 扩展线段模式: AtomicBool = AtomicBool::new(true); + /// 虚线 — 笔和线段的通用数据结构。 /// /// 笔和线段共享此 struct,通过 `标识` 字段区分("笔"/"线段"/"扩展线段"等)。 @@ -209,17 +215,17 @@ impl 虚线 { } } - /// 虚线高 - pub fn 高(&self) -> f64 { - if self.方向() == 相对方向::向下 { - self.文.中.高.get() - } else { + /// 虚线端点高 — 始终取文/武分型端点的中.高,不扫描基础序列 + pub fn 端点高(&self) -> f64 { + if self.方向() == 相对方向::向上 { self.武.read().unwrap().中.高.get() + } else { + self.文.中.高.get() } } - /// 虚线低 - pub fn 低(&self) -> f64 { + /// 虚线端点低 — 始终取文/武分型端点的中.低,不扫描基础序列 + pub fn 端点低(&self) -> f64 { if self.方向() == 相对方向::向下 { self.武.read().unwrap().中.低.get() } else { @@ -227,6 +233,54 @@ impl 虚线 { } } + /// 虚线高 + /// 默认同端点高;扩展线段模式开启时,取基础序列中所有子段文分型 + 最后子段武分型的最高 + pub fn 高(&self) -> f64 { + let 模式 = self.模式.read().unwrap(); + let 标识 = self.标识.read().unwrap(); + if 扩展线段模式.load(Ordering::Relaxed) + && *模式 != "文武" + && *标识 != "笔" + && 标识.contains("扩展") + { + let 基础序列 = self.基础序列.read().unwrap(); + if !基础序列.is_empty() { + return 基础序列 + .iter() + .map(|s| s.文.中.高.get()) + .chain(std::iter::once( + 基础序列.last().unwrap().武.read().unwrap().中.高.get(), + )) + .fold(f64::NEG_INFINITY, f64::max); + } + } + self.端点高() + } + + /// 虚线低 + /// 默认同端点低;扩展线段模式开启时,取基础序列中所有子段文分型 + 最后子段武分型的最低 + pub fn 低(&self) -> f64 { + let 模式 = self.模式.read().unwrap(); + let 标识 = self.标识.read().unwrap(); + if 扩展线段模式.load(Ordering::Relaxed) + && *模式 != "文武" + && *标识 != "笔" + && 标识.contains("扩展") + { + let 基础序列 = self.基础序列.read().unwrap(); + if !基础序列.is_empty() { + return 基础序列 + .iter() + .map(|s| s.文.中.低.get()) + .chain(std::iter::once( + 基础序列.last().unwrap().武.read().unwrap().中.低.get(), + )) + .fold(f64::INFINITY, f64::min); + } + } + self.端点低() + } + /// 判断两个虚线是否首尾相连 pub fn 之前是(&self, 之前: &虚线) -> bool { if *self.标识.read().unwrap() != *之前.标识.read().unwrap() {