MQL5自优化智能交易系统(第九部分):双移动平均线交叉·进阶篇
📘

MQL5自优化智能交易系统(第九部分):双移动平均线交叉·进阶篇

第 2/2 篇

◍ 先把均线柄和波动垫读进内存

这段初始化逻辑干的事很直接:把两条自定义均线的当前值用 CopyBuffer 拉进数组 ma_c_4、ma_c_3,各取 1 根(起始 0、数量 1),同时用 SymbolInfoDouble 抓当前品种的 bid 与 ask。 接着在 TF_3 周期上取最近 10 根 K 线的高低数组,high 和 low 都用 vector::Zeros(10) 预分配,再靠 CopyRates 填数。两者相减得到每根波幅向量 var,var.Mean() 就是这 10 根的平均真实波幅,我们叫它 padding——后续挂单间距和止损垫都参考它。 仓位判断放在末尾:PositionsTotal() 为 0 就跑 find_setup(padding) 找信号;大于 0 则进 manage_setup() 管仓。外汇与贵金属杠杆高,padding 只是波动度量,不代表任何方向确定性,开仓前请在 MT5 策略测试器用真实点差回测。

MQL5 / C++
  CopyBuffer(ma_c_4_handle,class="num">0,class="num">0,class="num">1,ma_c_4);
  CopyBuffer(ma_c_3_handle,class="num">0,class="num">0,class="num">1,ma_c_3);
  bid = SymbolInfoDouble(Symbol(),SYMBOL_BID);
  ask = SymbolInfoDouble(Symbol(),SYMBOL_ASK);
  vector high = vector::Zeros(class="num">10);
  vector low = vector::Zeros(class="num">10);
  low.CopyRates(Symbol(),TF_3,COPY_RATES_LOW,class="num">0,class="num">10);
  high.CopyRates(Symbol(),TF_3,COPY_RATES_HIGH,class="num">0,class="num">10);
  vector var = high - low;
  class="type">class="kw">double padding = var.Mean();
  class=class="str">"cmt">//Find an open position
  if(PositionsTotal() == class="num">0)
      find_setup(padding);
  class=class="str">"cmt">//Manage our open positions
  else
    if(PositionsTotal() > class="num">0)
        manage_setup();
}

把止损周期交给优化器反而卡在样本外

