MQL5自动化交易策略(第九部分):构建亚洲盘突破策略的智能交易系统(EA)·综合运用
📘

MQL5自动化交易策略(第九部分):构建亚洲盘突破策略的智能交易系统(EA)·综合运用

第 3/3 篇

◍ 亚盘突破挂单的双向触发逻辑

这段代码把价格相对均线的位置当成方向开关:现价高于均线判为多头,低于均线判为空头,再据此挂突破单。外汇与贵金属属高风险品种,这种判定只反映概率倾向,不保证后续一定走出趋势。 偏移量由 BreakoutOffsetPips 乘 _Point 换算,避免在 XAUUSD 这类点数不同于常规货币对的品种上算错距离。多头时挂 Buy Stop 于盘整高点加偏移,止损置于盘整低点减偏移,风险额 = 入场减止损,止盈按 RiskToReward 倍数延伸。 空头分支对称处理:Sell Stop 挂在低点减偏移,止损在高点加偏移,止盈向下按风险倍数推算。两个分支都靠 obj_Trade 返回码打印成败,方便在 MT5 专家日志里直接核对挂单是否落地。 当 currentTime >= tradeExitTime,不论挂单成交与否都走 CloseOpenPositions 与 CancelPendingOrders,把当天持仓和Pending清干净。实盘跑之前,建议先把 BoxHigh/BoxLow 的赋值段接上,否则编译能过、挂单价会是 0。

MQL5 / C++
class="type">class="kw">double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID); class=class="str">"cmt">//--- Get current bid price
class="type">bool bullish = (currentPrice > maValue);       class=class="str">"cmt">//--- Determine bullish condition
class="type">bool bearish = (currentPrice < maValue);       class=class="str">"cmt">//--- Determine bearish condition

class="type">class="kw">double offsetPrice = BreakoutOffsetPips * _Point; class=class="str">"cmt">//--- Convert pips to price units

class=class="str">"cmt">//--- If bullish, place a Buy Stop order
if(bullish){
    class="type">class="kw">double entryPrice = BoxHigh + offsetPrice; class=class="str">"cmt">//--- Set entry price just above the session high
    class="type">class="kw">double stopLoss   = BoxLow - offsetPrice;   class=class="str">"cmt">//--- Set stop loss below the session low
    class="type">class="kw">double risk       = entryPrice - stopLoss;  class=class="str">"cmt">//--- Calculate risk per unit
    class="type">class="kw">double takeProfit = entryPrice + risk * RiskToReward; class=class="str">"cmt">//--- Calculate take profit using risk/reward ratio
    if(obj_Trade.BuyStop(LotSize, entryPrice, _Symbol, stopLoss, takeProfit, ORDER_TIME_GTC, class="num">0, "Asian Breakout EA")){
        Print("Placed Buy Stop order at ", entryPrice); class=class="str">"cmt">//--- Print order confirmation
        ordersPlaced = true;                            class=class="str">"cmt">//--- Set flag indicating an order has been placed
    }
    else{
        Print("Buy Stop order failed: ", obj_Trade.ResultRetcodeDescription()); class=class="str">"cmt">//--- Print error if order fails
    }
}
class=class="str">"cmt">//--- If bearish, place a Sell Stop order
else if(bearish){
    class="type">class="kw">double entryPrice = BoxLow - offsetPrice;   class=class="str">"cmt">//--- Set entry price just below the session low
    class="type">class="kw">double stopLoss   = BoxHigh + offsetPrice;  class=class="str">"cmt">//--- Set stop loss above the session high
    class="type">class="kw">double risk       = stopLoss - entryPrice;  class=class="str">"cmt">//--- Calculate risk per unit
    class="type">class="kw">double takeProfit = entryPrice - risk * RiskToReward; class=class="str">"cmt">//--- Calculate take profit using risk/reward ratio
    if(obj_Trade.SellStop(LotSize, entryPrice, _Symbol, stopLoss, takeProfit, ORDER_TIME_GTC, class="num">0, "Asian Breakout EA")){
        Print("Placed Sell Stop order at ", entryPrice); class=class="str">"cmt">//--- Print order confirmation
        ordersPlaced = true;                            class=class="str">"cmt">//--- Set flag indicating an order has been placed
    }
    else{
        Print("Sell Stop order failed: ", obj_Trade.ResultRetcodeDescription()); class=class="str">"cmt">//--- Print error if order fails
    }
}
class=class="str">"cmt">//--- If current time is at or past trade exit time, close positions and cancel pending orders
if(currentTime >= tradeExitTime){
    CloseOpenPositions();                  class=class="str">"cmt">//--- Close all open positions for this EA
    CancelPendingOrders();                 class=class="str">"cmt">//--- Cancel all pending orders for this EA
}

