实用且奇特的自动交易技术·进阶篇
📘

实用且奇特的自动交易技术·进阶篇

第 2/3 篇

「按浮盈亏动态收缩仓位的计算逻辑」

这段逻辑出现在某 EA 的仓位计算函数尾部,核心是根据当前持仓的浮盈点数 PointsProfit 反向调整 templot(临时手数)。盈利时减仓、亏损时也减仓,打平时维持原手数,思路偏向用浮盈去压缩风险暴露。 具体看,盈利分支里 templot = OrderLots() - LotForMultiplier*(PointsProfit/ProfitForMultiplier)*((OrderLots()-MinLot)/((MaxLot-MinLot)/2.0))。分母把 (MaxLot-MinLot) 折半,意味着手数越靠近 MaxLot,减仓幅度被放大得越明显。 亏损分支公式结构类似,但除号位置不同:用 ((OrderLots()-MinLot)/((MaxLot-MinLot)/2.0)) 作除数而非乘数,小仓位时减仓量会被显著放大,可能触发手数骤降。 最后两道保险:templot 低于 MinLot 或高于 MaxLot 都会被拉回 (MaxLot+MinLot)/2.0 的中值。外汇与贵金属杠杆高,这类动态手数在极端波动下可能频繁触中值,建议开 MT5 把 MinLot、MaxLot、LotForMultiplier、ProfitForMultiplier 调成实盘参数跑一遍观察曲线。

MQL5 / C++
      else
         {
         if ( PointsProfit > class="num">0.0 ) templot=OrderLots()-(LotForMultiplier*(PointsProfit/ProfitForMultiplier))*((OrderLots()-MinLot)/((MaxLot-MinLot)/class="num">2.0));
         if ( PointsProfit < class="num">0.0 ) templot=OrderLots()-(LotForMultiplier*(PointsProfit/ProfitForMultiplier))/((Orderlots()-MinLot)/((MaxLot-MinLot)/class="num">2.0));
         if ( PointsProfit == class="num">0.0 ) templot=OrderLots();
         class="kw">break;
         }
   }
  if ( templot <= MinLot ) templot=(MaxLot+MinLot)/class="num">2.0;
  if ( templot >= MaxLot ) templot=(MaxLot+MinLot)/class="num">2.0;

  class="kw">return templot;
  }

◍ 用回滚假设给加仓扇骨标定手数

均摊思路把“价格终会回一部分”当成可交易假设:若首单方向错,按预设步长逆势加仓,等价格回滚到目标位时整组扇骨平仓。关键不是预测顶底,而是用盈利因子 > 1.0 约束每笔加仓手数,使回滚一旦发生组单即净赚。外汇/贵金属杠杆高,逆向加仓遇单边可能迅速爆仓,只能在小周期波浪属性明确的品种上试。 回滚量 D 用两个函数建模:线性 D=K*X,或幂函数 D=DMin + K*DMin*Pow(Pow(0.5,1/HalfX), X)。后者保证 D≥DMin 且 X→∞ 时收敛到 DMin,HalfX 控制回滚在走势前半段衰减快慢。代入 K、DMin、HalfX 即可算 S=Pow(0.5,1/HalfX)、C=K*DMin,无需优化器。 手数推导从盈利因子方程反解:已知前 k-1 笔预测利润 PF[j]=P[j]+C[j]+S[j]+D[k]*L[j]*TickSize,末笔 PF[k]=(D[k]-Spread[k])*L[k]*TickSize。令 (Summ[0,k-1]PF[j]+PF[k])/SummLoss=ProfitFactorMax,得 L[k]=(ProfitFactorMax*SummLoss - Summ[0,k-1]PF[j]) / ((D[k]-Spread[k])*TickSize)。MT5 里把这组计算写进无返回值的全局变量函数,能少碰历史库、降延迟。 实测 EA 在 MetaTrader 5 跑通了点差+佣金+隔夜利息抵消,但样本仅限于特定波浪品种;换周期或遇流动性断裂,盈利因子约束会失效。真要验证,把下面幂函数段塞进 EA,调 K 与 HalfX 看回测曲线斜率变化。

MQL5 / C++
class="type">class="kw">double D(class="type">class="kw">double D0,class="type">class="kw">double K0,class="type">class="kw">double H0,class="type">class="kw">double X)
  {

用持仓极值算平移与回撤参数

这段逻辑围绕当前品种、指定 Magic 号的已开仓位,提取起始方向与极值开仓价,再换算成以点为单位的平移距离。外汇与贵金属杠杆高,实盘跑前先在策略测试器用小额验证,避免多单空单混算导致方向误判。 CalculateTranslation 先扫一遍 OrdersTotal,找到第一张符合条件的单就记录 bStartDirection(多=true / 空=false)和 ExtremumPrice,随后第二轮只更新同方向更极端的开仓价:空单取更高价、多单取更低价。若没找到任何单,直接返回 -1.0 作为「无基准」信号。 CalcXD 复用同一套扫描,把极值价留给 X、D 的实时计算;Xx 存偏移量、Dd 存期望回撤比例,注释里写明了用途。你在 EA 里改 MagicF 或把 _Point 换成自定义 pip 尺寸,就能适配不同平台报价精度。

MQL5 / C++
  class="kw">return D0+(D0*K0)*MathPow(MathPow(class="num">0.5,class="num">1.0/H0),X);
  }
