观察者 线段分析/扩展分析 支持无限递归,保留之前序列为@property 形式

This commit is contained in:
YuWuKunCheng
2026-06-06 11:04:11 +08:00
parent e33b1f0744
commit dd40f95742
13 changed files with 838 additions and 1211 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "chanlun-py"
version = "26.6.17"
version = "26.6.34"
edition = "2024"
description = "缠论技术分析库 — Rust 高性能 Python 绑定"
authors = ["YuYuKunKun"]
-2
View File
@@ -6,8 +6,6 @@ from datetime import datetime
# ========== Module-level functions ==========
def get_rs_log_level() -> str: ...
def set_rs_log_level(level: str) -> None: ...
def get_log_level() -> str: ...
def set_log_level(level: str) -> None: ...
def get_分型模式() -> bool: ...
+1 -2
View File
@@ -14,7 +14,6 @@ __all__ = [
"指标容器",
"指标计算器",
"均线工具",
"测试_读取数据",
"相对强弱指数",
"相对方向",
"立体分析器",
@@ -49,7 +48,7 @@ __all__ = [
from ._chanlun import *
from ._chanlun import set_log_level as _rs_set_log_level, get_log_level as _rs_get_log_level
from . import chan
from .chan import 测试_读取数据, 转化为时间戳, 转化为时间戳_数字, set_log_level, get_log_level
from .chan import set_log_level, get_log_level
def set_rs_log_level(level: str):
-2
View File
@@ -11,8 +11,6 @@ def get_log_level() -> str: ...
def set_log_level(level: str) -> None: ...
def get_分型模式() -> bool: ...
def set_分型模式(value: bool) -> None: ...
def 转化为时间戳(ts: Any) -> int: ...
def 转化为时间戳_数字(ts: Any) -> int: ...
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]: ...
+180 -133
View File
@@ -2440,7 +2440,7 @@ class K线:
)
@classmethod
def 创建普K(cls, 标识: str, 时间戳: datetime, 开盘价: float, 最高价: float, 最低价: float, 收盘价: float, 成交量: float, 序号: int, 周期: int) -> K线:
def 创建普K(cls, 标识: str, 时间戳: datetime | int, 开盘价: float, 最高价: float, 最低价: float, 收盘价: float, 成交量: float, 序号: int, 周期: int) -> K线:
"""快捷构造普通K线
:param 标识: K线标识符
@@ -2458,7 +2458,7 @@ class K线:
标识=标识,
序号=序号,
周期=周期,
时间戳=时间戳,
时间戳=时间戳 if isinstance(时间戳, datetime) else 转化为时间戳(时间戳), # 注意此处只为兼容Rust绑定
开盘价=开盘价,
最高价=最高价,
最低价=最低价,
@@ -3520,7 +3520,6 @@ class 虚线:
return 结果
@classmethod
@lru_cache(maxsize=128)
def 买卖意义(cls, 实线: 虚线, 观察员: 观察者) -> Tuple[bool, str]:
"""
静止是相对的而运动是绝对的
@@ -4775,7 +4774,7 @@ class 线段:
@classmethod
def _缺口突破(cls, 线段序列: List[虚线], 配置: 缠论配置, 层级: int) -> bool:
"""内部方法:处理缺口突破修正, 正常修正
除此之外的修正皆为短路修正此修正为正常处理
:param 线段序列: 线段列表
:param 配置: 缠论配置
:param 层级: 递归深度
@@ -4870,6 +4869,7 @@ class 线段:
for 临时虚线 in 基础序列[基础序列.index(当前线段.基础序列[-1]) + 1 :]:
线段._添加虚线(当前线段, 临时虚线)
线段._刷新(当前线段, 配置)
当前线段.短路修正 = True
if 当前线段.特征序列[2] is not None:
= 虚线.创建线段([, , ])
@@ -4991,6 +4991,12 @@ class 线段:
return None
# raise RuntimeError("线段分析 层级过深")
# if len(笔序列) < 3:return None
try:
笔序列[2]
except IndexError:
return None
线段递归分析 = 线段.分析
# -------------------- 1. 初始化第一个线段 --------------------
@@ -5200,7 +5206,7 @@ class 线段:
if not 配置.扩展线段_当下分析:
, , = 当前线段.基础序列[:3]
if not 相对方向.分析(., ., ., .).是否缺口():
当前线段.基础序列[:] = 当前线段.序列[:3]
当前线段.基础序列[:] = 当前线段.基础序列[:3]
线段._武终(当前线段, sys._getframe().f_lineno)
else:
线段._弹出扩展线段(线段序列, 当前线段, sys._getframe().f_lineno)
@@ -5231,7 +5237,6 @@ class 线段:
return 线段递归扩展分析(虚线序列, 线段序列, 配置)
@classmethod
@lru_cache(maxsize=128)
def 判断线段内部是否背驰(cls, 当前段: 虚线, 观察员: 观察者) -> bool:
"""判断线段内部是否发生背驰(基于内部中枢和MACD)
@@ -5535,11 +5540,10 @@ class 中枢:
线段.扩展分析(self.基础序列, 扩展线段, 配置)
中枢.分析(扩展线段, 扩展中枢, False, f"{self.标识}_扩展中枢_")
def _校验合法性(self, 序列: Sequence[虚线], 中枢序列: List[中枢]) -> bool:
def _校验合法性(self, 序列: Sequence[虚线]) -> bool:
"""校验当前中枢在给定序列中是否仍然合法
:param 序列: 基础虚线序列
:param 中枢序列: 中枢列表
:return: 合法为True不合法会原地裁剪基础序列
"""
有效序列 = self.基础序列[:]
@@ -5754,7 +5758,7 @@ class 中枢:
当前中枢 = 中枢序列[-1]
if not 当前中枢._校验合法性(虚线序列, 中枢序列):
if not 当前中枢._校验合法性(虚线序列):
中枢._从中枢序列尾部弹出(中枢序列, 当前中枢)
return 中枢递归分析(虚线序列, 中枢序列, 跳过首部, 标识, 层级 + 1)
@@ -5829,8 +5833,41 @@ class 观察者:
self.配置: 缠论配置 = 配置
self.__终止时间戳: Optional[datetime] = 转化为时间戳(self.配置.手动终止) if self.配置.手动终止 else None
self.线段分析层次 = 3
self.扩展线段分析层次 = 3
self.混合扩展线段分析层次 = 3
self.重置基础序列()
def 重置基础序列(self):
"""清空所有分析序列,重置为初始状态"""
self.基础缠K序列: List[缠论K线] = []
self.普通K线序列: List[K线] = []
self.缠论K线序列: List[缠论K线] = []
self.分型序列: List[分型] = []
self.笔序列: List[虚线] = []
self.笔_中枢序列: List[中枢] = []
self.线段序列组: List[List[虚线],] = [] # 线段, 线段<线段>,线段<线段<线段>>...
self.中枢序列组: List[List[中枢],] = []
for i in range(self.线段分析层次):
self.线段序列组.append(list())
self.中枢序列组.append(list())
self.扩展线段序列组: List[List[虚线],] = [] # 扩展线段, 扩展线段<扩展线段>, 扩展线段<扩展线段<扩展线段>>...
self.扩展中枢序列组: List[List[中枢],] = []
for i in range(self.扩展线段分析层次):
self.扩展线段序列组.append(list())
self.扩展中枢序列组.append(list())
self.混合扩展线段序列组: List[List[虚线],] = [] # 扩展线段<线段>, 扩展线段<线段<线段>>, 扩展线段<线段<线段<线段>>>...
self.混合扩展中枢序列组: List[List[中枢],] = []
for i in range(self.混合扩展线段分析层次):
self.混合扩展线段序列组.append(list())
self.混合扩展中枢序列组.append(list())
@property
def 观察员(self):
"""观察员(自引用)"""
@@ -5857,32 +5894,45 @@ class 观察者:
"""
return self.缠论K线序列[-1] if self.缠论K线序列 else None
def 重置基础序列(self):
"""清空所有分析序列,重置为初始状态"""
self.基础缠K序列: List[缠论K线] = []
@property
def 线段序列(self) -> List[虚线]:
return self.线段序列组[0]
self.普通K线序列: List[K线] = []
self.缠论K线序列: List[缠论K线] = []
@property
def 中枢序列(self) -> List[中枢]:
return self.中枢序列组[0]
self.分型序列: List[分型] = []
@property
def 扩展线段序列(self) -> List[虚线]:
return self.扩展线段序列组[0]
self.笔序列: List[虚线] = []
self.笔_中枢序列: List[中枢] = []
@property
def 扩展中枢序列(self) -> List[中枢]:
return self.扩展中枢序列组[0]
self.线段序列: List[虚线] = []
self.中枢序列: List[中枢] = []
@property
def 扩展线段序列_线段(self) -> List[虚线]:
return self.混合扩展线段序列组[0]
self.扩展线段序列: List[虚线] = []
self.扩展中枢序列: List[中枢] = []
@property
def 扩展中枢序列_线段(self) -> List[中枢]:
return self.混合扩展中枢序列组[0]
self.扩展线段序列_线段: List[虚线] = []
self.扩展中枢序列_线段: List[中枢] = []
@property
def 线段_线段序列(self) -> List[虚线]:
return self.线段序列组[1]
self.线段_线段序列: List[虚线] = []
self.线段_中枢序列: List[中枢] = []
@property
def 线段_中枢序列(self) -> List[中枢]:
return self.中枢序列组[1]
self.扩展线段序列_扩展线段: List[虚线] = []
self.扩展中枢序列_扩展线段: List[中枢] = []
@property
def 扩展线段序列_扩展线段(self) -> List[虚线]:
return self.扩展线段序列组[1]
@property
def 扩展中枢序列_扩展线段(self) -> List[中枢]:
return self.扩展中枢序列组[1]
def 投喂原始数据(self, 时间戳: datetime, : float, : float, : float, : float, : float):
"""便捷入口,直接从 OHLCV 创建 K线 并投喂
@@ -5912,28 +5962,33 @@ class 观察者:
if 当前分型 is None:
return
self.配置.分析笔 and .分析(当前分型, self.分型序列, self.笔序列, self.缠论K线序列, self.普通K线序列, 0, self.配置)
.分析(当前分型, self.分型序列, self.笔序列, self.缠论K线序列, self.普通K线序列, 0, self.配置)
if not self.分型序列:
return
self.配置.分析笔中枢 and 中枢.分析(self.笔序列, self.笔_中枢序列)
中枢.分析(self.笔序列, self.笔_中枢序列, True, "", 0)
if not self.笔序列:
return
self.配置.分析线段 and 线段.分析(self.笔序列, self.线段序列, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.线段序列, self.中枢序列)
for i in range(self.线段分析层次):
if i == 0:
线段.分析(self.笔序列, self.线段序列组[i], self.配置)
中枢.分析(self.线段序列组[i], self.中枢序列组[i], True, "", 0)
continue
线段.分析(self.线段序列组[i - 1], self.线段序列组[i], self.配置)
中枢.分析(self.线段序列组[i], self.中枢序列组[i], True, "", 0)
self.配置.分析扩展线段 and 线段.扩展分析(self.笔序列, self.扩展线段序列, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.扩展线段序列, self.扩展中枢序列)
for i in range(self.扩展线段分析层次):
if i == 0:
线段.扩展分析(self.笔序列, self.扩展线段序列组[i], self.配置)
中枢.分析(self.扩展线段序列组[i], self.扩展中枢序列组[i], True, "", 0)
continue
线段.扩展分析(self.扩展线段序列组[i - 1], self.扩展线段序列组[i], self.配置)
中枢.分析(self.扩展线段序列组[i], self.扩展中枢序列组[i], True, "", 0)
self.配置.分析扩展线段 and 线段.扩展分析(self.线段序列, self.扩展线段序列_线段, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.扩展线段序列_线段, self.扩展中枢序列_线段)
self.配置.分析线段 and 线段.分析(self.线段序列, self.线段_线段序列, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.线段_线段序列, self.线段_中枢序列)
self.配置.分析扩展线段 and 线段.扩展分析(self.扩展线段序列, self.扩展线段序列_扩展线段, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.扩展线段序列_扩展线段, self.扩展中枢序列_扩展线段)
for i in range(self.混合扩展线段分析层次):
线段.扩展分析(self.线段序列[i], self.混合扩展线段序列组[i], self.配置)
中枢.分析(self.混合扩展线段序列组[i], self.混合扩展中枢序列组[i], True, "", 0)
def 测试_保存数据(self, root: str = None) -> str:
"""拆分各序列数据,单独存文件,文件名为对应变量名
@@ -5941,30 +5996,15 @@ class 观察者:
:param root: 保存根目录可选
:return: 保存目录
"""
# 提取各类文本数据
笔序列_文本数据 = [.获取数据文本() for in self.笔序列]
线段序列_文本数据 = [实线.获取数据文本() for 实线 in self.线段序列]
扩展线段序列_数据文本 = [实线.获取数据文本() for 实线 in self.扩展线段序列]
扩展线段序列_线段_数据文本 = [实线.获取数据文本() for 实线 in self.扩展线段序列_线段]
线段_线段序列_数据文本 = [实线.获取数据文本() for 实线 in self.线段_线段序列]
扩展线段序列_扩展线段_数据文本 = [实线.获取数据文本() for 实线 in self.扩展线段序列_扩展线段]
笔_中枢序列_数据文本 = [当前中枢.获取数据文本() for 当前中枢 in self.笔_中枢序列]
中枢序列_数据文本 = [当前中枢.获取数据文本() for 当前中枢 in self.中枢序列]
扩展中枢序列_数据文本 = [当前中枢.获取数据文本() for 当前中枢 in self.扩展中枢序列]
扩展中枢序列_线段_数据文本 = [当前中枢.获取数据文本() for 当前中枢 in self.扩展中枢序列_线段]
线段_中枢序列_数据文本 = [当前中枢.获取数据文本() for 当前中枢 in self.线段_中枢序列]
扩展中枢序列_扩展线段_数据文本 = [当前中枢.获取数据文本() for 当前中枢 in self.扩展中枢序列_扩展线段]
# ===================== 优化点:优先使用传入的 root 目录 =====================
if root is not None:
# 使用用户指定的根目录
根目录 = Path(root)
else:
# 默认:当前脚本所在目录
根目录 = Path(__file__).parent
# 默认:系统临时目录
根目录 = tempfile.gettempdir()
# 生成子目录名称(不变)
# 生成子目录名称
起始时间 = int(self.普通K线序列[0].时间戳.timestamp())
结束时间 = int(self.普通K线序列[-1].时间戳.timestamp())
目录标识 = f"Py_{self.标识}_{起始时间}_{结束时间}"
@@ -5973,28 +6013,29 @@ class 观察者:
保存路径 = 根目录 / 目录标识
保存路径.mkdir(exist_ok=True, parents=True) # parents=True 支持多级目录自动创建
# 映射:变量名 -> 数据列表
数据映射 = [
("笔序列_文本数据", 笔序列_文本数据),
("线段序列_文本数据", 线段序列_文本数据),
("扩展线段序列_数据文本", 扩展线段序列_数据文本),
("扩展线段序列_线段_数据文本", 扩展线段序列_线段_数据文本),
("线段_线段序列_数据文本", 线段_线段序列_数据文本),
("扩展线段序列_扩展线段_数据文本", 扩展线段序列_扩展线段_数据文本),
("笔_中枢序列_数据文本", 笔_中枢序列_数据文本),
("中枢序列_数据文本", 中枢序列_数据文本),
("扩展中枢序列_数据文本", 扩展中枢序列_数据文本),
("扩展中枢序列_线段_数据文本", 扩展中枢序列_线段_数据文本),
("线段_中枢序列_数据文本", 线段_中枢序列_数据文本),
("扩展中枢序列_扩展线段_数据文本", 扩展中枢序列_扩展线段_数据文本),
]
# 逐个写入独立文件
for 文件名, 数据列表 in 数据映射:
def 保存序列(序列):
if len(序列) == 0:
return
数据列表 = [对象.获取数据文本() for 对象 in 序列]
文件名 = 序列[0].标识
文件全路径 = 保存路径 / f"{文件名}.txt"
# 逐个写入独立文件
with open(文件全路径, "w", encoding="utf-8") as f:
f.write("\n".join(数据列表))
f.write("\n") # 向 C99 对齐
保存序列(self.笔序列)
保存序列(self.笔_中枢序列)
for i in range(self.线段分析层次):
保存序列(self.线段序列组[i])
保存序列(self.中枢序列组[i])
for i in range(self.扩展线段分析层次):
保存序列(self.扩展线段序列组[i])
保存序列(self.扩展中枢序列组[i])
for i in range(self.线段分析层次):
保存序列(self.混合扩展线段序列组[i])
保存序列(self.混合扩展中枢序列组[i])
logger.warning(f"全部数据拆分保存完成,目录:{保存路径.resolve()}")
return str(保存路径.resolve())
@@ -6003,44 +6044,55 @@ class 观察者:
pass
def 静态重新分析(self):
"""静态重新分析(占位方法)"""
"""静态重新分析"""
self.分型序列: List[分型] = []
self.笔序列: List[虚线] = []
self.笔_中枢序列: List[中枢] = []
self.线段序列: List[虚线] = []
self.中枢序列: List[中枢] = []
self.线段序列: List[List[虚线],] = [] # 线段, 线段<线段>,线段<线段<线段>>...
self.中枢序列: List[List[中枢],] = []
for i in range(self.线段分析层次):
self.线段序列组.append(list())
self.中枢序列组.append(list())
self.扩展线段序列: List[虚线] = []
self.扩展中枢序列: List[中枢] = []
self.扩展线段序列: List[List[虚线],] = [] # 扩展线段, 扩展线段<扩展线段>, 扩展线段<扩展线段<扩展线段>>...
self.扩展中枢序列: List[List[中枢],] = []
for i in range(self.扩展线段分析层次):
self.扩展线段序列组.append(list())
self.扩展中枢序列组.append(list())
self.扩展线段序列_线段: List[虚线] = []
self.扩展中枢序列_线段: List[中枢] = []
self.线段_线段序列: List[虚线] = []
self.线段_中枢序列: List[中枢] = []
self.扩展线段序列_扩展线段: List[虚线] = []
self.扩展中枢序列_扩展线段: List[中枢] = []
self.混合扩展线段序列: List[List[虚线],] = [] # 扩展线段<线段>, 扩展线段<线段<线段>>, 扩展线段<线段<线段<线段>>>...
self.混合扩展中枢序列: List[List[中枢],] = []
for i in range(self.混合扩展线段分析层次):
self.混合扩展线段序列组.append(list())
self.混合扩展中枢序列组.append(list())
for i in range(1, len(self.缠论K线序列) - 1):
当前分型 = 分型(self.缠论K线序列[i - 1], self.缠论K线序列[i], self.缠论K线序列[i + 1])
.分析(当前分型, self.分型序列, self.笔序列, self.缠论K线序列, self.普通K线序列, 0, self.配置)
self.配置.分析笔中枢 and 中枢.分析(self.笔序列, self.笔_中枢序列)
中枢.分析(self.笔序列, self.笔_中枢序列, True, "", 0)
self.配置.分析线段 and 线段.分析(self.笔序列, self.线段序列, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.线段序列, self.中枢序列)
for i in range(self.线段分析层次):
if i == 0:
线段.分析(self.笔序列, self.线段序列组[i], self.配置)
中枢.分析(self.线段序列组[i], self.中枢序列组[i], True, "", 0)
continue
线段.分析(self.线段序列组[i - 1], self.线段序列组[i], self.配置)
中枢.分析(self.线段序列组[i], self.中枢序列组[i], True, "", 0)
self.配置.分析扩展线段 and 线段.扩展分析(self.笔序列, self.扩展线段序列, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.扩展线段序列, self.扩展中枢序列)
for i in range(self.扩展线段分析层次):
if i == 0:
线段.扩展分析(self.笔序列, self.扩展线段序列组[i], self.配置)
中枢.分析(self.扩展线段序列组[i], self.扩展中枢序列组[i], True, "", 0)
continue
线段.扩展分析(self.扩展线段序列组[i - 1], self.扩展线段序列组[i], self.配置)
中枢.分析(self.扩展线段序列组[i], self.扩展中枢序列组[i], True, "", 0)
self.配置.分析扩展线段 and 线段.扩展分析(self.线段序列, self.扩展线段序列_线段, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.扩展线段序列_线段, self.扩展中枢序列_线段)
self.配置.分析线段 and 线段.分析(self.线段序列, self.线段_线段序列, self.配置)
self.配置.分析线段中枢 and 中枢.分析(self.线段_线段序列, self.线段_中枢序列)
for i in range(self.混合扩展线段分析层次):
线段.扩展分析(self.线段序列[i], self.混合扩展线段序列组[i], self.配置)
中枢.分析(self.混合扩展线段序列组[i], self.混合扩展中枢序列组[i], True, "", 0)
def 加载本地数据(self, 文件路径: str):
"""重置基础序列后加载数据文件
@@ -6417,7 +6469,6 @@ def K线相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
return True, "K线:全部字段结构、数值校验完全一致"
@lru_cache(4096)
def 缠论K线相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
"""缠论K线:基础字段+标的K线递归校验"""
比对字段 = ["序号", "时间戳", "", "", "方向", "分型", "周期", "标识", "分型特征值", "原始起始序号", "原始结束序号", "标的K线", "买卖点信息"]
@@ -6470,7 +6521,6 @@ def 缠论K线相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
return True, "缠论K线:全部字段、嵌套原始K线校验一致"
@lru_cache(4096)
def 分型相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
"""分型:左/中/右缠论K线递归 + 自有字段"""
比对字段 = ["", "", "", "_结构", "_时间戳", "_分型特征值"]
@@ -6542,18 +6592,17 @@ def 缺口相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
return True, "缺口:上下沿价格校验完全一致"
@lru_cache(4096)
def 线段特征相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
"""线段特征:基础序列虚线列表逐项校验"""
比对字段 = ["序号", "标识", "线段方向", "基础序列"]
标签 = f"线段特征校验[A标识={A.标识}, B标识={B.标识}]"
标签 = f"线段特征校验[A标识={A.标识},B标识={B.标识}]"
for 字段 in 比对字段:
a有 = hasattr(A, 字段)
b有 = hasattr(B, 字段)
if a有 and not b有:
return False, f"{标签}[{字段}]A存在、B缺失属性"
return False, f"{标签}: [{字段}]A存在、B缺失属性"
if not a有 and b有:
return False, f"{标签}[{字段}]B存在、A缺失属性"
return False, f"{标签}: [{字段}]B存在、A缺失属性"
if not (a有 and b有):
continue
@@ -6561,7 +6610,7 @@ def 线段特征相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
valB = getattr(B, 字段)
if 字段 == "基础序列":
if len(valA) != len(valB):
return False, f"{标签}[基础序列]列表长度不一致,A长度={len(valA)},B长度={len(valB)}"
return False, f"{标签}: [基础序列]列表长度不一致,A长度={len(valA)},B长度={len(valB)}"
for idx, (itemA, itemB) in enumerate(zip(valA, valB)):
eq, msg = 虚线相等(itemA, itemB, 浮点容差)
if not eq:
@@ -6569,25 +6618,24 @@ def 线段特征相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
elif 字段 == "线段方向":
if str(valA) != str(valB):
return False, f"{标签}[{字段}]数值不等,A={repr(valA)},B={repr(valB)}"
return False, f"{标签}: [{字段}]数值不等,A={repr(valA)},B={repr(valB)}"
else:
if valA != valB:
return False, f"{标签}[{字段}]数值不等,A={repr(valA)},B={repr(valB)}"
return False, f"{标签}: [{字段}]数值不等,A={repr(valA)},B={repr(valB)}"
return True, f"{标签}:字段与内部虚线序列全部一致"
@lru_cache(4096)
def 中枢相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
"""中枢:基础序列虚线列表+第三买卖线单虚线"""
比对字段 = ["序号", "标识", "级别", "基础序列", "第三买卖线", "本级_第三买卖线"]
标签 = f"中枢校验[A标识={A.标识}, B标识={B.标识}]"
标签 = f"中枢校验[A标识={A.标识},B标识={B.标识}]"
for 字段 in 比对字段:
a有 = hasattr(A, 字段)
b有 = hasattr(B, 字段)
if a有 and not b有:
return False, f"{标签}[{字段}]A存在、B缺失属性"
return False, f"{标签}: [{字段}]A存在、B缺失属性"
if not a有 and b有:
return False, f"{标签}[{字段}]B存在、A缺失属性"
return False, f"{标签}: [{字段}]B存在、A缺失属性"
if not (a有 and b有):
continue
@@ -6596,7 +6644,7 @@ def 中枢相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
if 字段 == "基础序列":
if len(valA) != len(valB):
return False, f"{标签}[基础序列]长度不一致 A={len(valA)},B={len(valB)}"
return False, f"{标签}: [基础序列]长度不一致 A={len(valA)},B={len(valB)}"
for idx, (itemA, itemB) in enumerate(zip(valA, valB)):
eq, msg = 虚线相等(itemA, itemB, 浮点容差)
if not eq:
@@ -6605,29 +6653,28 @@ def 中枢相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
if valA is None and valB is None:
continue
if valA is None or valB is None:
return False, f"{标签}[{字段}]空值不一致 A={valA is None},B={valB is None}"
return False, f"{标签}: [{字段}]空值不一致 A={valA is None},B={valB is None}"
eq, msg = 虚线相等(valA, valB, 浮点容差)
if not eq:
return False, f"{标签}[{字段}]子虚线异常 >> {msg}"
return False, f"{标签}: [{字段}]子虚线异常 >> {msg}"
else:
if valA != valB:
return False, f"{标签}[{字段}]数值不等,A={repr(valA)},B={repr(valB)}"
return False, f"{标签}: [{字段}]数值不等,A={repr(valA)},B={repr(valB)}"
return True, f"{标签}:基础序列+第三买卖线全部校验一致"
@lru_cache(4096)
def 虚线相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
"""虚线(笔/线段):全量字段、分型/缺口/K线/列表嵌套精细化报错"""
比对字段 = ["标识", "序号", "级别", "", "", "有效性", "基础序列", "特征序列", "实_中枢序列", "虚_中枢序列", "合_中枢序列", "确认K线", "模式", "_特征序列_显示", "前一缺口", "前一结束位置", "短路修正"]
标签 = f"虚线校验[A标识={A.标识}, B标识={B.标识}]"
标签 = f"虚线校验[A标识={A.标识},B标识={B.标识}]"
for 字段 in 比对字段:
a有 = hasattr(A, 字段)
b有 = hasattr(B, 字段)
if a有 and not b有:
return False, f"{标签}[{字段}]A存在属性、B缺失属性"
return False, f"{标签}: [{字段}]A存在属性、B缺失属性"
if not a有 and b有:
return False, f"{标签}[{字段}]B存在属性、A缺失属性"
return False, f"{标签}: [{字段}]B存在属性、A缺失属性"
if not (a有 and b有):
continue
@@ -6639,46 +6686,46 @@ def 虚线相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
if valA is None and valB is None:
continue
if valA is None or valB is None:
return False, f"{标签}[{字段}]分型空值不一致 A={valA is None},B={valB is None}"
return False, f"{标签}: [{字段}]分型空值不一致 A={valA is None},B={valB is None}"
eq, msg = 分型相等(valA, valB, 浮点容差)
if not eq:
return False, f"{标签}[{字段}]子分型异常 >> {msg}"
return False, f"{标签}: [{字段}]子分型异常 >> {msg}"
# 前一缺口
elif 字段 == "前一缺口":
if valA is None and valB is None:
continue
if valA is None or valB is None:
return False, f"{标签}[前一缺口]空值不一致 A={valA is None},B={valB is None}"
return False, f"{标签}: [前一缺口]空值不一致 A={valA is None},B={valB is None}"
eq, msg = 缺口相等(valA, valB, 浮点容差)
if not eq:
return False, f"{标签}[前一缺口]子缺口异常 >> {msg}"
return False, f"{标签}: [前一缺口]子缺口异常 >> {msg}"
# 前一缺口
elif 字段 == "前一结束位置":
if valA is None and valB is None:
continue
if valA is None or valB is None:
return False, f"{标签}[前一结束位置]空值不一致 A={valA is None},B={valB is None}"
return False, f"{标签}: [前一结束位置]空值不一致 A={valA is None},B={valB is None}"
eq, msg = 虚线相等(valA, valB, 浮点容差)
if not eq:
return False, f"{标签}[前一结束位置]异常 >> {msg}"
return False, f"{标签}: [前一结束位置]异常 >> {msg}"
# 确认K线
elif 字段 == "确认K线":
if valA is None and valB is None:
continue
if valA is None or valB is None:
return False, f"{标签}[确认K线]空值不一致 A={valA is None},B={valB is None}"
return False, f"{标签}: [确认K线]空值不一致 A={valA is None},B={valB is None}"
eq, msg = 缠论K线相等(valA, valB, 浮点容差)
if not eq:
return False, f"{标签}[确认K线]子缠论K线异常 >> {msg}"
return False, f"{标签}: [确认K线]子缠论K线异常 >> {msg}"
# 各类列表
elif 字段 in ("基础序列", "实_中枢序列", "虚_中枢序列", "合_中枢序列", "特征序列"):
if len(valA) != len(valB):
return False, f"{标签}[{字段}]列表长度不一致 A={len(valA)},B={len(valB)}"
return False, f"{标签}: [{字段}]列表长度不一致 A={len(valA)},B={len(valB)}"
for idx, (itemA, itemB) in enumerate(zip(valA, valB)):
if itemA is None and itemB is None:
continue
if itemA is None or itemB is None:
return False, f"{标签}[{字段}][{idx}]单项空值不一致 A={itemA is None},B={itemB is None}"
return False, f"{标签}: [{字段}][{idx}]单项空值不一致 A={itemA is None},B={itemB is None}"
if 字段 == "基础序列":
eq, msg = 虚线相等(itemA, itemB, 浮点容差)
elif "中枢" in 字段:
@@ -6686,11 +6733,11 @@ def 虚线相等(A, B, 浮点容差: float = 1e-9) -> tuple[bool, str]:
else:
eq, msg = 线段特征相等(itemA, itemB, 浮点容差)
if not eq:
return False, f"{标签}[{字段}][{idx}]子项异常 >> {msg}"
return False, f"{标签}: [{字段}][{idx}]子项异常 >> {msg}"
# 普通字段
else:
if valA != valB:
return False, f"{标签}[{字段}]数值不等 A={repr(valA)},B={repr(valB)}"
return False, f"{标签}: [{字段}]数值不等 A={repr(valA)},B={repr(valB)}"
return True, f"{标签}:全字段、所有嵌套子结构校验全部一致"
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "maturin"
[project]
name = "chanlun"
version = "2606.21"
version = "2606.34"
description = "缠论技术分析库 — Rust 高性能实现"
readme = { file = "README.md", content-type = "text/markdown" }
license = { file = "LICENSE", content-type = "text/plain" }
+118 -10
View File
@@ -675,6 +675,13 @@ impl 买卖点Py {
/// 线段_线段序列 / 线段_中枢序列
/// 扩展线段序列_扩展线段 / 扩展中枢序列_扩展线段
///
/// 分析层次(可读写):
/// 线段分析层次: int (默认 3) / 扩展线段分析层次: int (默认 3) / 混合扩展线段分析层次: int (默认 3)
///
/// 序列组(只读,返回 list[list[...]]:
/// 线段序列组 / 中枢序列组 / 扩展线段序列组 / 扩展中枢序列组
/// 混合扩展线段序列组 / 混合扩展中枢序列组
///
/// 核心方法:
/// 增加原始K线(普K) — 喂入一根普通K线,触发全层级增量计算
/// 重置基础序列() — 清空所有计算状态和序列
@@ -1040,7 +1047,7 @@ impl 观察者Py {
#[getter]
fn 线(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.obs().线 {
for d in self.obs().线() {
list.append(dashed_to_py(py, Arc::clone(d)))?;
}
Ok(list.into())
@@ -1049,7 +1056,7 @@ impl 观察者Py {
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in &self.obs(). {
for h in self.obs().() {
list.append(hub_to_py(py, Arc::clone(h)))?;
}
Ok(list.into())
@@ -1058,7 +1065,7 @@ impl 观察者Py {
#[getter]
fn 线(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.obs().线 {
for d in self.obs().线() {
list.append(dashed_to_py(py, Arc::clone(d)))?;
}
Ok(list.into())
@@ -1067,7 +1074,7 @@ impl 观察者Py {
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in &self.obs(). {
for h in self.obs().() {
list.append(hub_to_py(py, Arc::clone(h)))?;
}
Ok(list.into())
@@ -1076,7 +1083,7 @@ impl 观察者Py {
#[getter]
fn 线_线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.obs().线_线段 {
for d in self.obs().线_线段() {
list.append(dashed_to_py(py, Arc::clone(d)))?;
}
Ok(list.into())
@@ -1085,7 +1092,7 @@ impl 观察者Py {
#[getter]
fn _线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in &self.obs()._线段 {
for h in self.obs()._线段() {
list.append(hub_to_py(py, Arc::clone(h)))?;
}
Ok(list.into())
@@ -1094,7 +1101,7 @@ impl 观察者Py {
#[getter]
fn 线_线段序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.obs().线_线段序列 {
for d in self.obs().线_线段序列() {
list.append(dashed_to_py(py, Arc::clone(d)))?;
}
Ok(list.into())
@@ -1103,7 +1110,7 @@ impl 观察者Py {
#[getter]
fn 线_中枢序列(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in &self.obs().线_中枢序列 {
for h in self.obs().线_中枢序列() {
list.append(hub_to_py(py, Arc::clone(h)))?;
}
Ok(list.into())
@@ -1112,7 +1119,7 @@ impl 观察者Py {
#[getter]
fn 线_扩展线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for d in &self.obs().线_扩展线段 {
for d in self.obs().线_扩展线段() {
list.append(dashed_to_py(py, Arc::clone(d)))?;
}
Ok(list.into())
@@ -1121,11 +1128,112 @@ impl 观察者Py {
#[getter]
fn _扩展线段(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
let list = pyo3::types::PyList::empty(py);
for h in &self.obs()._扩展线段 {
for h in self.obs()._扩展线段() {
list.append(hub_to_py(py, Arc::clone(h)))?;
}
Ok(list.into())
}
// ---- 分析层次 getter/setter ----
#[getter]
fn 线(&self) -> usize {
self.obs().线
}
#[setter]
#[pyo3(name = "线段分析层次")]
fn _线段分析层次(&mut self, value: usize) {
self.obs_mut().线 = value;
}
#[getter]
fn 线(&self) -> usize {
self.obs().线
}
#[setter]
#[pyo3(name = "扩展线段分析层次")]
fn _扩展线段分析层次(&mut self, value: usize) {
self.obs_mut().线 = value;
}
#[getter]
fn 线(&self) -> usize {
self.obs().线
}
#[setter]
#[pyo3(name = "混合扩展线段分析层次")]
fn _混合扩展线段分析层次(&mut self, value: usize) {
self.obs_mut().线 = value;
}
// ---- 序列组 getters (返回 list[list[...]]) ----
#[getter]
fn 线(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
self._虚线序列组_to_py(&self.obs().线, py)
}
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
self._中枢序列组_to_py(&self.obs()., py)
}
#[getter]
fn 线(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
self._虚线序列组_to_py(&self.obs().线, py)
}
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
self._中枢序列组_to_py(&self.obs()., py)
}
#[getter]
fn 线(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
self._虚线序列组_to_py(&self.obs().线, py)
}
#[getter]
fn (&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
self._中枢序列组_to_py(&self.obs()., py)
}
}
impl Py {
fn _虚线序列组_to_py(
&self,
: &Vec<Vec<Arc<chanlun::structure::dash_line::线>>>,
py: Python<'_>,
) -> PyResult<Py<PyAny>> {
let outer = pyo3::types::PyList::empty(py);
for inner_vec in {
let inner_list = pyo3::types::PyList::empty(py);
for d in inner_vec {
inner_list.append(dashed_to_py(py, Arc::clone(d)))?;
}
outer.append(inner_list)?;
}
Ok(outer.into())
}
fn _中枢序列组_to_py(
&self,
: &Vec<Vec<Arc<chanlun::algorithm::hub::>>>,
py: Python<'_>,
) -> PyResult<Py<PyAny>> {
let outer = pyo3::types::PyList::empty(py);
for inner_vec in {
let inner_list = pyo3::types::PyList::empty(py);
for h in inner_vec {
inner_list.append(hub_to_py(py, Arc::clone(h)))?;
}
outer.append(inner_list)?;
}
Ok(outer.into())
}
}
// ========== K线合成器 ==========
+206 -234
View File
@@ -538,89 +538,75 @@ fn 中枢相等(
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_HUB,
4096,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
if let (Ok(a), Ok(b)) = (
A.cast::<crate::algorithm_py::Py>(),
B.cast::<crate::algorithm_py::Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
if let (Ok(a), Ok(b)) = (
A.cast::<crate::algorithm_py::Py>(),
B.cast::<crate::algorithm_py::Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let a标识 = (A);
let b标识 = (B);
let = format!("中枢校验[A标识={a标识},B标识={b标识}]");
for & in &[
"序号",
"标识",
"级别",
"基础序列",
"第三买卖线",
"本级_第三买卖线",
] {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
if == "基础序列" {
let len_a: usize = valA.len()?;
let len_b: usize = valB.len()?;
if len_a != len_b {
return Ok((
false,
format!("{标签}: [基础序列] 长度不一致 A={len_a},B={len_b}"),
));
}
let a标识 = (A);
let b标识 = (B);
let = format!("中枢校验[A标识={a标识},B标识={b标识}]");
for & in &[
"序号",
"标识",
"级别",
"基础序列",
"第三买卖线",
"本级_第三买卖线",
] {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在 B缺失属性")));
for idx in 0..len_a {
let itemA = valA.get_item(idx)?;
let itemB = valB.get_item(idx)?;
let (eq, msg) = 线(&itemA, &itemB, )?;
if !eq {
return Ok((false, format!("{标签}: 基础序列[{idx}]虚线异常 >> {msg}")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在 A缺失属性")));
}
if !a有 && !b有 {
}
} else if == "第三买卖线" || == "本级_第三买卖线" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
if == "基础序列" {
let len_a: usize = valA.len()?;
let len_b: usize = valB.len()?;
if len_a != len_b {
return Ok((
false,
format!("{标签}: [基础序列] 长度不一致 A={len_a},B={len_b}"),
));
}
for idx in 0..len_a {
let itemA = valA.get_item(idx)?;
let itemB = valB.get_item(idx)?;
let (eq, msg) = 线(&itemA, &itemB, )?;
if !eq {
return Ok((
false,
format!("{标签}: 基础序列[{idx}]虚线异常 >> {msg}"),
));
}
}
} else if == "第三买卖线" || == "本级_第三买卖线" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = 线(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [{字段}]子虚线异常 >> {msg}")));
}
} else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 基础序列+第三买卖线全部校验一致")))
let (eq, msg) = 线(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [{字段}]子虚线异常 >> {msg}")));
}
} else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}] 数值不等 A={ra},B={rb}")));
}
}
)
}
Ok((true, format!("{标签}: 基础序列+第三买卖线全部校验一致")))
}
// ========== 虚线相等 ==========
@@ -632,168 +618,154 @@ fn 虚线相等(
B: &Bound<'_, PyAny>,
: f64,
) -> PyResult<(bool, String)> {
with_cache!(
C_DASH,
4096,
(
A.as_ptr() as usize,
B.as_ptr() as usize,
.to_bits() as i64
),
{
if let (Ok(a), Ok(b)) = (
A.cast::<crate::structure_py::线Py>(),
B.cast::<crate::structure_py::线Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let a标识 = (A);
let b标识 = (B);
let = format!("虚线校验[A标识={a标识},B标识={b标识}]");
let = [
"标识",
"序号",
"级别",
"",
"",
"有效性",
"基础序列",
"特征序列",
"实_中枢序列",
"虚_中枢序列",
"合_中枢序列",
"确认K线",
"模式",
"_特征序列_显示",
"前一缺口",
"前一结束位置",
"短路修正",
];
for & in & {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在属性 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在属性 A缺失属性")));
}
if !a有 && !b有 {
if let (Ok(a), Ok(b)) = (
A.cast::<crate::structure_py::线Py>(),
B.cast::<crate::structure_py::线Py>(),
) {
return Ok(a.borrow().inner.(&b.borrow().inner, ));
}
let a标识 = (A);
let b标识 = (B);
let = format!("虚线校验[A标识={a标识},B标识={b标识}]");
let = [
"标识",
"序号",
"级别",
"",
"",
"有效性",
"基础序列",
"特征序列",
"实_中枢序列",
"虚_中枢序列",
"合_中枢序列",
"确认K线",
"模式",
"_特征序列_显示",
"前一缺口",
"前一结束位置",
"短路修正",
];
for & in & {
let (a有, b有) = (A.hasattr()?, B.hasattr()?);
if a有 && !b有 {
return Ok((false, format!("{标签}: [{字段}] A存在属性 B缺失属性")));
}
if !a有 && b有 {
return Ok((false, format!("{标签}: [{字段}] B存在属性 A缺失属性")));
}
if !a有 && !b有 {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
// 文/武:分型
if == "" || == "" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
let valA = A.getattr()?;
let valB = B.getattr()?;
// 文/武:分型
if == "" || == "" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = (&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [{字段}]子分型异常 >> {msg}")));
}
}
// 前一缺口
else if == "前一缺口" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = (&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [前一缺口]子缺口异常 >> {msg}")));
}
}
// 前一结束位置
else if == "前一结束位置" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = 线(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [前一结束位置]异常 >> {msg}")));
}
}
// 确认K线
else if == "确认K线" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = K线相等(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [确认K线]子缠论K线异常 >> {msg}")));
}
}
// 各类列表
else if == "基础序列"
|| == "实_中枢序列"
|| == "虚_中枢序列"
|| == "合_中枢序列"
|| == "特征序列"
{
let len_a: usize = valA.len()?;
let len_b: usize = valB.len()?;
if len_a != len_b {
return Ok((
false,
format!("{标签}: [{字段}]列表长度不一致 A={len_a},B={len_b}"),
));
}
for idx in 0..len_a {
let itemA = valA.get_item(idx)?;
let itemB = valB.get_item(idx)?;
if let Some(r) =
(&itemA, &itemB, &format!("{字段}[{idx}]"), &)
{
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = if == "基础序列" {
线(&itemA, &itemB, )?
} else if .contains("中枢") {
(&itemA, &itemB, )?
} else {
线(&itemA, &itemB, )?
};
if !eq {
return Ok((
false,
format!("{标签}: [{字段}][{idx}]子项异常 >> {msg}"),
));
}
}
}
// 普通字段
else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}]数值不等 A={ra},B={rb}")));
}
}
let (eq, msg) = (&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [{字段}]子分型异常 >> {msg}")));
}
}
// 前一缺口
else if == "前一缺口" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
Ok((true, format!("{标签}: 全字段所有嵌套子结构校验一致")))
let (eq, msg) = (&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [前一缺口]子缺口异常 >> {msg}")));
}
}
)
// 前一结束位置
else if == "前一结束位置" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = 线(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [前一结束位置]异常 >> {msg}")));
}
}
// 确认K线
else if == "确认K线" {
if let Some(r) = (&valA, &valB, , &) {
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = K线相等(&valA, &valB, )?;
if !eq {
return Ok((false, format!("{标签}: [确认K线]子缠论K线异常 >> {msg}")));
}
}
// 各类列表
else if == "基础序列"
|| == "实_中枢序列"
|| == "虚_中枢序列"
|| == "合_中枢序列"
|| == "特征序列"
{
let len_a: usize = valA.len()?;
let len_b: usize = valB.len()?;
if len_a != len_b {
return Ok((
false,
format!("{标签}: [{字段}]列表长度不一致 A={len_a},B={len_b}"),
));
}
for idx in 0..len_a {
let itemA = valA.get_item(idx)?;
let itemB = valB.get_item(idx)?;
if let Some(r) =
(&itemA, &itemB, &format!("{字段}[{idx}]"), &)
{
if !r.0 {
return Ok((false, r.1));
} else {
continue;
}
}
let (eq, msg) = if == "基础序列" {
线(&itemA, &itemB, )?
} else if .contains("中枢") {
(&itemA, &itemB, )?
} else {
线(&itemA, &itemB, )?
};
if !eq {
return Ok((false, format!("{标签}: [{字段}][{idx}]子项异常 >> {msg}")));
}
}
}
// 普通字段
else {
let eq: bool = valA.eq(&valB)?;
if !eq {
let ra = valA.repr()?.extract::<String>().unwrap_or_default();
let rb = valB.repr()?.extract::<String>().unwrap_or_default();
return Ok((false, format!("{标签}: [{字段}]数值不等 A={ra},B={rb}")));
}
}
}
Ok((true, format!("{标签}: 全字段所有嵌套子结构校验一致")))
}
pub fn register(m: &Bound<'_, PyModule>) -> PyResult<()> {
+1
View File
@@ -1050,6 +1050,7 @@ impl 线段 {
Self::_刷新(&线[idx], );
let 线 = Arc::clone(&线[idx]);
线..store(true, Ordering::Relaxed);
if 线..read().unwrap()[2].is_some() {
let = 线::线(&[
Arc::clone(&[.len() - 3]),
+291 -236
View File
@@ -52,25 +52,22 @@ pub struct 观察者 {
pub : Vec<Arc<线>>,
pub _中枢序列: Vec<Arc<>>,
// 线段
pub 线: Vec<Arc<线>>,
pub : Vec<Arc<>>,
// 分析层次配置
pub 线: usize,
pub 线: usize,
pub 线: usize,
// 扩展线段(笔级)
pub 线: Vec<Arc<线>>,
pub : Vec<Arc<>>,
// 线段组: [0]=线段, [1]=线段<线段>, [2]=线段<线段<线段>>
pub 线: Vec<Vec<Arc<线>>>,
pub : Vec<Vec<Arc<>>>,
// 扩展线段(线段级)
pub 线_线段: Vec<Arc<线>>,
pub _线段: Vec<Arc<>>,
// 扩展线段组: [0]=扩展线段, [1]=扩展线段<扩展线段>, [2]=扩展线段<扩展线段<扩展线段>>
pub 线: Vec<Vec<Arc<线>>>,
pub : Vec<Vec<Arc<>>>,
// 线段线段
pub 线_线段序列: Vec<Arc<线>>,
pub 线_中枢序列: Vec<Arc<>>,
// 扩展线段之扩展线段
pub 线_扩展线段: Vec<Arc<线>>,
pub _扩展线段: Vec<Arc<>>,
// 混合扩展线段组: [0]=扩展线段<线段>, [1]=扩展线段<线段<线段>>, [2]=扩展线段<线段<线段<线段>>>
pub 线: Vec<Vec<Arc<线>>>,
pub : Vec<Vec<Arc<>>>,
// 终止时间戳
: Option<i64>,
@@ -85,6 +82,30 @@ impl 观察者 {
None
};
let 线 = 3usize;
let 线 = 3usize;
let 线 = 3usize;
let mut 线: Vec<Vec<Arc<线>>> = Vec::with_capacity(线);
let mut : Vec<Vec<Arc<>>> = Vec::with_capacity(线);
let mut 线: Vec<Vec<Arc<线>>> = Vec::with_capacity(线);
let mut : Vec<Vec<Arc<>>> = Vec::with_capacity(线);
let mut 线: Vec<Vec<Arc<线>>> = Vec::with_capacity(线);
let mut : Vec<Vec<Arc<>>> = Vec::with_capacity(线);
for _ in 0..线 {
线.push(Vec::new());
.push(Vec::new());
}
for _ in 0..线 {
线.push(Vec::new());
.push(Vec::new());
}
for _ in 0..线 {
线.push(Vec::new());
.push(Vec::new());
}
let mut instance = Self {
: .clone(),
,
@@ -95,16 +116,15 @@ impl 观察者 {
: Vec::new(),
: Vec::new(),
_中枢序列: Vec::new(),
线: Vec::new(),
: Vec::new(),
线: Vec::new(),
: Vec::new(),
线_线段: Vec::new(),
_线段: Vec::new(),
线_线段序列: Vec::new(),
线_中枢序列: Vec::new(),
线_扩展线段: Vec::new(),
_扩展线段: Vec::new(),
线,
线,
线,
线,
,
线,
,
线,
,
,
};
instance.. = ;
@@ -126,6 +146,58 @@ impl 观察者 {
self.K线序列.last()
}
// ---- 向后兼容的属性访问器 ----
/// 线段序列 (线段序列组[0])
pub fn 线(&self) -> &Vec<Arc<线>> {
&self.线[0]
}
/// 中枢序列 (中枢序列组[0])
pub fn (&self) -> &Vec<Arc<>> {
&self.[0]
}
/// 线段_线段序列 (线段序列组[1])
pub fn 线_线段序列(&self) -> &Vec<Arc<线>> {
&self.线[1]
}
/// 线段_中枢序列 (中枢序列组[1])
pub fn 线_中枢序列(&self) -> &Vec<Arc<>> {
&self.[1]
}
/// 扩展线段序列 (扩展线段序列组[0])
pub fn 线(&self) -> &Vec<Arc<线>> {
&self.线[0]
}
/// 扩展中枢序列 (扩展中枢序列组[0])
pub fn (&self) -> &Vec<Arc<>> {
&self.[0]
}
/// 扩展线段序列_扩展线段 (扩展线段序列组[1])
pub fn 线_扩展线段(&self) -> &Vec<Arc<线>> {
&self.线[1]
}
/// 扩展中枢序列_扩展线段 (扩展中枢序列组[1])
pub fn _扩展线段(&self) -> &Vec<Arc<>> {
&self.[1]
}
/// 扩展线段序列_线段 (混合扩展线段序列组[0])
pub fn 线_线段(&self) -> &Vec<Arc<线>> {
&self.线[0]
}
/// 扩展中枢序列_线段 (混合扩展中枢序列组[0])
pub fn _线段(&self) -> &Vec<Arc<>> {
&self.[0]
}
/// 重置基础序列
pub fn (&mut self) {
self.K线序列.clear();
@@ -134,16 +206,27 @@ impl 观察者 {
self..clear();
self..clear();
self._中枢序列.clear();
self.线.clear();
self..clear();
self.线.clear();
self..clear();
self.线_线段.clear();
self._线段.clear();
self.线_线段序列.clear();
self.线_中枢序列.clear();
self.线_扩展线段.clear();
self._扩展线段.clear();
self.线.clear();
self..clear();
for _ in 0..self.线 {
self.线.push(Vec::new());
self..push(Vec::new());
}
self.线.clear();
self..clear();
for _ in 0..self.线 {
self.线.push(Vec::new());
self..push(Vec::new());
}
self.线.clear();
self..clear();
for _ in 0..self.线 {
self.线.push(Vec::new());
self..push(Vec::new());
}
}
/// 增加原始K线 — 单根K线投喂入口
@@ -166,7 +249,7 @@ impl 观察者 {
/// 核心数据处理管道
fn __处理数据(&mut self, K: K线) {
// Step 1: 缠论K线分析 (普K is consumed by 分析 as &mut)
// Step 1: 缠论K线分析
let (_, ) = K线::(
K,
&mut self.K线序列,
@@ -178,92 +261,73 @@ impl 观察者 {
None => return,
};
// Step 2: 笔分析
if self.. {
::(
,
&mut self.,
&mut self.,
&self.K线序列,
&self.K线序列,
0,
&self.,
);
}
// Step 2: 笔分析(无条件)
::(
,
&mut self.,
&mut self.,
&self.K线序列,
&self.K线序列,
0,
&self.,
);
if self..is_empty() {
return;
}
// Step 3: 笔中枢分析
if self.. {
::(&self., &mut self._中枢序列, true, "", 0);
}
// Step 3: 笔中枢分析(无条件)
::(&self., &mut self._中枢序列, true, "", 0);
if self..is_empty() {
return;
}
// Step 4: 线段分析
if self..线 {
线::(
&self.,
&mut self.线,
&self.,
0,
&[::, ::],
);
}
if self..线 {
::(&self.线, &mut self., true, "", 0);
// Step 4: 线段分析 — 3 级递归
for i in 0..self.线 {
if i == 0 {
线::(
&self.,
&mut self.线[i],
&self.,
0,
&[::, ::],
);
} else {
let = self.线[i - 1].clone();
线::(
&,
&mut self.线[i],
&self.,
0,
&[::, ::],
);
}
::(&self.线[i], &mut self.[i], true, "", 0);
}
// Step 5: 扩展线段(笔级)
if self..线 {
线::(&self., &mut self.线, &self.);
}
if self..线 {
::(&self.线, &mut self., true, "", 0);
}
// Step 6: 扩展线段(线段级)
if self..线 {
线::(&self.线, &mut self.线_线段, &self.);
}
if self..线 {
// Step 5: 扩展线段分析 — 3 级递归
for i in 0..self.线 {
if i == 0 {
线::(&self., &mut self.线[i], &self.);
} else {
let = self.线[i - 1].clone();
线::(&, &mut self.线[i], &self.);
}
::(
&self.线_线段,
&mut self._线段,
&self.线[i],
&mut self.[i],
true,
"",
0,
);
}
// Step 7: 线段之线段
if self..线 {
线::(
&self.线,
&mut self.线_线段序列,
&self.,
0,
&[::, ::],
);
}
if self..线 {
::(&self.线_线段序列, &mut self.线_中枢序列, true, "", 0);
}
// Step 8: 扩展线段之扩展线段
if self..线 {
线::(
&self.线,
&mut self.线_扩展线段,
&self.,
);
}
if self..线 {
// Step 6: 混合扩展线段分析 — 3 级递归 (源 = 线段序列组[i])
for i in 0..self.线 {
let = self.线[i].clone();
线::(&, &mut self.线[i], &self.);
::(
&self.线_扩展线段,
&mut self._扩展线段,
&self.线[i],
&mut self.[i],
true,
"",
0,
@@ -279,16 +343,27 @@ impl 观察者 {
self..clear();
self..clear();
self._中枢序列.clear();
self.线.clear();
self..clear();
self.线.clear();
self..clear();
self.线_线段.clear();
self._线段.clear();
self.线_线段序列.clear();
self.线_中枢序列.clear();
self.线_扩展线段.clear();
self._扩展线段.clear();
self.线.clear();
self..clear();
for _ in 0..self.线 {
self.线.push(Vec::new());
self..push(Vec::new());
}
self.线.clear();
self..clear();
for _ in 0..self.线 {
self.线.push(Vec::new());
self..push(Vec::new());
}
self.线.clear();
self..clear();
for _ in 0..self.线 {
self.线.push(Vec::new());
self..push(Vec::new());
}
for i in 1..self.K线序列.len() - 1 {
let = ::new(
@@ -307,102 +382,61 @@ impl 观察者 {
);
}
if self.. {
::(&self., &mut self._中枢序列, true, "", 0);
::(&self., &mut self._中枢序列, true, "", 0);
for i in 0..self.线 {
if i == 0 {
线::(
&self.,
&mut self.线[i],
&self.,
0,
&[::, ::],
);
} else {
let = self.线[i - 1].clone();
线::(
&,
&mut self.线[i],
&self.,
0,
&[::, ::],
);
}
::(&self.线[i], &mut self.[i], true, "", 0);
}
if self..线 {
线::(
&self.,
&mut self.线,
&self.,
0,
&[::, ::],
);
}
if self..线 {
::(&self.线, &mut self., true, "", 0);
}
if self..线 {
线::(&self., &mut self.线, &self.);
}
if self..线 {
::(&self.线, &mut self., true, "", 0);
}
if self..线 {
线::(&self.线, &mut self.线_线段, &self.);
}
if self..线 {
for i in 0..self.线 {
if i == 0 {
线::(&self., &mut self.线[i], &self.);
} else {
let = self.线[i - 1].clone();
线::(&, &mut self.线[i], &self.);
}
::(
&self.线_线段,
&mut self._线段,
&self.线[i],
&mut self.[i],
true,
"",
0,
);
}
if self..线 {
线::(
&self.线,
&mut self.线_线段序列,
&self.,
for i in 0..self.线 {
let = self.线[i].clone();
线::(&, &mut self.线[i], &self.);
::(
&self.线[i],
&mut self.[i],
true,
"",
0,
&[::, ::],
);
}
if self..线 {
::(&self.线_线段序列, &mut self.线_中枢序列, true, "", 0);
}
}
/// 测试_保存数据 — 输出各序列数据文本到文件
pub fn _保存数据(&self, root: Option<&str>) -> String {
let _文本数据: Vec<String> = self..iter().map(|b| b.()).collect();
let 线_文本数据: Vec<String> =
self.线.iter().map(|s| s.()).collect();
let 线_数据文本: Vec<String> =
self.线.iter().map(|s| s.()).collect();
let 线_线段_数据文本: Vec<String> = self
.线_线段
.iter()
.map(|s| s.())
.collect();
let 线_线段序列_数据文本: Vec<String> = self
.线_线段序列
.iter()
.map(|s| s.())
.collect();
let 线_扩展线段_数据文本: Vec<String> = self
.线_扩展线段
.iter()
.map(|s| s.())
.collect();
let _中枢序列_数据文本: Vec<String> =
self._中枢序列.iter().map(|h| h.()).collect();
let _数据文本: Vec<String> =
self..iter().map(|h| h.()).collect();
let _数据文本: Vec<String> =
self..iter().map(|h| h.()).collect();
let _线段_数据文本: Vec<String> = self
._线段
.iter()
.map(|h| h.())
.collect();
let 线_中枢序列_数据文本: Vec<String> = self
.线_中枢序列
.iter()
.map(|h| h.())
.collect();
let _扩展线段_数据文本: Vec<String> = self
._扩展线段
.iter()
.map(|h| h.())
.collect();
// 确定根目录
let = match root {
Some(r) => std::path::PathBuf::from(r),
@@ -420,7 +454,50 @@ impl 观察者 {
return String::new();
}
// 缠K data for debugging
// 辅助:保存序列到文件 (Python: 保存序列(序列))
let =
|: &Vec<Arc<线>>, : &std::path::Path| -> Result<(), std::io::Error> {
if .is_empty() {
return Ok(());
}
let : Vec<String> = .iter().map(|d| d.()).collect();
let = format!("{}.txt", [0]..read().unwrap());
std::fs::write(.join(&), .join("\n") + "\n")?;
Ok(())
};
let =
|: &Vec<Arc<>>, : &std::path::Path| -> Result<(), std::io::Error> {
if .is_empty() {
return Ok(());
}
let : Vec<String> = .iter().map(|h| h.()).collect();
let = format!("{}.txt", [0]..read().unwrap());
std::fs::write(.join(&), .join("\n") + "\n")?;
Ok(())
};
// 保存笔/笔中枢
let _ = (&self., &);
let _ = (&self._中枢序列, &);
// 保存线段组
for i in 0..self.线 {
let _ = (&self.线[i], &);
let _ = (&self.[i], &);
}
// 保存扩展线段组
for i in 0..self.线 {
let _ = (&self.线[i], &);
let _ = (&self.[i], &);
}
// 保存混合扩展线段组
for i in 0..self.线 {
let _ = (&self.线[i], &);
let _ = (&self.[i], &);
}
// 缠K/分型 debug data
let K序列_数据文本: Vec<String> = self
.K线序列
.iter()
@@ -438,6 +515,11 @@ impl 观察者 {
)
})
.collect();
let _ = std::fs::write(
.join("缠K序列_数据文本.txt"),
K序列_数据文本.join("\n") + "\n",
);
let _数据文本: Vec<String> = self
.
.iter()
@@ -454,37 +536,10 @@ impl 观察者 {
)
})
.collect();
let : Vec<(&str, &[String])> = vec![
("笔序列_文本数据", &_文本数据),
("线段序列_文本数据", &线_文本数据),
("扩展线段序列_数据文本", &线_数据文本),
("扩展线段序列_线段_数据文本", &线_线段_数据文本),
("线段_线段序列_数据文本", &线_线段序列_数据文本),
(
"扩展线段序列_扩展线段_数据文本",
&线_扩展线段_数据文本,
),
("笔_中枢序列_数据文本", &_中枢序列_数据文本),
("中枢序列_数据文本", &_数据文本),
("扩展中枢序列_数据文本", &_数据文本),
("扩展中枢序列_线段_数据文本", &_线段_数据文本),
("线段_中枢序列_数据文本", &线_中枢序列_数据文本),
(
"扩展中枢序列_扩展线段_数据文本",
&_扩展线段_数据文本,
),
("缠K序列_数据文本", &K序列_数据文本),
("分型序列_数据文本", &_数据文本),
];
for (, ) in & {
let = .join(format!("{}.txt", ));
let = .join("\n") + "\n";
if let Err(e) = std::fs::write(&, &) {
error!("写入文件失败: {} -> {}", .display(), e);
}
}
let _ = std::fs::write(
.join("分型序列_数据文本.txt"),
_数据文本.join("\n") + "\n",
);
info!("全部数据拆分保存完成,目录:{}", .display());
.display().to_string()
@@ -672,7 +727,7 @@ mod tests {
let obs_ref = obs.read().unwrap();
// 每个线段的基础序列中的笔 Rc 指针必须在 笔序列 中
for (i, seg) in obs_ref.线.iter().enumerate() {
for (i, seg) in obs_ref.线().iter().enumerate() {
for (j, bi_in_seg) in seg..read().unwrap().iter().enumerate() {
let bi_ptr = Arc::as_ptr(bi_in_seg);
let found = obs_ref..iter().any(|b| Arc::as_ptr(b) == bi_ptr);
@@ -706,10 +761,10 @@ mod tests {
}
}
for (i, hub) in obs_ref..iter().enumerate() {
for (i, hub) in obs_ref.().iter().enumerate() {
for (j, seg_in_hub) in hub..read().unwrap().iter().enumerate() {
let seg_ptr = Arc::as_ptr(seg_in_hub);
let found = obs_ref.线.iter().any(|s| Arc::as_ptr(s) == seg_ptr);
let found = obs_ref.线().iter().any(|s| Arc::as_ptr(s) == seg_ptr);
if !found {
info!("线段中枢 {} 的基础序列[{}] 不在线段序列中!", i, j);
}
@@ -738,8 +793,8 @@ mod tests {
let obs = obs_ref.read().unwrap();
(
obs..len(),
obs.线.len(),
obs..len(),
obs.线().len(),
obs.().len(),
obs._中枢序列.len(),
)
};
@@ -778,12 +833,12 @@ mod tests {
}
let = obs_ref.read().unwrap()..len();
let = obs_ref.read().unwrap().线.len();
let = obs_ref.read().unwrap().线().len();
// 重置
obs_ref.write().unwrap().();
assert_eq!(obs_ref.read().unwrap()..len(), 0);
assert_eq!(obs_ref.read().unwrap().线.len(), 0);
assert_eq!(obs_ref.read().unwrap().线().len(), 0);
// 重新投喂
for i in 0..data.len() / size {
@@ -794,7 +849,7 @@ mod tests {
}
let = obs_ref.read().unwrap()..len();
let = obs_ref.read().unwrap().线.len();
let = obs_ref.read().unwrap().线().len();
assert_eq!(, , "重置后重新投喂笔数不一致");
assert_eq!(, , "重置后重新投喂线段数不一致");
@@ -831,7 +886,7 @@ mod tests {
let _短路 = bi..load(Ordering::Relaxed);
let _前一缺口 = *bi..read().unwrap();
}
for seg in &obs_ref.线 {
for seg in obs_ref.线() {
let _ = seg..read().unwrap().clone();
let _ = seg..read().unwrap().len();
}
+7 -7
View File
@@ -96,13 +96,13 @@ fn 测试_读取数据(文件路径: &str) {
println!("分型数量: {}", ..len());
println!("笔数量: {}", ..len());
println!("笔中枢数量: {}", ._中枢序列.len());
println!("线段数量: {}", .线.len());
println!("中枢数量: {}", ..len());
println!("扩展线段数量: {}", .线.len());
println!("线段_线段序列数量: {}", .线_线段序列.len());
println!("线段数量: {}", .线().len());
println!("中枢数量: {}", .().len());
println!("扩展线段数量: {}", .线().len());
println!("线段_线段序列数量: {}", .线_线段序列().len());
println!(
"扩展线段_扩展线段数量: {}",
.线_扩展线段.len()
.线_扩展线段().len()
);
println!("\n===== 保存分析数据 =====\n");
@@ -167,8 +167,8 @@ fn 测试_周期合成(文件路径: &str) {
.K线序列.len(),
..len(),
..len(),
.线.len(),
..len(),
.线().len(),
.().len(),
);
}
}
+30 -581
View File
@@ -46,10 +46,7 @@ from jinja2 import Environment, FileSystemLoader
from pydantic import BaseModel
import backtrader as bt
try:
from chanlun import *
except ImportError:
from chan import *
from chanlun import *
from strategies import *
@@ -321,471 +318,6 @@ class 时间周期:
return 数值 * 60
class BSP点:
"""缠论买卖点(独立于旧买卖点类,对标 CBS_Point)。支持同一笔上叠加多种类型。"""
__slots__ = ("", "K线单元", "是否买点", "类型列表", "关联T1", "特征字典")
def __init__(self, : "虚线", 是否买点: bool, 类型: "买卖点类型", 关联T1: "Optional[BSP点]" = None, 特征字典: dict = None):
self. =
self.K线单元 = ..
self.是否买点 = 是否买点
self.类型列表: List["买卖点类型"] = [类型]
self.关联T1: "Optional[BSP点]" = 关联T1
self.特征字典 = 特征字典 or {}
def 添加类型(self, 类型: "买卖点类型", 关联T1: "Optional[BSP点]" = None):
if 类型 not in self.类型列表:
self.类型列表.append(类型)
if self.关联T1 is None:
self.关联T1 = 关联T1
elif 关联T1 is not None:
assert self.关联T1.K线单元.序号 == 关联T1.K线单元.序号
@property
def 类型字符串(self) -> str:
return ",".join([t.value for t in self.类型列表])
@property
def 备注(self) -> str:
return self.类型字符串
class 买卖点识别器:
"""缠论六类买卖点识别引擎。对标 ./对标/chan.py BuySellPoint 逻辑。"""
_最后确认位置: Dict[str, int] = {} # 对标 last_sure_pos
_BSP1字典: Dict[str, Dict[int, "BSP点"]] = {} # 对标 bsp1_dict: key → 笔序号 → BSP点
# ================================================================
# 主入口
# ================================================================
@staticmethod
def 计算(obs: "观察者") -> None:
if not obs.线段序列 or not obs.普通K线序列:
return
key = obs.标识
最后确认 = 买卖点识别器._最后确认位置.get(key, -1)
# 新确认的线段(仅用于 T1 增量识别,避免重复创建 T1)
新确认线段 = [seg for i, seg in enumerate(obs.线段序列) if i > 最后确认 and seg.特征序列[2] is not None]
if not 新确认线段:
return
买卖点识别器._最后确认位置[key] = obs.线段序列.index(新确认线段[-1])
配置 = obs.配置
if 配置.买卖点_计算线段BSP1:
买卖点识别器._计算线段BSP1(obs, 新确认线段)
# T2 / T3 需要全部已确认线段(含历史),以便在后续笔到达后重新检查
全部已确认 = [seg for seg in obs.线段序列 if seg.特征序列[2] is not None]
全量T1列表 = list(买卖点识别器._BSP1字典.get(key, {}).values()) if 配置.买卖点_依赖T1 else []
if 配置.买卖点_处理BSP2:
买卖点识别器._处理BSP2(obs, 全量T1列表, 全部已确认)
if 配置.买卖点_计算线段BSP3:
买卖点识别器._计算线段BSP3(obs, 全量T1列表, 全部已确认)
# ================================================================
# MACD 指标计算
# ================================================================
@staticmethod
def _计算MACD指标(: "虚线", K线序列: "List[K线]", 方式: str) -> float:
始K = ...标的K线
终K = ...标的K线
try:
始索引 = K线序列.index(始K)
终索引 = K线序列.index(终K)
except ValueError:
return float("inf")
if 方式 == "":
峰值 = 0.0
for i in range(始索引, 终索引 + 1):
k = K线序列[i]
if k.macd and k.macd.MACD柱 is not None:
v = abs(k.macd.MACD柱)
if v > 峰值:
峰值 = v
return 峰值
else:
macd_dict = K线.获取MACD(K线序列, 始K, 终K)
return abs(macd_dict.get("", 0.0))
# ================================================================
# 中枢查找工具
# ================================================================
@staticmethod
def _取线段内部中枢序列(: "虚线", 来源: str) -> "List[中枢]":
if 来源 == "":
return .实_中枢序列
elif 来源 == "":
return .虚_中枢序列
else:
return .合_中枢序列
@staticmethod
def _找多笔中枢(中枢序列: "List[中枢]") -> "Optional[中枢]":
for zs in reversed(中枢序列):
if len(zs) >= 3:
return zs
return None
@staticmethod
def _全局索引(: "虚线", 笔序列: "List[虚线]") -> int:
"""返回笔在全局笔序列中的索引。"""
try:
return 笔序列.index()
except ValueError:
return -1
@staticmethod
def _笔振幅(: "虚线") -> float:
"""笔的振幅 = |武分型特征值 - 文分型特征值|。对标 bi.amp()。"""
return abs(..分型特征值 - ..分型特征值)
@staticmethod
def _有重叠(低1: float, 高1: float, 低2: float, 高2: float) -> bool:
"""两区间是否有交集。对标 has_overlap。"""
return not (高1 < 低2 or 高2 < 低1)
@staticmethod
def _段末同向笔(: "虚线") -> "虚线":
"""对标 seg.end_bi:当前段内最后一个与段方向相同的笔。
.基础序列包含两段笔当前段 + 后一段开头不能直接用 [-1]/[-2]
必须用 分割序列 获取仅当前段的笔
"""
, _, _, _ = 线段.分割序列()
for in reversed():
if .方向 == .方向:
return
return [-1] # 理论上不会到这里,前[-1] 总是同向
# ================================================================
# BSP 创建 / 去重 — 对标 add_bs
# ================================================================
@staticmethod
def _创建BSP(obs: "观察者", 类型: "买卖点类型", : "虚线", 关联T1: "Optional[BSP点]" = None, 特征字典: dict = None):
"""对标 add_bs:创建或追加 BSP 类型(支持一笔多类型叠加)。"""
is_buy = .方向.是否向下()
if .序号 in obs.BSP字典:
exist_bsp = obs.BSP字典[.序号]
assert exist_bsp.是否买点 == is_buy, f"买卖方向冲突: {exist_bsp.类型字符串} vs {类型.value}"
exist_bsp.添加类型(类型, 关联T1)
return exist_bsp
bsp = BSP点(=, 是否买点=is_buy, 类型=类型, 关联T1=关联T1, 特征字典=特征字典)
obs.BSP字典[.序号] = bsp
return bsp
# ================================================================
# T1 / T1P — 对标 cal_seg_bs1point → treat_bsp1 / treat_pz_bsp1
# ================================================================
@staticmethod
def _计算线段BSP1(obs: "观察者", 已确认线段: "List[虚线]") -> "List[BSP点]":
"""一类买卖点(T1中枢突破背离)和一类盘整买卖点(T1P)。对标 cal_seg_bs1point。"""
结果: "List[BSP点]" = []
配置 = obs.配置
来源 = 配置.买卖点_中枢来源
笔序列 = obs.笔序列
for in 已确认线段:
if not .基础序列 or len(.基础序列) < 3:
continue
is_buy = .方向.是否向下()
if not is_buy and not .方向.是否向上():
continue
段末笔 = 买卖点识别器._段末同向笔()
中枢序列 = 买卖点识别器._取线段内部中枢序列(, 来源)
# 确定触发条件 — 对标 cal_single_bs1point
goto_T1 = False
相关中枢 = None
if 中枢序列:
相关中枢 = 中枢序列[-1] # zs_lst[-1]
if len(相关中枢) >= 3: # not is_one_bi_zs
进入笔全局索引 = 买卖点识别器._全局索引(相关中枢[0], 笔序列)
if 进入笔全局索引 > 0:
进入笔 = 笔序列[进入笔全局索引 - 1]
段末笔全局索引 = 买卖点识别器._全局索引(段末笔, 笔序列)
中枢末笔 = 相关中枢[-1]
中枢末笔全局索引 = 买卖点识别器._全局索引(中枢末笔, 笔序列)
bi_out = 笔序列[中枢末笔全局索引 + 1] if 中枢末笔全局索引 >= 0 and 中枢末笔全局索引 + 1 < len(笔序列) else None
中枢到达段末 = (bi_out is not None and bi_out.序号 >= 段末笔.序号) or 中枢末笔.序号 >= 段末笔.序号
if 中枢到达段末 and 段末笔.序号 - 进入笔.序号 > 2:
goto_T1 = True
if goto_T1 and 相关中枢 is not None:
# ---- T1 路径(对标 treat_bsp1----
进入笔全局索引 = 买卖点识别器._全局索引(相关中枢[0], 笔序列)
进入笔 = 笔序列[进入笔全局索引 - 1]
# 突破检查 — 对标 end_bi_break
if is_buy:
if not (段末笔. < 相关中枢.):
continue
else:
if not (段末笔. > 相关中枢.):
continue
# 可选峰值条件 — 对标 out_bi_is_peak
if 配置.买卖点_峰值条件:
if is_buy:
if not all(. >= 段末笔. for in 相关中枢 if .序号 <= 段末笔.序号):
continue
else:
if not all(. <= 段末笔. for in 相关中枢 if .序号 <= 段末笔.序号):
continue
# MACD背离 — 对标 is_divergence
进入指标 = 买卖点识别器._计算MACD指标(进入笔, obs.普通K线序列, 配置.买卖点_计算方式)
离开指标 = 买卖点识别器._计算MACD指标(段末笔, obs.普通K线序列, 配置.买卖点_计算方式)
if not (离开指标 <= 配置.买卖点_背离率 * 进入指标):
continue
类型 = 买卖点类型.T1买 if is_buy else 买卖点类型.T1卖
bsp = 买卖点识别器._创建BSP(obs, 类型=类型, =段末笔, 特征字典={"divergence_rate": 离开指标 / (进入指标 + 1e-7)})
结果.append(bsp)
买卖点识别器._BSP1字典.setdefault(obs.标识, {})[段末笔.序号] = bsp
else:
# ---- T1P 盘整背离路径(对标 treat_pz_bsp1----
离开笔 = 段末笔
段末笔全局索引 = 买卖点识别器._全局索引(段末笔, 笔序列)
if 段末笔全局索引 < 2:
continue
进入笔 = 笔序列[段末笔全局索引 - 2]
if 离开笔.方向 != .方向:
continue
# 创新低/高检查 — 对标 treat_pz_bsp1
if is_buy:
if 离开笔. > 进入笔.:
continue
else:
if 离开笔. < 进入笔.:
continue
# MACD背离 — 对标 in_metric vs out_metric
进入指标 = 买卖点识别器._计算MACD指标(进入笔, obs.普通K线序列, 配置.买卖点_计算方式)
离开指标 = 买卖点识别器._计算MACD指标(离开笔, obs.普通K线序列, 配置.买卖点_计算方式)
if not (离开指标 <= 配置.买卖点_背离率 * 进入指标):
continue
类型 = 买卖点类型.T1P买 if is_buy else 买卖点类型.T1P卖
bsp = 买卖点识别器._创建BSP(obs, 类型=类型, =离开笔, 特征字典={"divergence_rate": 离开指标 / (进入指标 + 1e-7)})
结果.append(bsp)
买卖点识别器._BSP1字典.setdefault(obs.标识, {})[段末笔.序号] = bsp
return 结果
# ================================================================
# T2 / T2S — 对标 treat_bsp2 / treat_bsp2s
# ================================================================
@staticmethod
def _处理BSP2(obs: "观察者", T1列表: "List[BSP点]", 已确认线段: "List[虚线]") -> "List[BSP点]":
"""二类买卖点(T2回调确认)和二类特殊买卖点(T2S多级次二类)。"""
结果: "List[BSP点]" = []
配置 = obs.配置
阈值 = 配置.买卖点_T2_回调阈值
最大层级 = 配置.买卖点_T2S_最大层级
笔序列 = obs.笔序列
for bsp1 in T1列表:
is_buy = bsp1.是否买点
bsp1_笔 = bsp1.
bsp1_笔全局索引 = 买卖点识别器._全局索引(bsp1_笔, 笔序列)
if bsp1_笔全局索引 < 0:
continue
# 对标:break_bi = bi_list[bsp1_bi.idx + 1]; bsp2_bi = bi_list[bsp1_bi.idx + 2]
if bsp1_笔全局索引 + 2 >= len(笔序列):
continue
突破笔 = 笔序列[bsp1_笔全局索引 + 1]
回调笔 = 笔序列[bsp1_笔全局索引 + 2]
# 对标:bsp2_bi.amp() / break_bi.amp() <= max_bs2_rate
突破振幅 = 买卖点识别器._笔振幅(突破笔)
if 突破振幅 == 0:
continue
回调率 = 买卖点识别器._笔振幅(回调笔) / 突破振幅
if 回调率 <= 阈值:
# ---- T2 成立 ----
类型 = 买卖点类型.T2买 if is_buy else 买卖点类型.T2卖
bsp = 买卖点识别器._创建BSP(obs, 类型=类型, =回调笔, 关联T1=bsp1)
结果.append(bsp)
continue
# ---- T2S 多级次二类(对标 treat_bsp2s----
if 最大层级 is None or 最大层级 <= 0:
continue
# 对标:初始重叠检查
if not 买卖点识别器._有重叠(回调笔., 回调笔., 突破笔., 突破笔.):
continue
重叠区低 = max(回调笔., 突破笔.) if is_buy else 回调笔.
重叠区高 = min(回调笔., 突破笔.) if not is_buy else 回调笔.
bias = 2 # 对标:从 bsp2_bi.idx + 2 开始,步进 2
while 回调笔.序号 + bias < len(笔序列):
bsp2s_笔 = 笔序列[买卖点识别器._全局索引(回调笔, 笔序列) + bias]
if 最大层级 is not None and bias // 2 > 最大层级:
break
# 对标:首次重叠建立 _low/_high,之后检查
if bias == 2:
if not 买卖点识别器._有重叠(回调笔., 回调笔., bsp2s_笔., bsp2s_笔.):
break
重叠区低 = max(回调笔., bsp2s_笔.)
重叠区高 = min(回调笔., bsp2s_笔.)
else:
if not 买卖点识别器._有重叠(重叠区低, 重叠区高, bsp2s_笔., bsp2s_笔.):
break
# 对标:bsp2s_break_bsp1 = 不能突破突破笔极值
if is_buy and bsp2s_笔. < 突破笔.:
break
if not is_buy and bsp2s_笔. > 突破笔.:
break
# 对标:回调率检查
bsp2s_回调率 = abs(bsp2s_笔..分型特征值 - 突破笔..分型特征值) / 突破振幅
if bsp2s_回调率 > 阈值:
break
# T2S成立
重叠区低 = max(重叠区低, bsp2s_笔.)
重叠区高 = min(重叠区高, bsp2s_笔.)
类型 = 买卖点类型.T2S买 if is_buy else 买卖点类型.T2S卖
bsp = 买卖点识别器._创建BSP(obs, 类型=类型, =bsp2s_笔, 关联T1=bsp1)
结果.append(bsp)
bias += 2 # 对标:每次+2(同方向笔)
return 结果
# ================================================================
# T3A / T3B — 对标 cal_seg_bs3point → treat_bsp3_after / treat_bsp3_before
# ================================================================
@staticmethod
def _计算线段BSP3(obs: "观察者", T1列表: "List[BSP点]", 已确认线段: "List[虚线]") -> None:
"""三类买卖点:T3A(后方回踩)和T3B(前方反抽)。"""
配置 = obs.配置
来源 = 配置.买卖点_中枢来源
笔序列 = obs.笔序列
for in 已确认线段:
seg_idx = obs.线段序列.index() if in obs.线段序列 else -1
is_buy = .方向.是否向下()
# ================================================================
# T3A:后段中枢回踩 — 对标 treat_bsp3_after
# ================================================================
if seg_idx >= 0 and seg_idx + 1 < len(obs.线段序列):
# 对标:if BSP_CONF.bsp3_follow_1 and bsp1_bi.idx not in bsp_store_flat_dict → skip
bsp1_笔 = 买卖点识别器._段末同向笔()
if not 配置.买卖点_依赖T1 or bsp1_笔.序号 in 买卖点识别器._BSP1字典.get(obs.标识, {}):
后段 = obs.线段序列[seg_idx + 1]
后段内部中枢 = 买卖点识别器._取线段内部中枢序列(后段, 来源)
# 对标:first_zs = next_seg.get_first_multi_bi_zs()
for 中枢_candidate in 后段内部中枢:
if len(中枢_candidate) < 3:
continue
中枢末笔全局索引 = 买卖点识别器._全局索引(中枢_candidate[-1], 笔序列)
bi_out = 笔序列[中枢末笔全局索引 + 1] if 中枢末笔全局索引 >= 0 and 中枢末笔全局索引 + 1 < len(笔序列) else None
if bi_out is None or 买卖点识别器._全局索引(bi_out, 笔序列) + 1 >= len(笔序列):
continue
bsp3_笔 = 笔序列[买卖点识别器._全局索引(bi_out, 笔序列) + 1]
# 对标:bsp3_bi.dir == next_seg.dir → break
if bsp3_笔.方向 == 后段.方向:
break
# 对标:bsp3_back2zs(bsp3_bi, zs)
if is_buy:
if bsp3_笔. < 中枢_candidate.:
continue
else:
if bsp3_笔. > 中枢_candidate.:
continue
# 对标:bs3_peak 检查
if 配置.买卖点_峰值条件:
if is_buy:
if not (bsp3_笔. >= 中枢_candidate.高高):
continue
else:
if not (bsp3_笔. <= 中枢_candidate.低低):
continue
类型 = 买卖点类型.T3A买 if is_buy else 买卖点类型.T3A卖
bsp1 = 买卖点识别器._BSP1字典.get(obs.标识, {}).get(bsp1_笔.序号)
买卖点识别器._创建BSP(obs, 类型=类型, =bsp3_笔, 关联T1=bsp1)
break # 对标:找到一个就跳出
# ================================================================
# T3B:前序买卖点位置向前搜索 — 对标 treat_bsp3_before
# ================================================================
段末笔 = 买卖点识别器._段末同向笔()
段内部中枢 = 买卖点识别器._取线段内部中枢序列(, 来源)
cmp_中枢 = 买卖点识别器._找多笔中枢(段内部中枢)
if cmp_中枢 is None:
continue
# 对标:if BSP_CONF.bsp3_follow_1 and bsp1_bi.idx not in bsp_store_flat_dict → skip
if 配置.买卖点_依赖T1 and 段末笔.序号 not in 买卖点识别器._BSP1字典.get(obs.标识, {}):
continue
bsp1_全局索引 = 买卖点识别器._全局索引(段末笔, 笔序列)
if bsp1_全局索引 < 0:
continue
# 对标:bsp3_peak check on cmp_zs
# 对标:从 bsp1_bi.idx+2 开始,步进 2
for offset in range(2, len(笔序列) - bsp1_全局索引, 2):
bsp3_笔全局索引 = bsp1_全局索引 + offset
if bsp3_笔全局索引 >= len(笔序列):
break
bsp3_笔 = 笔序列[bsp3_笔全局索引]
# 对标:bsp3_back2zs(bsp3_bi, cmp_zs)
if is_buy:
if bsp3_笔. < cmp_中枢.:
continue
else:
if bsp3_笔. > cmp_中枢.:
continue
类型 = 买卖点类型.T3B买 if is_buy else 买卖点类型.T3B卖
bsp1 = 买卖点识别器._BSP1字典.get(obs.标识, {}).get(段末笔.序号)
买卖点识别器._创建BSP(obs, 类型=类型, =bsp3_笔, 关联T1=bsp1)
break # 对标:找到一个就跳出
class 指令:
: Final[str] = "APPEND"
: Final[str] = "MODIFY"
@@ -823,19 +355,18 @@ class 观察者(观察者):
self.数据队列: queue.Queue = 数据队列
super().__init__(符号, 周期, 配置)
self.__终止时间戳: Optional[datetime] = 转化为时间戳(self.配置.手动终止) if self.配置.手动终止 else None
def 识别买卖点(self):
pass
self.买卖点字典 = dict()
@final
def 增加原始K线(self, 普K: K线):
if self.__终止时间戳 and 普K.时间戳 > self.__终止时间戳:
return
super().增加原始K线(普K)
if self.配置.推送K线:
self.报信(普K, 指令.添加("RawBar"), sys._getframe().f_lineno, 周期=普K.周期)
super().增加原始K线(普K)
try:
self.数据队列 and self.数据队列.put((普K.时间戳, 普K.开盘价, 普K., 普K., 普K.收盘价, 普K.成交量, 0))
if self.数据通道 is not None and self.配置.图表展示:
@@ -843,8 +374,6 @@ class 观察者(观察者):
try:
self.图表刷新()
self.识别买卖点()
# 买卖点识别器.计算(self)
# self.标注买卖点()
except:
print("~~~~~~~~~~~~~~", self.当前K线)
traceback.print_exc()
@@ -867,26 +396,12 @@ class 观察者(观察者):
def 重置基础序列(self):
self.买卖点字典 = dict()
self.BSP字典: Dict[int, "BSP点"] = dict()
self._买卖点最后确认线段索引: int = -1
买卖点识别器._最后确认位置.pop(self.标识, None)
买卖点识别器._BSP1字典.pop(self.标识, None)
self._已标注BSP序号: set = set()
super().重置基础序列()
def 读取任意数据(self, 魔法, **魔法参数):
魔法(**魔法参数)
return self
def 加载本地数据(self, 文件路径: str):
self.重置基础序列()
with open(文件路径, "rb") as f:
buffer = f.read()
size = struct.calcsize(">6d")
for i in range(len(buffer) // size):
k线 = K线.读取大端字节数组(buffer[i * size : i * size + size], self.周期)
self.增加原始K线(k线)
def 静态重新分析(self):
self.买卖点字典 = dict()
super().静态重新分析()
@@ -932,50 +447,25 @@ class 观察者(观察者):
if 当前买卖点.买卖点K线.时间戳 not in 活跃时间戳序列:
买卖点序列.add(当前买卖点)
当前买卖点.买卖点K线.买卖点信息.add(当前买卖点.备注)
self.报信(当前买卖点, 指令.添加(当前买卖点.备注), sys._getframe().f_lineno)
def 图表刷新(self):
# ===================== 笔(你原来的写法) =====================
for in self.序列[-3:]:
self.报信(, 指令.添加(.标识), 0)
def 报信(序列):
for 对象 in 序列[-3:]:
self.报信(对象, 指令.添加(对象.标识), 0)
# ===================== 笔中枢 =====================
for 中枢对象 in self.笔_中枢序列[-3:]:
self.报信(中枢对象, 指令.添加(中枢对象.标识), 0)
报信(self.笔序列)
报信(self.笔_中枢序列)
# ===================== 线段、中枢 =====================
for 线段对象 in self.线段序列[-3:]:
self.报信(线段对象, 指令.添加(线段对象.标识), 0)
for 中枢对象 in self.中枢序列[-3:]:
self.报信(中枢对象, 指令.添加(中枢对象.标识), 0)
# ===================== 扩展线段、扩展中枢 =====================
for 线段对象 in self.扩展线段序列[-3:]:
self.报信(线段对象, 指令.添加(线段对象.标识), 0)
for 中枢对象 in self.扩展中枢序列[-3:]:
self.报信(中枢对象, 指令.添加(中枢对象.标识), 0)
# ===================== 扩展线段(线段级)、扩展中枢(线段级) =====================
for 线段对象 in self.扩展线段序列_线段[-3:]:
self.报信(线段对象, 指令.添加(线段对象.标识), 0)
for 中枢对象 in self.扩展中枢序列_线段[-3:]:
self.报信(中枢对象, 指令.添加(中枢对象.标识), 0)
# ===================== 线段线段、线段中枢 =====================
for 线段对象 in self.线段_线段序列[-3:]:
self.报信(线段对象, 指令.添加(线段对象.标识), 0)
for 中枢对象 in self.线段_中枢序列[-3:]:
self.报信(中枢对象, 指令.添加(中枢对象.标识), 0)
# ===================== 扩展线段(扩展级)、扩展中枢(扩展级) =====================
for 线段对象 in self.扩展线段序列_扩展线段[-3:]:
self.报信(线段对象, 指令.添加(线段对象.标识), 0)
for 中枢对象 in self.扩展中枢序列_扩展线段[-3:]:
self.报信(中枢对象, 指令.添加(中枢对象.标识), 0)
for i in range(self.线段分析层次):
报信(self.线段序列[i])
报信(self.中枢序列组[i])
for i in range(self.扩展线段分析层次):
报信(self.扩展线段序列[i])
报信(self.扩展中枢序列组[i])
for i in range(self.混合扩展线段分析层次):
报信(self.混合扩展线段序列组[i])
报信(self.混合扩展中枢序列[i])
# self.将图表数据固化到本地()
@@ -1022,23 +512,6 @@ class 观察者(观察者):
"showLabel": False if 对象.偏移 <= 1 else True,
}
if type(对象) is BSP点:
message["type"] = "shape"
message["cmd"] = 命令.指令.upper()
message["id"] = f"BSP_{id(对象)}"
message["name"] = "arrow_down" if not 对象.是否买点 else "arrow_up"
k线 = 对象.K线单元
message["points"] = [{"time": int(k线.时间戳), "price": k线.分型特征值}]
arrowColor = "#FF2800" if not 对象.是否买点 else "#00FF22"
text = f"{对象.类型字符串}, {对象.特征字典.get('divergence_rate', '')}"
message["overrides"] = {
"color": "#FFA500",
"arrowColor": "#FFA500",
"text": text,
"title": 对象.类型字符串,
"showLabel": True,
}
if type(对象) is 虚线 and 对象.标识 == "" and not self.配置.推送笔:
return
@@ -1084,8 +557,8 @@ class 观察者(观察者):
message["name"] = "trend_line" if type(对象) is not 中枢 else "rectangle"
if 命令.指令 != 指令.:
message["points"] = [
{"time": int(缠论K线.时间戳对齐(self.缠论K线序列, 对象..)), "price": 对象..分型特征值 if type(对象) is not 中枢 else 对象.},
{"time": int(缠论K线.时间戳对齐(self.缠论K线序列, 对象..)), "price": 对象..分型特征值 if type(对象) is not 中枢 else 对象.},
{"time": int(缠论K线.时间戳对齐(self.基础缠K序列, 对象..)), "price": 对象..分型特征值 if type(对象) is not 中枢 else 对象.},
{"time": int(缠论K线.时间戳对齐(self.基础缠K序列, 对象..)), "price": 对象..分型特征值 if type(对象) is not 中枢 else 对象.},
]
linewidths = {"": 1, "线段": 2, "走势": 3, "线段特征": 2}
message["overrides"] = {
@@ -1107,6 +580,9 @@ class 观察者(观察者):
if type(对象) is not 线段特征:
message["overrides"]["text"] = f"{对象.标识} {对象.序号} 周期:{self.周期} {getattr(对象, '四象', '')} {getattr(对象, '特征序列状态', '')} {getattr(对象, '级别', '')} {getattr(对象, '备注', '')}"
if type(对象) is 中枢:
message["overrides"]["text"] = f"{对象.标识} {对象.序号} 周期:{self.周期} 基础序列数量: {len(对象.基础序列)}"
if 对象.标识 in ("线段", "线段<线段>"):
message["overrides"]["text"] = f"{对象.标识} {对象.序号} 周期:{self.周期} {线段.四象(对象)} {线段.特征序列状态(对象)} {getattr(对象, '级别', '')} {getattr(对象, '备注', '')}"
@@ -1133,17 +609,6 @@ class 观察者(观察者):
asyncio.ensure_future(self.数据通道.send_text(json.dumps(message)))
return
def 标注买卖点(self):
"""将 BSP字典 中的买卖点推送到图表。与旧的 添加买卖点/报信 独立。"""
if not self.配置.图表展示 or self.数据通道 is None:
return
已标注 = getattr(self, "_已标注BSP序号", set())
for 笔序号, bsp in self.BSP字典.items():
if 笔序号 in 已标注:
continue
已标注.add(笔序号)
self.报信(bsp, 指令.添加(bsp.备注), sys._getframe().f_lineno)
def 将图表数据固化到本地(self, static_shapes=None):
template_path = "./templates/static.html"
# 初始化 Jinja2 环境,模板目录为当前目录
@@ -1170,7 +635,6 @@ class 观察者(观察者):
全部 = []
for o in self.买卖点字典.values():
全部.extend(o)
全部.extend(self.BSP字典.values())
for 对象 in 全部:
if type(对象) in (, 线段, 中枢, 线段特征):
@@ -1234,23 +698,6 @@ class 观察者(观察者):
static_shapes.append(message)
continue
if type(对象) is BSP点:
message = dict()
message["type"] = "shape"
message["id"] = f"BSP_{id(对象)}"
message["shapeType"] = "arrow_down" if not 对象.是否买点 else "arrow_up"
k线 = 对象.K线单元
message["points"] = [{"time": int(k线.时间戳), "price": k线.分型特征值}]
arrowColor = "#FF2800" if not 对象.是否买点 else "#00FF22"
text = f"{对象.类型字符串}"
message["overrides"] = {
"color": "#FFA500",
"arrowColor": arrowColor,
"text": text,
"title": 对象.类型字符串,
"showLabel": True,
}
static_shapes.append(message)
else:
print(type(对象), 对象)
for item in static_shapes:
@@ -1284,7 +731,7 @@ class 观察者(观察者):
buffer = f.read()
size = struct.calcsize(">6d")
for i in range(len(buffer) // size):
k线 = K线.读取大端字节数组(buffer[i * size : i * size + size], int(周期))
k线 = K线.读取大端字节数组(buffer[i * size : i * size + size], int(周期), 符号)
实例.增加原始K线(k线)
return 实例
@@ -1858,7 +1305,7 @@ def 测试_随机生成(symbol: str = "btcusd", limit: int = 5000, freq: Support
def 魔法():
随机生成实例 = 观察者(symbol + "_gen", 周期=int(freq), 数据通道=ws, 配置=配置)
dt = datetime(2008, 8, 8)
原始K线 = K线.创建普K("随机", dt, 8888.55, 10000.00, 9000.22, 9527.33, 888, 0, int(freq))
原始K线 = K线.创建普K("随机", int(dt.timestamp()), 8888.55, 10000.00, 9000.22, 9527.33, 888, 0, int(freq))
随机生成实例.增加原始K线(原始K线)
for 方向 in 从序列中机选(
int(limit),
@@ -1910,7 +1357,7 @@ class Bitstamp:
for bar in data["data"]["ohlc"]:
K = K线.创建普K(
观察员.符号,
转化为时间戳(int(bar["timestamp"])),
int(bar["timestamp"]),
float(bar["open"]),
float(bar["high"]),
float(bar["low"]),
@@ -2779,6 +2226,7 @@ if __name__ == "__main__":
本地随机 = random.Random(os.urandom(64))
配置 = 随机配置(本地随机)
print(f"[线程{线程编号:02d}] 开始 ...")
print(f"[线程{线程编号:02d}] ", 配置.to_dict())
测试函数 = 测试_随机生成(symbol="btcusd", limit=10000, freq=时间周期.(5), ws=None, 配置=配置)
结果 = 测试函数() # 实际执行
print(f"[线程{线程编号:02d}] 完成 | 笔序列长度: {len(结果.笔序列)}")
@@ -2786,6 +2234,7 @@ if __name__ == "__main__":
print(f"[线程{线程编号:02d}] 异常: {e}")
traceback.print_exc()
start = datetime.now()
# 创建并启动 50 个线程
线程列表 = []
for i in range(1, 51):
@@ -2797,4 +2246,4 @@ if __name__ == "__main__":
for 线程 in 线程列表:
线程.join()
print("\n全部 50 个随机回测线程已完成。")
print("\n全部 50 个随机回测线程已完成。", datetime.now() - start)
+2 -2
View File
@@ -1001,7 +1001,7 @@ class 回测(高级策略基类):
def 检查买信号(self):
if self.p.观察员.笔序列:
k线 = self.p.观察员.缠论K线序列[-1]
self.日志(f"检查买信号 当前笔 {self.p.观察员.笔序列[-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
@@ -1024,7 +1024,7 @@ class 回测(高级策略基类):
def 检查卖信号(self):
if self.p.观察员.笔序列:
self.日志(f"检查卖信号 当前笔 {self.p.观察员.笔序列[-1]}")
# self.日志(f"检查卖信号 当前笔 {self.p.观察员.笔序列[-1]}")
k线 = self.p.观察员.缠论K线序列[-1]
if k线.买卖点信息:
print(f"回测-首 {self.p.观察员.__class__.__name__}", k线.买卖点信息)