创建动态多货币对EA(第二部分):投资组合多元化与优化·综合运用
按跳数分级的移动止损实现
这段代码演示了如何在 MT5 里对持仓做分级移动止损:价格每越过一段阈值,止损就向前推一截,而不是一次性拉到成本线。逻辑核心是倒序遍历所有持仓,只处理本 EA 的 magic 单,避免误改手动单或其他程序开的单。 阈值设定上,requiredMove 用了 70*pipSize(注释写 20 pips,说明示例品种 point 为 0.00001 级别,10 点=1 pip 的 5 位数报价),trailAmount 为 10*pipSize(即 10 点)。也就是说,浮盈每扩大 20 pips 触发一次,止损推进 10 pips,回撤容忍度始终留一半空间。 买入单的新 SL 计算为 openPrice + steps*trailAmount,并用 MathMax 保证只上移不下移;卖出单对称用 MathMin 只下移不上移。steps = int(priceMove / requiredMove),所以浮盈 40 pips 时 steps=2,止损推 20 pips,不会因价格小幅回撤就反复修改。 外汇与贵金属杠杆高、滑点大,这种分级止损只降低回撤概率,不保证不被扫;复制到 MT5 后先把 MagicNumber 和 requiredMove 按你自己品种点值核对一遍再跑。
for(class="type">int i = PositionsTotal()-class="num">1; i >= class="num">0; i--){ class="type">ulong ticket = PositionGetTicket(i); if(ticket <= class="num">0) class="kw">continue; if(!PositionSelectByTicket(ticket)) class="kw">continue; class=class="str">"cmt">// Get position details class="type">class="kw">string symbol = PositionGetString(POSITION_SYMBOL); class="type">long magic; if(!PositionGetInteger(POSITION_MAGIC, magic)) class="kw">continue; if(magic != MagicNumber) class="kw">continue; class=class="str">"cmt">// Get current prices class="type">MqlTick latestTick; if(!SymbolInfoTick(symbol, latestTick)) class="kw">continue; class="type">long type; class="type">class="kw">double openPrice, currentSl, currentTp; PositionGetInteger(POSITION_TYPE, type); PositionGetDouble(POSITION_PRICE_OPEN, openPrice); PositionGetDouble(POSITION_SL, currentSl); PositionGetDouble(POSITION_TP, currentTp); class=class="str">"cmt">// Calculate pip values class="type">class="kw">double pipSize = class="num">10 * SymbolInfoDouble(symbol, SYMBOL_POINT); class="type">class="kw">double currentPrice = type == POSITION_TYPE_BUY ? latestTick.bid : latestTick.ask; class="type">class="kw">double priceMove = MathAbs(currentPrice - openPrice); class=class="str">"cmt">// Calculate required moves class="type">class="kw">double requiredMove = class="num">70 * pipSize; class=class="str">"cmt">// class="num">20 pips class="type">class="kw">double trailAmount = class="num">10 * pipSize; class=class="str">"cmt">// class="num">10 pips class=class="str">"cmt">// Calculate new stop loss class="type">class="kw">double newSl = currentSl; class="type">bool inProfit = type == POSITION_TYPE_BUY ? (currentPrice > openPrice) : (currentPrice < openPrice); if(inProfit && priceMove >= requiredMove){ class="type">int steps = class="type">int(priceMove / requiredMove); if(type == POSITION_TYPE_BUY){ newSl = openPrice + (steps * trailAmount); newSl = MathMax(newSl, currentSl + trailAmount); } else{ newSl = openPrice - (steps * trailAmount); newSl = MathMin(newSl, currentSl - trailAmount); } } class=class="str">"cmt">// Validate and modify SL if(newSl != currentSl){ class=class="str">"cmt">// Check stop levels
◍ 止损移动前的点差硬门槛
在 MT5 里改止损不是想移就移,经纪商给每个品种设了 SYMBOL_TRADE_STOPS_LEVEL,单位是点。用 SymbolInfoInteger 取出这个值再乘 _Point,得到的 minDist 就是止损价和当前价之间必须守住的最小距离,低于它服务器直接拒单。 上面这段逻辑先对 newSl 做 NormalizeDouble 对齐报价精度,再分多空判断:买仓要求 currentPrice - newSl >= minDist,卖仓要求 newSl - currentPrice >= minDist,两个条件任一不满足就根本不会调 PositionModify。 实际盯盘时,黄金 XAUUSD 的 stops level 常见为 0 点,但部分平台对原油或交叉盘会设 20~50 点。开盘跳空或流动性薄的时候,这条距离校验能拦掉一大批自以为 Trailing 成功的假改单,Print 里看到的 Failed 多半是卡在这道门槛。外汇与贵金属杠杆高,校验失效可能导致止损未随价移动,风险敞口扩大。
class="type">class="kw">double minDist = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point; newSl = NormalizeDouble(newSl, _Digits); if(type == POSITION_TYPE_BUY && (currentPrice - newSl) >= minDist){ if(!trade.PositionModify(ticket, newSl, currentTp)) Print("Buy Trailing Failed: ", GetLastError()); } else if(type == POSITION_TYPE_SELL && (newSl - currentPrice) >= minDist){ if(!trade.PositionModify(ticket, newSl, currentTp)) Print("Sell Trailing Failed: ", GetLastError()); } } } }
「把这条线请下神坛」
把组合优化与分散化塞进 EA,本质是用历史波动和品种间关系去换更平滑的权益曲线,不是替你消掉外汇和贵金属的高风险。回测里那套 Dyna_MP 在 EURUSD 上跑 2022.2.1–2022.3.22、零延迟每 tick 建模、BreakoutMode=Reversed_signal 且开 TrailYourStop,只是理想缸里的影子,实盘滑点和周末跳空会撕开缺口。 想自己戳破这层滤镜,就把附带的 Dyna_MP.mq5 拖进 MT5 策略测试器,符号填 EURUSD、周期随意,照上述输入跑一遍,再关掉追踪止损对比回撤。概率上分散能扛单品种黑天鹅,但杠杆盘子依旧可能连错十次。 线可以信,别供着。