「按魔术码回收持仓与挂单」

会话盒重置时,两个布尔开关 boxCalculated 与 ordersPlaced 要同步置 false,否则 EA 会误判本时段逻辑已跑完,跳过重新画框和下单。 下面两个函数专门做收盘前清理:CloseOpenPositions 用 PositionsTotal 取当前持仓数,逆序从 totalPositions-1 循环到 0,避免删除元素导致索引错位;只处理 POSITION_MAGIC 等于本 EA 魔术码的单子,调用 obj_Trade.PositionClose 平仓,失败就打印 RetcodeDescription。 CancelPendingOrders 同理遍历 OrdersTotal,用 OrderGetTicket 拿 ticket 并 OrderSelect,过滤 ORDER_MAGIC 匹配项后撤挂单。实盘外汇与贵金属波动剧烈,逆序遍历是 MT5 批量平仓不出错的基础写法,开 MT5 把 MagicNumber 改成你的数值即可验证。

MQL5 / C++
  boxCalculated = false;                                                     class=class="str">"cmt">//--- Reset session box calculated flag
  ordersPlaced  = false;                                                     class=class="str">"cmt">//--- Reset order placed flag
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Function: CloseOpenPositions                                                    |
class=class="str">"cmt">//| Purpose: Close all open positions with the set magic number          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CloseOpenPositions(){
   class="type">int totalPositions = PositionsTotal();                                 class=class="str">"cmt">//--- Get total number of open positions
   for(class="type">int i = totalPositions - class="num">1; i >= class="num">0; i--){                         class=class="str">"cmt">//--- Loop through positions in reverse order
      class="type">ulong ticket = PositionGetTicket(i);                               class=class="str">"cmt">//--- Get ticket number for each position
      if(PositionSelectByTicket(ticket)){                               class=class="str">"cmt">//--- Select position by ticket
         if(PositionGetInteger(POSITION_MAGIC) == MagicNumber){         class=class="str">"cmt">//--- Check if position belongs to this EA
            if(!obj_Trade.PositionClose(ticket))                        class=class="str">"cmt">//--- Attempt to close position
               Print("Failed to close position ", ticket, ": ", obj_Trade.ResultRetcodeDescription()); class=class="str">"cmt">//--- Print error if closing fails
            else
               Print("Closed position ", ticket);                       class=class="str">"cmt">//--- Confirm position closed
         }
      }
   }
}

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Function: CancelPendingOrders                                                    |
class=class="str">"cmt">//| Purpose: Cancel all pending orders with the set magic number           |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CancelPendingOrders(){
   class="type">int totalOrders = OrdersTotal();                                      class=class="str">"cmt">//--- Get total number of pending orders
   for(class="type">int i = totalOrders - class="num">1; i >= class="num">0; i--){                            class=class="str">"cmt">//--- Loop through orders in reverse order
      class="type">ulong ticket = OrderGetTicket(i);                                  class=class="str">"cmt">//--- Get ticket number for each order
      if(OrderSelect(ticket)){                                           class=class="str">"cmt">//--- Select order by ticket
         class="type">int type = (class="type">int)OrderGetInteger(ORDER_TYPE);                    class=class="str">"cmt">//--- Retrieve order type
         if(OrderGetInteger(ORDER_MAGIC) == MagicNumber &&              class=class="str">"cmt">//--- Check if order belongs to this EA

挂单清理的 Buy Stop / Sell Stop 分支

这段逻辑只针对突破类挂单:当挂单类型属于 ORDER_TYPE_BUY_STOP 或 ORDER_TYPE_SELL_STOP 时,才进入撤单流程。其余限价类挂单(Buy Limit / Sell Limit)在此分支中被直接跳过,不会被动撤除。 实际调用使用 CTrade 对象的 OrderDelete(ticket) 方法,传入待删挂单的 ticket 编号。若返回 false,说明撤单失败,终端会打印 "Failed to cancel pending order " 加 ticket 号,便于在专家日志里定位具体哪一笔没删掉。 撤单成功时则打印 "Canceled pending order " 加 ticket 号作为确认。外汇与贵金属市场跳空频繁,这类 Stop 挂单在重大数据前若不及时清理,可能以不利价格成交,属典型高风险场景,建议回测时统计被删 ticket 的占比来验证逻辑触发频率。

MQL5 / C++
(type == ORDER_TYPE_BUY_STOP || type == ORDER_TYPE_SELL_STOP)){
      if(!obj_Trade.OrderDelete(ticket))       class=class="str">"cmt">//--- Attempt to class="kw">delete pending order
         Print("Failed to cancel pending order ", ticket); class=class="str">"cmt">//--- Print error if deletion fails
      else
         Print("Canceled pending order ", ticket); class=class="str">"cmt">//--- Confirm pending order canceled
      }
   }
}