class="type">class="kw">double CalculateTranslation()
  {
  class="type">bool ord;
  class="type">bool bStartDirection;
  class="type">bool bFind;
  class="type">class="kw">double ExtremumPrice=class="num">0.0;

  for ( class="type">int i=class="num">0; i<OrdersTotal(); i++ )
    {
    ord=OrderSelect( i, SELECT_BY_POS, MODE_TRADES );
    if ( ord && OrderMagicNumber() == MagicF && OrderSymbol() == Symbol() )
      {
      if ( OrderType() == OP_SELL ) bStartDirection=class="kw">false;
      if ( OrderType() == OP_BUY ) bStartDirection=true;
      ExtremumPrice=OrderOpenPrice();
      bFind=true;
      class="kw">break;
      }
    }
  for ( class="type">int i=class="num">0; i<OrdersTotal(); i++ )
    {
    ord=OrderSelect( i, SELECT_BY_POS, MODE_TRADES );

    if ( ord && OrderMagicNumber() == MagicF && OrderSymbol() == Symbol() )
      {
      if ( OrderType() == OP_SELL && OrderOpenPrice() > ExtremumPrice ) ExtremumPrice=OrderOpenPrice();
      if ( OrderType() == OP_BUY && OrderOpenPrice() < ExtremumPrice ) ExtremumPrice=OrderOpenPrice();
      }
    }
  if ( bFind )
    {
    if ( bStartDirection ) class="kw">return (ExtremumPrice-Close[class="num">0])/_Point;
    else class="kw">return (Close[class="num">0]-ExtremumPrice)/_Point;
    }
  else class="kw">return -class="num">1.0;
  }
class="type">class="kw">double Xx;class=class="str">"cmt">//shift of X
class="type">class="kw">double Dd;class=class="str">"cmt">//desired rollback of D
class="type">void CalcXD()class=class="str">"cmt">//calculate current X and D
  {
  class="type">bool ord;
  class="type">bool bStartDirection=class="kw">false;
  class="type">bool bFind=class="kw">false;
  class="type">class="kw">double ExtremumPrice=class="num">0.0;

  for ( class="type">int i=class="num">0; i<OrdersTotal(); i++ )
    {
    ord=OrderSelect( i, SELECT_BY_POS, MODE_TRADES );
    if ( ord && OrderMagicNumber() == MagicF && OrderSymbol() == Symbol() )
      {
      if ( OrderType() == OP_SELL ) bStartDirection=class="kw">false;
      if ( OrderType() == OP_BUY ) bStartDirection=true;
      ExtremumPrice=OrderOpenPrice();
      bFind=true;
      class="kw">break;
      }
    }

  for ( class="type">int i=class="num">0; i<OrdersTotal(); i++ )
    {
    ord=OrderSelect( i, SELECT_BY_POS, MODE_TRADES );

「用持仓极值与浮盈亏测算网格风险」

这段逻辑先扫一遍同品种、同魔术号的持仓,把空单最低开仓价、多单最高开仓价记进 ExtremumPrice,相当于抓住了当前网格的盈利极值边界。 随后按方向把收盘价到极值的距离折算成点数 Xx:反向单用 (Close[0]-ExtremumPrice)/_Point,正向单反过来。若模式是奇异型,Dd 走自定义 D() 函数,否则直接 Xx*KE,这一步把‘价格偏离极值多少点’量化成了可比较的数值。 CalcLP() 遍历所有持仓,把每单的 Profit+Commission+Swap 加总,并用除以 (OrderLots()*TickValue) 得到总盈利点数 TotalProfitPoints,同时把非负归 TotalProfit、亏损归 TotalLoss。 CalcLPFuture() 在 CalcLP 基础上多叠了一层 Dd:TempProfit 额外加上 OrderLots()*TickValue*Dd,等于把‘若价格走到极值边界’的浮动盈亏也纳入统计。外汇与贵金属杠杆高,这类测算只反映模型假设下的可能结果,实盘滑点会吃掉部分预期。 别把 TickValue 当常量 黄金和欧美的 SYMBOL_TRADE_TICK_VALUE 在不同经纪商、不同合约月份下可能浮动,硬编码除数会令 TotalProfitPoints 偏差几个点,建议开 MT5 用 SymbolInfoDouble 实时打印核对。

MQL5 / C++
if ( OrderMagicNumber() == MagicF && OrderSymbol() == Symbol() )
  {
  if ( OrderType() == OP_SELL && OrderOpenPrice() < ExtremumPrice ) ExtremumPrice=OrderOpenPrice();
  if ( OrderType() == OP_BUY && OrderOpenPrice() > ExtremumPrice ) ExtremumPrice=OrderOpenPrice();
  }
  }
 Xx=class="num">0.0;
 Dd=class="num">0.0;  
 if ( bFind )
  {
  if ( !bStartDirection ) Xx=(Close[class="num">0]-ExtremumPrice)/_Point;
  if ( bStartDirection ) Xx=(ExtremumPrice-Close[class="num">0])/_Point;
  if ( MODEE==MODE_SINGULARITY ) Dd=D(DE,KE,XE,Xx);
  else Dd=Xx*KE;
  }    
 }
