构建K线趋势约束模型(第九部分):多策略智能交易系统(EA)(三)(基础篇)
📘

构建K线趋势约束模型(第九部分):多策略智能交易系统(EA)(三)(基础篇)

第 1/3 篇

◍ 用分形结构给EA加一道趋势闸门

在 MT5 里做多策略 EA,最容易被忽略的是「趋势到底有没有成立」这一道前置约束。很多策略在震荡里反复开仓,回测漂亮、实盘失血,根子就在缺一个轻量的方向过滤器。 分形(Fractal)是 MT5 内置指标,默认参数 5 根 K 线:中间那根最高/最低,左右各两根更低/更高即构成一个顶/底分形。用它做趋势闸门很简单——只在最近一个上分形被突破时允许做多,下分形被跌破时允许做空。 下面这段 MQL5 片段演示如何抓取最近的分形并判断突破,可直接塞进 EA 的 OnTick 里做条件门禁。外汇与贵金属杠杆高、滑点突变频繁,分形假突破概率不低,实盘前务必用策略测试器跑至少 3 个月 tick 数据。

MQL5 / C++
class="type">class="kw">double lastFractalUp = iFractals(NULL,class="num">0,MODE_UPPER,class="num">1);
 class="type">class="kw">double lastFractalDown = iFractals(NULL,class="num">0,MODE_LOWER,class="num">1);
 class="type">class="kw">double bid = SymbolInfoDouble(_Symbol,SYMBOL_BID);
 class="type">class="kw">double ask = SymbolInfoDouble(_Symbol,SYMBOL_ASK);
 class="type">bool canBuy = (bid > lastFractalUp) && (lastFractalUp != class="num">0);
 class="type">bool canSell = (ask < lastFractalDown) && (lastFractalDown != class="num">0);

「用背离给趋势系统加一道过滤」

在日线级别做趋势跟随,最头疼的不是方向,而是噪音。一根无关紧要的回调毛刺,就足以让入场触发在错误位置,把原本能成的单子变成止损。统计上,纯趋势系统在这层的假信号率往往高于波段系统,尤其在低波动转高波动的切换段。 背离的思路很直接:价格新高(或新低)但动量指标不跟随,说明推动力量在衰减。把它当过滤器接进趋势约束 EA,等于强制要求「价格走出来」和「动量认账」同时成立,才放行订单。 MQL5 的价值在这儿就显出来了——指标计算和订单路由能在同一个 OnTick 里闭环,不需要外挂脚本轮询。后面几节会拆具体集成步骤,以及回测里假信号下降了多少。

背离到底在提示什么

背离的本质,是用指标和价格的「不同步」来暴露趋势内部的虚弱。价格还在沿原方向刷新极值,但 RSI、MACD 这类动能指标已经不肯跟了,这种方向撕扯往往意味着当前趋势的推动力在衰减,反转或至少回调的概率会抬升。 具体看两种基础形态:看涨背离是价格录得更低的低点,指标却走出更高的低点,卖压退潮的信号;看跌背离反过来,价格新高而指标高点下移,上行动能枯竭的先兆。两者都不是承诺反转,只是把「趋势可能乏力」这件事量化出来。 学术侧也不是空谈。Bart 和 Masse 在 1981 年讨论意见分歧与风险时指出,市场观点分化会放大波动与风险,这恰好解释了为什么背离能成为技术分析的支点。Tilehnouei 和 Shivaraj 2013 年的实证还提到,在特定样本里 MACD 抓背离动能的表现可能优于 RSI——所以实盘里拿 RSI 和 MACD 交叉验证,比单吊一个指标更稳。 外汇和贵金属杠杆高、跳空频繁,背离信号假突破不少,真要用的话先在 MT5 历史数据里把两种指标的背离命中率分开测一遍再上实盘。

◍ 把背离判据写进EA的检测函数

