MQL5中的自动化交易策略(第七部分):构建具备仓位动态调整功能的网格交易EA·综合运用
📘

MQL5中的自动化交易策略(第七部分):构建具备仓位动态调整功能的网格交易EA·综合运用

第 3/3 篇

◍ 网格加仓后的保本价怎么算

当账户跑的是网格类策略、同方向挂着多单或多空时,靠肉眼估保本价基本会错。下面这段逻辑直接遍历所有持仓,按成交量加权算出统一进场成本,比单看首仓价格实用。 外汇与贵金属网格策略本身杠杆高、回撤快,保本价只是风控参考,不代表价格触及就必然反转,实际滑点可能让平仓价偏离计算值十几点。 核心函数是 CalculateWeightedBreakevenPrice:先取第一张单的方向,再累加同方向每张单的「开仓价×手数」和总手数,最后相除得出加权均价。若总手数为 0 则返回 0,避免除零报错。 配套的 CloseAllPositions 用倒序循环(从 PositionsTotal()-1 到 0)逐个按 ticket 平仓,Print 出成功或失败的单号,方便在 MT5 Experts 日志里核对哪张单没平掉。

MQL5 / C++
class="type">void CloseAllPositions(){
  for(class="type">int i = PositionsTotal()-class="num">1; i >= class="num">0; i--){
    class="type">ulong posTkt = PositionGetTicket(i);
    if(PositionSelectByTicket(posTkt)){
      if(obj_Trade.PositionClose(posTkt))
        Print("Closed position ticket: ", posTkt);
      else
        Print("Failed to close position ticket: ", posTkt);
    }
  }
}

if(closureMode == CLOSE_BY_POINTS && PositionsTotal() > class="num">1)
  CheckBreakevenClose(ask, bid);

class="type">class="kw">double CalculateWeightedBreakevenPrice(){
  class="type">class="kw">double totalCost = class="num">0;
  class="type">class="kw">double totalVolume = class="num">0;
  class="type">int posType = -class="num">1;
  for(class="type">int i = class="num">0; i < PositionsTotal(); i++){
    class="type">ulong ticket = PositionGetTicket(i);
    if(PositionSelectByTicket(ticket)){
      posType = (class="type">int)PositionGetInteger(POSITION_TYPE);
      class="kw">break;
    }
  }
  for(class="type">int i = class="num">0; i < PositionsTotal(); i++){
    class="type">ulong ticket = PositionGetTicket(i);
    if(PositionSelectByTicket(ticket)){
      if(PositionGetInteger(POSITION_TYPE) == posType){
        class="type">class="kw">double price = PositionGetDouble(POSITION_PRICE_OPEN);
        class="type">class="kw">double volume = PositionGetDouble(POSITION_VOLUME);
        totalCost += price * volume;
        totalVolume += volume;
      }
    }
  }
  if(totalVolume > class="num">0)
    class="kw">return(totalCost / totalVolume);
  else
    class="kw">return(class="num">0);
}

网格持仓的加权保本平仓判定

在 CLOSE_BY_POINTS 模式下,若账户里挂着多笔同方向仓位(典型网格加仓),就不能用单笔开仓价当保本线。下面这段逻辑先算整体加权保本价,再比对实时报价是否越过阈值,触发则全平。 代码里第一步就卡了 PositionsTotal() <= 1 直接 return,说明这套判定只服务于多仓共存场景;单仓时由别的逻辑管。随后用 CalculateWeightedBreakevenPrice() 拿加权价,并随便挑一个仓位读出 POSITION_TYPE 决定多空方向。

MQL5 / C++
class=class="str">"cmt">//--- Function: CheckBreakevenClose
class=class="str">"cmt">//--- Description: When class="kw">using CLOSE_BY_POINTS and multiple positions exist,
class=class="str">"cmt">//---              calculates the weighted breakeven price and checks if the
class=class="str">"cmt">//---              current price has moved the specified points in a profitable
class=class="str">"cmt">//---              direction relative to breakeven. If so, closes all positions.
class=class="str">"cmt">//+-----------------------------------------------------------------------------+
class="type">void CheckBreakevenClose(class="type">class="kw">double ask, class="type">class="kw">double bid){
   class=class="str">"cmt">//--- Ensure we have more than one position(grid positions)
   if(PositionsTotal() <= class="num">1)
      class="kw">return;
      