上一版里止损缓冲写死用 10 根 H4 K线,这次改成让遗传优化器自己挑时间框架和历史根数,顺手把 update 里的止损逻辑拆出去。原 update 既算缓冲又管仓位,现在拆成 get_padding 算缓冲、find_setup 找机会、manage_setup 管持仓,止损能跟着盈利仓位往前挪。 代码里输入组暴露了 TF_3 和 HISTORICAL_BARS 两个变量,get_padding 默认拉 10 根 TF_3 的高低点算均值当 padding,update 只负责刷指标和调这两个子函数。想验证就把这段贴进 EA,把 HISTORICAL_BARS 从 10 改成允许 5~50 区间让优化器跑。 我拿一套回测和前瞻都盈利的参数做底,只放开风险参数让优化器找更优解。结果图 9、图 10 都显示:新组合在前瞻盈利、回测亏,老问题原样复现——优化器挑的风险设置过拟合了样本内。外汇和贵金属杠杆高,这种样本外崩坏的现象意味着实盘大概率踩坑,别直接信优化报告。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Inputs&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">input</span> group <span class="class="type">class="kw">string">"Money Management Settings"</span>
<span class="keyword">input</span> <span class="macro">ENUM_TIMEFRAMES</span> TF_3 =&nbsp;&nbsp;&nbsp;&nbsp;<span class="macro">PERIOD_H4</span>; <span class="comment">class=class="str">"cmt">//Risk Time Frame</span>
<span class="keyword">input</span> <span class="keyword">class="type">int</span> HISTORICAL_BARS&nbsp;&nbsp;=&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="number">class="num">10</span>; <span class="comment">class=class="str">"cmt">//Historical bars for risk calculation</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Get the stop loss size to use&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">class="kw">double</span> get_padding(<span class="keyword">class="type">void</span>)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">vector</span> high = <span class="keyword">vector</span>::Zeros(<span class="number">class="num">10</span>);
&nbsp;&nbsp; <span class="keyword">vector</span> low = <span class="keyword">vector</span>::Zeros(<span class="number">class="num">10</span>);
&nbsp;&nbsp; low.<span class="functions">CopyRates</span>(<span class="functions">Symbol</span>(),TF_3,<span class="macro">COPY_RATES_LOW</span>,<span class="number">class="num">0</span>,HISTORICAL_BARS);
&nbsp;&nbsp; high.<span class="functions">CopyRates</span>(<span class="functions">Symbol</span>(),TF_3,<span class="macro">COPY_RATES_HIGH</span>,<span class="number">class="num">0</span>,HISTORICAL_BARS);
&nbsp;&nbsp; <span class="keyword">vector</span> var = high - low;
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> padding = var.Mean();
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(padding);
&nbsp;&nbsp;}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Update our technical indicators and positions&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> update(<span class="keyword">class="type">void</span>)
&nbsp;&nbsp;{
<span class="comment">class=class="str">"cmt">//Update technical indicators and market readings</span>
&nbsp;&nbsp; <span class="functions">CopyBuffer</span>(ma_c_2_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,ma_c_2);
&nbsp;&nbsp; <span class="functions">CopyBuffer</span>(ma_c_1_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,ma_c_1);
&nbsp;&nbsp; <span class="functions">CopyBuffer</span>(ma_c_4_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,ma_c_4);
&nbsp;&nbsp; <span class="functions">CopyBuffer</span>(ma_c_3_handle,<span class="number">class="num">0</span>,<span class="number">class="num">0</span>,<span class="number">class="num">1</span>,ma_c_3);
&nbsp;&nbsp; bid = <span class="functions">SymbolInfoDouble</span>(<span class="functions">Symbol</span>(),<span class="macro">SYMBOL_BID</span>);
&nbsp;&nbsp; ask = <span class="functions">SymbolInfoDouble</span>(<span class="functions">Symbol</span>(),<span class="macro">SYMBOL_ASK</span>);
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> padding = get_padding();
<span class="comment">class=class="str">"cmt">//Find an open position</span>
&nbsp;&nbsp; <span class="keyword">if</span>(<span class="functions">PositionsTotal</span>() == <span class="number">class="num">0</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;find_setup(padding);
<span class="comment">class=class="str">"cmt">//Manage our open positions</span>
&nbsp;&nbsp; <span class="keyword">else</span>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(<span class="functions">PositionsTotal</span>() &gt; <span class="number">class="num">0</span>)
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; manage_setup();
&nbsp;&nbsp;}
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Manage our open positions&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>

「持仓后的止损平移与均线出场」

这段函数管的是开仓之后怎么盯盘:先确认当前品种有没有持仓,再按多空状态把止损往有利方向挪,最后用两组均线交叉决定要不要平掉。 卖单(state==0)时,新止损算成 ask 加 padding;只有当新止损比原止损更靠下,才调用 PositionModify 把 SL 改掉。买单(state==1)反过来,新止损是 bid 减 padding,且只在比原 SL 更高时才改,避免把止损往亏损方向拉。 出场分两个时间框架开关。closing_tf==0 用 ma_c_1 与 ma_c_2 的零号元素比较:卖单遇到快线高于慢线、买单遇到快线低于慢线就直接 PositionClose。closing_tf==1 则切到 ma_c_3 与 ma_c_4 做同样判断。 把 padding 调小,止损跟得更紧但可能被扫得频繁;调大则容错高、回撤也大。外汇和贵金属波动剧烈,这套逻辑在实盘里可能频繁触发修改与平仓,建议先上 MT5 策略测试器用历史数据跑一遍再上真仓。

MQL5 / C++
class="type">void manage_setup(class="type">void)
  {
  class=class="str">"cmt">//Does the position exist?
  if(PositionSelect(Symbol()))
   {
    class=class="str">"cmt">//Get the current stop loss
    class="type">class="kw">double current_sl = PositionGetDouble(POSITION_SL);
    class="type">class="kw">double padding = get_padding();
    class="type">class="kw">double new_sl;
    

    class=class="str">"cmt">//Sell position
    if((state == class="num">0))
    {
       new_sl = (ask + padding);
       
       if(new_sl < current_sl) Trade.PositionModify(Symbol(),new_sl,class="num">0);
    }
    class=class="str">"cmt">//Buy position
    if((state == class="num">1))
    {
       new_sl = (bid - padding);
       
       if(new_sl > current_sl) Trade.PositionModify(Symbol(),new_sl,class="num">0);
    }


   if(closing_tf == class="num">0)
    {
     if((state ==class="num">0) && (ma_c_1[class="num">0] > ma_c_2[class="num">0]))
        Trade.PositionClose(Symbol());
     if((state ==class="num">1) && (ma_c_1[class="num">0] < ma_c_2[class="num">0]))
        Trade.PositionClose(Symbol());
    }
   else
     if(closing_tf == class="num">1)
      {
       if((state ==class="num">0) && (ma_c_3[class="num">0] > ma_c_4[class="num">0]))
          Trade.PositionClose(Symbol());
       if((state ==class="num">1) && (ma_c_3[class="num">0] < ma_c_4[class="num">0]))
          Trade.PositionClose(Symbol());
      }
   }
  }

◍ 全参数搜索才是下一步

双均线交叉的这次改造证明了一件事:滞后性可以被压,但单点调参的红利有限。只动一个参数、焊死其他变量,往往看不到全局最优解在哪。 把均线周期、止损幅度、过滤阈值全部放开做遗传优化,回测里更容易撞出在样本内和样本外都站得住的组合。外汇与贵金属波动跳变频繁,这类全参数搜索只是降低滞后概率,不承诺任何收益。 等最优参数落定,再拿它们训一个统计模型去预判交叉质量,比裸跑均线更稳。但眼下只是开头——优化器没批量吐出前瞻测试仍盈利的 EA 之前,说明跑得还不够多。 开发者自己不下场反复跑流程,AI 替代不了这份苦功。市场高风险,参数漂亮和实盘能活是两回事。

常见问题

把止损周期交给优化器容易过拟合,建议固定波动垫参数、只放开均线周期做搜索,样本外更稳。
价格回碰慢均线且动能转弱时平移止损至成本上,后续被均线反穿即平,能锁利也控回撤。
小布可读取你的参数组合跑样本外诊断,标红过拟合区间,并给出波动垫与均线柄的分离建议。
只动均线会漏掉止损平移步长的影响,全参数网格搜一遍才能看到真实稳健区,但耗时成倍涨。
贵金属波动大,慢线用 50~120 根、快线 10~30 根较常见,具体看品种振幅,勿照搬股指参数。