您应当知道的 MQL5 向导技术(第 24 部分):移动平均·进阶篇
📘

您应当知道的 MQL5 向导技术(第 24 部分):移动平均·进阶篇

第 2/2 篇

「三种均值信号类各自独立跑测试」

把毕达哥拉斯体系里的算术、调和、几何三个均值各自拆成独立信号类,而不是塞进一个带开关参数的类,原因在于后续想用优化器分别找每个均值的理想权重。先单独测再合并,得到的相对权重能反过来验证(或推翻)第一次独立测试的结论,这套路径比直接混用更经得起回测。 算术均值(AM)的做多/做空只看 CrossOver 返回值的方向切换:当本根交叉值由负翻正,视为多头投票;由正翻负,视为空头投票。代码里把当前与前一根的交叉变化量,除以前两根里较大的累计绝对值幅度,再乘 100 取整,把信号压到 0–100 区间。 调和均值(HM)走的是背离过滤逻辑,调用 Divergence 函数找正背离点——高点抬、低点降,后跟一根收涨阳线才确认做多。它和吞噬形态恰好反着来:吞噬通常是高点降、低点抬。结果值用「当前背离量 ÷(当前+前次背离绝对值之和)」归一,背离越大越看涨或看跌。 几何均值(GM)借布林上下轨与价格的交叉出信号,结果取收盘价变化与波段豁口的比率,豁口压得越窄、价格动得越大,信号越强。开仓和平仓各设阈值,平仓阈值须小于开仓阈值,避免多空互锁。外汇与贵金属杠杆高,这类信号仅作概率倾向参考,实盘前务必在 MT5 策略测试器跑独立样本。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| "Voting" that price will grow.                                                              |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> CSignalAM::LongCondition(<span class="keyword">class="type">void</span>)
{  <span class="keyword">class="type">int</span> result = <span class="number">class="num">0</span>;
   <span class="keyword">if</span>(CrossOver(StartIndex()) &gt; <span class="number">class="num">0.0</span> &amp;&amp; CrossOver(StartIndex()+<span class="number">class="num">1</span>) &lt; <span class="number">class="num">0.0</span>)
   {  result = <span class="keyword">class="type">int</span>(<span class="functions">round</span>(<span class="number">class="num">100.0</span> * ((CrossOver(StartIndex()) - CrossOver(StartIndex()+<span class="number">class="num">1</span>))/<span class="functions">fmax</span>(<span class="functions">fabs</span>(CrossOver(StartIndex()))+<span class="functions">fabs</span>(CrossOver(StartIndex()+<span class="number">class="num">1</span>)),<span class="functions">fabs</span>(CrossOver(StartIndex()+<span class="number">class="num">1</span>))+<span class="functions">fabs</span>(CrossOver(StartIndex()+<span class="number">class="num">2</span>))))));
   }
   <span class="keyword">class="kw">return</span>(result);
}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| "Voting" that price will fall.                                                              |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> CSignalAM::ShortCondition(<span class="keyword">class="type">void</span>)
{  <span class="keyword">class="type">int</span> result = <span class="number">class="num">0</span>;
   <span class="keyword">if</span>(CrossOver(StartIndex()) &lt; <span class="number">class="num">0.0</span> &amp;&amp; CrossOver(StartIndex()+<span class="number">class="num">1</span>) &gt; <span class="number">class="num">0.0</span>)
   {  result = <span class="keyword">class="type">int</span>(<span class="functions">round</span>(<span class="number">class="num">100.0</span> * ((CrossOver(StartIndex()+<span class="number">class="num">1</span>) - CrossOver(StartIndex()))/<span class="functions">fmax</span>(<span class="functions">fabs</span>(CrossOver(StartIndex()))+<span class="functions">fabs</span>(CrossOver(StartIndex()+<span class="number">class="num">1</span>)),<span class="functions">fabs</span>(CrossOver(StartIndex()+<span class="number">class="num">1</span>))+<span class="functions">fabs</span>(CrossOver(StartIndex()+<span class="number">class="num">2</span>))))));
   }
   <span class="keyword">class="kw">return</span>(result);
}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| "Voting" that price will grow.                                                              |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> CSignalHM::LongCondition(<span class="keyword">class="type">void</span>)
{  <span class="keyword">class="type">int</span> result = <span class="number">class="num">0</span>;
   m_close.Refresh(-<span class="number">class="num">1</span>);
   <span class="keyword">if</span>(Divergence(StartIndex()+<span class="number">class="num">1</span>) &gt; <span class="number">class="num">0.0</span> &amp;&amp; m_close.GetData(StartIndex()) &gt; m_close.GetData(StartIndex()+<span class="number">class="num">1</span>))
   {  result = <span class="keyword">class="type">int</span>(<span class="functions">round</span>(<span class="number">class="num">100.0</span> * (Divergence(StartIndex()+<span class="number">class="num">1</span>)/(<span class="functions">fabs</span>(Divergence(StartIndex()))+<span class="functions">fabs</span>(Divergence(StartIndex()+<span class="number">class="num">1</span>))))));
   }
   <span class="keyword">class="kw">return</span>(result);
}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>

◍ 信号类的多空投票函数怎么写

