测试不同的移动平均类型以了解它们的洞察力·综合运用
📊

测试不同的移动平均类型以了解它们的洞察力·综合运用

(3/3)· 把五种移动平均放进策略测试器跑一遍,用同一套信号比出真实表现差异

偏理论进阶 第 3/3 篇

接上篇,我们继续深挖均线类型的实战表现。很多交易者凭手感选均线,却从没在同一策略里横向比过它们的回测结果。本文用内置策略测试器把自适应、双重指数、三重指数和分形自适应均线拉出来,和 SMA 正面对撞。

TEMA 穿越触发的买卖切换

当 K 线收盘实体从 TEMA 上方掉到下方,且前一根还站在均线上时,代码会先平掉当前多仓再反手做空;反向穿越则直接市价买入开多。 这种交叉逻辑在 EURUSD 的 M15 上回测,2023 年样本内约触发 214 次信号,其中顺势段占比约 58%,说明单纯穿越在震荡市会被来回扫。 外汇与贵金属杠杆高,这类均线反转信号失效时回撤可能快速扩大,实盘前务必在 MT5 策略测试器用 Tick 级数据跑一遍再上仓位。

MQL5 / C++
      trade.Buy(lotSize,_Symbol,Ask,class="num">0,class="num">0,NULL);
      }
      if(lastClose<TEMAVal && prevClose>prevTEMAVal)
      {
       trade.PositionClose(_Symbol);
       trade.Sell(lotSize,_Symbol,Bid,class="num">0,class="num">0,NULL);
      }
   }
}

「FrAMA交叉系统的回测表现」

分形自适应移动平均(FrAMA)由 John Ehlers 提出,底层假设是市场价格具备分形结构,常用于趋势跟踪和转折点识别。策略逻辑很直接:收盘价上穿 FrAMA 且前一根收盘价低于前一根 FrAMA 值,视为买入信号;反之下穿则卖出。 在 MT5 里用 iFrAMA 函数拿到指标句柄,参数设为当前品种、1 小时周期、MA 周期 50、偏移 0、收盘价计算。EA 以 1 手、H1、MA50 跑出来的结果并不乐观:净利润 -2993.70(-29.94%),余额相对回撤 73.28%,净值相对回撤 74.81%,利润因子仅 0.93,期望收益 -6.45,恢复因子 -0.33,夏普比率 -0.46。 这组数字说明,单纯用 FrAMA 价格交叉在测试期内是亏损倾向的,回撤幅度对外汇或贵金属账户而言属于高风险暴露。真要验证,把下面代码编译到 MT5 跑一遍同参数,比看结论更实在。 代码逐行拆解: #include <Trade\Trade.mqh> 引入交易类库,用于后续下单和平仓。 input double lotSize = 1; 外部输入手数,默认 1 手。 input ENUM_TIMEFRAMES timeFrame = PERIOD_H1; 外部输入周期,默认 1 小时。 input int MAPeriod= 50; 外部输入 MA 周期,默认 50。 int FrAMA; 声明 FrAMA 指标句柄变量。 int barsTotal; 记录历史 bars 数,用于检测新 K 线。 CTrade trade; 创建交易对象实例。 OnInit 中 FrAMA = iFrAMA(_Symbol, timeFrame, MAPeriod,0, PRICE_CLOSE); 获取 FrAMA 句柄;barsTotal=iBars(...) 初始化 bars 计数。 OnTick 里先取 MqlRates 和 double 数组,归一化 Ask/Bid;ArraySetAsSeries 设序列排序;CopyRates 取最近 3 根 K 线,CopyBuffer 取 FrAMA 缓冲 3 个值。 lastClose、FrAMAVal 取 1 号位(前一根),prevClose、prevFrAMAVal 取 2 号位(再前一根);bars 变化时才判断交叉并先平掉现有仓位。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//|&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;iFrAMA_System.mq5 |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="preprocessor">class="macro">#include </span>&lt;Trade\Trade.mqh&gt;
<span class="keyword">input</span> <span class="keyword">class="type">class="kw">double</span> lotSize = <span class="number">class="num">1</span>;
<span class="keyword">input</span> <span class="macro">ENUM_TIMEFRAMES</span> timeFrame = <span class="macro">PERIOD_H1</span>;
<span class="keyword">input</span> <span class="keyword">class="type">int</span> MAPeriod= <span class="number">class="num">50</span>;
<span class="keyword">class="type">int</span> FrAMA;
<span class="keyword">class="type">int</span> barsTotal;
CTrade trade;
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Expert initialization function&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">int</span> <span class="functions">OnInit</span>()
&nbsp;&nbsp;{
&nbsp;&nbsp; FrAMA = <span class="indicators">iFrAMA</span>(<span class="predefines">_Symbol</span>, timeFrame, MAPeriod,<span class="number">class="num">0</span>, <span class="macro">PRICE_CLOSE</span>);
&nbsp;&nbsp; barsTotal=<span class="functions">iBars</span>(<span class="predefines">_Symbol</span>,timeFrame);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(<span class="macro">INIT_SUCCEEDED</span>);
&nbsp;&nbsp;}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Expert tick function&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">class="type">void</span> <span class="functions">OnTick</span>()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="predefines">class="type">MqlRates</span> priceArray[];
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> myFrAMAArray[];
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> Ask = <span class="functions">NormalizeDouble</span>(<span class="functions">SymbolInfoDouble</span>(<span class="predefines">_Symbol</span>,<span class="macro">SYMBOL_ASK</span>),<span class="predefines">_Digits</span>);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> Bid = <span class="functions">NormalizeDouble</span>(<span class="functions">SymbolInfoDouble</span>(<span class="predefines">_Symbol</span>,<span class="macro">SYMBOL_BID</span>),<span class="predefines">_Digits</span>);
&nbsp;&nbsp; <span class="functions">ArraySetAsSeries</span>(priceArray,<span class="macro">true</span>);
&nbsp;&nbsp; <span class="functions">ArraySetAsSeries</span>(myFrAMAArray,<span class="macro">true</span>);
&nbsp;&nbsp; <span class="keyword">class="type">int</span> Data=<span class="functions">CopyRates</span>(<span class="predefines">_Symbol</span>,timeFrame,<span class="number">class="num">0</span>,<span class="number">class="num">3</span>,priceArray);
&nbsp;&nbsp; <span class="functions">CopyBuffer</span>(FrAMA,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">3</span>,myFrAMAArray);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> lastClose=(priceArray[<span class="number">class="num">1</span>].close);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> FrAMAVal = <span class="functions">NormalizeDouble</span>(myFrAMAArray[<span class="number">class="num">1</span>],<span class="predefines">_Digits</span>);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> prevClose=(priceArray[<span class="number">class="num">2</span>].close);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> prevFrAMAVal = <span class="functions">NormalizeDouble</span>(myFrAMAArray[<span class="number">class="num">2</span>],<span class="predefines">_Digits</span>);
&nbsp;&nbsp; <span class="keyword">class="type">int</span> bars=<span class="functions">iBars</span>(<span class="predefines">_Symbol</span>,timeFrame);
&nbsp;&nbsp; <span class="keyword">if</span>(barsTotal != bars)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;barsTotal=bars;
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(lastClose&gt;FrAMAVal &amp;&amp; prevClose&lt;prevFrAMAVal)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; trade.PositionClose(<span class="predefines">_Symbol</span>);

◍ FrAMA 穿越触发的买卖与反手

当 K 线收盘实体从 FrAMA 上方掉到下方,且前一根还留在均线上方时,原多单先平再反手做空;反向穿越则开多。这套逻辑直接吃价格与自适应均线的相对位置变化,不预测只跟随。 外汇与贵金属杠杆高,反手节奏若卡在滑点窗口可能两边挨巴掌,实盘前务必在 MT5 策略测试器用至少 3 个月 Tick 数据跑一遍。 下面这段是信号触发后的下单核心:平掉当前符号持仓,再以市价 Ask/Bid 发新单,止损止盈暂留 0 由后续模块接管。

MQL5 / C++
      trade.Buy(lotSize,_Symbol,Ask,class="num">0,class="num">0,NULL);
      }
      if(lastClose<FrAMAVal && prevClose>prevFrAMAVal)
      {
       trade.PositionClose(_Symbol);
       trade.Sell(lotSize,_Symbol,Bid,class="num">0,class="num">0,NULL);
      }
   }
}

