乐于分享
好东西不私藏

FqChan05缠论插件(子浪划分的设计与实现)

FqChan05缠论插件(子浪划分的设计与实现)

让缠论的长笔,露出内部的真实结构

「量化实战手记」— 记录从想法到落地的真实开发历程

本文所述「子浪」划分方法,由交易研究者 Coding狂人 发明。fqchan05 将这套理论做成了可运行的工程实现,本文记录其设计、踩坑与真实数据回归。

引言:一根长笔,藏了多少故事

缠论用「笔」描绘价格的转折。两个相邻的顶底分型之间,构成一根笔。这是结构分析的最小单元。

但问题也随之而来——一根跨了 34 根 K 线以上的「大笔」,在图上只是一条直线。中间几十个交易日的真实波动,被压成了一个不起眼的线段。

一只趋势股的长上涨里,藏着两三个回调低点;一只宽幅股的长下跌里,也藏着两三个反弹高点。这些位置,恰恰是交易真正关心的买卖点。把它们抹平,等于丢掉了大笔内部的呼吸。

子浪,正是 Coding狂人 为填补这道缺口而发明的方法:在大笔内部插入更细的结构,让被过滤掉的波动重新参与分析。fqchan05 把它落成了代码。

第一章:浪的初衷与不足

要理解子浪,先回到「浪」(笔)本身的初衷。Coding狂人 讲得很直白——浪是为了降噪:把一幅复杂的 K 线图,分解成直观的线段连线,过滤掉其中的小波动。

这个降噪是有代价的。如果过滤得过头,把本该参与分析的波动也去掉了,分析参考意义就会大大降低。

浪的基本条件,是3 根 K 线有效突破 MA10。这里有两个维度:MA10 衡量空间力度,3 根 K 线衡量时间周期。而这个「3 根」的设计,参考的正是缠论的笔——笔有结合律,两笔之间不少于 3 根 K 线。

问题出在极端行情:某一浪会出现跨周期很长的情况,把其中的波动过滤得太多,可参考的因素随之降低。经反复思考与实验,Coding狂人 推出子浪,来填补浪的不足。

拆分前:一根大笔VaVb跨 34+ K 线,内部结构被压平拆分后:子浪端点交替

图 1:大笔在子浪拆分前后——一条直线,变成顶底交替的精细结构

原则
:降噪是为了看清结构,但降噪过头会丢失信号。子浪的本质,是在「过滤波动」和「保留信息」之间,为长笔找回一个更细的刻度。

第二章:从理论定义到工程约束

Coding狂人 给出的子浪定义,清晰且可执行。fqchan05 的工作,是把它逐条翻译成代码。

触发条件:某一浪跨周期超过 34 根 K 线,就在这一浪里插入子浪。34 是个有意思的数字——它是 13+21,斐波那契数列里两个相邻项之和,对应一个中等长度的时间窗口。

子浪端点定义:某根 K 线的最高点在 5 根 K 线范围内是最高,记为 A;某根 K 线的最低点在 5 根 K 线范围内是最低,记为 B。把 A、B 连接,就是一根子浪。

双向落差约束——这是子浪有意义的关键,Coding狂人 强调了两点:

  • A 的最高点,必须高于 B 的最高点;
  • A 的最低点,必须高于 B 的最低点。

只有同时满足,才说明 A 和 B 之间出现了真实的高低点波动。否则 B 完全落在 A 的振幅内(无落差),没有分析意义,不拆。

最小距离:A 到 B 至少包含 3 根 K 线。低于 3 根则跨周期太短,瞬间的波动无分析意义——这恰好呼应了缠论笔的结合律。

这四条定义,在 fqchan05 里对应四个配置项与一段约束函数:

// 双向落差约束:A 整体高于 B + 距离达标// 对应 Coding狂人 定义:A.high>B.high && A.low>B.low && 距离≥3bool meets_constraint(prev, curr, high, low, min_bars) {    if (abs(curr.pos - prev.pos) < min_bars) return false;    if (prev.type > 0)  // A(高) → B(低),向下子浪        return high[prev.pos] > high[curr.pos]            && low[prev.pos]  > low[curr.pos];    else                // B(低) → A(高),向上子浪        return low[prev.pos]  < low[curr.pos]            && high[prev.pos] < high[curr.pos];}

还有一个工程上极其重要的特性:子浪是非未来函数。它只参考时间周期(5 根 K 线的窗口),而时间周期一旦出现就不会变化。这意味着无论行情怎么走、回测到哪个时刻,已确认的子浪都不会被推翻——因果稳定,这是它能上实盘的前提。

原则
:约束先于选择。先把 Coding狂人 的四条定义变成硬约束(5K 极值、双向落差、最小距离),再在合法候选里贪心提升,才不会拆出违反结构的乱序笔。非未来函数,则是这一切能被信任的根基。

第三章:贪心提升的实现

核心循环的骨架很简洁:在大笔两端点 Va、Vb 的开区间内,遍历所有局部极值候选,异向且过约束就提升,否则跳过。关键是提升后要把当前端点存为 prev,下一轮用它做参照。

// 贪心提升:强制顶底交替 + 落差约束BiVertex prev = Va;std::vector<BiVertex> promoted;for (auto& c : candidates) {    BiVertex curr{c.pos, (float)c.type};    // 同向候选的处理见第四章    if (c.type == prev.type) continue;    if (!meets_constraint(prev, curr, high, low, min_bars))        continue;    promoted.push_back(curr);    prev = curr;           // 关键:prev 跟随提升}// 末尾修剪:丢弃与 Vb 同向的提升端点

