MQL5自动化交易策略(第九部分):构建亚洲盘突破策略的智能交易系统(EA)·综合运用
◍ 亚盘突破挂单的双向触发逻辑
这段代码把价格相对均线的位置当成方向开关:现价高于均线判为多头,低于均线判为空头,再据此挂突破单。外汇与贵金属属高风险品种,这种判定只反映概率倾向,不保证后续一定走出趋势。 偏移量由 BreakoutOffsetPips 乘 _Point 换算,避免在 XAUUSD 这类点数不同于常规货币对的品种上算错距离。多头时挂 Buy Stop 于盘整高点加偏移,止损置于盘整低点减偏移,风险额 = 入场减止损,止盈按 RiskToReward 倍数延伸。 空头分支对称处理:Sell Stop 挂在低点减偏移,止损在高点加偏移,止盈向下按风险倍数推算。两个分支都靠 obj_Trade 返回码打印成败,方便在 MT5 专家日志里直接核对挂单是否落地。 当 currentTime >= tradeExitTime,不论挂单成交与否都走 CloseOpenPositions 与 CancelPendingOrders,把当天持仓和Pending清干净。实盘跑之前,建议先把 BoxHigh/BoxLow 的赋值段接上,否则编译能过、挂单价会是 0。
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 改成你的数值即可验证。
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 的占比来验证逻辑触发频率。
(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 单位是报价点,不是点差。
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">//--- 有持仓才触发追踪
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 美元/盎司,回测时注意滑点会吃掉部分保护效果。
applyTrailingSTOP(class="num">30*_Point,obj_Trade,class="num">0); class=class="str">"cmt">//--- Apply a trailing stop }
一点提醒
这套亚洲时段突破 EA 把时段区间识别、MA 趋势过滤和动态风控塞进一个 mq5 文件,实盘前你最好先在 MT5 策略测试器里把近半年数据跑一遍,重点看亚洲盘突破后的回撤幅度是否超出你账户能扛的止损带。 外汇和贵金属杠杆高、跳空频繁,任何自动化框架都只是把人工判断转成代码,不保证概率优势必然兑现;部署前务必用自有参数做全面回测,并给每单设硬止损。 把这套东西吃透后,你可以照它的结构改伦敦或纽约时段参数,也可以把 MA 过滤换成自己的价格行为规则,算法交易的迭代就从这种可验证的小系统开始。