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

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

第 1/2 篇

◍ 用高低周期双交叉压住信号滞后

传统均线交叉大多在同一个周期上跑两条不同周期均线,毛病很直白:行情已经发动了,交叉信号才慢吞吞冒出来,入场要么滞后要么直接踏空。这篇换了个老交易员熟路——大周期定方向,小周期找顺势入口。 具体做法是把策略用两次:先在日线(D1)看均线交叉确立当日偏向,再切到15分钟(M15)等一个同方向的交叉才动手。思路上回归了“顺着大周期方向做”的口头禅,但用算法把两层过滤固化下来,期望靠小周期入场抵消大周期信号本身的滞后。 入场有两种理念可切:趋势跟踪模式下,大周期看涨就去小周期等看涨交叉;均值回归模式下,大周期看涨反而等小周期看跌交叉反向做。出场也更麻烦,可选在小周期偏向破裂时平,或者死扛到日线偏向翻转才平。组合太多,只能丢给遗传优化器在真实数据里筛。 代码里先把时间框架锁死成常量:D1 为高周期、M15 为低周期、H4 算止损,后续才可能放开给优化器调。 [CODE] 段里第一行就定义了高周期常量 TF_1 = PERIOD_D1,这是整套双交叉的锚点。 回测用 EURUSD、1分钟帧跑2020年1月至今共五年,一半数据做前瞻。结果挺打脸:回测里均值回归模式大多盈利,前瞻里却换成趋势跟踪扛盈;更糟的是回测好的一批,前瞻里几乎全翻车,只有极少数参数组合两段都赚钱。外汇与贵金属属高风险品种,这种不稳定现象说明实盘前必须自测验证。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                Double Crossover.mq5 |
class=class="str">"cmt">//|                                     Copyright class="num">2024, MetaQuotes Ltd. |
class=class="str">"cmt">//|                                                 [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2024, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| System constants                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- System time frames
class="macro">#define TF_1          PERIOD_D1

先把多周期参数和开关定义清楚

在 MT5 写 EA 时,先把周期与手数倍数固化成宏,能减少后续逻辑里的魔法数字。下面这段把 M15 与 H4 定义成 TF_2、TF_3,LOT_MULTILPLE 设为 1,意味着每笔下单暂不放大仓位,外汇与贵金属波动剧烈,杠杆风险高,实盘前建议在策略测试器里先按 1 倍验证。 策略本身留了两条路:趋势跟随(TREND=0)与均值回归(MEAN_REVERTING=1),用枚举 STRATEGY_MODES 切换;平仓参考周期也分更高周期(HIGHER_TIME_CLOSE=0)与更低周期(LOWER_TIME_CLOSE=1),由 CLOSING_TIME_FRAME 控制。这两个 input 枚举就是 EA 的『模式拨杆』,改一个值就能让同一套均线逻辑跑出完全不同的风格。 指标端用了两组均线:高周期 ma_1_period=10、ma_1_gap=20,低周期 ma_2_period=10、ma_2_gap=20。gap 参数代表取价偏移根数,若你发现信号总是慢半拍,优先调的是这个 20 而不是周期本身。全局区声明了四个均线句柄与数组、最小成交量 volume_min、买卖价 bid/ask 及 state 状态机变量,并挂了 CTrade 交易库——这些都是后续 OnTick 里直接调用的底层素材。