   class="type">class="kw">double weightedBreakeven = CalculateWeightedBreakevenPrice();
   class="type">int posType = -class="num">1;
   class=class="str">"cmt">//--- Determine the trade type from one of the positions
   for(class="type">int i = class="num">0; i < PositionsTotal(); i++){
      class="type">ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket)){
         posType = (class="type">int)PositionGetInteger(POSITION_TYPE);
         class="kw">break;
      }
   }
   if(posType == -class="num">1)
      class="kw">return;
      
   class=class="str">"cmt">//--- For BUY positions, profit when Bid >= breakeven + threshold
   if(posType == POSITION_TYPE_BUY){
      if(bid >= weightedBreakeven + breakevenPoints){
         Print("Closing BUY positions: Bid(", bid, ") >= Breakeven(", weightedBreakeven, ") + ", breakevenPoints);
         CloseAllPositions();
      }
   }
   class=class="str">"cmt">//--- For SELL positions, profit when Ask <= breakeven - threshold
   else if(posType == POSITION_TYPE_SELL){
      if(ask <= weightedBreakeven - breakevenPoints){
         Print("Closing SELL positions: Ask(", ask, ") <= Breakeven(", weightedBreakeven, ") - ", breakevenPoints);
         CloseAllPositions();
      }
   }
}
逐行拆解:函数入口取 ask/bid 双报价;PositionsTotal()<=1 时退出,避免单仓误用网格逻辑。weightedBreakeven 来自外部加权计算,posType 通过遍历 tickets 取首个有效仓位的类型。BUY 分支看 bid 是否 ≥ 加权保本价加 breakevenPoints 点;SELL 分支看 ask 是否 ≤ 加权保本价减阈值,满足就 Print 并 CloseAllPositions()。 回测覆盖过 2024 全年,视频里能看到这一平保模块在多点触发时的动作节奏。外汇与贵金属网格策略自带高杠杆与滑点风险,加权保本只能降低回撤概率,不保证免亏。

MQL5 / C++
class=class="str">"cmt">//--- Function: CheckBreakevenClose
class=class="str">"cmt">//--- Description: When class="kw">using CLOSE_BY_POINTS and multiple positions exist,
class=class="str">"cmt">//---              calculates the weighted breakeven price and checks if the
class=class="str">"cmt">//---              current price has moved the specified points in a profitable
class=class="str">"cmt">//---              direction relative to breakeven. If so, closes all positions.
class=class="str">"cmt">//+-----------------------------------------------------------------------------+
class="type">void CheckBreakevenClose(class="type">class="kw">double ask, class="type">class="kw">double bid){
   class=class="str">"cmt">//--- Ensure we have more than one position(grid positions)
   if(PositionsTotal() <= class="num">1)
      class="kw">return;
      
   class="type">class="kw">double weightedBreakeven = CalculateWeightedBreakevenPrice();
   class="type">int posType = -class="num">1;
   class=class="str">"cmt">//--- Determine the trade type from one of the positions
   for(class="type">int i = class="num">0; i < PositionsTotal(); i++){
      class="type">ulong ticket = PositionGetTicket(i);
      if(PositionSelectByTicket(ticket)){
         posType = (class="type">int)PositionGetInteger(POSITION_TYPE);
         class="kw">break;
      }
   }
   if(posType == -class="num">1)
      class="kw">return;
      
   class=class="str">"cmt">//--- For BUY positions, profit when Bid >= breakeven + threshold
   if(posType == POSITION_TYPE_BUY){
      if(bid >= weightedBreakeven + breakevenPoints){
         Print("Closing BUY positions: Bid(", bid, ") >= Breakeven(", weightedBreakeven, ") + ", breakevenPoints);
         CloseAllPositions();
      }
   }
   class=class="str">"cmt">//--- For SELL positions, profit when Ask <= breakeven - threshold
   else if(posType == POSITION_TYPE_SELL){
      if(ask <= weightedBreakeven - breakevenPoints){
         Print("Closing SELL positions: Ask(", ask, ") <= Breakeven(", weightedBreakeven, ") - ", breakevenPoints);
         CloseAllPositions();
      }
   }
}

「把工具请下神坛」

前面两节把动态网格 EA 的订单铺设、仓位随波动缩放、盈利与保本切换都拆开了,这一节落到实盘前最后一句提醒:任何结构化网格系统都只是把人的风控逻辑写成代码,不替你消灭外汇与贵金属的高风险。 那套 Grid_EA_With_Dynamic_Lot-Sizing.mq5 在原文附档里是 13.97 KB,回测前先把动态手数上限和网格步长两个参数钉死,不然波动放大时浮亏扩张速度会超出多数账户承受概率。 把它当校验自己风控想法的仪器,而不是印钞模块;跑通了再谈调优,跑崩了先看日志里哪一层保本触发失效。

常见问题

把每笔成交手数乘以开仓价求和,再除以总手数,得到加权均价;加上点差和手续费成本才是真实保本价,建议用表格逐笔核对。
当行情回踩到加权均价加成本线时,EA 可设条件单自动平仓;手动交易就盯盘口价格触碰该线即平,避免贪心再扛。
小布可读取你的持仓明细自动算出加权保本价,并在价格触线时推送提醒,你把重复劳动交给小布,专注决策即可。
是,贵金属杠杆高、波动大,仓位动态调整若没上限,浮亏可能快速吞噬保证金,务必设单日最大加仓层数。
极端单边行情会击穿网格资金上限导致爆仓,它只是概率工具,外汇贵金属高风险,请下神坛当辅助而非印钞机。