做背离集成,第一步是用 iRSI() 算出指标序列,再拿 iHigh() / iLow() 抓指定 K 线的极值,跟价格摆在一起比。项目里把背离拆成两族:常规背离偏向反转预警,隐藏背离偏向趋势延续,EA 里要分函数写,别混在一个判断里。 常规看跌背离的典型结构是价格后一个高点更高、RSI 后一个高点却更低,概率上提示上行推力衰减;常规看涨则反过来,价格新低而 RSI 低点抬高。隐藏看涨出现在上升趋势里价格低点抬高、RSI 低点却压低,视作回踩续涨的参考。 下面两段是可直接塞进 EA 的骨架。CheckRegularBearishDivergence 取 H1 周期、RSI(14),比第 2 根与第 8 根 K 线的价格高点和 RSI 高点:价格高1>高2 且 RSI高1<高2 才返回 true。CheckHiddenBullishDivergence 同理比低点,价格低1>低2 且 RSI低1<低2 判为隐藏看涨。 实盘接进去时,建议在每个 K 线收盘触发一次,而不是每 tick 跑,能少算很多次 iRSI。外汇与贵金属杠杆高、滑点大,背离只是概率信号,必须叠一层趋势或情绪过滤再下单。

MQL5 / C++
class=class="str">"cmt">// Regular divergence conditions in code
class="type">bool CheckRegularBearishDivergence(class="type">int period = class="num">14, ENUM_TIMEFRAMES timeframe = PERIOD_H1)
{
   class="type">class="kw">double priceHigh1 = iHigh(_Symbol, timeframe, class="num">2);
   class="type">class="kw">double priceHigh2 = iHigh(_Symbol, timeframe, class="num">8);
   class="type">class="kw">double rsiHigh1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, class="num">2);
   class="type">class="kw">double rsiHigh2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, class="num">8);
   
   if(priceHigh1 > priceHigh2 && rsiHigh1 < rsiHigh2) class="kw">return true;
   class="kw">return false;
}
class=class="str">"cmt">//RSI and Price Levels declaration and hidden divergence condition
class="type">bool CheckHiddenBullishDivergence(class="type">int period = class="num">14, ENUM_TIMEFRAMES timeframe = PERIOD_H1)
{
   class="type">class="kw">double priceLow1 = iLow(_Symbol, timeframe, class="num">2);
   class="type">class="kw">double priceLow2 = iLow(_Symbol, timeframe, class="num">8);
   class="type">class="kw">double rsiLow1 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, class="num">2);
   class="type">class="kw">double rsiLow2 = iRSI(_Symbol, timeframe, period, PRICE_CLOSE, class="num">8);
   
   if(priceLow1 > priceLow2 && rsiLow1 < rsiLow2) class="kw">return true;
   class="kw">return false;
}

「用 MACD 给背离信号做二次过滤」

背离刚被确认还不够稳,实盘里容易吃假突破。项目里选 MACD 搭配 RSI 做二次验证:RSI 抓到背离后,看 MACD 线是否下穿信号线、或柱状图开始收缩,看跌背离才倾向算有效。看跌背离若同步出现 MACD 线跌破信号线,信号可靠性会提升,但外汇和贵金属波动大,仍属概率性触发。 代码层先声明缓冲区与输入参数,MACD 句柄在 OnInit 里用 iMACD 建好,失败就返回 INIT_FAILED。LookBack 设 8,意味着只回看最近 8 根 K 线找背离,避免拿太老的数据误判。 持仓身份用 magic 编号 87654321 锁定,CountOrdersByMagic 统计同编号持仓,背离策略最多开 3 笔,防过度交易。盈利保护上,LockProfits 扫活跃仓,盈利超 100 点就把止损按 profitLockerPoints(如 20 点)向价格方向移动,买仓上移、卖仓下移,仅当新止损更优才改。 日线趋势对齐是硬约束:日线看涨只做多,看空只做空,且只在完整 K 线执行。以下为参数与初始化片段,可直接拷进 MT5 的 EA 里改周期验证。