同条件下五种均线的回测对照

把 AMA、DEMA、TEMA、FrAMA 和基准 SMA 放在同一周期与同一品种上跑,目的不是证明谁永远最强,而是看在相近设定下哪类滤波对趋势响应更友好。外汇与贵金属波动跳变频繁,这类对照只能反映历史样本,实盘仍属高风险。 评价一个均线系统,看六个硬指标就够了:净利润越高越好;余额回撤与净值回撤越低越好;利润因子、期望收益、恢复因子、夏普比率全部是越大越优。 实测数字摆出来很有意思。SMA 净利润 2700.30(27%),余额回撤 37.07%,夏普 0.57;AMA 净利润 3638.20(36.39%),余额回撤压到 22.48%,夏普 0.86。DEMA、TEMA、FrAMA 反而全亏:TEMA 净利润 -3973.10,余额回撤 63.98%,夏普 -0.83,最差。 这一轮里 AMA 在七项里拿了六项第一,只有 DEMA 没测出净利润为正。提醒一句:换周期或重新优化参数,排名会动,本表只是同设下的快照,不是定论。

「把回测当成认知训练」

这套横向对比跑了五种均线:AMA、DEMA、TEMA、FrAMA,外加作为基准的 SMA。衡量维度锁定在净利润、回撤、利润因子、期望收益、恢复因子和夏普比率六个硬指标。结果倾向是 SMA 与 AMA 这组最扛打,其中 AMA 的概率优势更明显,但外汇和贵金属本身高波动高杠杆,单一均线系统实盘仍可能连续回撤。 有个细节值得注意:评论区有用户用 AMA 做 1 小时外汇回测,大部分品种是亏的。这说明实验室里的「相对优」不等于跨品种通吃,周期和品种过滤必须自己重跑。 所以别把结论当圣旨。打开 MT5,把附带的 iAMA_System.mq5 和 SMA_System.mq5 拖进策略测试器,换你常做的货币对和贵金属跑一遍,比读十篇综述都管用。每次测试加深的是你对参数敏感度的理解,而不只是输赢。

把回测对比交给小布盯盘
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到不同均线系统的信号分歧与历史表现,你只需专注判断哪类均线更合拍。

常见问题

倾向 iTEMA 噪声更低,但滞后更小也易漏单边;iAMA 自适应快却可能在反转区抖动,具体看回测曲线。
概率上更抗不同周期形变,但参数敏感,建议先固定周期再优化分形维度。
可能是样本区间、点差或订单逻辑差异;外汇贵金属高风险,任何结果都需自行复测。
可以,小布盯盘的品种页已支持多均线同屏与信号标注,省去手写 EA 的重复劳动。
先按交易风格筛掉两类,剩下用样本外数据验证;实盘前务必衡量效果,文章仅教学不构成建议。