让缠论的长笔,露出内部的真实结构
「量化实战手记」— 记录从想法到落地的真实开发历程
本文所述「子浪」划分方法,由交易研究者 Coding狂人 发明。fqchan05 将这套理论做成了可运行的工程实现,本文记录其设计、踩坑与真实数据回归。
引言:一根长笔,藏了多少故事
缠论用「笔」描绘价格的转折。两个相邻的顶底分型之间,构成一根笔。这是结构分析的最小单元。
但问题也随之而来——一根跨了 34 根 K 线以上的「大笔」,在图上只是一条直线。中间几十个交易日的真实波动,被压成了一个不起眼的线段。
一只趋势股的长上涨里,藏着两三个回调低点;一只宽幅股的长下跌里,也藏着两三个反弹高点。这些位置,恰恰是交易真正关心的买卖点。把它们抹平,等于丢掉了大笔内部的呼吸。
子浪,正是 Coding狂人 为填补这道缺口而发明的方法:在大笔内部插入更细的结构,让被过滤掉的波动重新参与分析。fqchan05 把它落成了代码。
第一章:浪的初衷与不足
要理解子浪,先回到「浪」(笔)本身的初衷。Coding狂人 讲得很直白——浪是为了降噪:把一幅复杂的 K 线图,分解成直观的线段连线,过滤掉其中的小波动。
这个降噪是有代价的。如果过滤得过头,把本该参与分析的波动也去掉了,分析参考意义就会大大降低。
浪的基本条件,是3 根 K 线有效突破 MA10。这里有两个维度:MA10 衡量空间力度,3 根 K 线衡量时间周期。而这个「3 根」的设计,参考的正是缠论的笔——笔有结合律,两笔之间不少于 3 根 K 线。
问题出在极端行情:某一浪会出现跨周期很长的情况,把其中的波动过滤得太多,可参考的因素随之降低。经反复思考与实验,Coding狂人 推出子浪,来填补浪的不足。
图 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。
图 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 股近一年的日线,按四个维度回归:
前三项干净通过,说明同向择优修复在真实数据上有效——子浪端点确实落在了真正的极值。
但第四项诚实暴露了一个问题:长江电力的某段平台整理(31 根 K 线,27-28 元窄幅徘徊)被拆成了 5 段,相邻落差最低只有 0.30%。原因很清楚——落差约束用的是「严格大于」,只判断「有没有落差」,不判断「落差大不大」。平台里差几分钱的噪音波动也满足严格不等式,照样被拆。
这是一个已知的、被接受的限制。修复 backlog 是给落差约束加一个相对阈值(比如 0.8% 的 min_drop),但目前选择先记录、不修——子浪端点仍然满足交替和落在极值,结构上合规,只是平台噪音拆得细了些。
原则:测试不只验证「能跑」,要验证「在真实数据上行为合理」。诚实地把已知缺陷记进 backlog,比假装完美更有价值——它防止问题被遗忘,也为下一次迭代留下入口。
总结
子浪是 Coding狂人 的发明,初衷是为缠论的长笔找回一个更细的刻度——在大笔内部用 5K 局部极值拆出小笔,但用顶底交替和双向落差守住结构边界。
fqchan05 做的,是把这套定义忠实翻译成工程实现,并在其中补上了「同向择优」这一笔:把「同向跳过」改成「同向择优」,保证子浪端点落在该方向真正极值,不被中途小反弹截断。
真实数据回归则是质量底线——四项指标里三项干净通过,第四项诚实记录为已知限制。而贯穿始终的「非未来函数」特性,保证了这套拆分因果稳定,值得被信任。
核心原则:约束划定边界,择优追求精度,回归守住底线。理论由 Coding狂人 提出,工程由代码落地,三者共同让子浪从「能跑的玩具」变成「能上实盘的工具」。
附录:技术细节
fqchan05 的架构是 C++17 核心 + Cython 封装。缠论计算在 C++ 里跑性能,Python 侧只做接口。
图 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狂人。本文为该理论的工程实现笔记,向原创者致谢。
夜雨聆风