构建K线图趋势约束模型(第九部分):多策略EA(第一部分)·综合运用
🧩

构建K线图趋势约束模型(第九部分):多策略EA(第一部分)·综合运用

(3/3)·单一RSI约束在震荡市频频失效,本文用模块化合入多策略与魔法数字补上最后一块拼图

实战向 第 3/3 篇
只靠日线约束加RSI极端值入场,遇到横盘会反复打脸。把多种经典策略拆成模块挂在同一EA下,一个失效还有下一个顶上,这才是应对多变汇市的务实做法。

用 Magic 号隔离 EA 持仓再反向清仓

在趋势跟随逻辑里,最容易被忽略的是多 EA 同台时的持仓串味。下面这段先按持仓 ticket 取出当前单,并只用 MagicNumber 做身份过滤,避免把别的脚本开的单误平掉。 ulong ticket = PositionGetInteger(POSITION_TICKET); if (currentMagicNumber == MagicNumber) // Ensure only this EA's orders are checked {

if ((position_type == POSITION_TYPE_BUY && is_downtrend)

(position_type == POSITION_TYPE_SELL && is_uptrend)) { trade.PositionClose(ticket); } } 逐行拆解:第1行用 PositionGetInteger(POSITION_TICKET) 抓出当前遍历到的持仓 ticket;第2行比对 currentMagicNumber 与本 EA 的 MagicNumber,不等就直接跳过;第4~5行判断「持多单却走 downtrend」或「持空单却走 uptrend」这种趋势反转情形;第6行才真正发平仓指令。 实盘里若 MagicNumber 设成 888888 而另一策略也用同值,这段就会交叉平仓。开 MT5 把自家 EA 的 Magic 改成非常规随机数(如 20240517),回测里能直接看到冲突单减少。外汇与贵金属杠杆高,误平可能瞬间吃掉缓冲,参数隔离必须先于信号逻辑。

MQL5 / C++
class="type">class="kw">ulong ticket = PositionGetInteger(POSITION_TICKET);
if (currentMagicNumber == MagicNumber) class=class="str">"cmt">// Ensure only this EA&class="macro">#x27;s orders are checked
  {
   if ((position_type == POSITION_TYPE_BUY && is_downtrend) ||
       (position_type == POSITION_TYPE_SELL && is_uptrend))
     {
      trade.PositionClose(ticket);
     }
  }

「按魔术码隔离持仓再挂卖单与移动止损」

EA 在开仓前会先遍历当前品种的全部持仓,用 PositionGetInteger(POSITION_MAGIC) 读出每单的魔术码,只有与自身 MagicNumber 相等时才置 open_position=true 并 break。这样多 EA 同跑一个品种时,不会把别人的单子误认成自己的,避免重复下单。 若扫完一圈没找到同魔术码持仓,就取 SYMBOL_BID 作为卖价,按 StopLoss*_Point 向上推止损、TakeProfit*_Point 向下推止盈,调 trade.Sell 下卖单。下成功 Print 出魔术码,失败则 Print GetLastError 返回码,方便你直接在 MT5 Experts 日志里抓错误。 移动止损段用 for(int i=0;i<PositionsTotal();i++) 重扫持仓,PositionSelect 按符号选中后再次比对魔术码。以买仓为例:当 SYMBOL_BID 与开仓价之差大于 TrailingStop*_Point,且原 SL 小于 current_price-TrailingStop*_Point 时,才把止损改到 current_price-TrailingStop*_Point。外汇与贵金属杠杆高,移动止损只护住浮盈但无法消除跳空风险,参数请按品种波动微调。

MQL5 / C++
class="type">long currentMagicNumber = PositionGetInteger(POSITION_MAGIC);
if (currentMagicNumber == MagicNumber)
  {
   open_position = true;
   class="kw">break;
  }
   }
  }
