拓冰建站拓冰建站
首页 / 资讯中心 / 正文

ChanlunX:缠论代码化实战,从K线包含处理到中枢构建

缠论这套东西做技术分析的人大多绕不开但真正把它落到代码里的人不多。原因很实在手画一笔一线段看着简单一旦品种数量上去、周期切来切去人就废了。ChanlunX 出现的意义本质上是把缠论从看图说话的手艺活拉回到可计算、可校验、可批量复现的工程轨道上。它把 K 线包含处理、分型识别、笔、线段、中枢这一整条链路抽象成函数调用你喂进去的是带时间戳的 OHLC 数据吐出来的是带索引的结构对象。对做量化回测的人来说这意味着缠论分析第一次可以像均线、MACD 那样被塞进策略框架里跑历史数据对只想提高看图效率的人来说它意味着一套稳定的辅助标注工具。这篇文章我不打算讲玄学只讲三件事它背后的计算链路长什么样、三步怎么把它跑起来、以及我在实际调参和排错时踩过哪些坑。1. 为什么缠论分析必须走向代码化1.1 手工标注绕不开的三个硬伤先说最扎心的一个主观性不可消除。同一段 K 线十个人画出来的笔可能有三四种版本差异集中在两个地方——包含关系处理的方向以及分型的取舍标准。有人习惯把所有的顶底分型都留有人只保留有效的那几个还有人画线段时会根据后面的走势回头修改前面的画法。这些分歧在盘后复盘时无所谓反正怎么画都能自圆其说但一旦要拿来做信号就成了灾难因为你根本不知道下一根 K 线出来之后之前那个已经确认的笔会不会被推翻。第二个硬伤是无法回测。技术分析里最值钱的东西不是这个位置是买点而是这个买点在历史上出现了一千次后面的表现分布是什么样的。手工画图做不到这一点你不可能把过去五年的数据一根根画完再统计。而没有统计所有的经验都只是记忆偏差的产物你记住的是那几个赚钱的案例亏钱的早就选择性遗忘了。第三个是时间成本。跟踪五个品种、三个周期就是十五张图。每次结构变化都要重新确认一遍一天下来光看图就能耗掉两三个小时而且状态还会随着疲劳下降。这不是勤奋这是低效。代码化的核心诉求就是从这三件事里把人解放出来。1.2 算法能覆盖缠论的哪些部分这里得先把边界说清楚免得期待错位。缠论里可算法化的部分是它的几何骨架K 线包含关系的合并、顶底分型的识别、笔的成立判定、线段的划分含特征序列处理、中枢的递归构造、以及基于中枢位置的买卖点分类。这些都是有明确规则的形式化定义只要有规则就能写成代码。不太容易算法化的部分是级别的主观选择和市场环境的定性判断。比如同样是 30 分钟图有人按日线级别操作有人按 30 分钟本级操作这个选择取决于你的持仓周期和资金体量代码没法替你决定。还有像当前市场是不是处于极端情绪中这类判断属于定性范畴硬编码进去只会引入噪音。我的建议是把工具当放大镜而不是决策机。ChanlunX 帮你把结构客观地算出来告诉你这里有一个中枢中枢的 ZG 在哪个价位当前价格相对中枢的位置在哪至于要不要动手还是得你自己结合仓位和风险承受度来定。这个定位想清楚了用起来就不会拧巴。2. ChanlunX 的技术底座从原始 K 线到中枢的完整链路2.1 第一道工序K 线包含关系的合并整个缠论计算的入口就是包含处理。包含关系的定义是相邻两根 K 线如果其中一根的高点大于等于另一根的高点同时低点小于等于另一根的低点那么这两根 K 线就构成包含关系。直观理解就是一根 K 线罩住了另一根。处理原则跟当前走势方向绑定。如果前面的走势是向上的就做向上合并合并后的 K 线高点取两者的高者低点也取两者的高者相当于把区间整体上移如果前面走势向下就做向下合并高点取两者的低者低点也取两者的低者。这里的关键细节是第一根 K 线的处理方向如何确定。常见做法是向前寻找第一根不构成包含关系的 K 线用它的相对位置来确定初始方向。如果实在找不到就默认按向上处理。判断方向不能只看相邻两根要看当前合并后的 K 线与上一根已确认的 K 线之间的关系否则会出现方向反复横跳的情况。下面是一段示意性实现接口命名按常见习惯书写实际以项目文档为准import numpy as np import pandas as pd def merge_inclusion(df): K线包含处理返回合并后的K线序列及原始索引映射 high df[high].values low df[low].values idx np.arange(len(df)) m_h, m_l, m_i [], [], [] direction 1 # 1 向上-1 向下 for i in range(len(df)): if len(m_h) 2: m_h.append(high[i]); m_l.append(low[i]); m_i.append(i) continue # 与上一根合并K线比较判断是否包含 prev_h, prev_l m_h[-1], m_l[-1] contain (high[i] prev_h and low[i] prev_l) or \ (high[i] prev_h and low[i] prev_l) if contain: if direction 1: new_h max(high[i], prev_h) new_l max(low[i], prev_l) else: new_h min(high[i], prev_h) new_l min(low[i], prev_l) m_h[-1], m_l[-1] new_h, new_l else: # 更新方向新高则向上新低则向下 if high[i] prev_h and low[i] prev_l: direction 1 elif high[i] prev_h and low[i] prev_l: direction -1 m_h.append(high[i]); m_l.append(low[i]); m_i.append(i) return pd.DataFrame({high: m_h, low: m_l, src_idx: m_i})注意包含处理是不可逆的。合并之后原始 K 线的索引信息一定要保留下来否则后续要把中枢位置映射回真实价格轴时会找不到锚点。我在第一次写的时候就吃了这个亏合并后直接丢了原始索引结果可视化时中枢画在了错误的时间坐标上排查了大半天。2.2 分型识别三根 K 线的相对关系包含处理完之后分型识别就变得很干净了。顶分型的定义是连续三根已合并的K 线中中间那根的高点最高同时低点也最高底分型反之中间那根的高点最低、低点也最低。代码实现上就是一个三窗口的滑动判断但有几个工程细节值得说。第一首尾两根 K 线无法形成分型因为它们凑不齐三根循环边界要处理对。第二顶底分型的识别结果里我建议同时记录分型区间的高低价和对应的原始索引因为这个信息后面判笔、判中枢都要反复用。第三实际数据里会出现连续的同向分型比如连着三个顶分型这时候通常只保留最高的那个其余的需要在判笔阶段过滤掉。def find_fractals(merged): h, l merged[high].values, merged[low].values top, bottom [], [] for i in range(1, len(merged) - 1): if h[i] h[i-1] and h[i] h[i1] and l[i] l[i-1] and l[i] l[i1]: top.append(i) if l[i] l[i-1] and l[i] l[i1] and h[i] h[i-1] and h[i] h[i1]: bottom.append(i) return top, bottom2.3 笔的成立条件里最容易被忽略的细节笔是连接两个相邻的、方向相反的顶底分型的线段但并不是所有相邻的顶底分型都能成笔这中间有硬性间距要求。按较严格的定义常被称为老笔顶分型的最高点与底分型的最低点之间至少要包含 5 根经过包含处理的 K 线含首尾换句话说两个分型之间至少要隔开一根独立 K 线而且不能共用 K 线。实际编码时常见的三条校验是顶分型的最高价必须高于底分型的最高价底分型的最低价必须低于顶分型的最低价方向不能乱两个分型对应索引之间的 K 线数量满足最小间隔顶底必须交替出现出现连续同向分型时只保留极端值。这套判定的工程价值在于它把笔从一种视觉判断变成了可枚举的序列。一旦成了序列你就可以做统计平均一笔持续多少根 K 线、笔的长度分布是什么样、笔的斜率与后续走势有没有相关性。这些都是手工画图完全给不出的信息。我在自己的回测里就发现笔的长度中位数在 30 分钟周期上大概落在 8 到 12 根 K 线之间超过 20 根的长笔之后出现反向笔的概率明显高于平均水平这个观察后来直接变成了我过滤信号的一个条件。2.4 线段划分特征序列是真正的门槛如果说笔还能靠肉眼勉强对齐线段就是分水岭。线段的定义是至少三笔且有重叠但真正决定划分结果的是特征序列这个概念。简单说对于一段向上的线段它的特征序列由所有向下笔构成判断线段是否结束要看这个特征序列有没有形成顶分型结构经过包含处理后。这里面最容易出错的是两件事。一是特征序列的包含处理规则和 K 线包含处理方向相反向上线段的特征序列按向下方向处理包含关系。二是第一种破坏和第二种破坏的区分前者是特征序列出现顶分型且后一笔跌破前低后者是特征序列出现顶分型但没有跌破需要看后续是否形成反向特征序列的分型来确认。我的实操建议是先别急着写线路划分直接把这一段逻辑当作黑盒调用重点验证输出结果和你手工画的能不能对上。选一段结构清晰的行情比如一段明显的单边上涨手工标一遍再跑一遍代码逐个线段端点比对。如果差异超过两个说明参数或包含方向有问题。这一步花两个小时能省下后面二十个小时的怀疑人生。2.5 中枢构造数量的递归与重叠的判定中枢是缠论里定位最核心的构件定义是至少三个连续次级别走势类型的重叠区间。在笔中枢这个层级上就是连续三笔的重叠部分。计算逻辑是取这三笔的高点中枢上沿 ZG 等于这些高点中的最小值取这三笔的低点中枢下沿 ZD 等于这些低点中的最大值。只有 ZG 大于 ZD 时中枢才成立否则说明这三笔没有重叠构不成中枢。中枢形成之后还有延伸的问题。后续的笔如果仍然落在 [ZD, ZG] 区间内中枢就延伸GG 和 DD 会被更新GG 是所有笔高点的最大值DD 是所有笔低点的最小值如果某一笔离开了区间并且随后一笔没能回来中枢就算结束开始计算新的中枢。这一段逻辑看起来简单但边界条件非常多特别是中枢结束的判定不同实现的理解会有差异。概念计算方式常见踩坑点ZG中枢上沿min(各段高点)用笔高低点还是线段高低点必须统一ZD中枢下沿max(各段低点)三笔无重叠时不能强行构造GGmax(各段高点)只在延伸时更新不等于 ZGDDmin(各段低点)与 ZD 混淆是最常见错误3. 三步走实操搭一套能跑起来的缠论分析管线3.1 第一步环境与依赖准备这一步没什么花活但有几个细节会决定后面顺不顺。ChanlunX 的计算核心偏底层实现Python 侧主要负责数据喂入和结果包装所以环境上我建议尽量用干净的解释器环境避免和已有的科学计算库版本打架。基础依赖一般包括数值计算、数据框处理、以及可选的绘图库。# 建议在独立虚拟环境里操作 python -m venv cxl_env source cxl_env/bin/activate # Windows 下用 cxl_env\Scripts\activate pip install numpy pandas matplotlib # 按项目文档安装 ChanlunX 本体装完之后第一件事是做一次冒烟测试拿几十根构造好的 K 线跑一遍看分型识别能不能给出符合直觉的结果。别小看这一步环境层面的编译问题往往在这一关就暴露了早发现早处理。提示如果你的数据里存在停牌、涨跌停一字板这类极端 K 线包含处理的结果会非常集中容易产生大量连续包含导致合并后的序列异常短。这不是 bug是数据特性后面得单独处理。3.2 第二步数据接入与清洗数据质量决定了计算结果的上限这一步我花的精力比写代码还多。需要关注的几个点周期对齐。缠论的级别是递归定义的理论上不同周期之间可以互相推导但实际实现里都是按你给的那个周期的数据直接算。所以给你的数据必须是同一周期的连续序列不能把日线数据混进 30 分钟序列里。复权处理。涉及除权除息的话不复权的价格序列会出现跳空缺口这个缺口会被当作真实的走势方向变化直接污染分型和笔的识别。做回测建议用前复权或后复权具体用哪种取决于你的开仓价计算方式。我个人在做结构分析时用后复权做收益率统计时切回真实价格。缺失与重复。同一时间戳重复出现要先去重缺失的时间戳要看是停牌还是数据源问题。如果是停牌不能用插值补直接留空并让结构计算自然跳过更稳妥。字段规范。统一成 open/high/low/close/volume/datetime 六列high 和 low 要保证满足 high max(open, close) 且 low min(open, close)这个校验能拦下相当一部分脏数据。3.3 第三步计算与可视化数据准备好之后整个链路就是一次顺序调用包含处理 → 分型 → 笔 → 线段 → 中枢。我在实际项目里习惯把它包成一个函数输出一个包含所有结构的字典方便后续复用。import chanlunx as cxl # 示意性接口以实际文档为准 def analyze(df): merged merge_inclusion(df) result cxl.compute(merged) # 一次性算出笔、线段、中枢 return { merged: merged, bi: result.bi, # 笔序列 xd: result.xd, # 线段序列 zs: result.zhongshu, # 中枢序列 buy_sell: result.bs_points # 买卖点分类 } out analyze(df_30m)可视化我一般是三件事叠在一张图上原始 K 线 笔的折线 中枢矩形。中枢画矩形时用 ZG 和 ZD 作为上下边横向范围从中枢起始笔的起始时间到结束笔的结束时间。这一步能直观看出结构和自己的预期是否一致。3.4 结果校验怎么判断算得对不对这一步最容易被跳过但它是整套流程里最有价值的环节。我的做法是双轨校验选三段典型行情单边上涨、单边下跌、震荡分别手工标注笔和中枢然后和代码输出做逐项比对。重点看三处笔的端点数量是否一致、中枢的 ZG/ZD 数值是否接近、中枢的起止位置是否吻合。如果出现系统性偏差比如代码算出的笔总是比你画的少那大概率是最小 K 线间隔的阈值设得太严如果中枢普遍比你画的大可能是包含了不该纳入的笔。这种排查思路比盲调参数高效得多因为它把问题定位到了具体环节。4. 参数调优与信号过滤的实战经验4.1 笔的最小间隔怎么取值这个参数直接决定了结构的粗细。取值偏大笔的数量少结构干净但会漏掉短周期的机会取值偏小笔多且碎噪音信号一大堆。我在 30 分钟和日线两个周期上都做过对比结论是日线以上周期用严格定义短周期可以适度放宽。短周期本身的噪音就大强行用严格标准会导致长段时间里一笔都画不出来图表上看着像是信号消失了实际上是参数把结构过滤掉了。一个实用的做法是把这个参数做成可配置项然后观察它在样本内的信号数量曲线。当你发现参数从 A 变到 B 时信号数量出现断崖式变化那个位置通常就是数据本身的特征边界。4.2 中枢延伸与新生判断趋势还是震荡中枢的处理方式决定了你把行情看成趋势还是震荡。一个中枢不断延伸说明市场在这个区间里反复消化中枢一个接一个地抬升说明是上升趋势。这里的可控点在于中枢结束的判定阈值。如果要求必须有一笔完全离开并且下一笔不回到区间内才算结束中枢会偏长如果允许价格触碰边界就算突破中枢会偏短。我自己的偏好是偏保守宁可让中枢多延伸一会儿也不要频繁切换中枢。因为中枢一旦切换买卖点的判定基准就变了频繁切换会导致信号反复触发又反复失效。4.3 买卖点信号的过滤条件三类买卖点的原始定义是清晰的价格结构描述但直接拿来用会有大量低质量的触发。我常用的几个过滤器幅度过滤离开中枢的那一笔其幅度必须超过中枢高度的一定比例否则视为无效突破时间过滤突破后必须在若干根 K 线内完成回抽确认超过时间窗口的视为结构失效量能配合突破笔对应的成交量相对中枢区间均量有放大多周期共振本级出现信号的同时上一级结构不能是明确的反向中枢延伸中。过滤维度常用阈值作用突破幅度中枢高度的 0.5 到 1 倍剔除假突破确认窗口3 到 8 根 K 线控制信号时效量能比中枢区间均量的 1.3 倍以上确认资金参与上级结构非反向中枢延伸避免逆势这几个条件不需要全开我一般开两个幅度和上级结构。开太多了信号会少到没法用而且容易过拟合到特定行情。5. 常见问题与排查技巧实录5.1 典型问题速查表现象可能原因排查方向一笔都识别不出来最小 K 线间隔阈值过大或数据周期太短打印合并后序列长度看是否够 5 根笔的数量异常多包含处理方向错误导致合并失效检查 direction 的更新逻辑中枢重叠区域为空高低点取法不统一确认 ZG/ZD 是否用同一套笔的高低点中枢时间坐标错位合并后丢失原始索引回溯 src_idx 映射结果每次运行不一致数据未排序或存在重复时间戳先做去重和按时间升序排列线段端点与手工差异大特征序列包含方向写反向上线段按向下方向处理包含5.2 数据层面的坑我遇到过最隐蔽的一个问题是时区。数据源返回的时间戳如果是 UTC而你按本地时间切分交易日会导致每天多出一两根本不属于该交易日的 K 线这种污染在结构上是看不出来的但会让中枢的位置整体偏移。排查方法很简单把数据按日期分组统计每天的 K 线数量如果是标准交易时段数量应该高度一致出现波动就说明切分有问题。另一个高频坑是涨跌停一字板。这种 K 线的高低价完全重合或者与前一根构成极端包含会导致合并后大量 K 线消失。处理方式有两种一是在包含处理时对一字板做特殊标记二是在分析前把这类 K 线剔除并记录避免它们干扰结构判断。我倾向后者因为剔除后的序列更接近真实交易的形态。5.3 计算层面的坑精度问题。浮点数比较在做分型判断时会出现边界误判尤其是当两根 K 线的高点只差万分之几的时候。做法是引入一个极小的容差判断大小关系时用带容差的比较或者干脆把价格按最小变动单位整数化再比较。这个改动很小但能消除相当一部分偶发的分型漏识别。性能问题。分段递归的算法复杂度不低如果是分钟级数据、几年的长度纯 Python 循环会跑到让人失去耐心。可行的优化是把包含处理和分型识别向量化或者只对最新的增量部分做计算历史部分缓存起来。我自己的做法是维护一个结构缓存每来一根新 K 线就只更新最后一段速度能提升一个数量级。状态污染。这个坑比较隐蔽如果你在一个进程里连续分析多个品种而计算对象持有了上一次的状态比如 direction 变量没重置第二个品种的结果就会被第一个污染。表现是单独跑没问题批量跑就乱。解决办法是把所有中间状态封装在函数内部每次调用都从零开始。5.4 几条我自己的使用心得第一条别指望它替代你的判断。工具把结构算准了但它不知道你的资金曲线最近在回撤、也不知道你手上已经有几个同向仓位。我见过有人把信号当成自动交易指令结果在一个震荡区间里被反复打脸问题不在工具在于他把辅助工具当成了决策系统。第二条参数要固定不要频繁调。缠论的参数本质上是结构分辨率的旋钮不是收益率的旋钮。今天调一下明天调一下最后得到的是对历史数据的完美拟合和对未来的完全失效。选定一套参数至少跑完一个完整的市场周期再评估。第三条把结构数据存下来。每次分析算出的笔、线段、中枢我都落库保存因为这些东西的历史序列本身就是极好的研究素材。你可以统计中枢突破后的第 N 根 K 线平均涨跌幅、背驰发生后多久出现反向笔这类问题这些统计结论比任何主观经验都可靠。我做过一轮这样的统计发现中枢突破后的前 3 根 K 线胜率明显高于第 5 到 8 根这个结论后来直接改了我入场时机的选择。最后再分享一个小技巧如果你用的是较短周期建议在计算前先做一次轻度的数据平滑检查——不是去改价格而是检查有没有连续多根 K 线的高低点几乎完全一致。这种形态通常是流动性极差造成的它会让包含处理产生一连串合并最终把一段时间内的结构全部抹平。发现这种情况时宁可把这段数据标记为结构不可靠也不要硬算因为算出来的结果只会误导后面的判断。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门