实现 Deus EA:使用 MQL5 中的 RSI 和移动平均线进行自动交易·综合运用
◍ EA 骨架与参数预设
这套均线+RSI 的 EA 把超买超卖阈值压得很低:RSI_Overbought 设 35.0、RSI_Oversold 设 15.0,跟常见的 70/30 完全两码事,倾向在弱势震荡里抓反转。MA_Period 取 25、用 SMA 收 PRICE_CLOSE,属于慢均线过滤噪音的思路。 初始化段只打一行日志就返回 INIT_SUCCEEDED,没做句柄缓存;反初始化把 reason 码直接 Print 出来,方便你查 EA 被卸掉的诱因。 OnTick 里先抓 Ask/Bid,再用 ArrayResize 把 Close 写死成 2 个元素,分别存当根和前一根收盘价。RSI 用 iRSI 取 0 号缓冲,若返回 WRONG_VALUE 就报错退出——外汇和贵金属杠杆高,这类指标调用失败若不停 tick,可能连发错单。 下面这段是可直接贴进 MT5 看的声明与三个回调函数骨架,参数和变量名都保留原样,方便你改完立刻编译。
input class="type">class="kw">double RSI_Overbought = class="num">35.0; class=class="str">"cmt">// RSI overbought level input class="type">class="kw">double RSI_Oversold = class="num">15.0; class=class="str">"cmt">// RSI oversold level class=class="str">"cmt">//--- Moving Average parameters input class="type">int MA_Period = class="num">25; class=class="str">"cmt">// Moving Average period input ENUM_MA_METHOD MA_Method = MODE_SMA; class=class="str">"cmt">// Moving Average method input ENUM_APPLIED_PRICE MA_Price = PRICE_CLOSE; class=class="str">"cmt">// Applied price for MA class=class="str">"cmt">//--- Variables class="type">class="kw">double rsiValue; class=class="str">"cmt">// RSI value class="type">class="kw">double maValue; class=class="str">"cmt">// Moving Average value class="type">class="kw">double Ask; class="type">class="kw">double Bid; class="type">class="kw">double Close[class="num">2]; class=class="str">"cmt">// Initializing Close array with two elements class=class="str">"cmt">//--- Function prototypes class="type">void ApplyTrailingStop(); class="type">void OpenPosition(CTrade &trade, class="type">int orderType); class=class="str">"cmt">// Pass CTrade by reference class="type">void ClosePositions(class="type">int orderType); class=class="str">"cmt">// pass orderType directly class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { Print("Deus EA initialized successfully."); class="kw">return(INIT_SUCCEEDED); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(const class="type">int reason) { Print("Deus EA deinitialized. Reason: ", reason); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- Update current prices Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); ArrayResize(Close, class="num">2); Close[class="num">0] = iClose(_Symbol, Period(), class="num">0); Close[class="num">1] = iClose(_Symbol, Period(), class="num">1); class=class="str">"cmt">//--- Calculate RSI value rsiValue = iRSI(_Symbol, _Period, RSI_Period, PRICE_CLOSE, class="num">0); if (rsiValue == WRONG_VALUE) { Print("Error calculating RSI"); class="kw">return; } class=class="str">"cmt">//--- Calculate Moving Average value
「RSI 碰均线后的开平与止损逻辑」
这段逻辑把 RSI 与移动平均线拧成一条交易触发链:先取当前均线值,若返回 WRONG_VALUE 直接打印错误并退出,避免拿脏数据下单。 买侧判定为 rsiValue < RSI_Oversold 且前一根收盘价 Close[1] 站上 maValue;此时若账户无持仓,先平掉可能的卖单再开买单。卖侧对称:rsiValue > RSI_Overbought 且 Close[1] 跌破 maValue,无持仓时平买开卖。 TrailingStop 大于 0 才调用尾随止损函数,参数设 0 就完全不跟。外汇与贵金属杠杆高,这种反转信号在震荡市可能连续假突破,实盘前用 MT5 策略测试器跑至少 3 个月 tick 数据。 OpenPosition 里止损止盈用 StopLoss、TakeProfit 乘 _Point 算距离:买单 sl=price - StopLoss*_Point,tp=price + TakeProfit*_Point;卖单反向。成交通过 trade.PositionOpen 并打日志,失败用 GetLastError 查码。 ClosePositions 倒序遍历持仓,只平掉与传入 orderType 同方向的仓,不会误伤反向单。
maValue = iMA(_Symbol, _Period, MA_Period, class="num">0, MA_Method, MA_Price, class="num">0); if (maValue == WRONG_VALUE) { Print("Error calculating Moving Average"); class="kw">return; } class=class="str">"cmt">//--- Check for Buy Signal if(rsiValue < RSI_Oversold && Close[class="num">1] > maValue) { if(PositionsTotal() == class="num">0) { ClosePositions(ORDER_TYPE_SELL); OpenPosition(ORDER_TYPE_BUY); } } class=class="str">"cmt">//--- Check for Sell Signal if(rsiValue > RSI_Overbought && Close[class="num">1] < maValue) { if(PositionsTotal() == class="num">0) { ClosePositions(ORDER_TYPE_BUY); OpenPosition(ORDER_TYPE_SELL); } } class=class="str">"cmt">//--- Apply trailing stop if specified if(TrailingStop > class="num">0) { ApplyTrailingStop(); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Function to open a position | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OpenPosition(class="type">int orderType) { class=class="str">"cmt">//--- Determine price stop loss, and take profit levels class="type">class="kw">double price = (orderType == ORDER_TYPE_BUY) ? Ask : Bid; class="type">class="kw">double sl = (orderType == ORDER_TYPE_BUY) ? price - StopLoss * _Point : price + StopLoss * _Point; class="type">class="kw">double tp = (orderType == ORDER_TYPE_BUY) ? price + TakeProfit * _Point : price - TakeProfit * _Point; class="type">bool result = trade.PositionOpen(_Symbol, orderType, Lots, price, sl, tp, "Deus EA"); if(result) { Print("Order opened successfully. Type: ", orderType, ", Price: ", price); } else { Print("Failed to open order. Error code: ", GetLastError()); } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Function to close positions | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void ClosePositions(class="type">int orderType) { for(class="type">int i = PositionsTotal() - class="num">1; i >= class="num">0; i--) { if(PositionSelectByIndex(i)) { class=class="str">"cmt">//--- Check if the positions type matches the order type to be closed if(PositionGetInteger(POSITION_TYPE) == orderType) {
用 PositionModify 把止损拖着走
这段逻辑干的事很直接:遍历当前品种的所有持仓,按市价 Bid/Ask 反推一条 trailing stop 距离,再调用 CTrade 的 PositionModify 把止损往有利方向挪。 先取持仓票据:ulong ticket = PositionGetInteger(POSITION_TICKET); 若 trade.PositionClose(ticket) 返回 false,就 Print 出 GetLastError() 的错误码,方便在 MT5 专家日志里定位是成交拒绝还是上下文锁问题。 ApplyTrailingStop() 里用 for(int i = PositionsTotal()-1; i >= 0; i--) 倒序遍历,PositionSelectByIndex(i) 且 PositionGetSymbol()==_Symbol 才处理。买仓以 Bid - TrailingStop*_Point 为新 SL,卖仓以 Ask + TrailingStop*_Point 为新 SL;仅当原 SL 落后于新 SL(买仓 POSITION_SL < sl,卖仓 POSITION_SL > sl)才修改,避免无谓发单。 外汇与贵金属杠杆高,trailing 间距若设得过小(比如 10 点内),在 EURUSD 正常毛刺里可能频繁触发修改请求并被经纪商限流;建议先在策略测试器用真实点差回放验证发单频率。
class="type">class="kw">ulong ticket = PositionGetInteger(POSITION_TICKET); if(!trade.PositionClose(ticket) { Print("Failed to close position. Error code: ", GetLastError()); } } } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Function to apply trailing stop | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void ApplyTrailingStop() { for (class="type">int i = PositionsTotal() - class="num">1; i >= class="num">0; i--) { if (PositionSelectByIndex(i) && PositionGetSymbol() == _Symbol) { class="type">class="kw">double price = (PositionGetInteger(POSITION_TYPE) == ORDER_TYPE_BUY) ? Bid : Ask; class="type">class="kw">double sl = (PositionGetInteger(POSITION_TYPE) == ORDER_TYPE_BUY) ? price - TrailingStop * _Point : price + TrailingStop * _Point; class=class="str">"cmt">//--- Trailing stop logic for buy positions if (PositionGetInteger(POSITION_TYPE) == ORDER_TYPE_BUY && PositionGetDouble(POSITION_SL) < sl) { if (!trade.PositionModify(PositionGetInteger(POSITION_TICKET), sl, PositionGetDouble(POSITION_TP))) { Print("Failed to modify position. Error code: ", GetLastError()); } } class=class="str">"cmt">//--- Trailing stop logic for sell positions else if (PositionGetInteger(POSITION_TYPE) == ORDER_TYPE_SELL && PositionGetDouble(POSITION_SL) > sl) { if (!trade.PositionModify(PositionGetInteger(POSITION_TICKET), sl, PositionGetDouble(POSITION_TP))) { Print("Failed to modify position. Error code: ", GetLastError()); } } } } }
◍ 把这条线请下神坛
Deus EA 以均线叠加 RSI 生成信号,逻辑上把决策自动化,能压住手工交易里的情绪漂移,但社区回测讨论已经戳破一层窗户纸:有用户指出用 15 作超卖、35 作超买,在单月样本里像刻意调参,换成 85 才对称;另有三人实测编译报错、缺括号、函数签名错。这类现象说明,回测漂亮不等于实盘能打。 外汇与贵金属杠杆高、滑点跳空频发,EA 在小样本优化的低回撤,很可能只是过拟合的影子。部署前拿 EURUSD、XAUUSD 各跑至少半年 tick 数据,比对 15/35 与 15/85 两版 RSI 边界的胜率和最大回撤,比信原文结论更实在。 定期拉参数、看成交日志,才是让自动化系统活过不同市况的唯一笨办法。线画得再神,也只是一段 await 里的判断分支。