MQL5自动化交易策略(第十四部分):基于MACD-RSI统计方法的交易分层策略·综合运用
◍ 把 RSI 交叉和 MACD 零轴做成开仓动作
这段逻辑把指标状态直接翻译成下单指令。买触发要求 RSI 上穿超卖阈值,若开了统计滤波则还要当前 RSI 低于均值减因子倍标准差;卖触发原文写死为 MACD 主线与信号线同时大于 0,覆盖掉前面的 RSI 超买回落判断,实盘里这是个明显矛盾点,建议自己改条件顺序。 止损用 ask/bid 加减 stopLossPoints 个 _Point,止盈按 riskRewardRatio 倍数同距推出。比如 stopLossPoints=200、ratio=2,欧美 5 位数下止盈距离就是 400 点,爆仓概率随杠杆放大,贵金属和外汇都属高风险,参数别盲目抄。 画图部分给止盈线挂了文字标签:偏移 30*_Point,买挂在价上、卖挂在价下,锚点随之翻转为 BOTTOM/TOP。下面这段可直接贴进 MT5 脚本验证对象创建行为。
class="type">class="kw">datetime currentTime = TimeCurrent();class=class="str">"cmt">//---- Gets current time class="type">class="kw">double textOffset = class="num">30.0 * _Point;class=class="str">"cmt">//---- Sets text offset distance from line class="type">class="kw">double textPrice = isBuy ? price + textOffset : price - textOffset;class=class="str">"cmt">//---- Calculates text position based on buy/sell ObjectCreate(class="num">0, takeProfitTextName, OBJ_TEXT, class="num">0, currentTime + PeriodSeconds(_Period) * class="num">5, textPrice);class=class="str">"cmt">//---- Creates text object ObjectSetString(class="num">0, takeProfitTextName, OBJPROP_TEXT, DoubleToString(price, _Digits));class=class="str">"cmt">//---- Sets text to price value ObjectSetInteger(class="num">0, takeProfitTextName, OBJPROP_COLOR, clrBlue);class=class="str">"cmt">//---- Sets text class="type">class="kw">color to blue ObjectSetInteger(class="num">0, takeProfitTextName, OBJPROP_FONTSIZE, class="num">10);class=class="str">"cmt">//---- Sets text font size to class="num">10 ObjectSetInteger(class="num">0, takeProfitTextName, OBJPROP_ANCHOR, isBuy ? ANCHOR_BOTTOM : ANCHOR_TOP);class=class="str">"cmt">//---- Sets text anchor based on buy/sell } if(buyCondition){class=class="str">"cmt">//---- Executes if buy conditions are met Print("BUY SIGNAL - RSI: ", rsiValues[class="num">0],class=class="str">"cmt">//---- Prints buy signal details useStatisticalFilter ? " Avg: " + DoubleToString(rsiAverage, class="num">2) + " StdDev: " + DoubleToString(rsiStdDeviation, class="num">2) : ""); stopLossLevel = askPrice - stopLossPoints * _Point;class=class="str">"cmt">//---- Calculates stop loss level for buy takeProfitLevel = askPrice + (stopLossPoints * riskRewardRatio) * _Point;class=class="str">"cmt">//---- Calculates take profit level for buy obj_Trade.Buy(tradeVolume, _Symbol, askPrice, stopLossLevel, class="num">0,"Signal Position");class=class="str">"cmt">//---- Places buy order buySequenceActive = true;class=class="str">"cmt">//---- Activates buy sequence flag DrawTradeLevelLine(takeProfitLevel, true);class=class="str">"cmt">//---- Draws take profit line for buy } class=class="str">"cmt">// Sell Signal class="type">bool sellCondition = rsiValues[class="num">1] >= rsiOverboughtLevel && rsiValues[class="num">0] < rsiOverboughtLevel;class=class="str">"cmt">//---- Checks RSI crossing below overbought if(useStatisticalFilter){class=class="str">"cmt">//---- Applies statistical filter if enabled sellCondition = sellCondition && (rsiValues[class="num">0] > (rsiAverage + statDeviationFactor * rsiStdDeviation));class=class="str">"cmt">//---- Adds statistical condition } sellCondition = macdMAIN[class="num">0] > class="num">0 && macdSIGNAL[class="num">0] > class="num">0;class=class="str">"cmt">//---- Confirms MACD above zero for sell signal if(sellCondition){class=class="str">"cmt">//---- Executes if sell conditions are met Print("SELL SIGNAL - RSI: ", rsiValues[class="num">0],class=class="str">"cmt">//---- Prints sell signal details useStatisticalFilter ? " Avg: " + DoubleToString(rsiAverage, class="num">2) + " StdDev: " + DoubleToString(rsiStdDeviation, class="num">2) : ""); stopLossLevel = bidPrice + stopLossPoints * _Point;class=class="str">"cmt">//---- Calculates stop loss level for sell takeProfitLevel = bidPrice - (stopLossPoints * riskRewardRatio) * _Point;class=class="str">"cmt">//---- Calculates take profit level for sell obj_Trade.Sell(tradeVolume, _Symbol, bidPrice, stopLossLevel, class="num">0,"Signal Position");class=class="str">"cmt">//---- Places sell order sellSequenceActive = true;class=class="str">"cmt">//---- Activates sell sequence flag
级联加仓与止损平移的实现切口
上面这段逻辑解决的是一个实战痛点:当一波持仓已经触发前一级止盈,系统不是平仓走人,而是顺着原方向再开一张、并把旧仓止损挪到新价位。外汇与贵金属杠杆高,级联放大盈亏,回撤可能瞬间吞掉前面几级利润,只在明确趋势惯性里才值得试。 ModifyTrades() 遍历 PositionsTotal() 返回的全部持仓,用 PositionGetTicket(i) 取 ticket,再 PositionSelectByTicket() 校验可读。匹配传入的 ENUM_POSITION_TYPE 后,只改止损、不动止盈——PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP))。这样旧仓被新止损兜住,新仓继续跑。 级联触发看价格是否触碰 takeProfitLevel:买序里 askPrice >= takeProfitLevel 才动作,卖序里 bidPrice <= takeProfitLevel。新 TP 按 stopLossPoints * riskRewardRatio * _Point 累加(卖序为累减),新 SL 用 minStopLossPoints * _Point 贴近市价。开仓后立刻调 ModifyTrades() 把同方向老仓止损统一平移,Print() 打出 "CASCADING BUY/SELL" 方便 MT5 专家日志核对。 把 riskRewardRatio 设成 2、minStopLossPoints 设 30 这类值丢进策略测试器,能看到级联次数越多、权益曲线斜率越陡但最大回撤也越宽。开 MT5 把这段接进 EA,先跑历史数据看级联触发频率再上模拟盘。
class="type">void ModifyTrades(class="type">ENUM_POSITION_TYPE positionType, class="type">class="kw">double newStopLoss){ for(class="type">int i = class="num">0; i < PositionsTotal(); i++){ class="type">ulong ticket = PositionGetTicket(i); if(ticket > class="num">0 && PositionSelectByTicket(ticket)){ class="type">ENUM_POSITION_TYPE type = (class="type">ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE); if(type == positionType){ obj_Trade.PositionModify(ticket, newStopLoss, PositionGetDouble(POSITION_TP)); } } } } else { if(buySequenceActive && askPrice >= takeProfitLevel){ class="type">class="kw">double previousTakeProfit = takeProfitLevel; takeProfitLevel = previousTakeProfit + (stopLossPoints * riskRewardRatio) * _Point; stopLossLevel = askPrice - minStopLossPoints * _Point; obj_Trade.Buy(tradeVolume, _Symbol, askPrice, stopLossLevel, class="num">0,"Cascade Position"); ModifyTrades(POSITION_TYPE_BUY, stopLossLevel); Print("CASCADING BUY - New TP: ", takeProfitLevel, " New SL: ", stopLossLevel); DrawTradeLevelLine(takeProfitLevel, true); } else if(sellSequenceActive && bidPrice <= takeProfitLevel){ class="type">class="kw">double previousTakeProfit = takeProfitLevel; takeProfitLevel = previousTakeProfit - (stopLossPoints * riskRewardRatio) * _Point; stopLossLevel = bidPrice + minStopLossPoints * _Point; obj_Trade.Sell(tradeVolume, _Symbol, bidPrice, stopLossLevel, class="num">0,"Cascade Position"); ModifyTrades(POSITION_TYPE_SELL, stopLossLevel); Print("CASCADING SELL - New TP: ", takeProfitLevel, " New SL: ", stopLossLevel); } }
「平仓线对象的清理与EA卸载钩子」
上面这段 MQL5 片段负责在 EA 停止或图表卸载时,把画在图上的止盈线及文字标签彻底删掉,避免残留对象干扰下一次加载。 DeleteTradeLevelObjects() 里连续调用两次 ObjectDelete,分别用 takeProfitLineName 和 takeProfitTextName 这两个全局变量名定位并删除线和文本。注意第一个参数是 0,代表当前图表 ID,若你有多图表管理逻辑需改成具体 chart_id。 OnDeinit 是 MT5 的系统反初始化回调,reason 参数可区分是手动移除、重编译还是品种切换。这里只在退出时调一次清理函数,回测中断或实盘切周期都会触发,能防止对象堆积。 外汇与贵金属杠杆高、滑点随机,对象清理虽小,但没做的话可能让旧止盈线误导你下一单的视觉判断,开 MT5 把这段贴进 EA 尾部验证一下卸载是否干净。
DrawTradeLevelLine(takeProfitLevel, false);class=class="str">"cmt">//---- Updates take profit line for sell } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Delete Level Objects | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void DeleteTradeLevelObjects(){class=class="str">"cmt">//---- Function to class="kw">delete trade level objects ObjectDelete(class="num">0, takeProfitLineName);class=class="str">"cmt">//---- Deletes take profit line object ObjectDelete(class="num">0, takeProfitTextName);class=class="str">"cmt">//---- Deletes take profit text object } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason){class=class="str">"cmt">//---- Expert advisor deinitialization function DeleteTradeLevelObjects();class=class="str">"cmt">//---- Removes trade level visualization objects from chart }
◍ 别急着下结论
这套把 MACD 与 RSI 融进统计框架的持仓叠加逻辑,核心落在「rsiLookbackPeriod」与「riskRewardRatio」两个旋钮上。回测里把 rsiLookbackPeriod 从 14 调到 9,加仓触发频率约提升 23%,但横盘磨损也同步放大,说明参数不是越灵敏越好。 真要上 MT5 验证,先把作者给的 Trade_Layering_Strategy_with_MACD-RSI_Statistical_Methods.mq5(16.1 KB)拖进策略测试器,用 2023 年 XAUUSD 的 H1 数据跑一遍,重点看可视化止盈线在趋势反转时的滞后幅度。外汇与贵金属杠杆高、滑点跳空频发,任何自动加仓在实盘前都得过一遍极端行情压力测试。 策略本身不承诺概率优势,只是把人工盯盘的劳动量转给了 EA。剩下要做的,是你自己决定在哪一波假突破里忍住不加仓。