MQL5 / C++
class="macro">#define TF_2              PERIOD_M15
class="macro">#define TF_3              PERIOD_H4
class="macro">#define LOT_MULTILPLE     class="num">1

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Custom enumerations                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- What trading style should we follow when opening our positions, trend following or mean reverting?
enum STRATEGY_MODES  {
   TREND = class="num">0,           class=class="str">"cmt">//Trend Following Mode
   MEAN_REVERTING = class="num">1   class=class="str">"cmt">//Mean Reverting Mode
};
class=class="str">"cmt">//--- Which time frame should we consult, when determining if we should close our position?
enum CLOSING_TIME_FRAME {
   HIGHER_TIME_CLOSE = class="num">0, class=class="str">"cmt">//Close on higher time frames
   LOWER_TIME_CLOSE = class="num">1   class=class="str">"cmt">//Close on lower time frames
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Inputs                                                           |
class=class="str">"cmt">//+------------------------------------------------------------------+
input group "Technical Indicators"
input class="type">int ma_1_period = class="num">10;   class=class="str">"cmt">//Higher Time Frame Period
input class="type">int ma_1_gap    = class="num">20;   class=class="str">"cmt">//Higher Time Frame Period Gap
input class="type">int ma_2_period = class="num">10;   class=class="str">"cmt">//Lower Time Frame Period
input class="type">int ma_2_gap    = class="num">20;   class=class="str">"cmt">//Lower Time Frame Period Gap
input group "Strategy Settings"
input STRATEGY_MODES     strategy_mode = class="num">0;   class=class="str">"cmt">//Strategy Operation Mode
input CLOSING_TIME_FRAME closing_tf    = class="num">0;   class=class="str">"cmt">//Strategy Closing Timeframe
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Global variables                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int     ma_c_1_handle,ma_c_2_handle,ma_c_3_handle,ma_c_4_handle;
class="type">class="kw">double  ma_c_1[],ma_c_2[],ma_c_3[],ma_c_4[];
class="type">class="kw">double  volume_min;
class="type">class="kw">double  bid,ask;
class="type">int     state;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Libraries                                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include <Trade\Trade.mqh>
CTrade Trade;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {

「双时间框架均线与信号触发骨架」

这段初始化与回调代码搭起了一个双时间框架(TF_1 / TF_2)均线系统的底层骨架。先在 OnInit 里抓取品种最小交易量 volume_min,再分别用 iMA 在 TF_1 建两条周期差为 ma_1_gap 的收盘价 SMA,在 TF_2 建两条周期差为 ma_2_gap 的 SMA,state 初值设为 -1 表示无持仓无方向。 OnDeinit 里用 IndicatorRelease 逐个释放四个均线句柄,避免 EA 卸载后指标句柄泄漏。OnTick 只做一件事:取 TF_2 的 0 号 K 线时间,若与静态变量 time_stamp 不同才调用 update(),相当于用高周期新柱来节流,避免每 tick 重算。 find_setup 的入口判断 PositionsTotal()==0,有仓就跳过,无仓才把 state 复位为 -1 并寻找信号。高周期多头条件是 ma_c_1[0] > ma_c_2[0](快线在慢线上方);若 strategy_mode==0 且低周期也满足 ma_c_3[0] > ma_c_4[0],则 Trade.Buy 以 volume_min 手数市价买入,止损放在 bid - padding,state 置 1。外汇与贵金属杠杆高,这类顺势单触发后回撤打掉止损的概率不低,实盘前务必在 MT5 策略测试器用真实点差回测。 别把句柄当自动回收 MQL5 的 iMA 返回的是句柄不是数值,OnInit 里建了四个就必须配对 IndicatorRelease,否则 EA 反复加载会堆积无效句柄。手动加载卸载 EA 两次以上,若没释放就会在终端日志看到 handle leak 警告。

MQL5 / C++
   volume_min      = SymbolInfoDouble(Symbol(),SYMBOL_VOLUME_MIN);
   ma_c_2_handle   = iMA(Symbol(),TF_1,ma_1_period,class="num">0,MODE_SMA,PRICE_CLOSE);
   ma_c_1_handle   = iMA(Symbol(),TF_1,(ma_1_period + ma_1_gap),class="num">0,MODE_SMA,PRICE_CLOSE);
   ma_c_4_handle   = iMA(Symbol(),TF_2,ma_2_period,class="num">0,MODE_SMA,PRICE_CLOSE);
   ma_c_3_handle   = iMA(Symbol(),TF_2,(ma_2_period + ma_2_gap),class="num">0,MODE_SMA,PRICE_CLOSE);
   state           = -class="num">1;
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
   }
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//---
   IndicatorRelease(ma_c_1_handle);
   IndicatorRelease(ma_c_2_handle);
   IndicatorRelease(ma_c_3_handle);
   IndicatorRelease(ma_c_4_handle);
  }
class="type">void OnTick()
  {
class=class="str">"cmt">//---
   class="type">class="kw">datetime current_time = iTime(Symbol(),TF_2,class="num">0);
   class="kw">static class="type">class="kw">datetime time_stamp;
   if(time_stamp != current_time)
     {
      time_stamp = current_time;
      update();
     }
  }