MQL5 / C++
class=class="str">"cmt">//--- indicator buffers
class="type">class="kw">double ExtMacdBuffer[];
class="type">class="kw">double ExtSignalBuffer[];
class="type">class="kw">double ExtFastMaBuffer[];
class="type">class="kw">double ExtSlowMaBuffer[];
class="type">int    ExtFastMaHandle;
class="type">int    ExtSlowMaHandle;
class="macro">#include <Trade\Trade.mqh>
CTrade trade;
input class="type">bool UseDivergenceStrategy = true;           class=class="str">"cmt">// Enable/Disable Divergence Strategy
input class="type">int DivergenceMACDPeriod = class="num">12;               class=class="str">"cmt">// MACD Fast EMA period
input class="type">int DivergenceSignalPeriod = class="num">9;              class=class="str">"cmt">// MACD Signal period
input class="type">class="kw">double DivergenceLots = class="num">1.0;                 class=class="str">"cmt">// Lot size for Divergence trades
input class="type">class="kw">double DivergenceStopLoss = class="num">300;             class=class="str">"cmt">// Stop Loss in points for Divergence
input class="type">class="kw">double DivergenceTakeProfit = class="num">500;           class=class="str">"cmt">// Take Profit in points for Divergence
input class="type">int DivergenceMagicNumber = class="num">87654321;        class=class="str">"cmt">// Magic number for Divergence Strategy
input class="type">int DivergenceLookBack = class="num">8;                  class=class="str">"cmt">// Number of periods to look back for divergence
class="type">class="kw">double ExtMacdBuffer[]; class=class="str">"cmt">// MACD values
class="type">class="kw">double ExtSignalBuffer[]; class=class="str">"cmt">// Signal line values
class="type">int macd_handle; class=class="str">"cmt">// MACD indicator handle
class="type">int OnInit()
{
   macd_handle = iMACD(_Symbol, PERIOD_CURRENT, DivergenceMACDPeriod, class="num">26, DivergenceSignalPeriod, PRICE_CLOSE);
   if (macd_handle == INVALID_HANDLE)
   {
      Print("Failed to initialize MACD. Error: ", GetLastError());
      class="kw">return INIT_FAILED;
   }
   ArrayResize(ExtMacdBuffer, DivergenceLookBack);
   ArrayResize(ExtSignalBuffer, DivergenceLookBack);
   class="kw">return INIT_SUCCEEDED;
}
class="type">bool CheckBullishRegularDivergence()
{
   class="type">class="kw">double priceLow1 = iLow(_Symbol, PERIOD_CURRENT, class="num">2);

把背离信号接进日内方向过滤

日线收阳才考虑多、收阴才考虑空,这是这段逻辑给背离信号加的硬约束。代码里用 iClose(_Symbol, PERIOD_D1, 0) 与 iOpen 比大小,日线实体方向直接决定后续只允许同向背离单触发,逆日线的常规背离被挡在门外。 隐藏背离的判定不依赖信号线交叉:牛市隐藏背离要求 priceHigh1 > priceHigh2 且 macdHigh1 < macdHigh2,熊市则反过来比低点。常规背离还要叠一层 ExtMacdBuffer[0] 与 ExtSignalBuffer[0] 的位置关系,主线在信号线上方才放行多单。 下单函数里止损止盈按点距算:DivergenceStopLoss 和 DivergenceTakeProfit 乘上 _Point,买价加减、卖价反加减。外汇与贵金属杠杆高,这类基于背离的 EA 分支在震荡市可能连续假信号,实盘前务必在 MT5 策略测试器用真实点差跑一遍。 开 MT5 把 DivergenceLookBack 从默认改到 20~50 之间试,回看 EURUSD 的 H1 上隐藏背离触发频率,能直观看到日线过滤砍掉了多少逆势单。

MQL5 / C++
class="type">class="kw">double priceLow2 = iLow(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
  class="type">class="kw">double macdLow1 = ExtMacdBuffer[class="num">2];
  class="type">class="kw">double macdLow2 = ExtMacdBuffer[DivergenceLookBack - class="num">1];
  class="kw">return (priceLow1 < priceLow2 && macdLow1 > macdLow2);
}
class="type">bool CheckBearishHiddenDivergence()
{
  class="type">class="kw">double priceHigh1 = iHigh(_Symbol, PERIOD_CURRENT, class="num">2);
  class="type">class="kw">double priceHigh2 = iHigh(_Symbol, PERIOD_CURRENT, DivergenceLookBack);
  class="type">class="kw">double macdHigh1 = ExtMacdBuffer[class="num">2];
  class="type">class="kw">double macdHigh2 = ExtMacdBuffer[DivergenceLookBack - class="num">1];
  class="kw">return (priceHigh1 < priceHigh2 && macdHigh1 > macdHigh2);
}
class="type">void CheckDivergenceTrading()
{
  if (!UseDivergenceStrategy) class="kw">return;
  class="type">int openDivergencePositions = CountOrdersByMagic(DivergenceMagicNumber);
  if (openDivergencePositions == class="num">0 || openDivergencePositions < class="num">3)
  {
    if (CopyBuffer(macd_handle, class="num">0, class="num">0, DivergenceLookBack, ExtMacdBuffer) > class="num">0 &&
        CopyBuffer(macd_handle, class="num">1, class="num">0, DivergenceLookBack, ExtSignalBuffer) > class="num">0)
    {
      class="type">class="kw">double dailyClose = iClose(_Symbol, PERIOD_D1, class="num">0);
      class="type">class="kw">double dailyOpen = iOpen(_Symbol, PERIOD_D1, class="num">0);
      class="type">bool isDailyBullish = dailyClose > dailyOpen;
      class="type">bool isDailyBearish = dailyClose < dailyOpen;
      if (isDailyBullish &&
          (CheckBullishRegularDivergence() && ExtMacdBuffer[class="num">0] > ExtSignalBuffer[class="num">0]) ||
          CheckBullishHiddenDivergence())
      {
        ExecuteDivergenceOrder(true);
      }
      if (isDailyBearish &&
          (CheckBearishRegularDivergence() && ExtMacdBuffer[class="num">0] < ExtSignalBuffer[class="num">0]) ||
          CheckBearishHiddenDivergence())
      {
        ExecuteDivergenceOrder(false);
      }
    }
  }
}
class="type">void ExecuteDivergenceOrder(class="type">bool isBuy)
{
  trade.SetExpertMagicNumber(DivergenceMagicNumber);
  class="type">class="kw">double currentPrice = isBuy ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID);
  class="type">class="kw">double stopLossPrice = isBuy ? currentPrice - DivergenceStopLoss * _Point : currentPrice + DivergenceStopLoss * _Point;
  class="type">class="kw">double takeProfitPrice = isBuy ? currentPrice + DivergenceTakeProfit * _Point : currentPrice - DivergenceTakeProfit * _Point;
  if (isBuy)
  {

常见问题

分形默认看左右各2根K线,震荡市可缩到1根提敏,但需配合背离过滤防假突破,回测时重点看漏单率而非单纯信号数。
贵金属波动大,单纯价格-MACD背离假信号偏多,建议叠加分形趋势闸门,仅在下降趋势分形被破坏后的反弹中才信顶背离。
可以,小布盯盘的AIGC已内置多品种分形+MACD背离诊断,打开对应品种页就能直接看当前是否触发趋势闸门与过滤条件。
原文未给具体胜率数字;实测中加背离二次过滤后,震荡段回撤倾向减小,但需你自己在平台跑EA验证样本表现。
在检测函数里加阈值缓冲,例如MACD柱差值绝对值小于均值0.1倍直接丢弃,并用连续2根确认代替单根触发。