◍ 追踪止损把回测再往前推一步

  • 全年用默认参数跑完完整回测,图形表现已经算顺眼,但权益曲线在趋势反转段回吐偏多。引入追踪止损后,回测样本里最大回撤倾向收窄,胜率没掉,说明这套逻辑在外汇与贵金属上值得上机验证,但杠杆品种高风险仍在,参数错配可能放大亏损。

核心做法是在 OnTick 里判断有持仓就调用追踪函数,按 slPoints 距离动态改挂止损。下面这段可直接贴进 EA 里跑,注意 slPoints 单位是报价点,不是点差。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void applyTrailingSTOP(class="type">class="kw">double slPoints, CTrade &trade_object,class="type">int magicNo=class="num">0){
   class="type">class="kw">double buySL = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID)-slPoints,_Digits); class=class="str">"cmt">//--- 多单止损=当前买价减slPoints并规范精度
   class="type">class="kw">double sellSL = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK)+slPoints,_Digits); class=class="str">"cmt">//--- 空单止损=当前卖价加slPoints并规范精度
   for (class="type">int i = PositionsTotal() - class="num">1; i >= class="num">0; i--){ class=class="str">"cmt">//--- 倒序遍历所有持仓避免索引错位
      class="type">ulong ticket = PositionGetTicket(i);          class=class="str">"cmt">//--- 取持仓票据号
      if (ticket > class="num">0){                              class=class="str">"cmt">//--- 票据有效才继续
         if (PositionGetString(POSITION_SYMBOL) == _Symbol &&
            (magicNo == class="num">0 || PositionGetInteger(POSITION_MAGIC) == magicNo)){ class=class="str">"cmt">//--- 校验品种与魔术码
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
               buySL > PositionGetDouble(POSITION_PRICE_OPEN) &&
               (buySL > PositionGetDouble(POSITION_SL) ||
               PositionGetDouble(POSITION_SL) == class="num">0)){ class=class="str">"cmt">//--- 多单且新止损优于原止损或原止损为空才改
               trade_object.PositionModify(ticket,buySL,PositionGetDouble(POSITION_TP));
            }
            else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
               sellSL < PositionGetDouble(POSITION_PRICE_OPEN) &&
               (sellSL < PositionGetDouble(POSITION_SL) ||
               PositionGetDouble(POSITION_SL) == class="num">0)){ class=class="str">"cmt">//--- 空单对称逻辑
               trade_object.PositionModify(ticket,sellSL,PositionGetDouble(POSITION_TP));
            }
         }
      }
   }
}
class=class="str">"cmt">//---- 在行情事件里这样调
if (PositionsTotal() > class="num">0){               class=class="str">"cmt">//--- 有持仓才触发追踪
回测报告里新曲线在 2023 下半年震荡市回吐更少,但样本仅一年,换周期可能失效。建议先拿 XAUUSD 的 M15 用 slPoints=150 试,再按品种波动率调。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void applyTrailingSTOP(class="type">class="kw">double slPoints, CTrade &trade_object,class="type">int magicNo=class="num">0){
   class="type">class="kw">double buySL = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_BID)-slPoints,_Digits); class=class="str">"cmt">//--- Calculate SL for buy positions
   class="type">class="kw">double sellSL = NormalizeDouble(SymbolInfoDouble(_Symbol,SYMBOL_ASK)+slPoints,_Digits); class=class="str">"cmt">//--- Calculate SL for sell positions
   for (class="type">int i = PositionsTotal() - class="num">1; i >= class="num">0; i--){ class=class="str">"cmt">//--- Iterate through all open positions
      class="type">ulong ticket = PositionGetTicket(i);          class=class="str">"cmt">//--- Get position ticket
      if (ticket > class="num">0){                              class=class="str">"cmt">//--- If ticket is valid
         if (PositionGetString(POSITION_SYMBOL) == _Symbol &&
            (magicNo == class="num">0 || PositionGetInteger(POSITION_MAGIC) == magicNo)){ class=class="str">"cmt">//--- Check symbol and magic number
            if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY &&
               buySL > PositionGetDouble(POSITION_PRICE_OPEN) &&
               (buySL > PositionGetDouble(POSITION_SL) ||
               PositionGetDouble(POSITION_SL) == class="num">0)){ class=class="str">"cmt">//--- Modify SL for buy position if conditions are met
               trade_object.PositionModify(ticket,buySL,PositionGetDouble(POSITION_TP));
            }
            else if (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL &&
               sellSL < PositionGetDouble(POSITION_PRICE_OPEN) &&
               (sellSL < PositionGetDouble(POSITION_SL) ||
               PositionGetDouble(POSITION_SL) == class="num">0)){ class=class="str">"cmt">//--- Modify SL for sell position if conditions are met
               trade_object.PositionModify(ticket,sellSL,PositionGetDouble(POSITION_TP));
            }
         }
      }
   }
}
class=class="str">"cmt">//---- CALL THE FUNCTION IN THE TICK EVENT HANDLER
if (PositionsTotal() > class="num">0){               class=class="str">"cmt">//--- If there are open positions

「用 30 点步长挂上追踪止损」

这段调用把追踪止损逻辑直接接进交易对象,步长取 30 倍 _Point,对多数外汇品种即 3 个报价单位(如 EURUSD 的 0.00030)。 参数 0 表示不对单笔订单做额外偏移,纯按市价回撤距离推进止损线;obj_Trade 需是已初始化且绑定当前账户的 CTrade 实例,否则调用静默失效。 开 MT5 把这段代码塞进 OnTradeTransaction 或定时器的尾段,跑一小时就能看到盈利单的 SL 随最高价自动上移,贵金属 XAUUSD 因点值大,30 点约 0.30 美元/盎司,回测时注意滑点会吃掉部分保护效果。

MQL5 / C++
  applyTrailingSTOP(class="num">30*_Point,obj_Trade,class="num">0);   class=class="str">"cmt">//--- Apply a trailing stop
}

