Files
chanlun.rs/chan.py-skill.md
2026-06-27 18:17:57 +08:00

37 KiB
Raw Permalink Blame History

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]  # 标识="扩展线段<线段<线段<线段>>>" 再递归

生成流程静态重新分析 中可见):

  1. 笔序列 → 线段.分析() → 线段序列组[0]
  2. 线段序列组[0] → 线段.分析() → 线段序列组[1](线段作为笔再划分)
  3. 线段序列组[1] → 线段.分析() → 线段序列组[2]
  4. 笔序列 → 线段.扩展分析() → 扩展线段序列组[0]
  5. 扩展线段序列组[0] → 线段.扩展分析() → 扩展线段序列组[1]
  6. 扩展线段序列组[1] → 线段.扩展分析() → 扩展线段序列组[2]
  7. 线段序列组[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 且后半段满足特定条件时,将一段拆分为两段:

  1. 分割序列 → 前段+后段
  2. 后段 ≥ 6 个元素且偶数
  3. 后段倒数第3和第1元素方向 = 线段方向
  4. 满足条件 → 拆分为 新段1(后段[:-3]) + 新段2(后段[-3:])
  5. 标记 短路修正=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].武 穿透了 段.文——向上段的"后"终点比"文"还低、或向下段的"后"终点比"文"还高。意味着这段根本没真正转折。

线段.获取内部中枢序列 — 阴阳合三套中枢

输入: 段, 配置
输出: (虚中枢列表, 实中枢列表, 合中枢列表)  # 注释: 阴 阳 合

先用 分割序列 把线段基础序列切成"前"(确认部分)和"后"(暂挂部分),然后分别在三块区域跑中枢识别:

返回值 别称 识别范围 含义
实中枢序列 分割序列的"前" 段已确认部分的中枢——段主体运行阶段形成的
虚中枢序列 分割序列的"后" 段暂挂部分的中枢——离开段所在的
合中枢序列 整个基础序列(前+后) 全景视角——但跨了段边界,含下一段笔

核心用途:给 判断线段内部是否背驰 提供判定依据。

背驰判断的三种情况(对照 判断线段内部是否背驰 源码):

  1. 有阴(暂挂部分非空)→ 段还未确认终点,不判断背驰(直接返回 False)
  2. 有实中枢 → 检查最后一笔是否在末个阳中枢里:
    • 在:取中枢起点前一笔作进入段,最后一笔作离开段 → 按配置模式判断背驰
    • 不在但末中枢有第三买卖线:取倒数第三笔和倒数第一笔比较 → 判断盘整背驰
  3. 无中枢 → 只有 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. 取段.基础序列,若存在 前一结束位置 则从此位置-1处截取
  2. 调用 线段特征.静态分析(基础序列, 段.方向, 四象, 忽视老阴老阳) — 将笔序列做包含处理得到特征序列
  3. 从特征序列提取分型序列
  4. 若首分型方向与段.方向相同(特征分型终结条件)→ 截取基础序列到特征分型终点 + 调用 _设置特征序列

特征序列方向 = 线段方向的翻转。向上线段取向下的特征(调整段),向下线段取向上的特征(反弹段)。

线段特征.静态分析 — 笔的包含处理

将多笔同向特征元素做包含处理(与缠K的包含处理原理相同,但在笔级别操作):

  • 同向特征:取极值合并
  • 反向特征:独立成新元素
  • 老阴/老阳(有缺口)且不忽视时:跳过包含处理

输出一段 List[线段特征],每个特征持有一组被合并的笔。

线段._武斗 — 线段终结判断

输入: 段, 特征, 行号

当特征序列右元素 ≠ None 时,判断线段是否被终结:

  1. 特征序列方向应与线段方向相同(特征分型终结)
  2. 检查特征分型是否有效(顶底交替,特征序列包含处理完毕)
  3. 满足条件 → 用 _武终 终结线段,重置基础序列,开启新段

线段._添加线段 — 含缺口处理

输入: 线段序列, 待添加线段, 配置, 行号

在追加线段前,检查前一段的缺口状态:

  • 若前一段 短路修正=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