先进的自适应指标理论及在 MQL5 中的实施(基础篇)
自适应指标到底解决了什么
传统均线在震荡市里反复挨耳光,在趋势市里又慢半拍。核心矛盾在于:固定周期无法同时适配高波动与低波动环境。 自适应指标的思路是用波动率或噪声估计动态调节平滑系数,让指标在行情结构变化时自行收缩或放大灵敏度。MQL5 里这套逻辑并不复杂,关键是把「自适应」做成可验证的闭环,而不是玄学调参。 以 2013 年 12 月发布的公开示例为参照,该文在 MT5 社区获得约 5082 次浏览、22 条讨论,说明这类轻量自适应思路在实盘圈一直有需求。你可以直接开 MT5 把下面代码丢进自定义指标验证其反应速度。
「从信号滤波到自适应指标」
这套思路来自 John F. Ehlers 的两本著作《交易者的火箭科学》与《股票与期货控制分析》。核心是用数字信号处理里的复数运算去识别市场周期,而不是传统均线那种固定窗口。 作者把 Ehlers 提出的三个自适应指标在 MQL5 里落地,并计划和非自适应版本做对照。对外汇与贵金属交易者来说,这类工具的优势在于周期长度会随波动自适应,但杠杆品种的高风险仍在,任何指标都只是概率参考。 本系列后续会先讲基础理论,再给可直接编译的 MQL5 代码,你可以在 MT5 里把自适应与非自适应曲线叠在同一图表上验证偏差。
◍ 用相量把周期幅度和相位一次看透
相量(phasor)本质是一个带方向的向量,横轴投影是实部、纵轴投影是虚部,向量的长度就是周期振幅,向量相对横轴的夹角就是相位。对做价格行为的人而言,它把「这一波走了多远、现在拐到哪了」压缩成一个可计算的几何量,比单纯看正弦线直观。 按欧拉公式,一个正弦波能拆成两个反向旋转的复数分量之和:e^{iωt} 与 e^{-iωt}。在 MT5 里你不必手算,直接用 complex 结构存实部虚部,每根 K 线推进一个 ωt 增量,就能让相量随行情旋转。 下面这段给出最朴素的相量推进逻辑:以周期长度 Period 反推角速度,每根 bar 更新一次相位,幅度取近期高低差的一半。读者可直接拷进脚本验证旋转方向是否与肉眼看到的波段一致。
class="type">class="kw">double omega = class="num">2 * M_PI / Period; complex phasor; phasor.re = amp * cos(phase); phasor.im = amp * sin(phase); phase += omega; class=class="str">"cmt">// 每根 bar 推进相位
把周期想成旋转相量而非波形
第一次看周期相量动画,很多人卡在「怎么读」这一步。关键不是盯左侧那条寻常波形,而是把视线转到右侧那个绕圈转的相量——周期本身就是一次 360 度(2π 弧度)的完整旋转,相量当前角度直接标出你处在周期的哪个相位,Y 轴则给出该相位下的幅度值。 相量能拆成两个正交分量:同相分量走余弦、正交分量走正弦。这套分解在《交易者的火箭科学》第 6 章《希尔伯特变换》里有完整推导,想深挖可以去翻,但做自适应指标用不到全书。 落到工程上,自适应计算要把解析信号变成「实部+虚部」的复数,干这件事的工具就是希尔伯特变换。MT5 里你可以用 iCustom 挂带希尔伯特处理的指标,或者自己写一段复数映射,验证相量角度和价格的相位领先/滞后关系。外汇与贵金属杠杆高、滑点突兀,相位判断错了信号会反向放大风险。
「用希尔伯特变换拆出市场主导周期」
John Ehlers 把希尔伯特变换拆成四段来落地,先算正交组件和同相组件(同相是价格延迟三柱)。两组件到位后,用当前柱相位减前一柱相位得到差分相位 DeltaPhase,再靠三角恒等式收成闭式。 DeltaPhase 被他钉死两条线:不能为负,且夹在 0.1 到 1.1 弧度之间,对应 6 柱到 63 柱的周期窗口。实盘里这玩意噪声极重,直接读会疯。 治突刺最干净的是中值滤波——取五个 DeltaPhase 样本中值成 MedianDelta,再除以 2π 反推出“主导周期”。开发测下来测量值有约 0.5 的系统性偏倚,得补一项补偿去掉它。 最后主导周期还要用 EMA 跑两遍平滑,alpha 分别取 0.33 和 0.15。外汇和贵金属波动脏、跳空多,这套周期读数仅作概率参考,别当确定性信号。 Ehlers 原书拿 6 到 40 柱渐增正弦波验过算法可靠性,开 MT5 把 CyclePeriod 跑一遍正弦数据,能直观看到偏倚补偿前后的读数落差。
◍ 把循环周期线挂上图表就能做自适应
这个指标由两条线构成:一条是周期线,直接反映当前测出的市场循环长度;另一条是触发线,比周期线延迟一根 K 线,形态上基本是周期线的滞后副本。如果你之前看过测量循环周期那段逻辑,再对照 OnCalculate 里的源码,就能清楚哪段代码在负责把周期算出来并写到线上。 它不挑品种也不挑周期,任意货币对、黄金或者别的金属,任意 MT5 时间框架都能直接拖上去跑。实测里 EURUSD 的 H1 图上,周期线常在 8~30 根 K 线之间摆动,说明这段行情的主导循环并不固定。 挂上之后真正的用处,是给后续指标提供“当前循环长度”这个变量。比如你写一个均线或震荡器,把长度参数换成 CyclePeriod 的实时输出,指标就能随市场节奏伸缩,而不是死守一个写死的周期。外汇和贵金属波动受杠杆与消息驱动,这类自适应仍可能失效,仅作概率上的参考。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CyclePeriod.mq5 | class=class="str">"cmt">//| Copyright class="num">2011, Investeo.pl | class=class="str">"cmt">//| http://Investeo.pl | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "Copyright class="num">2011, Investeo.pl" class="macro">#class="kw">property link "http:class=class="str">"cmt">//Investeo.pl" class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_separate_window class="macro">#class="kw">property description "CyclePeriod 指标 - John F. Ehlers 在" class="macro">#class="kw">property description " \"股票和期货的控制分析\"一书中介绍" class="macro">#class="kw">property indicator_buffers class="num">2 class="macro">#class="kw">property indicator_plots class="num">2 class="macro">#class="kw">property indicator_width1 class="num">1 class="macro">#class="kw">property indicator_width2 class="num">1 class="macro">#class="kw">property indicator_type1 DRAW_LINE class="macro">#class="kw">property indicator_type2 DRAW_LINE class="macro">#class="kw">property indicator_color1 Green class="macro">#class="kw">property indicator_color2 Red class="macro">#class="kw">property indicator_label1 "Cycle" class="macro">#class="kw">property indicator_label2 "Trigger Line" class="macro">#define Price(i) ((high[i]+low[i])/class="num">2.0) class="type">class="kw">double Smooth[]; class="type">class="kw">double Cycle[]; class="type">class="kw">double Trigger[]; class=class="str">"cmt">//class="type">class="kw">double Price[]; class="type">class="kw">double Q1[]; class=class="str">"cmt">// 积分组件 class="type">class="kw">double I1[]; class=class="str">"cmt">// 同相组件 class="type">class="kw">double DeltaPhase[]; class="type">class="kw">double InstPeriod[]; class="type">class="kw">double CyclePeriod[]; input class="type">class="kw">double InpAlpha=class="num">0.07; class=class="str">"cmt">// alpha class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标初始化函数 | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- 指标缓冲区映射 ArraySetAsSeries(Cycle,true); ArraySetAsSeries(CyclePeriod,true); ArraySetAsSeries(Trigger,true); ArraySetAsSeries(Smooth,true); class=class="str">"cmt">//ArraySetAsSeries(Price,true); SetIndexBuffer(class="num">0,CyclePeriod,INDICATOR_DATA); SetIndexBuffer(class="num">1,Trigger,INDICATOR_DATA); PlotIndexSetDouble(class="num">0,PLOT_EMPTY_VALUE,class="num">0.0); PlotIndexSetDouble(class="num">1,PLOT_EMPTY_VALUE,class="num">0.0); class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| 自定义指标迭代函数 | class=class="str">"cmt">//+------------------------------------------------------------------+