一点提醒

这套亚洲时段突破 EA 把时段区间识别、MA 趋势过滤和动态风控塞进一个 mq5 文件,实盘前你最好先在 MT5 策略测试器里把近半年数据跑一遍,重点看亚洲盘突破后的回撤幅度是否超出你账户能扛的止损带。 外汇和贵金属杠杆高、跳空频繁,任何自动化框架都只是把人工判断转成代码,不保证概率优势必然兑现;部署前务必用自有参数做全面回测,并给每单设硬止损。 把这套东西吃透后,你可以照它的结构改伦敦或纽约时段参数,也可以把 MA 过滤换成自己的价格行为规则,算法交易的迭代就从这种可验证的小系统开始。

常见问题

在亚洲盘高低点外各挂 Buy Stop 和 Sell Stop,任一侧触发即市价进场,另一侧挂单按魔术码回收,避免双向持仓。
给 EA 挂单和持仓打同一个魔术码,遍历订单时只处理该魔术码,其他单子原样保留。
小布可接入品种页自动标注亚盘区间与挂单触发状态,提醒你哪侧成交、哪侧被回收,省去手动翻终端。
用 30 点步长挂追踪止损,回测中回撤被砍得更细,权益曲线更平滑,但样本内胜率可能略降,需换周期验证。
贵金属夜盘流动性薄,假突破概率偏高,建议先开模拟跑两周,确认滑点可控再上实盘,外汇贵金属均属高风险。