class="type">void find_setup(class="type">class="kw">double padding)
  {
   if(PositionsTotal() == class="num">0)
     {
      state = -class="num">1;
      if(ma_c_1[class="num">0] > ma_c_2[class="num">0])
        {
         if((ma_c_3[class="num">0] > ma_c_4[class="num">0]) && (strategy_mode == class="num">0))
           {
            Trade.Buy(volume_min,Symbol(),ask,(bid - padding),class="num">0,"");
            state = class="num">1;
           }
        }
     }
  }

◍ 双时间框架下的开平仓与指标刷新

高周期方向由 ma_c_1 与 ma_c_2 两根均线决定:ma_c_1[0] 在 ma_c_2[0] 之上偏多,之下偏空。低周期 ma_c_3 与 ma_c_4 则用于触发动作,strategy_mode 切换趋势跟随或均值回归两种逻辑。 趋势跟随模式下(strategy_mode==0),高周期多头且低周期 ma_c_3[0]>ma_c_4[0] 才做多;高周期空头且 ma_c_3[0]<ma_c_4[0] 才做空。均值回归模式(strategy_mode==1)则反着来:高周期多头却出现低周期死叉时买,高周期空头却金叉时卖,属于抓回摆的思路。外汇与贵金属杠杆高,这类逆趋势单亏损概率不低,实盘前先用策略测试器跑一遍。 平仓由 manage_setup 接管。closing_tf==0 表示看高周期:持多单后若 ma_c_1[0]<ma_c_2[0] 就平,持空单后若 ma_c_1[0]>ma_c_2[0] 就平;closing_tf==1 则盯低周期 ma_c_3 与 ma_c_4 的交叉来收尾。 update 函数里只用 CopyBuffer 把两个均线句柄的最新值刷进数组,每次 tick 或定时器触发时重读,保证上面的判断用的是实时数据。开 MT5 把这段接进 EA 的 OnTick,就能看信号是否如预期触发。

MQL5 / C++
if((ma_c_3[class="num">0] < ma_c_4[class="num">0]) && (strategy_mode == class="num">1))
  {
   Trade.Buy(volume_min,Symbol(),ask,(bid - padding),class="num">0,"");
   state = class="num">1;
  }
 }
 class=class="str">"cmt">//--- Bearish on the higher time frame
 if(ma_c_1[class="num">0] < ma_c_2[class="num">0])
  {
  class=class="str">"cmt">//--- Trend following mode
  if((ma_c_3[class="num">0] < ma_c_4[class="num">0]) && (strategy_mode == class="num">0))
   {
    Trade.Sell(volume_min,Symbol(),bid,(ask + padding),class="num">0,"");
    state = class="num">0;
   }
  class=class="str">"cmt">//--- Mean reverting mode
  if((ma_c_3[class="num">0] > ma_c_4[class="num">0]) && (strategy_mode == class="num">1))
   {
    Trade.Sell(volume_min,Symbol(),bid,(ask + padding),class="num">0,"");
    state = class="num">0;
   }
  }
 }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Manage our open positions                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void manage_setup(class="type">void)
  {
  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());
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update our technical indicators and positions                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void update(class="type">void)
  {
  class=class="str">"cmt">//Update technical indicators and market readings
  CopyBuffer(ma_c_2_handle,class="num">0,class="num">0,class="num">1,ma_c_2);
  CopyBuffer(ma_c_1_handle,class="num">0,class="num">0,class="num">1,ma_c_1);

常见问题

在高周期用慢均线定方向,低周期用快均线找入场,高低周期交叉配合能压住滞后,但贵金属和外汇波动大,仍属高风险。
先把高低周期数值、快慢均线周期、信号开关和手数上限写清楚,避免运行时逻辑混乱,具体数值按品种波动调。
小布可自动加载双周期均线并标记高低周期交叉点,省去手动切图,你只专注确认信号真假。
平仓信号需高低周期同时反转才动,指标刷新放在新柱生成时调用,防止重复计算拖慢终端。
黄金跳空和避险脉冲更频,低周期噪音大,建议拉高低周期过滤或降杠杆,外汇贵金属均高风险。