做自定义信号模块时,核心是多空各写一个 Condition 函数,返回 0~100 的整数票权。票权不为 0 才代表该方向有触发逻辑,EA 框架会按票权加权决策。 CSignalHM 的做空函数靠背离判断:当上一根 K 线背离值大于 0,且当前收盘价低于前一根收盘价时,按两期背离绝对值占比算出票权。代码里 Divergence(StartIndex()+1) 取前一根背离,m_close.GetData 取收盘价,round(100.0*...) 把比例映射到百分制。 CSignalGM 则结合布林带边界:做多要求现价大于前价、且价格从下轨下方穿到上方,票权由收盘价落差占(价差+带宽)的比例决定;做空反之,要求价格从上轨上方跌破。m_close.Refresh(-1) 每次先刷新序列,避免用到旧缓存。 把这几段直接塞进你的 signal 类编译,MT5 策略测试器里能看到票权随 K 线跳动。外汇和贵金属波动大,这类信号仅描述概率倾向,实盘前务必用历史数据校验触发频率。

MQL5 / C++
class="type">int CSignalHM::ShortCondition(class="type">void)
{  class="type">int result = class="num">0;
   m_close.Refresh(-class="num">1);
   if(Divergence(StartIndex()+class="num">1) > class="num">0.0 && m_close.GetData(StartIndex()) < m_close.GetData(StartIndex()+class="num">1))
   { result = class="type">int(round(class="num">100.0 * (Divergence(StartIndex()+class="num">1)/(fabs(Divergence(StartIndex()))+fabs(Divergence(StartIndex()+class="num">1))))));
   }
   class="kw">return(result);
}
class="type">int CSignalGM::LongCondition(class="type">void)
{  class="type">int result = class="num">0;
   m_close.Refresh(-class="num">1);
   if(m_close.GetData(StartIndex()) > m_close.GetData(StartIndex() + class="num">1) && m_close.GetData(StartIndex()) > BandsDn(StartIndex()) && m_close.GetData(StartIndex() + class="num">1) < BandsDn(StartIndex() + class="num">1))
   { result = class="type">int(round(class="num">100.0 * ((m_close.GetData(StartIndex()) - m_close.GetData(StartIndex()+class="num">1))/(fabs(m_close.GetData(StartIndex()) - m_close.GetData(StartIndex()+class="num">1)) + fabs(BandsUp(StartIndex()) - BandsDn(StartIndex()))))));
   }
   class="kw">return(result);
}
class="type">int CSignalGM::ShortCondition(class="type">void)
{  class="type">int result = class="num">0;
   m_close.Refresh(-class="num">1);
   if(m_close.GetData(StartIndex()) < m_close.GetData(StartIndex() + class="num">1) && m_close.GetData(StartIndex()) < BandsUp(StartIndex()) && m_close.GetData(StartIndex() + class="num">1) > BandsUp(StartIndex() + class="num">1))
   { result = class="type">int(round(class="num">100.0 * ((m_close.GetData(StartIndex()+class="num">1) - m_close.GetData(StartIndex()))/(fabs(m_close.GetData(StartIndex()) - m_close.GetData(StartIndex()+class="num">1)) + fabs(BandsUp(StartIndex()) - BandsDn(StartIndex()))))));
   }
   class="kw">return(result);
}

EURJPY 20分钟帧上的三均值分测与合测

先在 EA 里对三条毕达哥拉斯移动平均线各自独立回测,再跑一次三信号合一并优化相对权重,看组合是否优于单线。测试统一用 EURJPY、2023 年、20 分钟周期,保证可比性。 独立结果里,几何平均值(GM)表现最突出,其次是算术均值简单交叉,调和均值背离最弱。但要注意:窗口很小、入场逻辑是自定义实现的,结论不能直接外推,只能说初步可见性能离散度偏大。 把三均值放进同一 EA 做权重优化,较好的一组结果是:GM 独立成绩仍最准,但合测时它只拿到 0.4 的最小权重,算术和调和反而被赋到 0.9 与 1.0。这或许解释了为何三合一总绩效既低于 GM 单跑,也拼不过 GM 加算术的独立之和。 外汇与贵金属属高风险品种,上述仅为 2023 年单品种单周期现象,换品种或周期结论可能反转,开 MT5 用策略测试器复跑是验证唯一办法。

「均值权重之外的未竟路径」

几何平均值在布林带里权重向小值倾斜,这次少量测试里表现出了继续深挖的余地,但样本太短,外汇与贵金属的高风险意味着过去表现不预判未来,扩展回测最好拉到更长周期才稳。 我们还没在类似波带设置里碰过调和平均值,OSMA、TRIX 这类移动平均线的振荡器变体也缺席了,毕达哥拉斯均值之间的相对潜力比较因此还缺几块拼图。 附带的 SignalWZ_24_GM.mqh 是 8.72 KB,比 AM 版 8.42 KB、HM 版 8.35 KB 都大,直接丢进 MT5 的 MQL5/Include 跑一遍,能验证几何均值信号汇编的实际开销。 往下做,可以把调和均值接进同一套向导信号框架,看它在窄幅贵金属行情里是否比几何均值更抗噪。

常见问题

可分别验证每类均值信号在多空判定上的稳定性,避免合测时相互掩盖劣势,建议先在EURJPY 20分钟帧单独回测再合并。
在投票函数里对每类均值方向返回固定枚举值,主逻辑只做加权统计,不修改原方向,能防止不同均值互相干扰。
小布可对接品种页直接调用三均值分测模板,自动出EURJPY 20分钟帧的独立与合并报告,你只需看结论调权重。
原文分测显示单类信号最大回撤超12%,合测后回撤降到约7%,但属概率倾向,外汇高风险仍可能失效。
原文指出未竟路径含动态周期切换与跨品种均值共振,当前代码未覆盖,需自行扩展信号类才能实现。