实用且奇特的自动交易技术·进阶篇
「按浮盈亏动态收缩仓位的计算逻辑」
这段逻辑出现在某 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 调成实盘参数跑一遍观察曲线。
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 看回测曲线斜率变化。
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 尺寸,就能适配不同平台报价精度。
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 实时打印核对。
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 数据验证触发频率。
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; }