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

976 lines
37 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# chan.py — 缠论技术分析库 AI 技能
> 约 6900 行 Python,从原始 K 线到买卖点的全链路流式缠论分析。所有标识符使用中文。
---
## Q1: 如何从 .nb 文件加载数据并完成分析?
```python
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: 如何流式实时分析?
```python
obs = chan.观察者(符号="btcusd", 周期=300, 配置=config)
# 方式一:投喂原始数据
obs.投喂原始数据(时间戳=datetime.now(), =50000, =50100, =49900, =50050, =100)
# 方式二:投喂预构建 K 线对象
obs.增加原始K线(k线对象)
# 每次投喂都增量更新全链路:缠K → 分型 → 笔 → 线段 → 中枢
# 结果即刻可用
```
---
## Q3: 如何做多周期立体分析?
```python
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 线合成器?
```python
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: 缠论配置有哪些关键参数?
```python
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: 如何创建和对比配置?
```python
# 构造
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: 虚线的标识有哪些?如何区分笔和线段?
`虚线` 是笔和线段的通用数据结构:
```python
虚线(序号, 标识, , , 级别, 有效性=True)
# 文=起点分型, 武=终点分型
# 标识类型
"笔" # 来自 笔.分析
"线段" # 来自 线段.分析
"扩展线段" # 来自 线段.扩展分析
"线段_线段" # 线段再做一次线段分析
"扩展线段_扩展线段" # 扩展线段再做一次线段分析
```
关键属性和方法:
```python
seg.方向 # 相对方向.向上 / 相对方向.向下
seg. / seg. # max(文,武).高 / min(文,武).低
seg. / seg. # 起点分型 / 终点分型
seg.笔序列 # 内部笔序列(仅线段有)
seg.之前是(prev) # prev.武 == seg.文 ?
seg.之后是(next) # seg.武 == next.文 ?
seg.获取普K序列(观察员) # 虚线覆盖的原始K线区间
seg.获取缠K序列(观察员) # 虚线覆盖的缠论K线区间
```
笔和线段类本身只包含 `@staticmethod` 算法方法,不实例化:
```python
.分析(当前分型, 分型序列, 笔序列, 缠K序列, 普K序列, 层级, 配置)
线段.分析(笔序列, 线段序列, 配置, 层级=0)
```
---
## Q8: 如何判断背驰?
```python
# 需要离开段和进入段(都是虚线对象)
进入段 = 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 背驰也可通过虚线的类方法直接判断:
```python
chan.虚线.武之全量MACD均值(obs.普通K线序列, 离开段) # 武端MACD < 均值?
chan.虚线.武之MACD均值(obs.普通K线序列, 离开段) # 按方向比较
chan.虚线.武之MACD极值(obs.普通K线序列, 离开段) # 武端是否为极值?
chan.虚线.买卖意义(离开段, obs) # 盘整背驰 → (bool, 描述)
```
---
## Q9: 中枢如何操作?
```python
# 中枢 = 三段连续虚线重叠区间
hub = chan.中枢.创建(左段, 中段, 右段, 级别=0, 标识="线段中枢")
# 属性
hub.方向 # 基础序列[0].方向.翻转()
hub. / hub. # 中枢区间: min(前三段.高) / max(前三段.低)
hub.高高 / hub.低低 # 全部延伸段的最高/最低
hub.离开段 # 基础序列[-1]
hub.第三买卖线 # 第三类买卖点参考线
hub.当前状态() # "中枢之中" / "中枢之上" / "中枢之下"
# 延伸与扩展
hub._添加虚线(新段) # 中枢延伸
hub.获取扩展中枢(扩展中枢列表, config) # 9段以上获得扩展中枢
# 类方法
chan.中枢.基础检查(, , ) # 三段是否首尾相连
chan.中枢.从序列中获取中枢(虚线序列, 起始方向, 标识)
chan.中枢.分析(虚线序列, 中枢序列) # 自动识别中枢
```
---
## Q10: 如何判断中枢的第三类买卖点?
第三类买卖点出自中枢"离开-回抽不破"
```python
# 分割序列获取第三买卖线
, , 第三买卖线, 贯穿伤 = chan.线段.分割序列(离开段, 所属中枢=hub)
# 第三买卖线 != None → 存在第三类买卖点
# 或通过中枢完整性验证
hub.完整性(虚实="实") # 验证第三买卖点是否有效
```
---
## Q11: 买卖点如何生成?
`观察者.识别买卖点()` 当前为空实现(`pass`)。买卖点类型已定义,可手动创建:
```python
# 买卖点类型枚举 (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 线的 `指标` 容器:
```python
# 自动挂载(观察者内部调用)
指标计算器.计算并挂载(当前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线, 计算方式="收盘价")
```
配置变更后需回填指标:
```python
指标计算器._回填新指标(全序列, config) # 重算所有K线的指标
```
---
## Q13: 如何验证双端(Rust/Python)分析结果一致?
```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 线如何创建和序列化?
```python
# 创建
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线包含处理逻辑是什么?
```python
# 缠论K线.分析 的返回值
状态, 分型 = chan.缠论K线.分析(当前K线, 缠K序列, 普K序列, config)
# 状态值含义
"缠K完成" # 形成独立的缠论K线,可能伴随顶/底分型
"包含处理中" # 当前K线被包含处理,未形成独立缠K
"等" # 等待更多K线(如方向未确定)
```
缠K 方向由包含处理决定,有向上和向下两种状态。包含处理规则:
- 同向:取 高者之高、低者之低(向上)/ 高者之低、低者之高(向下)
- 异向:先确定方向,再按方向处理
---
## Q16: 分型如何识别?
```python
# 分型由左中右三根缠K构成
fx = chan.分型(=缠K_左, =缠K_中, =缠K_右)
fx.结构 # 分型结构.顶分型 / 分型结构.底分型 / 分型结构.三连向上 / 分型结构.三连向下
# 分型结构分析
chan.分型结构.分析(, , , 可以逆序包含=False, 忽视顺序包含=False)
# 返回: 三连向上 / 三连向下 / 顶分型 / 底分型 / 向右扩散 / None
# 分型辅助方法
chan.分型.判断分型(左分型, 右分型, 模式="中")
chan.分型.从缠K序列中获取分型(缠K序列, 中间缠K)
chan.分型.向序列中添加(分型序列, 新分型) # 自动处理冲突
# 分型属性
fx.分型特征值 # 中.高 + 中.低 * sign(顶=-1, 底=+1
fx.关系组 # (左→中, 中→右, 左→右) 相对方向元组
fx.强度 # 高低差比例
```
---
## Q17: 如何获取虚线范围内的所有停顿位置?
```python
# 获取线段中所有笔的端点停顿位置
停顿 = chan.线段.获取所有停顿位置(, 观察员)
# 返回笔级虚线列表,可用于判断中枢区间
# 四象归类
= chan.线段.四象()
# "老阳" / "小阳" / "少阴" / "老阴"
```
---
## Q18: 数据如何保存和恢复?
```python
# 观察者保存到 .nb 文件(48字节/根格式)
path = obs.测试_保存数据(root="/tmp") # 返回文件路径
# 从 .nb 文件恢复
obs.加载本地数据(path)
# 立体分析器保存
ma.测试_保存数据(root="/tmp")
# 静态重新分析(从已有K线全量重建所有序列)
obs.静态重新分析()
```
---
## Q19: 相对方向的判断逻辑?
```python
chan.相对方向.分析(前高, 前低, 后高, 后低)
# 返回: 向上 / 向下 / 包含 / 向上缺口 / 向下缺口 / 衔接
# 方向翻转
方向.翻转() # 向上 ←→ 向下
# 判断方法
方向.是否向上() / 方向.是否向下()
方向.是否包含() / 方向.是否缺口() / 方向.是否衔接()
```
---
## Q20: 常见错误排查
| 现象 | 可能原因 |
|------|----------|
| 笔序列为空 | `配置.分析笔=False` 或笔内缠K不足(需 ≥ 笔内元素数量) |
| 线段序列为空 | `配置.分析线段=False` 或笔序列长度不足 |
| 中枢序列为空 | 虚线序列长度不足 3 段,或三段不重叠 |
| 指标为 None | 指标未挂载,需先调用 `指标计算器.计算并挂载` |
| 配置变更不生效 | 需要 `指标计算器._回填新指标(全序列, 配置)``obs.静态重新分析()` |
| 买卖点序列为空 | `识别买卖点()` 是空实现,需自行调用 `买卖点.生成买卖点` |
| 多周期结果不一致 | 检查 `配置组` 是否覆盖了对应周期的配置 |
| 浮点比较不通过 | 双端一致性测试使用 `浮点容差=1e-9` |
---
## Q21: 三个线段序列组有什么不同?分别用于什么场景?
`观察者` 内部有三条并行的多级序列树,每条树有 3 层(由 `线段分析层次=3` 控制):
```python
# 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):
```python
obs.线段序列 = obs.线段序列组[0]
obs.线段_线段序列 = obs.线段序列组[1]
obs.扩展线段序列 = obs.扩展线段序列组[0]
obs.扩展线段序列_扩展线段 = obs.扩展线段序列组[1]
obs.混合扩展线段序列 = obs.混合扩展线段序列组[0]
```
---
## Q22: 特征序列是什么?老阴/老阳/小阳/少阴如何区分?
**特征序列**是线段划分算法的核心概念。当从笔划分线段时,每根笔的方向与线段方向之间有一个关系:
- **线段方向向上** → 特征序列取每笔的"向下"特征(特征方向=向下)
- **线段方向向下** → 特征序列取每笔的"向上"特征(特征方向=向上)
特征序列用于处理**缺口**和**包含**:多笔同特征方向的要素需要先做包含处理,再通过特征序列的分型来判断线段是否终结。
**线段特征**`线段特征` 类)是对应笔的抽象:
```python
线段特征(标识, 基础序列, 线段方向)
# 持有一组同向虚线(笔),是线段划分的中间结构
# 特征.方向 = 线段方向的翻转
# 特征.高/特征.低 → 按线段方向取极值
```
**四象**描述线段与缺口的关系:
```python
线段.四象()
# "老阳": 向下线段 + 存在前一缺口(向下线段后有向上缺口)
# "老阴": 向上线段 + 存在前一缺口(向上线段后有向下缺口)
# "小阳": 向上线段 + 无缺口(普通向上)
# "少阴": 向下线段 + 无缺口(普通向下)
```
**老阴老阳在特征序列中的作用**
`线段_特征序列忽视老阴老阳=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 中完成**。
但这不意味着买卖点不可用——可以通过以下路径手动生成:
```python
# 路径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: 如何判断线段内部是趋势还是盘整?
```python
, , = 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% 的笔——它移除了那些"看起来像笔但实际上阻挡了更合理划分"的过短笔。
### 中枢._校验合法性 — 确保中枢不包含已删除的元素
遍历中枢.基础序列,检查每个元素是否仍存在于源序列中。如果某段已被线段修正删除,中枢需要修剪。这保证了中枢始终反映当前有效的线段结构。
### 中枢.当前状态 — 判断价格相对中枢的位置
```python
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` |