37 KiB
chan.py — 缠论技术分析库 AI 技能
约 6900 行 Python,从原始 K 线到买卖点的全链路流式缠论分析。所有标识符使用中文。
Q1: 如何从 .nb 文件加载数据并完成分析?
from chanlun import chan
config = chan.缠论配置(标识="btcusd")
obs = chan.观察者.读取数据文件("btcusd-300-xxx.nb", 配置=config)
# 加载即完成全部分析,直接读取结果
obs.笔序列 # List[虚线],标识="笔"
obs.线段序列 # List[虚线],标识="线段"
obs.中枢序列 # List[中枢],标识="线段中枢"
obs.笔_中枢序列 # List[中枢],标识="笔中枢"
obs.分型序列 # List[分型]
obs.缠论K线序列 # List[缠论K线]
# 高级层级
obs.扩展线段序列 # 标识="扩展线段"
obs.扩展中枢序列 # 标识="扩展线段中枢"
obs.线段_线段序列 # 线段作为笔再划分
obs.扩展线段序列_扩展线段 # 扩展线段作为笔再划分
.nb 文件格式:每根 K 线 48 字节大端 struct.pack(">6d"),依次为 时间戳/开/高/低/收/量(均为 float64)。
Q2: 如何流式实时分析?
obs = chan.观察者(符号="btcusd", 周期=300, 配置=config)
# 方式一:投喂原始数据
obs.投喂原始数据(时间戳=datetime.now(), 开=50000, 高=50100, 低=49900, 收=50050, 量=100)
# 方式二:投喂预构建 K 线对象
obs.增加原始K线(k线对象)
# 每次投喂都增量更新全链路:缠K → 分型 → 笔 → 线段 → 中枢
# 结果即刻可用
Q3: 如何做多周期立体分析?
ma = chan.立体分析器(
符号="btcusd",
周期组=[300, 1800], # [输入周期, 显示周期]
配置=config,
配置组={1800: chan.缠论配置(笔内元素数量=7)}, # 可选:按周期覆盖配置
)
# 只投喂最小周期 K 线,合成器自动合成大周期
for kline in klines_300:
ma.投喂K线(kline)
# 获取各周期结果
obs_300 = ma._单体分析器[300]
obs_1800 = ma._单体分析器[1800]
内部机制:
- 内置
K线合成器将小周期 K 线合成为大周期 - 合成器事件回调 → 大周期 K 线完成 → 触发对应
观察者.__处理数据 - 非显示周期的
基础缠K序列引用显示周期的缠论K线序列
Q4: 如何单独使用 K 线合成器?
def 回调(信号类型, 标识, 周期, 完成K线):
"""信号类型: "K线完成" """
print(f"{标识} {周期}s 完成一根K线: {完成K线.收盘价}")
synth = chan.K线合成器(标识="btcusd", 周期组=[300, 900, 1800], 事件回调=回调)
# 投喂最小周期数据
synth.投喂(时间戳, 开, 高, 低, 收, 量)
# 或
synth.投喂K线(普K)
# 查询当前合成中的 K 线
k = synth.获取当前K线(周期=900)
# 支持后续设置回调
synth.设置事件回调(新回调函数)
合成器按 (时间戳 // 周期) * 周期 对齐时间戳到周期边界。
Q5: 缠论配置有哪些关键参数?
config = chan.缠论配置(
标识="btcusd",
# ---- 分析开关(按需关闭以跳过某层级)----
分析笔=True, # 是否分析笔
分析线段=True, # 是否分析线段
分析扩展线段=True, # 是否分析扩展线段
分析笔中枢=True, # 是否分析笔中枢
分析线段中枢=True, # 是否分析线段中枢
计算指标=True, # 是否计算技术指标
# ---- 笔参数(影响笔划分粒度)----
笔内元素数量=5, # 笔内最少缠K数(含端点)
笔内相同终点取舍=False, # True=取最后, False=取第一个
笔内起始分型包含整笔=False,
笔弱化=False, # 笔弱化开关
笔弱化_原始数量=3, # 弱化参考原始K线数量
笔次级成笔=False, # 次级成笔
# ---- 线段参数 ----
线段_非缺口下穿刺=False, # 非缺口状态的贯穿伤回退
线段_特征序列忽视老阴老阳=False, # True=忽视缺口全以无缺口对待
线段_缺口后紧急修正=True, # 缺口后紧急修正
线段_修正=False, # 短路修正(不建议使用)
线段内部中枢图显=True, # 显示线段内部中枢
扩展线段_当下分析=False, # True=以当下分析, False=以事后分析
# ---- 指标参数 ----
指标计算方式="收", # "开"/"高"/"低"/"收"/"高低均值"/"高低收均值"/"开高低收均值"
平滑异同移动平均线_快线周期=13,
平滑异同移动平均线_慢线周期=31,
平滑异同移动平均线_信号周期=11,
相对强弱指数_周期=13,
相对强弱指数_超买阈值=75,
相对强弱指数_超卖阈值=25,
随机指标_RSV周期=13,
随机指标_K值平滑周期=5,
随机指标_D值平滑周期=5,
随机指标_超买阈值=80,
随机指标_超卖阈值=20,
计算BOLL=False, # 是否计算布林带
布林带_周期=20,
布林带_标准差倍数=2.0,
# ---- 多指标参数列表 ----
MACD_参数列表=[], # [(key, 快线, 慢线, 信号), ...]
RSI_周期列表=[], # [(key, 周期), ...]
KDJ_参数列表=[], # [(key, RSV周期, K平滑, D平滑), ...]
BOLL_参数列表=[], # [(key, 周期, 标准差倍数), ...]
均线_类型列表=[], # SMA/EMA 类型列表
均线_周期列表=[], # 对应周期列表
# ---- 买卖点 ----
买卖点偏移=1, # 最大偏移量
买卖点激进识别=False, # 激进模式不检查分型完整性
买卖点与MACD柱强相关=False, # True=卖点需正值 买点需负值
买卖点错过误差值=0.01, # 距离买卖点值的容差
买卖点_指标模式="配置", # "任意"/"配置"/"全量"/"相对"
买卖点_指标匹配_MACD=True,
买卖点_指标匹配_KDJ=True,
买卖点_指标匹配_RSI=True,
# ---- 线段内部背驰 ----
线段内部背驰_MACD=True,
线段内部背驰_斜率=True,
线段内部背驰_测度=True,
线段内部背驰_模式="相对", # "任意"/"配置"/"全量"/"相对"
# ---- 推送/图显 ----
推送K线=True, 推送笔=True, 推送线段=True, 推送中枢=True,
# ...另有 图表展示, 图表展示_笔, _线段, _中枢_* 等 20+ 图显字段
)
全部 74 个字段通过 config.model_fields() 获取。
Q6: 如何创建和对比配置?
# 构造
c1 = chan.缠论配置(笔内元素数量=5, 线段_缺口后紧急修正=True)
c2 = chan.缠论配置(笔内元素数量=7, 线段_缺口后紧急修正=False)
# 差异对比 → {字段名: 新值}
c1.对比(c2) # {"笔内元素数量": 7}
# 深拷贝 + 部分更新
c3 = c1.model_copy(update={"笔内元素数量": 8}, deep=True)
# 创建不推送变体(所有推送标志 = False)
c_no_push = c1.不推送()
# 序列化
c1.to_dict() # dict
c1.to_json() # JSON 字符串
c1.保存配置("path.json")
# 反序列化
chan.缠论配置.from_dict(data)
chan.缠论配置.from_json(json_str)
chan.缠论配置.加载配置("path.json")
# 按序号重组(兼容旧版扁平键名:"1_笔模式" → {1: {笔模式: ...}})
chan.缠论配置.按序号重组字典(默认配置, 原始字典)
Q7: 虚线的标识有哪些?如何区分笔和线段?
虚线 是笔和线段的通用数据结构:
虚线(序号, 标识, 文, 武, 级别, 有效性=True)
# 文=起点分型, 武=终点分型
# 标识类型
"笔" # 来自 笔.分析
"线段" # 来自 线段.分析
"扩展线段" # 来自 线段.扩展分析
"线段_线段" # 线段再做一次线段分析
"扩展线段_扩展线段" # 扩展线段再做一次线段分析
关键属性和方法:
seg.方向 # 相对方向.向上 / 相对方向.向下
seg.高 / seg.低 # max(文,武).高 / min(文,武).低
seg.文 / seg.武 # 起点分型 / 终点分型
seg.笔序列 # 内部笔序列(仅线段有)
seg.之前是(prev) # prev.武 == seg.文 ?
seg.之后是(next) # seg.武 == next.文 ?
seg.获取普K序列(观察员) # 虚线覆盖的原始K线区间
seg.获取缠K序列(观察员) # 虚线覆盖的缠论K线区间
笔和线段类本身只包含 @staticmethod 算法方法,不实例化:
笔.分析(当前分型, 分型序列, 笔序列, 缠K序列, 普K序列, 层级, 配置)
线段.分析(笔序列, 线段序列, 配置, 层级=0)
Q8: 如何判断背驰?
# 需要离开段和进入段(都是虚线对象)
进入段 = obs.线段序列[-2] # 倒数第二段
离开段 = obs.线段序列[-1] # 最后一段
# 单项背驰
chan.背驰分析.MACD背驰(进入段, 离开段, obs.普通K线序列, 方式="总")
chan.背驰分析.斜率背驰(进入段, 离开段)
chan.背驰分析.测度背驰(进入段, 离开段)
# 组合背驰
chan.背驰分析.全量背驰(进入段, 离开段, obs.普通K线序列) # 三项全满足
chan.背驰分析.任意背驰(进入段, 离开段, obs.普通K线序列) # 至少一项
chan.背驰分析.任选背驰(进入段, 离开段, obs.普通K线序列) # 至少两项
# 按配置组合
chan.背驰分析.配置背驰(进入段, 离开段, obs.普通K线序列, config)
chan.背驰分析.背驰模式(进入段, 离开段, obs.普通K线序列, config, "全量")
# 判断线段内部是否背驰
chan.线段.判断线段内部是否背驰(离开段, obs) # 分析段内中枢背驰
MACD 背驰也可通过虚线的类方法直接判断:
chan.虚线.武之全量MACD均值(obs.普通K线序列, 离开段) # 武端MACD < 均值?
chan.虚线.武之MACD均值(obs.普通K线序列, 离开段) # 按方向比较
chan.虚线.武之MACD极值(obs.普通K线序列, 离开段) # 武端是否为极值?
chan.虚线.买卖意义(离开段, obs) # 盘整背驰 → (bool, 描述)
Q9: 中枢如何操作?
# 中枢 = 三段连续虚线重叠区间
hub = chan.中枢.创建(左段, 中段, 右段, 级别=0, 标识="线段中枢")
# 属性
hub.方向 # 基础序列[0].方向.翻转()
hub.高 / hub.低 # 中枢区间: min(前三段.高) / max(前三段.低)
hub.高高 / hub.低低 # 全部延伸段的最高/最低
hub.离开段 # 基础序列[-1]
hub.第三买卖线 # 第三类买卖点参考线
hub.当前状态() # "中枢之中" / "中枢之上" / "中枢之下"
# 延伸与扩展
hub._添加虚线(新段) # 中枢延伸
hub.获取扩展中枢(扩展中枢列表, config) # 9段以上获得扩展中枢
# 类方法
chan.中枢.基础检查(左, 中, 右) # 三段是否首尾相连
chan.中枢.从序列中获取中枢(虚线序列, 起始方向, 标识)
chan.中枢.分析(虚线序列, 中枢序列) # 自动识别中枢
Q10: 如何判断中枢的第三类买卖点?
第三类买卖点出自中枢"离开-回抽不破":
# 分割序列获取第三买卖线
前, 后, 第三买卖线, 贯穿伤 = chan.线段.分割序列(离开段, 所属中枢=hub)
# 第三买卖线 != None → 存在第三类买卖点
# 或通过中枢完整性验证
hub.完整性(虚实="实") # 验证第三买卖点是否有效
Q11: 买卖点如何生成?
观察者.识别买卖点() 当前为空实现(pass)。买卖点类型已定义,可手动创建:
# 买卖点类型枚举 (18种)
chan.买卖点类型.一买 / chan.买卖点类型.一卖
chan.买卖点类型.二买 / chan.买卖点类型.二卖
chan.买卖点类型.三买 / chan.买卖点类型.三卖
chan.买卖点类型.类一买 / chan.买卖点类型.类一卖
chan.买卖点类型.类二买 / chan.买卖点类型.类二卖
chan.买卖点类型.T1B买 / chan.买卖点类型.T1B卖
chan.买卖点类型.T2B买 / chan.买卖点类型.T2B卖
chan.买卖点类型.T3B买 / chan.买卖点类型.T3B卖
# 工厂方法
chan.买卖点.一买点(买卖点分型, 当前K线, 标识, 备注, 中枢破位值)
chan.买卖点.一卖点(买卖点分型, 当前K线, 标识, 备注, 中枢破位值)
# ... 二买点 / 二卖点 / 三买点 / 三卖点 / ...
# 属性
bsp.类型 # 买卖点类型枚举
bsp.当前K线 # 关联K线
bsp.买卖点分型 # 关联分型
bsp.破位值 # 中枢破位值
bsp.有效性 # 是否有效
bsp.偏移 / bsp.失效偏移
Q12: 技术指标如何计算和访问?
指标通过 指标计算器 自动挂载到每根 K 线的 指标 容器:
# 自动挂载(观察者内部调用)
指标计算器.计算并挂载(当前K线, 全序列, config)
# 访问指标
k线.指标.macd # 平滑异同移动平均线 或 None
k线.指标.rsi # 相对强弱指数 或 None
k线.指标.kdj # 随机指标 或 None
k线.指标.boll # 布林带 或 None
# MACD 字段
macd.DIF / macd.DEA / macd.MACD柱 / macd.快线EMA / macd.慢线EMA
# 手动计算(不依赖观察者)
macd1 = 平滑异同移动平均线.首次计算(收盘价, 时间戳, 快线周期=12, 慢线周期=26, 信号周期=9)
macd2 = 平滑异同移动平均线.增量计算(macd1, 新收盘价, 新时间戳)
# 或从 K 线直接计算
macd = 平滑异同移动平均线.首次计算_K线(k线, 计算方式="收盘价")
macd = 平滑异同移动平均线.增量计算_K线(前MACD, k线, 计算方式="收盘价")
# RSI / KDJ / BOLL 同理
rsi = 相对强弱指数.首次计算(收盘价, 时间戳, 周期=14)
rsi = 相对强弱指数.增量计算(前RSI, 新收盘价, 新时间戳)
kdj = 随机指标.首次计算(最高价, 最低价, 收盘价, 时间戳, N=9, M1=3, M2=3)
kdj = 随机指标.增量计算(前KDJ, 最高价, 最低价, 收盘价, 时间戳)
boll = 布林带.首次计算(k线, 计算方式="收盘价", 周期=20, 标准差倍数=2.0)
boll = 布林带.增量计算(前BOLL, k线, 计算方式="收盘价")
配置变更后需回填指标:
指标计算器._回填新指标(全序列, config) # 重算所有K线的指标
Q13: 如何验证双端(Rust/Python)分析结果一致?
from chanlun import chan as chan_rs # Rust 绑定
from chanlun import chan as chan_py # Python 参考
# 逐类型相等性检查(返回 (bool, 原因))
chan.K线相等(k_rs, k_py, 浮点容差=1e-9)
chan.缠论K线相等(ck_rs, ck_py, 浮点容差=1e-9)
chan.分型相等(fx_rs, fx_py, 浮点容差=1e-9)
chan.虚线相等(dl_rs, dl_py, 浮点容差=1e-9)
chan.中枢相等(hub_rs, hub_py, 浮点容差=1e-9)
chan.观察者相等(obs_rs, obs_py, 浮点容差=1e-9)
chan.立体分析器相等(ma_rs, ma_py, 浮点容差=1e-9)
所有相等函数返回 (True, "") 或 (False, "差异描述")。
Q14: K 线如何创建和序列化?
# 创建
k = chan.K线.创建普K(
标识="btcusd", 时间戳=datetime.now(),
开盘价=50000, 最高价=50100, 最低价=49900, 收盘价=50050,
成交量=100, 序号=0, 周期=300,
)
# 属性
k.方向 # 相对方向(开盘价 vs 收盘价)
k.指标 # 指标容器
# 二进制序列化 (48字节大端,兼容 Rust)
data = bytes(k) # __bytes__ → struct.pack(">6d")
# 从二进制解析
k2 = chan.K线.读取大端字节数组(data, 周期=300, 标识="btcusd")
# 批量保存/加载
chan.K线.保存到DAT文件("output.dat", k线列表)
# 区间截取
subset = chan.K线.截取(序列, 始=k起始, 终=k结束)
# 区间MACD面积
chan.K线.获取MACD(序列, 始, 终) # {"MACD正面积": ..., "MACD负面积": ...}
Q15: 缠论K线包含处理逻辑是什么?
# 缠论K线.分析 的返回值
状态, 分型 = chan.缠论K线.分析(当前K线, 缠K序列, 普K序列, config)
# 状态值含义
"缠K完成" # 形成独立的缠论K线,可能伴随顶/底分型
"包含处理中" # 当前K线被包含处理,未形成独立缠K
"等" # 等待更多K线(如方向未确定)
缠K 方向由包含处理决定,有向上和向下两种状态。包含处理规则:
- 同向:取 高者之高、低者之低(向上)/ 高者之低、低者之高(向下)
- 异向:先确定方向,再按方向处理
Q16: 分型如何识别?
# 分型由左中右三根缠K构成
fx = chan.分型(左=缠K_左, 中=缠K_中, 右=缠K_右)
fx.结构 # 分型结构.顶分型 / 分型结构.底分型 / 分型结构.三连向上 / 分型结构.三连向下
# 分型结构分析
chan.分型结构.分析(左, 中, 右, 可以逆序包含=False, 忽视顺序包含=False)
# 返回: 三连向上 / 三连向下 / 顶分型 / 底分型 / 向右扩散 / None
# 分型辅助方法
chan.分型.判断分型(左分型, 右分型, 模式="中")
chan.分型.从缠K序列中获取分型(缠K序列, 中间缠K)
chan.分型.向序列中添加(分型序列, 新分型) # 自动处理冲突
# 分型属性
fx.分型特征值 # 中.高 + 中.低 * sign(顶=-1, 底=+1)
fx.关系组 # (左→中, 中→右, 左→右) 相对方向元组
fx.强度 # 高低差比例
Q17: 如何获取虚线范围内的所有停顿位置?
# 获取线段中所有笔的端点停顿位置
停顿 = chan.线段.获取所有停顿位置(段, 观察员)
# 返回笔级虚线列表,可用于判断中枢区间
# 四象归类
象 = chan.线段.四象(段)
# "老阳" / "小阳" / "少阴" / "老阴"
Q18: 数据如何保存和恢复?
# 观察者保存到 .nb 文件(48字节/根格式)
path = obs.测试_保存数据(root="/tmp") # 返回文件路径
# 从 .nb 文件恢复
obs.加载本地数据(path)
# 立体分析器保存
ma.测试_保存数据(root="/tmp")
# 静态重新分析(从已有K线全量重建所有序列)
obs.静态重新分析()
Q19: 相对方向的判断逻辑?
chan.相对方向.分析(前高, 前低, 后高, 后低)
# 返回: 向上 / 向下 / 包含 / 向上缺口 / 向下缺口 / 衔接
# 方向翻转
方向.翻转() # 向上 ←→ 向下
# 判断方法
方向.是否向上() / 方向.是否向下()
方向.是否包含() / 方向.是否缺口() / 方向.是否衔接()
Q20: 常见错误排查
| 现象 | 可能原因 |
|---|---|
| 笔序列为空 | 配置.分析笔=False 或笔内缠K不足(需 ≥ 笔内元素数量) |
| 线段序列为空 | 配置.分析线段=False 或笔序列长度不足 |
| 中枢序列为空 | 虚线序列长度不足 3 段,或三段不重叠 |
| 指标为 None | 指标未挂载,需先调用 指标计算器.计算并挂载 |
| 配置变更不生效 | 需要 指标计算器._回填新指标(全序列, 配置) 和 obs.静态重新分析() |
| 买卖点序列为空 | 识别买卖点() 是空实现,需自行调用 买卖点.生成买卖点 |
| 多周期结果不一致 | 检查 配置组 是否覆盖了对应周期的配置 |
| 浮点比较不通过 | 双端一致性测试使用 浮点容差=1e-9 |
Q21: 三个线段序列组有什么不同?分别用于什么场景?
观察者 内部有三条并行的多级序列树,每条树有 3 层(由 线段分析层次=3 控制):
# 1. 线段序列组 — 标准线段递归
obs.线段序列组[0] # 标识="线段" 笔→线段(标准)
obs.线段序列组[1] # 标识="线段<线段>" 线段→线段(段作为笔再划段)
obs.线段序列组[2] # 标识="线段<线段<线段>" 再递归一层
# 2. 扩展线段序列组 — 扩展分析递归
obs.扩展线段序列组[0] # 标识="扩展线段" 笔→扩展线段
obs.扩展线段序列组[1] # 标识="扩展线段<扩展线段>" 扩展段→段
obs.扩展线段序列组[2] # 标识="扩展线段<扩展线段<扩展线段>>" 再递归
# 3. 混合扩展线段序列组 — 混合递归(扩展线段分析标准线段)
obs.混合扩展线段序列组[0] # 标识="扩展线段<线段>" 线段→扩展线段
obs.混合扩展线段序列组[1] # 标识="扩展线段<线段<线段>>" 线段<线段>→扩展线段
obs.混合扩展线段序列组[2] # 标识="扩展线段<线段<线段<线段>>>" 再递归
生成流程(静态重新分析 中可见):
- 笔序列 →
线段.分析()→ 线段序列组[0] - 线段序列组[0] →
线段.分析()→ 线段序列组[1](线段作为笔再划分) - 线段序列组[1] →
线段.分析()→ 线段序列组[2] - 笔序列 →
线段.扩展分析()→ 扩展线段序列组[0] - 扩展线段序列组[0] →
线段.扩展分析()→ 扩展线段序列组[1] - 扩展线段序列组[1] →
线段.扩展分析()→ 扩展线段序列组[2] - 线段序列组[i] →
线段.扩展分析()→ 混合扩展线段序列组[i]
实际验证(2500 根 BTCUSD 300s K 线):
线段序列组: 26段, 3段<线段>, 无更高级
扩展线段序列组: 48段, 14段<扩展>, 4段<扩展<扩展>>
混合扩展线段组: 8段<线段>, 1段<线段<线段>>
关键差异:
线段.分析要求笔/线段之间方向交替(上下上下)线段.扩展分析允许同向连续(将同向虚线合并处理),因此产生更多扩展段
等效属性访问(@property):
obs.线段序列 = obs.线段序列组[0]
obs.线段_线段序列 = obs.线段序列组[1]
obs.扩展线段序列 = obs.扩展线段序列组[0]
obs.扩展线段序列_扩展线段 = obs.扩展线段序列组[1]
obs.混合扩展线段序列 = obs.混合扩展线段序列组[0]
Q22: 特征序列是什么?老阴/老阳/小阳/少阴如何区分?
特征序列是线段划分算法的核心概念。当从笔划分线段时,每根笔的方向与线段方向之间有一个关系:
- 线段方向向上 → 特征序列取每笔的"向下"特征(特征方向=向下)
- 线段方向向下 → 特征序列取每笔的"向上"特征(特征方向=向上)
特征序列用于处理缺口和包含:多笔同特征方向的要素需要先做包含处理,再通过特征序列的分型来判断线段是否终结。
线段特征(线段特征 类)是对应笔的抽象:
线段特征(标识, 基础序列, 线段方向)
# 持有一组同向虚线(笔),是线段划分的中间结构
# 特征.方向 = 线段方向的翻转
# 特征.高/特征.低 → 按线段方向取极值
四象描述线段与缺口的关系:
线段.四象(段)
# "老阳": 向下线段 + 存在前一缺口(向下线段后有向上缺口)
# "老阴": 向上线段 + 存在前一缺口(向上线段后有向下缺口)
# "小阳": 向上线段 + 无缺口(普通向上)
# "少阴": 向下线段 + 无缺口(普通向下)
老阴老阳在特征序列中的作用:
当 线段_特征序列忽视老阴老阳=False(默认)时:
- 老阳/老阴的特征序列不参与包含处理——缺口状态下的特征序列元素直接跳过包含合并
- 相当于"缺口破坏了特征序列的连续性"
当 线段_特征序列忽视老阴老阳=True 时:
- 忽略缺口,所有特征序列元素都按无缺口处理(严格包含)
- 实际效果:线段和中枢数量都增加(更多特征序列参与包含处理,产生更多分型终结)
验证数据(1500 根 K 线):
忽视老阴老阳=False: 线段数=374, 中枢数=48
忽视老阴老阳=True: 线段数=390, 中枢数=56 (+16段, +8中枢)
Q23: 笔弱化、笔次级成笔到底改变了什么?
笔弱化 (笔弱化=True)
当一笔只有 3 个内部缠K(武.中.序号 - 文.中.序号 + 1 == 3)且下一分型直接穿透它时:
- 向上笔 + 下一底分型低于该笔的低点 → 弹出这笔 → 重分析
- 向下笔 + 下一顶分型高于该笔的高点 → 弹出这笔 → 重分析
效果是移除过短的阻挡笔,让分型能连接更合理的笔。实测效果:
1500根K线:
笔弱化=False: 笔数=2796, 线段数=374
笔弱化=True: 笔数=3630, 线段数=500
5000根K线:
笔弱化=False: 笔数=9358, 线段数=1261
笔弱化=True: 笔数=11878, 线段数=1678
笔弱化开启后笔数和线段数都显著增加(~30%),因为移除阻挡笔后产生了更多有效笔和段。
笔弱化_原始数量 (笔弱化_原始数量=3)
控制笔弱化判断时参考的原始K线数量。不同的值对结果影响:
笔弱化_原始数量=3: 笔数=3556
笔弱化_原始数量=5: 笔数=3556 (无变化)
笔弱化_原始数量=7: 笔数=3556 (无变化)
当前实现中此参数变化不明显,因为笔弱化主要受"原始分型数量为3"这个硬编码条件驱动。
笔次级成笔 (笔次级成笔=False)
控制是否在笔的分析中启用次级递归成笔。默认关闭。实测效果:
1500根K线:
笔次级成笔=False: 笔数=2796, 线段数=374
笔次级成笔=True: 笔数=3228, 线段数=468 (+432笔, +94段)
开启后笔数和线段数都有明显增加,因为允许在笔内部递归划分子笔,产生更多有效分型。
Q24: 线段修正算法 _修正 / _缺口突破 / _非缺口下穿刺 做什么?
_修正 (短路修正, 配置.线段_修正)
当线段基础序列 ≥ 9 且后半段满足特定条件时,将一段拆分为两段:
- 分割序列 → 前段+后段
- 后段 ≥ 6 个元素且偶数
- 后段倒数第3和第1元素方向 = 线段方向
- 满足条件 → 拆分为 新段1(后段[:-3]) + 新段2(后段[-3:])
- 标记
短路修正=True,清空老阴老阳段的缺口
_缺口突破 (缺省启用,无配置开关)
处理有缺口的线段序列:
- 当前线段特征序列右元素 ≠ None
- 特征序列方向与线段方向匹配
- 连续特征序列长度 ≥ 5 且奇数
- 弹出当前段 → 根据特征分型终结点切分为两段
_非缺口下穿刺 (配置.线段_非缺口下穿刺)
处理"贯穿伤":当一段线段的基础序列被另一段完全穿透时修复。只有在小阳/少阴(非缺口)状态下才触发。
Q25: 买卖点的实际集成路径是什么?为什么识别买卖点()是空的?
观察者.识别买卖点() 是 pass 占位——具体买卖点识别逻辑原本计划实现但未在 chan.py 中完成。
但这不意味着买卖点不可用——可以通过以下路径手动生成:
# 路径1:手动创建(已有工厂方法)
bsp = chan.买卖点.一卖点(
买卖点分型=obs.分型序列[-1], # 或中枢离开段终点的分型
当前K线=obs.普通K线序列[-1],
标识="btcusd-300",
备注="第一类卖点",
中枢破位值=hub.低, # 中枢下沿
)
# 路径2:通过生成买卖点路由
chan.买卖点.生成买卖点(
特征="一卖", # 特征字符串路由到工厂
序号="1",
级别="线段",
买卖点分型=fx,
当前缠K=ck,
)
# 路径3:中枢完整性验证 → 判断第三买卖点
hub.完整性(虚实="实") # 检查是否有第三买卖点条件
买卖点类型的完整列表(18种):
一买/一卖 第一类(中枢背驰转折)
二买/二卖 第二类(回抽不进中枢)
三买/三卖 第三类(离开后回抽不破中枢)
T1买/T1卖 第一类扩展
T1P买/T1P卖 盘整型第一类
T2买/T2卖 第二类扩展
T2S买/T2S卖 强势第二类
T3A买/T3A卖 第三类A型
T3B买/T3B卖 第三类B型
Rust 绑定层(chanlun-py)中的 识别买卖点 已实现完整逻辑,使用 生成买卖点 路由。
内部算法机制
以下方法不是公开 API,但理解它们的逻辑才能理解线段为什么这样划分、中枢为什么这样识别。
线段.分割序列 — 用终点切开基础序列
输入: 段 (虚线), 所属中枢 (可选)
输出: (前序列, 后序列, 第三买卖线列表, 贯穿伤)
切割规则:遍历段.基础序列,找到第一笔满足 笔.文 is 段.武 的位置——从此笔开始全部归入"后",之前的归入"前"。
为什么:线段的终点分型(武)同时是下一笔的起点分型(文)。段.武 为起点的笔暂挂在当前段的基础序列里,但它已属于"下一段"范畴。分割序列区分"已确认"和"暂挂"。
9 处调用各自取什么:
| 调用方 | 取值 | 用途 |
|---|---|---|
获取内部中枢序列 |
前, 后 | 分别在前后找中枢 |
_缺口突破 |
[0] 前 |
只在确认部分判断缺口 |
_缺口后紧急修正 |
[1] 后 |
检查暂挂部分是否需要修正 |
_修正 |
前, 后 | 后≥6且方向一致 → 拆段 |
分析(递归) |
[1] 后 |
用暂挂部分做下一轮划分 |
判断线段内部是否背驰 |
前=阳, 后=阴 | 进入段 vs 离开段 |
获取所有停顿位置 |
前, 后 | 前后端点都算停顿 |
获取数据文本 |
全部 | 格式化输出 |
贯穿伤:后[0].武 穿透了 段.文——向上段的"后"终点比"文"还低、或向下段的"后"终点比"文"还高。意味着这段根本没真正转折。
线段.获取内部中枢序列 — 阴阳合三套中枢
输入: 段, 配置
输出: (虚中枢列表, 实中枢列表, 合中枢列表) # 注释: 阴 阳 合
先用 分割序列 把线段基础序列切成"前"(确认部分)和"后"(暂挂部分),然后分别在三块区域跑中枢识别:
| 返回值 | 别称 | 识别范围 | 含义 |
|---|---|---|---|
实中枢序列 |
阳 | 分割序列的"前" | 段已确认部分的中枢——段主体运行阶段形成的 |
虚中枢序列 |
阴 | 分割序列的"后" | 段暂挂部分的中枢——离开段所在的 |
合中枢序列 |
合 | 整个基础序列(前+后) | 全景视角——但跨了段边界,含下一段笔 |
核心用途:给 判断线段内部是否背驰 提供判定依据。
背驰判断的三种情况(对照 判断线段内部是否背驰 源码):
- 有阴(暂挂部分非空)→ 段还未确认终点,不判断背驰(直接返回 False)
- 有实中枢 → 检查最后一笔是否在末个阳中枢里:
- 在:取中枢起点前一笔作进入段,最后一笔作离开段 → 按配置模式判断背驰
- 不在但末中枢有第三买卖线:取倒数第三笔和倒数第一笔比较 → 判断盘整背驰
- 无中枢 → 只有 3 笔时判断盘整背驰
阴阳为什么分开:同一个段内,前半(阳)是已完成的走势,后半(阴)是终点出现后的暂挂部分。背驰判断时,取阳中枢的起点前一笔作"进入段"、阳的最后一笔作"离开段",比较两者是否发生趋势衰减——这是段内部是否背驰的判定基础。
实测验证(2000 根 K 线):
段6 [向上] 10笔: 实中枢1个[114596-114189] 虚中枢1个[115382-114523] 合中枢1个[114596-114189]
段7 [向下] 10笔: 实中枢1个[115382-114523] 虚中枢0个 合中枢2个[115382-114523][113297-112218]
段8 [向上] 8笔: 实中枢0个 虚中枢1个[111409-111008] 合中枢2个[113641-112754][111409-111008]
注意段7:虚中枢为空但合中枢有2个——因为跨"前/后"边界的笔凑出了合中枢里的第二个。但后部的笔属于下一段,这个中枢不应该算在此段头上。判断单段是否趋势,应该用实中枢,不能用合中枢:实中枢只看"前"(本段自己的笔),不会混入下一段的结构。
实测对比(2000根K线):
段7: 实中枢=1(盘整) vs 合中枢=2(趋势) ← 合中枢误判
段8: 实中枢=0(盘整) vs 合中枢=2(趋势) ← 合中枢误判
两例都被合中枢误标为趋势,实际是盘整。
Q26: 如何判断线段内部是趋势还是盘整?
虚, 实, 合 = chan.线段.获取内部中枢序列(段, 观察员.配置)
if len(实) >= 2:
if 段.方向.是否向上():
return "上涨趋势"
else:
return "下跌趋势"
else:
return "盘整"
趋势必须有方向——段方向是向上 + 实中枢≥2 = 上涨趋势,段方向向下 + 实中枢≥2 = 下跌趋势。
为什么是实中枢,不是合中枢?
分割序列 把基础序列切成"前"(本段)和"后"(下一段起始)。合中枢在"前+后"上跑,会混入下一段的笔。段7 前7笔只有1个实中枢,但跨边界的笔在合中枢凑出第2个——这个多出来的中枢不属于此段。
辅助信号:
| 信号 | 上涨趋势 | 下跌趋势 | 盘整 |
|---|---|---|---|
| 段.方向 | 向上 | 向下 | 皆可 |
| 实中枢数 | ≥2 | ≥2 | ≤1 |
买卖意义 |
可能 False | 可能 False | 可能 True(盘整背驰) |
判断线段内部是否背驰 |
可能 True | 可能 True | True=盘整衰竭 |
线段._刷新特征序列 — 线段级别包含处理
输入: 段, 配置
- 取段.基础序列,若存在
前一结束位置则从此位置-1处截取 - 调用
线段特征.静态分析(基础序列, 段.方向, 四象, 忽视老阴老阳)— 将笔序列做包含处理得到特征序列 - 从特征序列提取分型序列
- 若首分型方向与段.方向相同(特征分型终结条件)→ 截取基础序列到特征分型终点 + 调用
_设置特征序列
特征序列方向 = 线段方向的翻转。向上线段取向下的特征(调整段),向下线段取向上的特征(反弹段)。
线段特征.静态分析 — 笔的包含处理
将多笔同向特征元素做包含处理(与缠K的包含处理原理相同,但在笔级别操作):
- 同向特征:取极值合并
- 反向特征:独立成新元素
- 老阴/老阳(有缺口)且不忽视时:跳过包含处理
输出一段 List[线段特征],每个特征持有一组被合并的笔。
线段._武斗 — 线段终结判断
输入: 段, 特征, 行号
当特征序列右元素 ≠ None 时,判断线段是否被终结:
- 特征序列方向应与线段方向相同(特征分型终结)
- 检查特征分型是否有效(顶底交替,特征序列包含处理完毕)
- 满足条件 → 用
_武终终结线段,重置基础序列,开启新段
线段._添加线段 — 含缺口处理
输入: 线段序列, 待添加线段, 配置, 行号
在追加线段前,检查前一段的缺口状态:
- 若前一段
短路修正=True→ 缺口 = None - 否则 → 调用
获取缺口(前一段) - 将缺口存入新段的
前一缺口
缺口影响四象判断和后续的特征序列包含处理。
笔._相对关系 — 判断笔是否反向
输入: 筆, 配置
验证笔的方向是否"真正"匹配分型方向。如果 笔内起始分型包含整笔=True,会用分型的左中右三根缠K构造缺口区间,判断区间方向与笔方向是否一致。防止"分型形态在但实际价格不匹配"的假笔。
笔弱化触发条件
if 配置.笔弱化 and 笔序列:
前一笔.武.中.序号 - 前一笔.文.中.序号 + 1 == 3: # 只有3根内部缠K
if (向上笔 and 前一笔.低 > 当前分型.分型特征值 and 当前分型是底) or
(向下笔 and 前一笔.高 < 当前分型.分型特征值 and 当前分型是顶):
弹出旧笔 → 递归重分析
只有最短的笔(3缠K)且被下一分型完全穿透时才会触发弱化。这就是为什么笔弱化能增加 30% 的笔——它移除了那些"看起来像笔但实际上阻挡了更合理划分"的过短笔。
中枢._校验合法性 — 确保中枢不包含已删除的元素
遍历中枢.基础序列,检查每个元素是否仍存在于源序列中。如果某段已被线段修正删除,中枢需要修剪。这保证了中枢始终反映当前有效的线段结构。
中枢.当前状态 — 判断价格相对中枢的位置
if 中枢.高 >= 尾部.分型特征值 >= 中枢.低 → "中枢之中"
elif 中枢.高 < 尾部.分型特征值 → "中枢之上"
elif 中枢.低 > 尾部.分型特征值 → "中枢之下"
第三买卖点判断依赖此状态——只有离开中枢(之上/之下)后才可能产生第三类买卖点。
类型速查
| 类型 | 职责 | 关键方法 |
|---|---|---|
K线 |
OHLCV 数据 + 指标容器 | 创建普K, 读取大端字节数组, 截取 |
缠论K线 |
包含处理后的K线 | 分析, 创建缠K, 与MACD柱子匹配 |
分型 |
顶/底分型 | 从缠K序列中获取分型, 向序列中添加 |
分型结构 |
分型形态枚举 | 分析 |
虚线 |
笔/线段数据结构 | 创建笔, 创建线段, 买卖意义, 武之MACD极值 |
笔 |
笔算法 (@staticmethod) | 分析 |
线段 |
线段算法 (@staticmethod) | 分析, 扩展分析, 判断线段内部是否背驰 |
中枢 |
三段重叠区间 | 创建, 分析, 获取扩展中枢, 当前状态 |
买卖点 |
买卖点工厂 | 一买点~T3B卖点 18 个工厂方法 |
背驰分析 |
背驰判断 (@staticmethod) | MACD背驰, 斜率背驰, 测度背驰, 全量背驰 |
观察者 |
单周期分析器 | 投喂原始数据, 读取数据文件, 静态重新分析 |
立体分析器 |
多周期分析器 | 投喂K线, _单体分析器 |
K线合成器 |
周期合成 | 投喂, 投喂K线, 获取当前K线 |
缠论配置 |
全局参数 | to_dict, from_dict, model_copy, 对比, 不推送 |
指标计算器 |
指标挂载 | 计算并挂载, _回填新指标 |
均线工具 |
SMA/EMA 辅助 | 增量SMA, 增量EMA |