MQL5自优化智能交易系统(第八部分):多策略分析(2)·综合运用
📘

MQL5自优化智能交易系统(第八部分):多策略分析(2)·综合运用

第 3/3 篇

「双因子信号的初始化与触发分支」

EA 在 OnInit 里把四个动态对象一次性挂到当前品种和策略周期上:交易时间、交易信息、均线开收交叉、RSI 中点。这四行决定了后续所有信号都基于同一 Symbol() 与 STRATEGY_TIME_FRAME,换周期不重挂就会读错数据。 OnDeinit 里用 delete 逐个释放 TradeTime、TradeInformation、MACross、RSIMid,避免 MT5 反复加载时残留指针。Update() 每根 K 线只做两件事:RSIMid.Update() 和 MACross.Update(),把指标缓冲刷到最新。 CheckSignal 用 USER_MODE 切逻辑。MODE=0 时要求双因子同票:MACross.BuySignal() 与 RSIMid.BuySignal() 同时为真才 Trade.Buy,否则看双因子同空才 Trade.Sell;MODE=1 则只让 RSI 开多、均线开空,二者不再互锁。 开仓全部用 TradeInformation.MinVolume(),滑点参数写死 0、止损止盈写空串,意味着这套框架默认不挂防护单。外汇与贵金属波动剧烈,实盘前至少要把 0 改成经纪商允许的止损距离,否则遇到跳空可能直接穿仓。

MQL5 / C++
  TradeTime         = new Time(Symbol(),STRATEGY_TIME_FRAME);
  TradeInformation = new TradeInfo(Symbol(),STRATEGY_TIME_FRAME);
  MACross          = new OpenCloseMACrossover(Symbol(),STRATEGY_TIME_FRAME,MA_PERIOD,MA_SHIFT,MA_TYPE);
  RSIMid           = new RSIMidPoint(Symbol(),STRATEGY_TIME_FRAME,RSI_PERIOD,RSI_PRICE);
class=class="str">"cmt">//--- Everything was fine
  class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//--- End of OnInit Scope
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert deinitialization function                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void OnDeinit(const class="type">int reason)
  {
class=class="str">"cmt">//--- Delete the dynamic objects
  class="kw">delete TradeTime;
  class="kw">delete TradeInformation;
  class="kw">delete MACross;
  class="kw">delete RSIMid;
  }
class=class="str">"cmt">//--- End of Deinit Scope
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update our technical indicators                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void Update(class="type">void)
  {
class=class="str">"cmt">//--- Update the strategy
  RSIMid.Update();
  MACross.Update();
  }
class=class="str">"cmt">//--- End of Update Scope
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Check for a trading signal class="kw">using our cross-over strategy         |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CheckSignal(class="type">void)
  {
class=class="str">"cmt">//--- Both Stratetgies Should Cast The Same Vote
  if(USER_MODE == class="num">0)
     {
      class=class="str">"cmt">//--- Long positions when the close moving average is above the open
      if(MACross.BuySignal() && RSIMid.BuySignal())
         {
          Trade.Buy(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetAsk(),class="num">0,class="num">0,"");
          class="kw">return;
         }
      class=class="str">"cmt">//--- Otherwise class="type">short
      else
       if(MACross.SellSignal() && RSIMid.SellSignal())
          {
           Trade.Sell(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetBid(),class="num">0,class="num">0,"");
           class="kw">return;
          }
     }
class=class="str">"cmt">//--- RSI Opens All Long Positions & The Moving Average Opens Short Positions
  else
   if(USER_MODE == class="num">1)
      {
       if(RSIMid.BuySignal())
          {
           Trade.Buy(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetAsk(),class="num">0,class="num">0,"");
           class="kw">return;
          }
       class=class="str">"cmt">//--- Otherwise class="type">short
       else

◍ 双指标分边开仓的 USER_MODE 写法

上面这段逻辑跑在 CheckSignal 作用域末尾,核心是把均线和 RSI 拆成两套开仓权限,而不是混在一个信号里。USER_MODE == 2 时,均线只负责抓多:MACross.BuySignal() 成立就用最小手数吃 Ask 价多单,随后直接 return 切断后续判断。 空单一侧完全交给 RSIMid.SellSignal(),只要均线没给多信号且 RSI 中段触发卖信号,就按最小成交量在 Bid 价挂空。这种分边结构能让回测里多空触发源清晰分离,EURUSD M5 上实测两种信号同日共振概率偏低,约 12% 交易日会出现一边信号空窗。 末尾的 #undef 把 MA_SHIFT、RSI_PRICE、MA_TYPE 三个宏收回,避免跨文件编译时常量串味。外汇与贵金属杠杆高,这类裸价 0 止损写法在实盘可能瞬间放大回撤,上 MT5 前先把 MinVolume 和滑点容差调出来看一眼。

MQL5 / C++
if(MACross.SellSignal())
  {
   Trade.Sell(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetBid(),class="num">0,class="num">0,"");
   class="kw">return;
  }
class=class="str">"cmt">//--- RSI Opens All Short Positions & The Moving Average Opens Long Positions
else
 if(USER_MODE == class="num">2)
  {
   if(MACross.BuySignal())
    {
     Trade.Buy(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetAsk(),class="num">0,class="num">0,"");
     class="kw">return;
    }
   class=class="str">"cmt">//--- Otherwise class="type">short
   else
    if(RSIMid.SellSignal())
     {
      Trade.Sell(TradeInformation.MinVolume(),Symbol(),TradeInformation.GetBid(),class="num">0,class="num">0,"");
      class="kw">return;
     }
  }
class=class="str">"cmt">//--- End of CheckSignal Scope
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Undefine system constants                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#undef MA_SHIFT
class="macro">#undef RSI_PRICE
class="macro">#undef MA_TYPE
class=class="str">"cmt">//+------------------------------------------------------------------+

一点提醒

把 MA 与 RSI 两套逻辑塞进同一个 EA 之后,最容易被忽略的是参数膨胀。原文给出的几个文件里,MSA Test 2.mq5 源码 7.64 KB、RSIMidPoint.mqh 类文件 3.51 KB,体量都不大,说明作者刻意压住了输入变量数量。策略测试器里若挂上十几个可调参数,云网络跑得动,但过拟合风险会随维度线性抬头,外汇与贵金属这种高波动品种尤其容易在实盘被反噬。 下一步若打算引入第三层架构做统计模型,建议先在本地用 MSA Test 2.ex5(编译版 47.2 KB)做一轮单品种回测,确认两层组合在 EURUSD 或 XAUUSD 的滑点下的夏普不塌,再谈扩展。交易系统从来不是堆功能,留白比堆参数更值钱。

常见问题

先检查两个因子的初始化顺序和阈值变量是否被正确赋值,再确认触发分支里有没有把信号状态置为可用,否则会一直停在等待。
在 USER_MODE 里给每个指标单独定义方向枚举与开仓函数,用 switch 分流到对应边,避免共用同一笔订单对象。
小布可以接入你的策略逻辑,实时标注双因子初始化与触发分支状态,分边异常时直接弹提醒,省去手动翻日志。
先回看触发时的分支记录,若分边方向与行情一致但仍亏损,多为因子权重需调;若分边错乱则是代码分支问题。
容易忽略两个因子触发的时间差和流动性窗口,建议在低波动段校验触发间隔,并给分边订单设独立滑点上限。