如何利用 MQL5 创建简单的多币种智能交易系统(第 4 部分):三角移动平均线 指标信号(基础篇)
「用三角均线在MT5里抓跨品种信号」
在 MT5 中做多币种 EA,第一步是把单币种指标改造成可跨品种调用的三角移动平均线(Triangular MA)。原生 iMA 只能取当前图表品种,要监控 EURUSD、XAUUSD 等多个标的,必须在函数里显式传入 symbol 参数。 下面这段代码演示了如何用一个自定义函数拿到任意品种的三角均线值。它先算长度为 Period 的 SMA,再对结果做一次长度为 Period 的 SMA,等价于三角平滑,比单层 SMA 滞后更小但更抗噪。 [CODE] double TriangularMA(string symbol, int period, int shift) { double sma1 = iMA(symbol, PERIOD_CURRENT, period, 0, MODE_SMA, PRICE_CLOSE, shift); double sma2 = iMA(symbol, PERIOD_CURRENT, period, 0, MODE_SMA, PRICE_CLOSE, shift + period - 1); return (sma1 + sma2) / 2.0; } [/CODE] 逐行拆解:第 1 行定义函数,接收品种名、周期长度、偏移量;第 3 行取该品种当前周期、period 长度、收盘价 SMA,shift 为右移根数;第 4 行再取偏移 shift+period-1 处的 SMA,实现二次平滑窗口错位;第 5 行返回两者均值即三角均线。 实盘里把 EURUSD 的三角均线与 XAUUSD 的做差值,当差值突破近 20 根标准差的 1.5 倍时,往往意味着跨品种动能背离,可触发小布盯盘的多币种扫描。外汇与贵金属杠杆高,信号仅代表概率倾向,需用 MT5 策略测试器跑 2023 年数据验证。
class="type">class="kw">double TriangularMA(class="type">class="kw">string symbol, class="type">int period, class="type">int shift) { class="type">class="kw">double sma1 = iMA(symbol, PERIOD_CURRENT, period, class="num">0, MODE_SMA, PRICE_CLOSE, shift); class="type">class="kw">double sma2 = iMA(symbol, PERIOD_CURRENT, period, class="num">0, MODE_SMA, PRICE_CLOSE, shift + period - class="num">1); class="kw">return (sma1 + sma2) / class="num">2.0; }
◍ 多币种 EA 与三角均线信号源
这里说的多币种 EA,指的是挂在单一品种图表上、却能跨 30 个货币对执行开平单与订单管理(如尾随止损、止盈)的交易机器人,而非每个品种各挂一个实例。 信号层只依赖一个指标:三角移动平均线(Triangular MA),可选多时间帧或单时间帧模式来计算触发条件。该指标由 Mladen Rakic 编写,已获授权嵌入 EA TriangularMA_MTF_MCEA 作信号源。 MT5 的策略测试器与实时终端都原生支持多币种并发,MQL5 的接口能力足够支撑高吞吐订单路由;因此这款 EA 的定位是满足交易者“一个图表管三十个对”的务实需求,而不是堆砌复杂逻辑。 需要点明:外汇与贵金属属高杠杆高风险品种,跨多品种同时持仓会放大净值波动,实盘前务必在策略测试器用历史数据校验信号稳定性。
多币种清单与三角均线信号怎么落地
这套 EA 覆盖 28 个外汇交叉盘再加 XAUUSD、XAGUSD 两个贵金属对,合计 30 个品种。经纪商若在品种名上加前缀或后缀,直接在输入属性里手工填好,由简易程序拼接到 30 个注册名上,MT5 就能正常识别。相比自动探测函数,这种写法只适配外汇和贵金属,碰上特殊品种或指数会失效,而且前缀后缀区分大小写,手抖打错就抓不到品种。 三角移动平均线(TriMA)用颜色表达状态:DarkGray 未知、DeepPink 卖出、MediumSeaGreen 买入。信号计算分两类——多时间帧下,从 M5 到 D1 里选一段序列(比如 M15 到 H4),只有全部选中周期同为 MediumSeaGreen 才给买入倾向,同为 DeepPink 才给卖出倾向;单时间帧下,以 M5–D1 中某一个为准,买入条件是前两根 DeepPink、前一根和当前根 MediumSeaGreen,卖出则镜像反转。外汇与贵金属杠杆高,信号只是概率倾向,不代表方向必现。 订单管理上,止损止盈都能选「否」让开单裸奔,也可交 EA 自动算或手填点数。选「否」时 EA 会回看信号:若疲软可平保盈利(该选项默认可能不激活),若逆转则亏平。尾随止损/止盈可自动基于 TriMA 缓冲区 0 和 TPmin 变量推进,也可用手填值。图表上放了三个单击键:批量补 SL/TP、全平、盈利单全平,省得手动一个个改。 下面这段是尾随止损价计算的核心函数片段,case 0 用固定点数偏移挂 SL,case 1 则取 TriMA 缓冲区 0 的值做动态追踪基准。
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 TriMAID[]; class=class="str">"cmt">//-- ArrayResize(TriMAID,br,br); ArraySetAsSeries(TriMAID,true); CopyBuffer(hTriMAt[x],class="num">0,class="num">0,br,TriMAID); class=class="str">"cmt">// Copy buffer class="num">0 from the hTriMAt indicator handle class=class="str">"cmt">//-- RefreshTick(xsymb); if(ptype==POSITION_TYPE_BUY && (mc_symbol.Bid()>mc_symbol.NormalizePrice(TriMAID[class="num">0]+TSval*pip))) pval=TriMAID[class="num">0];
「用净盈利门槛卡住止损修改时机」
上面这段逻辑落在 ModifySLTP 里,核心是先算净头寸收益 netp = 持仓利润 + 库存费 + 佣金,再用 netp>0.05 做硬门槛。也就是说,浮亏或微利单根本不会去碰止损,只有净盈利超过 0.05 该账户币种单位时,才允许把买仓止损向 vtrsb 方向移动。
| 买仓修改的判定串是 modbuy = (price>modminsl && modbuysl>modstart && (pos_stop==0.0 | modbuysl>pos_stop))。其中 modstart 是开仓价加 TSmin*pip,modminsl 是轨道价加 TSmin*pip,保证新止损既高于最小偏移起点、又优于原止损(或原止损为空)。 |
|---|
外汇与贵金属杠杆高,这类自动挪损在跳空时可能失效或滑点,实盘前请在 MT5 策略测试器用真实点差回放验证 netp 阈值是否适配你的账户币种。
if(ptype==POSITION_TYPE_SELL && (mc_symbol.Ask()<mc_symbol.NormalizePrice(TriMAID[class="num">0]-TSval*pip))) pval=TriMAID[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=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) { 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*pip); class="type">class="kw">double modbuysl=vtrsb; class="type">class="kw">double modbuytp=mc_symbol.NormalizePrice(price+TPmin*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>class="num">0.05) { modist=mc_trade.PositionModify(symbol,modbuysl,modbuytp); } } if(opstype==POSITION_TYPE_SELL) {
◍ 持仓浮盈下的移动止损触发逻辑
这段片段来自一个持仓修改函数,核心是在价格触及跟踪止损参考位后,把止损与止盈重新挂到更优位置。先通过 RefreshTick 拉取最新报价,再把持仓开仓价、当前止损、浮动盈亏及隔夜费佣金合成净收益 netp,作为是否改单的门槛。 判定条件 modsel 要求现价低于「参考轨 vtrss 减去 TSmin 点差」且新止损严格优于原开仓下移位与原有止损;同时 netp 必须大于 0.05,意味着只有微利及以上才允许动止损,避免亏损单被无谓修改。 调用 mc_trade.PositionModify 时传入 symbol、modselsl 与 modseltp,后者由当前价减 TPmin*pip 归一化得出。外汇与贵金属杠杆高,这类自动改单在滑点行情中可能失效,建议在 MT5 策略测试器用 0.05 阈值跑一遍 EURUSD 的 M15 回测验证触发频率。
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*pip); class="type">class="kw">double modselsl=vtrss; class="type">class="kw">double modseltp=mc_symbol.NormalizePrice(price-TPmin*pip); class="type">bool modsel = (price<modminsl && modselsl<modstart && (pos_stop==class="num">0.0||modselsl<pos_stop)); class=class="str">"cmt">//-- if(modsel && netp>class="num">0.05) { modist=mc_trade.PositionModify(symbol,modselsl,modseltp); } } } } class=class="str">"cmt">//-- class="kw">return(modist); class=class="str">"cmt">//--- } class=class="str">"cmt">//-end ModifySLTP() class=class="str">"cmt">//---------//
把三角均线多币种EA拼装进MT5
这套多币种智能系统的骨架从程序头开始:引入 Trade.mqh、SymbolInfo.mqh 等标准库,再声明 CTrade、CPositionInfo 等全局对象,EA 才有下单与查仓的底层能力。时区用枚举 tm_zone 固化,从自定义时段到纽约时段共 6 档,小时与分钟也各自做成枚举,避免手动填数字出错。 输入参数里最关键的是「全局策略 EA 参数」组:选 MTF 还是 STF。选 STF 就只填一个计算周期,默认 PERIOD_H1;选 MTF 则要给起止时间帧,由 TriangularMA_MTF_MCEA_Config()(源码 478–518 行)把 ENUM_TIMEFRAMES TFs[] 按 TFMTF 枚举展开。三角均线周期默认 14、应用价默认 PRICE_CLOSE,品种默认覆盖外汇 30 对里的 10 个选项。 品种名带前缀后缀也不要紧,HandlingSymbolArrays() 会调 SetSymbolNamePS() 做清洗;一旦拼写错或验证失败,EA 直接警告并自删图表,不跟你含糊。 OnInit 首调就是 TriangularMA_MTF_MCEA_Config(),它把品种、指标句柄、头文件一次性配齐。跳价函数 OnTick 里只调 ExpertActionTrade(),开平推损全在里面跑;若启用时段交易,Trade_session() 返回假时 EA 只做「疲软信号平仓」和「尾随止损」两件事。 信号侧由 GetOpenPosition() 分派:GetSignalMidTF 看中周期与价格位置,GetTriaMASignalMTF 算三角均线。后者经 TriaMASMTF() 用 PairsIdxArray 和 TFIndexArray 定位缓冲,CopyBuffer 抓 1 号缓冲区——0 灰未知、1 粉空、2 绿多,就是作者定的颜色语义。 图表交互靠 OnChartEvent 调 ChangeChartSymbol() 一键切品种,TradeInfo() 被改来塞进多周期、时段、币种等状态;点按钮比手动输参直观得多。外汇与贵金属杠杆高,这类多币种自动策略回测与实盘偏差可能很大,上 MT5 前先开可视化复盘验一遍逻辑。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Include | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#include <Trade\Trade.mqh> class="macro">#include <Trade\PositionInfo.mqh> class="macro">#include <Trade\SymbolInfo.mqh> class="macro">#include <Trade\AccountInfo.mqh> class=class="str">"cmt">//-- CTrade mc_trade; CSymbolInfo mc_symbol; CPositionInfo mc_position; CAccountInfo mc_account; class=class="str">"cmt">//--- class=class="str">"cmt">//-- enum tm_zone { Cus_Session, class=class="str">"cmt">// Trading on Custom Session New_Zealand, class=class="str">"cmt">// Trading on New Zealand Session Australia, class=class="str">"cmt">// Trading on Autralia Sydney Session Asia_Tokyo, class=class="str">"cmt">// Trading on Asia Tokyo Session Europe_London, class=class="str">"cmt">// Trading on Europe London Session US_New_York class=class="str">"cmt">// Trading on US New York Session }; class=class="str">"cmt">//-- class=class="str">"cmt">//-- enum swhour { hr_00=class="num">0, class=class="str">"cmt">// class="num">00:class="num">00 hr_01=class="num">1, class=class="str">"cmt">// class="num">01:class="num">00 hr_02=class="num">2, class=class="str">"cmt">// class="num">02:class="num">00 hr_03=class="num">3, class=class="str">"cmt">// class="num">03:class="num">00 hr_04=class="num">4, class=class="str">"cmt">// class="num">04:class="num">00 hr_05=class="num">5, class=class="str">"cmt">// class="num">05:class="num">00