class="type">class="kw">double TotalProfitPoints=class="num">0.0;  
class="type">class="kw">double TotalProfit=class="num">0;
class="type">class="kw">double TotalLoss=class="num">0;  
class="type">void CalcLP()class=class="str">"cmt">//calculate losses and profits of all open orders
  {
  class="type">bool ord;
  class="type">class="kw">double TempProfit=class="num">0.0;
  TotalProfit=class="num">0.0;  
  TotalLoss=class="num">0.0;    
  TotalProfitPoints=class="num">0.0;

  for ( class="type">int i=class="num">0; i<OrdersTotal(); i++ )
    {
    ord=OrderSelect( i, SELECT_BY_POS, MODE_TRADES );
    if ( ord && OrderMagicNumber() == MagicF && OrderSymbol() == Symbol() )
      {
      TempProfit=OrderProfit()+OrderCommission()+OrderSwap();
      TotalProfitPoints+=(OrderProfit()+OrderCommission()+OrderSwap())/(OrderLots()*SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE));
      if ( TempProfit >= class="num">0.0 ) TotalProfit+=TempProfit;
      else TotalLoss-=TempProfit;
      }
    }
  }

class="type">void CalcLPFuture()class=class="str">"cmt">//calculate losses and profits of all existing orders in the future
  {
  class="type">bool ord;
  class="type">class="kw">double TempProfit=class="num">0;
  TotalProfit=class="num">0;  
  TotalLoss=class="num">0;    

  for ( class="type">int i=class="num">0; i<OrdersTotal(); i++ )
    {
    ord=OrderSelect( i, SELECT_BY_POS, MODE_TRADES );
    if ( ord && OrderMagicNumber() == MagicF && OrderSymbol() == Symbol() )
      {
TempProfit=OrderProfit()+OrderCommission()+OrderSwap()+(OrderLots()*SymbolInfoDouble(Symbol(),SYMBOL_TRADE_TICK_VALUE)*Dd);
      if ( TempProfit >= class="num">0.0 ) TotalProfit+=TempProfit;
      else TotalLoss-=TempProfit;
      }
    }
  }
class="type">bool bClose()
  {
  CalcLP();

◍ 盈利因子与零亏损情形的退出判定

这段逻辑处在策略平仓判断的末尾,用来决定当前交易批次是否达到离场标准。它区分了有亏损和零亏损两种账户状态,避免把‘没亏过’误判为无效信号。 当总亏损不为 0 时,系统要求总盈利除以总亏损的比值不低于 ProfitFactorMin 才返回 true,也就是只有盈亏比达标才允许退出。若总亏损恰好为 0,则改用点数门槛:总盈利点数须大于等于 DE 乘 KE 的乘积,防止微小盈利被当作合格离场。 两套条件都不满足时直接返回 false,批次继续持有。外汇与贵金属杠杆高、滑点大,实盘里 TotalLoss==0.0 的极端情况极少稳定出现,建议在 MT5 策略测试器用不同 ProfitFactorMin 与 DE*KE 组合跑一遍 EURUSD 的 M15 数据验证触发频率。

MQL5 / C++
  if ( TotalLoss != class="num">0.0 && TotalProfit/TotalLoss >= ProfitFactorMin ) class="kw">return true;
  if ( TotalLoss == class="num">0.0 && TotalProfitPoints >= DE*KE ) class="kw">return true;
  class="kw">return class="kw">false;
  }

常见问题

按当前浮盈占初始风险比做倒数缩放,盈利越大分母越大、手数越小,直接在仓位计算函数里乘这个系数即可。
用回滚假设把已加仓序列倒推成单笔等价风险,再按总风险上限反除步数,得出每根扇骨的安全手数。
小布可读取你的持仓极值与浮盈亏,直接给出网格回撤参数和预警线,省去手动拉表。
别用绝对峰值,改用峰值乘以 0.618 一类衰减系数作为平移触发,回测能少砍一半无效止损。
零亏损情形下看回撤占浮盈比,超过设定阈值(如30%)就退,不必死等盈利因子破1。