如何利用 MQL5 创建简单的多币种智能交易系统(第 6 部分):两条 RSI 指标相互交叉(基础篇)
◍ 用双 RSI 交叉搭多币种 EA 的骨架
在 MT5 里做多币种交易,不一定非得上复杂的神经网络,两条不同周期的 RSI 互相交叉就能当触发逻辑。思路是:快线 RSI 上穿慢线 RSI 视为动能转强,下穿则转弱,再按币种分别挂单。 原文示例里把这套逻辑跑在多个符号上,单篇阅读量 2041、收藏 11,说明社区对轻量多币种方案有真实需求。 落地时先在 EA 的 OnTick 里遍历 SymbolTotal 拿到的品种,逐个算 iRSI 句柄;句柄建一次就缓存,别每 tick 重算,否则 CPU 占用会肉眼可见地飙。外汇与贵金属杠杆高,多币种同开仓会放大回撤,参数没验过前先用策略测试器跑历史。
「一图多标的的 RSI 交叉机器人怎么跑」
多货币智能系统(EA)能从单一图表驱动多个品种对的自动开平与订单管理,包括尾随止损和尾随止盈。本文实例中它同时覆盖 30 个货币对,交易者只需挂在一个图表上即可。 核心信号用两条 RSI:快速 RSI 与慢速 RSI 发生交叉作为入场触发。MQL5 在交易终端和策略测试器里都原生支持多品种上下文,因此这类 EA 的回测与实盘路径是一致的。 对外汇与贵金属而言,多标的同向暴露会放大回撤,30 对同策略运行在极端行情下可能集中止损,部署前务必在 MT5 策略测试器用历史数据验证参数敏感性。
多币种 EA 怎么缩成单品种跑
这套多币种智能系统默认覆盖 28 个外汇对加 XAUUSD、XAGUSD 两个贵金属对,共 30 个品种;若经纪商品种名带前缀或后缀,系统内部用函数自动清洗,只认 MT5 里的外汇和贵金属标准名,指数和特殊品种不在范围内。 想把它当单货币 EA 用,有两条路。一是保持多对模式(MP),在“Trader Wishes Pairs”里只填一个名字,比如 XAUUSD,EA 挂在任何图上都只做 XAUUSD;二是把交易对模式切到“SP”,EA 只看自己所在图表那个对,挂 EURUSD 就只跑 EURUSD。 RSI 是 Wilder 在 1978 年提出的振荡器,经典参数是 14 周期、超买 70 超卖 30;本文信号不用裸 RSI 阈值,而是快 RSI 穿慢 RSI。有复盘指出 RSI 在日内外汇上并不实用,但穿越法仍值得在 MT5 里自测验证。 止损逻辑里有个细节:选“使用订单止损=否”时,EA 会盯信号疲软来保盈利。多头平仓条件是快 RSI 在慢 RSI 上方、且本根 K 快 RSI 值小于前一根;空头反之。该开关不激活就硬扛到信号逆转。 尾随用 VIDYA 而非抛物线 SAR,实测 VIDYA 比 SAR 贴价更紧,又比 AMA/DEMA 离价稍远,作为跟踪基准更顺。外汇和贵金属杠杆高,实盘前先用策略测试器跑历史数据。 下面这段是自动算止损价的成员函数,买单选 atprice-38*pip,卖单加 38*pip,关掉自动就取手工 SLval。
class="type">class="kw">double MCEA::OrderSLSet(const class="type">class="kw">string xsymb,ENUM_ORDER_TYPE type,class="type">class="kw">double atprice) { class=class="str">"cmt">//--- slv=class="num">0.0; class="type">int x=PairsIdxArray(xsymb); Pips(xsymb); RefreshTick(xsymb); class=class="str">"cmt">//-- class="kw">switch(type) { case (ORDER_TYPE_BUY): { if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice-class="num">38*pip); else if(use_sl==Yes && autosl==No) slv=mc_symbol.NormalizePrice(atprice-SLval*pip); else slv=class="num">0.0; class=class="str">"cmt">//-- break; } case (ORDER_TYPE_SELL): { if(use_sl==Yes && autosl==Yes) slv=mc_symbol.NormalizePrice(atprice+class="num">38*pip); else
◍ 弱信号平仓与止盈价的代码落点
这段逻辑里,GetCloseInWeakSignal 用快慢 RSI 的差值判断持仓是否该在动能转弱时退出。它取最近 3 根 bar 的 RSIFast 与 RSISlow,当持空单且快线从低于慢线、上一根快线走低、当前快线反弹超过 rsidiff 时,返回 rise 信号,倾向平空;持多单则对称处理。 OrderTPSet 对 BUY 单在自动止盈开启时统一挂 atprice+50*pip,关闭自动则用外部 TPval*pip;SELL 单方向相反减点。注意 50 这个硬编码点数在黄金或交叉盘里可能偏窄,复制前先核对 pip 变量在你的品种上是否已是真实点值。 外汇与贵金属杠杆高,这类基于 RSI 背离的弱信号平仓只是概率优势,实盘前务必在 MT5 策略测试器用真实点差回测。
if(use_sl==Yes && autosl==No) slv=mc_symbol.NormalizePrice(atprice+SLval*pip); else slv=class="num">0.0; } } class=class="str">"cmt">//--- class="kw">return(slv); class=class="str">"cmt">//--- } class=class="str">"cmt">//-end OrderSLSet() class=class="str">"cmt">//---------// class="type">int MCEA::GetCloseInWeakSignal(const class="type">class="kw">string symbol,class="type">int exis) class=class="str">"cmt">// Signal Indicator Position Close in profit { class=class="str">"cmt">//--- class="type">int ret=class="num">0; class="type">int rise=class="num">1, down=-class="num">1; class="type">int bar=class="num">3; class=class="str">"cmt">//-- class="type">class="kw">double rdif=rsidiff; class="type">class="kw">double RSIFast[], RSISlow[]; class=class="str">"cmt">//-- ArrayResize(RSIFast,bar,bar); ArrayResize(RSISlow,bar,bar); ArraySetAsSeries(RSIFast,true); ArraySetAsSeries(RSISlow,true); class=class="str">"cmt">//-- class="type">int x=PairsIdxArray(symbol); UpdatePrice(symbol,TFt); class=class="str">"cmt">//-- CopyBuffer(hRSIFast[x],class="num">0,class="num">0,bar,RSIFast); CopyBuffer(hRSISlow[x],class="num">0,class="num">0,bar,RSISlow); class=class="str">"cmt">//-- if(exis==down && RSIFast[class="num">1]<=RSISlow[class="num">1] && RSIFast[class="num">1]<RSIFast[class="num">2] && RSIFast[class="num">0]>RSIFast[class="num">1]+rdif) ret=rise; if(exis==rise && RSIFast[class="num">1]>=RSISlow[class="num">1] && RSIFast[class="num">1]>RSIFast[class="num">2] && RSIFast[class="num">0]<RSIFast[class="num">1]-rdif) ret=down; class=class="str">"cmt">//-- class="kw">return(ret); class=class="str">"cmt">//--- } class=class="str">"cmt">//-end GetCloseInWeakSignal() class=class="str">"cmt">//---------// class="type">class="kw">double MCEA::OrderTPSet(const class="type">class="kw">string xsymb,ENUM_ORDER_TYPE type,class="type">class="kw">double atprice) { class=class="str">"cmt">//--- tpv=class="num">0.0; class="type">int x=PairsIdxArray(xsymb); Pips(xsymb); RefreshTick(xsymb); class=class="str">"cmt">//-- class="kw">switch(type) { case (ORDER_TYPE_BUY): { if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice+class="num">50*pip); else if(use_tp==Yes && autotp==No) tpv=mc_symbol.NormalizePrice(atprice+TPval*pip); else tpv=class="num">0.0; class=class="str">"cmt">//-- break; } case (ORDER_TYPE_SELL): { if(use_tp==Yes && autotp==Yes) tpv=mc_symbol.NormalizePrice(atprice-class="num">50*pip); else if(use_tp==Yes && autotp==No) tpv=mc_symbol.NormalizePrice(atprice-TPval*pip);
「追踪止损价的两种算法落地」
MCEA 类的 TSPrice() 给了两套追踪止损价计算路径,由入参 TS_type 切换。case 0 是纯市价偏移:多单取 Bid 减去 TSval*pip 再 NormalizePrice,空单取 Ask 加 TSval*pip,逻辑直白,适合手数小、点值固定的品种。 case 1 则引入 VIDyAv 指标缓冲:先 ArrayResize 到 br=2 并设为时间序列,CopyBuffer 拉最近两根,再用指标值对比市价偏移阈值。多单仅在 VIDyAv[0] 低于 Bid-TSval*pip 时才把止损压到指标值,空单反之,相当于用波动带兜住回撤。 ModifySLTP() 里有一行容易看漏:TRSP=(Close_by_Opps==No && TS_type==1) ? 0 : TS_type。意思是当关闭反向平仓且走指标追踪时,实际传给下层的是 0,也就是退化成市价偏移模式,避免指标未就绪就改单。外汇与贵金属杠杆高,这类隐性分支可能导致止损比预期宽松,上 MT5 跑一遍 Print 出 TRSP 才能确认。
else tpv=class="num">0.0; } } class=class="str">"cmt">//--- class="kw">return(tpv); class=class="str">"cmt">//--- } class=class="str">"cmt">//-end OrderTPSet() class=class="str">"cmt">//---------// class="type">class="kw">double MCEA::TSPrice(const class="type">class="kw">string xsymb,class="type">ENUM_POSITION_TYPE ptype,class="type">int TS_type) { class=class="str">"cmt">//--- class="type">int br=class="num">2; class="type">class="kw">double pval=class="num">0.0; class="type">int x=PairsIdxArray(xsymb); Pips(xsymb); class=class="str">"cmt">//-- class="kw">switch(TS_type) { case class="num">0: { RefreshTick(xsymb); if(ptype==POSITION_TYPE_BUY) pval=mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip); if(ptype==POSITION_TYPE_SELL) pval=mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip); break; } case class="num">1: { class="type">class="kw">double VIDyAv[]; ArrayResize(VIDyAv,br,br); ArraySetAsSeries(VIDyAv,true); CopyBuffer(hVIDyAv[x],class="num">0,class="num">0,br,VIDyAv); RefreshPrice(xsymb,TFt,br); class=class="str">"cmt">//-- if(ptype==POSITION_TYPE_BUY && (VIDyAv[class="num">0]<mc_symbol.NormalizePrice(mc_symbol.Bid()-TSval*pip))) pval=VIDyAv[class="num">0]; if(ptype==POSITION_TYPE_SELL && (VIDyAv[class="num">0]>mc_symbol.NormalizePrice(mc_symbol.Ask()+TSval*pip))) pval=VIDyAv[class="num">0]; break; } } class=class="str">"cmt">//-- class="kw">return(pval); class=class="str">"cmt">//--- } class=class="str">"cmt">//-end TSPrice() class=class="str">"cmt">//---------// class="type">bool MCEA::ModifySLTP(const class="type">class="kw">string symbx,class="type">int TS_type) { class=class="str">"cmt">//--- ResetLastError(); class="type">MqlTradeRequest req={}; class="type">MqlTradeResult res={}; MqlTradeCheckResult check={}; class=class="str">"cmt">//-- class="type">int TRSP=(Close_by_Opps==No && TS_type==class="num">1) ? class="num">0 : TS_type; class="type">bool modist=false; class="type">int x=PairsIdxArray(symbx); Pips(symbx); class=class="str">"cmt">//-- class="type">int total=PositionsTotal(); class=class="str">"cmt">//-- for(class="type">int i=total-class="num">1; i>=class="num">0; i--) { class="type">class="kw">string symbol=PositionGetSymbol(i); if(symbol==symbx && mc_position.Magic()==magicEA) {
多空持仓的追踪止损改写逻辑
上面这段是 EA 在逐笔持仓里做追踪止损改单的核心分支。先取持仓类型,如果是 BUY 就走多头分支,SELL 走空头分支,两个分支结构对称,只是价格运算方向相反。 以 BUY 为例:先 RefreshTick 拿最新报价,再从持仓对象里取出开仓价、止损、利润、库存费、佣金,合成 netp 净盈亏。modstart 以开仓价加 TSmin*pip 作为改单启动线,modminsl 以追踪线 vtrsb 再垫 TSmin-1 个 pip 作为最低允许止损。
| 改单触发条件写成 modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==0.0 | modbuysl>pos_stop)),即现价过改单线、新止损优于启动线、且比原止损更优或原无止损。再叠加 netp>minprofit 才真正调用 PositionModify 把止损和止盈挂上去,外汇和贵金属杠杆高,滑点可能让改单失败,需在 MT5 里打印 modist 返回值排查。 |
|---|
SELL 分支里 modstart 变成开仓价减 TSmin*pip,modminsl 用 vtrss 减 (TSmin+1)*pip,modseltp 是现价减 TPval*pip,逻辑一致。把 TSmin 从 1.0 调到 2.0,你会看到 modstart 与 modminsl 间距拉大,改单更迟钝,适合高波动时段降低被扫频率。
class="type">ENUM_POSITION_TYPE opstype = mc_position.PositionType(); if(opstype==POSITION_TYPE_BUY) { RefreshTick(symbol); class="type">class="kw">double price = mc_position.PriceCurrent(); class="type">class="kw">double vtrsb = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP)); class="type">class="kw">double pos_open = mc_position.PriceOpen(); class="type">class="kw">double pos_stop = mc_position.StopLoss(); class="type">class="kw">double pos_profit = mc_position.Profit(); class="type">class="kw">double pos_swap = mc_position.Swap(); class="type">class="kw">double pos_comm = mc_position.Commission(); class="type">class="kw">double netp=pos_profit+pos_swap+pos_comm; class="type">class="kw">double modstart=mc_symbol.NormalizePrice(pos_open+TSmin*pip); class="type">class="kw">double modminsl=mc_symbol.NormalizePrice(vtrsb+((TSmin-class="num">1.0)*pip)); class="type">class="kw">double modbuysl=vtrsb; class="type">class="kw">double modbuytp=mc_symbol.NormalizePrice(price+TPval*pip); class="type">bool modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==class="num">0.0||modbuysl>pos_stop)); class=class="str">"cmt">//-- if(modbuy && netp>minprofit) { modist=mc_trade.PositionModify(symbol,modbuysl,modbuytp); } } if(opstype==POSITION_TYPE_SELL) { RefreshTick(symbol); class="type">class="kw">double price = mc_position.PriceCurrent(); class="type">class="kw">double vtrss = mc_symbol.NormalizePrice(TSPrice(symbx,opstype,TRSP)); class="type">class="kw">double pos_open = mc_position.PriceOpen(); class="type">class="kw">double pos_stop = mc_position.StopLoss(); class="type">class="kw">double pos_profit = mc_position.Profit(); class="type">class="kw">double pos_swap = mc_position.Swap(); class="type">class="kw">double pos_comm = mc_position.Commission(); class="type">class="kw">double netp=pos_profit+pos_swap+pos_comm; class="type">class="kw">double modstart=mc_symbol.NormalizePrice(pos_open-TSmin*pip); class="type">class="kw">double modminsl=mc_symbol.NormalizePrice(vtrss-((TSmin+class="num">1.0)*pip)); class="type">class="kw">double modselsl=vtrss; class="type">class="kw">double modseltp=mc_symbol.NormalizePrice(price-TPval*pip);