if (!open_position)
  {
   class="type">class="kw">double price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   class="type">class="kw">double sl = price + StopLoss * _Point;
   class="type">class="kw">double tp = price - TakeProfit * _Point;
   class=class="str">"cmt">// Open a sell order
   if (trade.Sell(Lots, _Symbol, price, sl, tp, "TrendFollowing Sell"))
     Print("Sell order placed with Magic Number: ", MagicNumber);
   else
     Print("Error placing sell order: ", GetLastError());
  }
  }
 class=class="str">"cmt">// Apply trailing stop
 for (class="type">int i = class="num">0; i < PositionsTotal(); i++) class=class="str">"cmt">// Correct loop initialization
  {
   if (PositionGetSymbol(i) == _Symbol && PositionSelect(PositionGetSymbol(i))) class=class="str">"cmt">// Select position by symbol
     {
      class="type">long currentMagicNumber = PositionGetInteger(POSITION_MAGIC);
      if (currentMagicNumber == MagicNumber) class=class="str">"cmt">// Apply trailing stop only to this EA&class="macro">#x27;s positions
        {
         class="type">class="kw">double price = PositionGetDouble(POSITION_PRICE_OPEN);
         class="type">class="kw">double stopLoss = PositionGetDouble(POSITION_SL);
         class="type">class="kw">double current_price;
         if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY)
           {
            current_price = SymbolInfoDouble(_Symbol, SYMBOL_BID);
            if (current_price - price > TrailingStop * _Point)
              {
               if (stopLoss < current_price - TrailingStop * _Point)
                 {
                  trade.PositionModify(PositionGetInteger(POSITION_TICKET), current_price - TrailingStop * _Point, PositionGetDouble(POSITION_TP));
                 }
              }

◍ 空单 trailing stop 的回调逻辑

这段处理的是已有 SELL 持仓时的移动止损分支。先通过 PositionGetInteger(POSITION_TYPE) 判断持仓类型,若为 POSITION_TYPE_SELL,则取 SYMBOL_ASK 作为当前报价——卖单是用 ask 价平仓,因此追踪基准必须用卖价而非买价。 核心判断是 price - current_price > TrailingStop * _Point:price 一般是曾经的最优价位,当价格从开仓后回撤超过设定的 TrailingStop 点数,才触发保护动作。这里用的是「回撤触发」而非「创新低跟随」,逻辑上更偏向锁定已有浮盈。

修改条件里 stopLoss > current_price + TrailingStop * _PointstopLoss == 0 意味着:要么原止损还远在更上方(说明还没被推过),要么从未设止损,才会把止损位推到 current_price + TrailingStop*_Point。最后调用 trade.PositionModify 只改止损、保留原 TP。外汇与贵金属杠杆高,这类回撤式追踪在剧烈反转行情中可能来不及成交,开 MT5 用策略测试器跑 EURUSD M15 能直接验证触发频率。
MQL5 / C++
      }
      else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
      {
         current_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
         if (price - current_price > TrailingStop * _Point)
           {
            if (stopLoss > current_price + TrailingStop * _Point || stopLoss == class="num">0)
              {
               trade.PositionModify(PositionGetInteger(POSITION_TICKET), current_price + TrailingStop * _Point, PositionGetDouble(POSITION_TP));
              }
           }
      }
   }
  }
 }
}
class=class="str">"cmt">//+------------------------------------------------------------------+

Boom 500 指数上的实测表现

在 Boom 500 指数上跑过这套 EA,主要观察了订单的触发与持仓分布。一个明显的短板是:同屏可能挂出多笔订单,去重逻辑还得补,否则实盘容易被重复信号刷屏。 用 10,000 美元的模拟账户做验证,系统整体走出正向结果;但账户规模一缩、或者切到真实账户,曲线大概率会变形,不能拿模拟表现直接外推。 这套东西本质是用于教学、实验和研究,不保证盈利。外汇与指数类品种杠杆高、滑点跳空频繁,接实盘前务必先在 MT5 策略测试器里用自己的点差重跑一遍。

「把工具请下神坛」

这套趋势约束 EA 只落地了七种策略里的第一种,核心是用双均线判方向、RSI 过滤超买超卖,再靠 Magic Number 把不同策略的订单隔开管理。附件 Trend_Constraint_Expert.mq5 约 7.7 KB,编译跑一遍就能看到均线+RSI 共振的开仓逻辑实际表现。 外汇和贵金属杠杆高、滑点跳空频繁,回测顺滑不等于实盘稳,Magic Number 配错就可能把两套策略的平仓搞混。 剩下六种策略是空位不是承诺,拿这份源码当骨架改参数、加信号,比等现成圣杯实在。

让小布替你跑这套多策略回测
这些诊断与小布盯盘的 AIGC 已内置,打开对应品种页即可看到策略冲突与胜率分布,把重复劳动交给小布,你专注决策。

常见问题

用HigherTimeframe的Open/Close比较判定看涨或看跌,再用if()包裹入场逻辑,使小周期信号必须同向才放行。
魔法数字用于区分同一品种上不同策略开出的订单,避免平仓逻辑误删其他模块持仓,便于分模块统计。
订单ticket或某些API返回值为long时,用int接就会告警,改成long即可消除,不影响逻辑但更规范。
可以,小布的品种页支持读取EA输出的策略标签与魔法数字,自动汇总各模块盈亏与活跃度,省去手动切图。
文中趋势跟踪类在带杠杆的外汇贵金属上概率上更稳,但高波动时段仍可能连续回撤,需配合约束模型降频。