这段循环干净地处理了「异向 + 过约束」的正常路径。但真实数据很快就给它上了一课。

第四章:踩坑——同向次极值截断了真极值

第一版实现里,同方向候选的处理是 if (c.type == prev.type) continue;——无条件跳过。逻辑上看似合理:既然同向,就不该成为新端点。

但真实走势常常这样:价格下跌过程中,先出现一个浅低点 A,满足落差约束,被提升为子浪低点端点;价格继续往下,走到一个更深的低点 B 才真正反转。B 和 A 同向(都是低点),按原逻辑被跳过——于是子浪端点停在 A,错过了真正极值 B。

修复前:次极值 A 截断 BA被提升B同向跳过端点停在 A,错过更深的 B修复后:择优取 BA已舍弃B更极端,胜出端点落在真正的极值

图 2:同向择优——A 先到但较浅,B 更深,修复后 B 取代 A 成为子浪端点

修复方案是「同向择优」:同方向候选如果比已提升端点更极端(低点更低、高点更高),就移动端点位置到真极值,而不是跳过。同时必须同步更新 prev,否则下一轮异向候选的落差约束会基于过期的 A 位置。

// 同向择优判据:候选 c 是否比已提升端点更极端bool is_more_extreme(c, v, high, low) {    if (c.type < 0) return low[c.pos]  < low[v.pos];   // 低点更低    return high[c.pos] > high[v.pos];                  // 高点更高}// 贪心循环里的同向分支(替换原 continue)if (c.type == prev.type) {    if (!promoted.empty()        && is_more_extreme(c, promoted.back(), high, low)) {        promoted.back() = curr;   // 移动到真极值        prev = curr;              // prev 必须同步    }    continue;}

这个修复并未违背 Coding狂人 的原始定义——A、B 的判定和双向落差约束都没有变,只是让同方向的多个候选里,更极端的那个胜出,确保子浪端点真正落在该方向的极值上。

原则
:贪心提升里,同向不是「跳过」而是「择优」。让端点落在该方向真正极值,才不会被中途的小反弹截断——这是子浪精度的关键。

第五章:拿真实 A 股验证

单元测试只验证「能拆」「能交替」。但子浪是要上实盘的,必须在真实数据上证明它行为合理。于是拉了 8 只典型 A 股近一年的日线,按四个维度回归:

指标
含义
结果
顶底交替
拆分后端点严格交替
8/8 通过
极值命中
子浪端点落在 5K 严格局部极值
100%
开关回退
关闭子浪后原笔端点全部保留
8/8 通过
密度合理性
平台/一字板不过度拆分
1 处过度拆分(已知)

前三项干净通过,说明同向择优修复在真实数据上有效——子浪端点确实落在了真正的极值。

但第四项诚实暴露了一个问题:长江电力的某段平台整理(31 根 K 线,27-28 元窄幅徘徊)被拆成了 5 段,相邻落差最低只有 0.30%。原因很清楚——落差约束用的是「严格大于」,只判断「有没有落差」,不判断「落差大不大」。平台里差几分钱的噪音波动也满足严格不等式,照样被拆。

这是一个已知的、被接受的限制。修复 backlog 是给落差约束加一个相对阈值(比如 0.8% 的 min_drop),但目前选择先记录、不修——子浪端点仍然满足交替和落在极值,结构上合规,只是平台噪音拆得细了些。

原则
:测试不只验证「能跑」,要验证「在真实数据上行为合理」。诚实地把已知缺陷记进 backlog,比假装完美更有价值——它防止问题被遗忘,也为下一次迭代留下入口。

总结

子浪是 Coding狂人 的发明,初衷是为缠论的长笔找回一个更细的刻度——在大笔内部用 5K 局部极值拆出小笔,但用顶底交替和双向落差守住结构边界。

fqchan05 做的,是把这套定义忠实翻译成工程实现,并在其中补上了「同向择优」这一笔:把「同向跳过」改成「同向择优」,保证子浪端点落在该方向真正极值,不被中途小反弹截断。

真实数据回归则是质量底线——四项指标里三项干净通过,第四项诚实记录为已知限制。而贯穿始终的「非未来函数」特性,保证了这套拆分因果稳定,值得被信任。

核心原则
:约束划定边界,择优追求精度,回归守住底线。理论由 Coding狂人 提出,工程由代码落地,三者共同让子浪从「能跑的玩具」变成「能上实盘的工具」。

附录:技术细节

fqchan05 的架构是 C++17 核心 + Cython 封装。缠论计算在 C++ 里跑性能,Python 侧只做接口。

C++17 核心zilang.cpp · recognise_zilang · find_local_extremaCython 封装fqchan05.pyx · ChanOptions · zilang_enable 参数Python · fq_recognise_bi(zilang_enable=1)

图 3:fqchan05 分层架构——C++ 算性能,Cython 做桥,Python 出接口

可配置项(ChanOptions):子浪阈值 zilang_threshold(默认 34,对应 13+21)、窗口 zilang_window(默认 5K)、最小距离 zilang_min_bars(默认 3,呼应缠论笔结合律)。

开启方式:调用时传 zilang_enable=1,传 0 则完全回退到原笔行为,向后兼容。

from fqchan05 import fq_recognise_bi# zilang_enable=1 开启子浪,=0 回退原行为bi = fq_recognise_bi(len(high), high, low, close, zilang_enable=1)

量化实战手记

本系列记录作者用代码理解市场的真实历程——每个想法如何变成设计,每个设计如何变成可运行的系统。不谈理论,只聊实战。

子浪理论原创:Coding狂人。本文为该理论的工程实现笔记,向原创者致谢。

加入量化探索之旅

下载:纷传圈子