如何利用 MQL5 创建简单的多币种智能交易系统(第 1 部分):基于 ADX 指标的信号,并结合抛物线 SAR·综合运用
「多品种遍历里的交易开关与信号落地」
这段逻辑跑在 EA 的主循环里,针对监控列表里的每一个品种 x 做分支处理。若外部没指定品种,就取当前图表 Symbol(),否则从 mc.DIRI[x] 数组里拿第 x 个品种名,再调 mc.CurrentSymbolSet(symbol) 把该品种激活到 MarketWatch 并通知交易服务器。 品种就绪后,mc.TradingToday() 决定今天是否允许开仓。返回否时 EA 只做尾随止损、尾随止盈和平仓这类订单管理;返回是才继续往下走 ThisTime(mc.sec) 的时间过滤。 允许交易的前提下,mc.GetOpenPosition(symbol) 读出该品种的开仓信号存进 OpOr[x]。若结果是 mc.Buy(数值 1),先跑 mc.CheckOpenPMx(symbol) 做开仓前检查;若输入项 Close_by_Opps 为 Yes 且已存在卖单(mc.xos[x]>0),就调 mc.CloseSellPositions(symbol) 平掉卖单。 最后这道闸门很实在:只有当该品种没有多单(mc.xob[x]==0)且已开总单数 mc.xtto 小于允许上限 mc.ALO 时,才真正 mc.OpenBuy(symbol)。外汇与贵金属杠杆高,这类总单数上限若设太大,回撤可能超出预期,建议先在策略测试器里把 mc.ALO 调小验证。
symbol=Symbol(); else symbol=mc.DIRI[x]; class=class="str">"cmt">//-- After the symbol or pair name is set, we declare or notify the symbol to MarketWatch class=class="str">"cmt">//-- and the trade server by calling the CurrentSymbolSet(symbol) function. mc.CurrentSymbolSet(symbol); class=class="str">"cmt">//-- if(mc.TradingToday()) class=class="str">"cmt">//-- The TradingToday() function checks whether today is allowed for trading { class=class="str">"cmt">//-- If today is not allowed for trading, then the Expert will only perform management class=class="str">"cmt">//-- orders such as trailing stops or trailing profits and closing orders. class=class="str">"cmt">//-- according to the expert class="kw">input class="kw">property Day Trading On/Off group class=class="str">"cmt">//-- If TradingToday() == Yes, then the next process is to call the function ThisTime(mc.sec) class=class="str">"cmt">//-- mc.OpOr[x]=mc.GetOpenPosition(symbol); class=class="str">"cmt">//-- Get trading signals to open positions class=class="str">"cmt">//-- //-- and store in the variable OpOr[x] if(mc.OpOr[x]==mc.Buy) class=class="str">"cmt">//-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Buy" (value=class="num">1) { class=class="str">"cmt">//-- mc.CheckOpenPMx(symbol); class=class="str">"cmt">//-- class=class="str">"cmt">//-- If it turns out that the "Sell Order" has been opened, class=class="str">"cmt">//-- and Close Trade By Opposite Signal according to the class="kw">input class="kw">property is(Yes), class=class="str">"cmt">//-- then call the function CloseSellPositions(symbol) to close the sell order on that symbol. class=class="str">"cmt">//-- if(Close_by_Opps==Yes && mc.xos[x]>class="num">0) mc.CloseSellPositions(symbol); class=class="str">"cmt">//-- if(mc.xob[x]==class="num">0 && mc.xtto<mc.ALO) mc.OpenBuy(symbol); class=class="str">"cmt">//-- Open BUY order for this symbol else class=class="str">"cmt">//-- OR class=class="str">"cmt">//-- If Close Trade and Save profit due to weak signal according to the class="kw">input class="kw">property is(Yes)
◍ 订单触顶后的反手与平仓逻辑
当账户持仓与挂单总数 mc.xtto 达到券商上限 mc.ALO 时,EA 会先弹警报告知「已达最大持仓限制 = N Orders」,随后调用 CheckOpenPMx(symbol) 复查当前品种的开仓权限与风控状态。这一步很关键:触顶不等于直接砍仓,而是先确认环境再决定动作。 若此时空单已开(mc.xos[x]>0)且该单浮亏超过 1.02 美元(mc.profits[x]<-1.02),同时多单尚未建仓(mc.xob[x]==0),则执行 CloseSellPositions(symbol) 平掉空单并立刻 OpenBuy(symbol) 反手做多。该阈值写死在代码里,实盘前建议按点值重新核算,外汇与贵金属杠杆高,1.02 美元在不同品种上对应的点数差异极大。 若不满足反手条件,则走另一条分支:当输入参数允许「弱信号平仓锁利」时,系统会调用 CloseAllProfit() 把全部盈利单了结。注意这是 else 分支,意味着触顶场景下优先尝试反手,只有反手条件不成立才考虑批量止盈。
if(mc.xtto>=mc.ALO) { mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+ "\n the limit = "+class="type">class="kw">string(mc.ALO)+" Orders "); mc.CheckOpenPMx(symbol); if(mc.xos[x]>class="num">0 && mc.profits[x]<-class="num">1.02 && mc.xob[x]==class="num">0) { mc.CloseSellPositions(symbol); mc.OpenBuy(symbol); } else class=class="str">"cmt">//-- If Close Trade and Save profit due to weak signal according to the class="kw">input class="kw">property is(Yes) class=class="str">"cmt">//-- then call the CloseAllProfit() function to close all orders class=class="str">"cmt">//-- who are already in profit. }
卖单触发时的持仓与限额判定
当信号层判定 OpOr[x] 等于 Sell(即取值 -1)时,EA 先调用 CheckOpenPMx(symbol) 摸清该品种当前的多空持仓底细,再决定后续动作。 若输入项 Close_by_Opps 设为 Yes 且 mc.xob[x] 大于 0,说明已有买单在场,则直接 CloseBuyPositions(symbol) 平掉多头,这是反向信号清仓的逻辑分支。 开仓限制写在后面:仅当 mc.xos[x]==0(该符号无持仓)且 mc.xtto < mc.ALO(总订单数低于券商允许上限)时才 OpenSell(symbol)。一旦 mc.xtto >= mc.ALO,就走 Do_Alerts 弹出“已达最大持仓与挂单数”的警告,不再加仓。 外汇与贵金属杠杆高,券商的 ALO 限额常在 100~200 之间,实盘前应在 MT5 策略测试器里把这组条件跑一遍,确认不会在限额临界值反复触发报警。
if(SaveOnRev==Yes) mc.CloseAllProfit(); } } if(mc.OpOr[x]==mc.Sell) class=class="str">"cmt">//-- If variable OpOr[x] get result of GetOpenPosition(symbol) as "Sell" (value=-class="num">1) { class=class="str">"cmt">//-- class=class="str">"cmt">//-- Call the CheckOpenPMx(symbol) function to check whether there are class=class="str">"cmt">//-- already open "Buy" or "Sell" orders or no open orders. class=class="str">"cmt">//-- mc.CheckOpenPMx(symbol); class=class="str">"cmt">//-- class=class="str">"cmt">//-- If it turns out that the "Buy Order" has been opened, class=class="str">"cmt">//-- and Close Trade By Opposite Signal according to the class="kw">input class="kw">property is(Yes), class=class="str">"cmt">//-- then call the function CloseBuyPositions(symbol) to close the buy order on that symbol. class=class="str">"cmt">//-- if(Close_by_Opps==Yes && mc.xob[x]>class="num">0) mc.CloseBuyPositions(symbol); class=class="str">"cmt">//-- class=class="str">"cmt">//-- The algorithm below means that the expert will only open class="num">1 order per symbol, class=class="str">"cmt">//-- provided that the total number of orders is still less than class=class="str">"cmt">//-- the account limit orders allowed by the broker. class=class="str">"cmt">//-- if(mc.xos[x]==class="num">0 && mc.xtto<mc.ALO) mc.OpenSell(symbol); class=class="str">"cmt">//-- Open SELL order for this symbol else if(mc.xtto>=mc.ALO) { class=class="str">"cmt">//-- If the total number of orders is greater than or equal class=class="str">"cmt">//-- to the account limit orders allowed by the broker, then turn on alerts class=class="str">"cmt">//-- mc.Do_Alerts(symbol,"Maximum amount of open positions and active pending orders has reached"+
「反向信号下的仓单翻转逻辑」
这段代码片段展示了一个基于信号弱化的反向开仓判定:当某品种已持有多单、浮亏超过 1.02 美元且空单尚未建立时,系统会平掉多单并反向开空。外汇与贵金属杠杆高,这类硬止损翻转若遇跳空可能滑点放大,实盘前务必在 MT5 策略测试器跑一遍。
代码中 mc.xob[x]>0 表示多单存在,mc.profitb[x]<-1.02 是浮亏阈值,mc.xos[x]==0 确保空单未开,三者同时满足才触发 CloseBuyPositions 与 OpenSell。另一分支 SaveOnRev==Yes 则调用 CloseAllProfit 锁利,属于弱信号下的保守退出。
下方 CheckOpenPMx(symbol) 再次执行后,算法会扫描 iSAR 是否反转来确认信号弱化;若确认,则平掉亏损单并立刻反向开仓。把 1.02 改成你账户的可承受点数,能直接改变翻转频率。
the limit = "+class="type">class="kw">string(mc.ALO)+" Orders "); class=class="str">"cmt">//-- mc.CheckOpenPMx(symbol); class=class="str">"cmt">//-- Call the CheckOpenPMx(symbol) function class=class="str">"cmt">//-- class=class="str">"cmt">//-- If it turns out that the "Buy Order" has been opened, class=class="str">"cmt">//-- and the condition of the Buy order has lost more than class="num">1.02 USD, class=class="str">"cmt">//-- and the "Sell Order" has not been opened, then call CloseBuyPositions(symbol) class=class="str">"cmt">//-- to close "Buy order" and open "Sell order". if(mc.xob[x]>class="num">0 && mc.profitb[x]<-class="num">1.02 && mc.xos[x]==class="num">0) { mc.CloseBuyPositions(symbol); mc.OpenSell(symbol); } else class=class="str">"cmt">//-- OR class=class="str">"cmt">//-- If Close Trade and Save profit due to weak signal according to the class="kw">input class="kw">property is(Yes) class=class="str">"cmt">//-- then call the CloseAllProfit() function to close all orders class=class="str">"cmt">//-- who are already in profit. class=class="str">"cmt">//-- if(SaveOnRev==Yes) mc.CloseAllProfit(); } } class=class="str">"cmt">//-- mc.CheckOpenPMx(symbol); class=class="str">"cmt">//-- The algorithm block below will check whether there is a weakening of the signal on Buy or Sell positions. class=class="str">"cmt">//-- If it is true that there is a weakening signal and the iSAR indicator has reversed direction, class=class="str">"cmt">//-- then close the losing order and immediately opened an order in the opposite direction. class=class="str">"cmt">//--
◍ 弱信号下的平仓与保本逻辑
这段逻辑处理的是「弱信号反转」场景:当持仓方向与弱信号提示相反时,系统倾向先平仓再反手,避免扛单。外汇与贵金属波动跳变频繁,这类信号误触发概率不低,实盘前务必在 MT5 策略测试器跑一遍。 若开启 SaveOnRev,代码会先查当前多空持仓浮利是否大于 0.02(注意这是按点值或账户单位的阈值,不是金额),达标才允许在弱信号反向时平仓保利润。不达标则只平掉反向仓,不强制锁利。 TrailingSLTP 分支给了两种跟损模式:autotrl 为 Yes 时调 ModifySLTP(symbol,1) 走自动追踪,为 No 时传 0 用手动参数。建议黄金品种把自动档跑两周,对比滑点侵蚀再定档。
if(mc.xob[x]>class="num">0 && mc.CheckToCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) {mc.CloseBuyPositions(symbol); mc.OpenSell(symbol);} if(mc.xos[x]>class="num">0 && mc.CheckToCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) {mc.CloseSellPositions(symbol); mc.OpenBuy(symbol);} } class=class="str">"cmt">//-- if(mc.xtto>class="num">0) { class=class="str">"cmt">//-- if(SaveOnRev==Yes) class=class="str">"cmt">//-- Close Trade and Save profit due to weak signal(Yes) { mc.CheckOpenPMx(symbol); if(mc.profitb[x]>class="num">0.02 && mc.xob[x]>class="num">0 && mc.GetCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) { mc.CloseBuyPositions(symbol); mc.Do_Alerts(symbol,"Close BUY order "+symbol+" to save profit due to weak signal."); } if(mc.profits[x]>class="num">0.02 && mc.xos[x]>class="num">0 && mc.GetCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) { mc.CloseSellPositions(symbol); mc.Do_Alerts(symbol,"Close SELL order "+symbol+" to save profit due to weak signal."); } class=class="str">"cmt">//-- if(mc.xob[x]>class="num">0 && mc.CheckToCloseInWeakSignal(symbol,mc.Buy)==mc.Sell) mc.CloseBuyPositions(symbol); if(mc.xos[x]>class="num">0 && mc.CheckToCloseInWeakSignal(symbol,mc.Sell)==mc.Buy) mc.CloseSellPositions(symbol); } class=class="str">"cmt">//-- if(TrailingSLTP==Yes) class=class="str">"cmt">//-- Use Trailing SL/TP(Yes) { if(autotrl==Yes) mc.ModifySLTP(symbol,class="num">1); class=class="str">"cmt">//-- If Use Automatic Trailing(Yes) if(autotrl==No) mc.ModifySLTP(symbol,class="num">0); class=class="str">"cmt">//-- Use Automatic Trailing(No) } }
多条件共振下的开仓信号判定
开仓信号不是单指标拍板,而是把 ADX 交叉、抛物转向(M15)、方向位移与 M15 抛物转向四组状态同时拉齐再给返回值。GetOpenPosition() 里用 rise=1、down=-1 作多空标记,只有当 sigADX、parsOp、dirmov、pars15 四个变量同取 rise 才回多,同取 down 才回空,否则 ret 保持 0 表示不出手。 PARSAR15() 专门取 M15 周期的抛物转向做趋势侧校验:把 PSAR 数组按时间序列倒排,取最新两根(br=2)缓冲,再用当前 K 线的 iHigh(0) 与 iLow(0) 比价。若 PSAR[0] 低于 LOW0 判为上升共振,高于 HIG0 判为下降共振。 实盘里你可以直接把 hPar15[xx] 的句柄周期从 PERIOD_M15 改成 PERIOD_H1 跑一下,看黄金在亚盘突破时信号触发频率是变稀还是变密,外汇品种可能呈现不同过滤效果。这类多指标耦合逻辑在贵金属与外汇上都属于高风险策略,共振失效时回撤可能偏大,验证时先用策略测试器跑历史段。
class="type">int MCEA::GetOpenPosition(const class="type">class="kw">string symbol) class=class="str">"cmt">// Signal Open Position { class=class="str">"cmt">//--- class="type">int ret=class="num">0; class="type">int rise=class="num">1, down=-class="num">1; class=class="str">"cmt">//-- class="type">int dirmov=DirectionMove(symbol); class="type">int pars15=PARSAR15(symbol); class="type">int parsOp=PARSAROp(symbol); class="type">int sigADX=iADXCross(symbol); class=class="str">"cmt">//-- if(sigADX==rise && parsOp==rise && dirmov==rise && pars15==rise) ret=rise; if(sigADX==down && parsOp==down && dirmov==down && pars15==down) ret=down; class=class="str">"cmt">//-- class="kw">return(ret); class=class="str">"cmt">//--- } class=class="str">"cmt">//-end GetOpenPosition() class="type">int MCEA::PARSAR15(const class="type">class="kw">string symbol) class=class="str">"cmt">// formula Parabolic SAR M15 { 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 br=class="num">2; class=class="str">"cmt">//-- class="type">class="kw">double PSAR[]; ArrayResize(PSAR,br,br); ArraySetAsSeries(PSAR,true); class="type">int xx=PairsIdxArray(symbol); CopyBuffer(hPar15[xx],class="num">0,class="num">0,br,PSAR); class=class="str">"cmt">//-- RefreshPrice(symbol,TFT15,br); class="type">class="kw">double HIG0=iHigh(symbol,TFT15,class="num">0); class="type">class="kw">double LOW0=iLow(symbol,TFT15,class="num">0); class=class="str">"cmt">//-- if(PSAR[class="num">0]<LOW0) ret=rise; if(PSAR[class="num">0]>HIG0) ret=down; class=class="str">"cmt">//-- class="kw">return(ret); class=class="str">"cmt">//--- } class=class="str">"cmt">//-end PARSAR15()
「图表按钮的事件分发逻辑」
在 MT5 自定义面板里,批量平仓、统一挂止损这些动作往往靠图表上的按钮触发,底层都收口在 OnChartEvent 的 CHARTEVENT_OBJECT_CLICK 分支。下面这段实现演示了如何根据 sparam 拿到被点击对象的名字,再路由到对应的管理类方法。 代码先调用 ResetLastError 清掉上一次错误码,把当前图表周期读进 CCS 备用;随后判断事件 id 是否等于 CHARTEVENT_OBJECT_CLICK。若 sparam 等于 "Set SL/TP All Orders",就跑 mc.SetSLTPOrders() 给所有订单补止损止盈,弹 Alert 后把按钮 OBJPROP_STATE 置 false 取消按下态,并将 OBJPROP_ZORDER 归 0 让出事件优先级,最后重绘手动面板。 "Close All Order" 与 "Close All Profit" 两个分支结构完全一致,只是分别调用 mc.CloseAllOrders() 与 mc.ManualCloseAllProfit(),前者清掉全部持仓,后者只剥掉浮盈单。外汇与贵金属杠杆高,这类一键全平在滑点扩大时可能把预期外的亏损单也一起撮掉,实盘前建议在策略测试器里用历史 tick 跑一遍按钮响应延迟。 顺手记一个细节:StringLen(Symbol()) 和 StringLen(sparam) 在这段里算了却没用上,属于预留的防误触判断位,你抄代码时可以删掉,也可以拿它做品种前缀校验。
class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam) { class=class="str">"cmt">//-- class=class="str">"cmt">//--- handling CHARTEVENT_CLICK event("Clicking the chart") ResetLastError(); class=class="str">"cmt">//-- ENUM_TIMEFRAMES CCS=mc.TFt; class=class="str">"cmt">//-- if(id==CHARTEVENT_OBJECT_CLICK) { class="type">int lensymbol=StringLen(Symbol()); class="type">int lensparam=StringLen(sparam); class=class="str">"cmt">//-- class=class="str">"cmt">//--- if "Set SL/TP All Orders" button is click if(sparam=="Set SL/TP All Orders") { mc.SetSLTPOrders(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Set SL/TP All Orders"); class=class="str">"cmt">//--- unpress the button ObjectSetInteger(class="num">0,"Set SL/TP All Orders",OBJPROP_STATE,false); class=class="str">"cmt">//-- Button state(depressed button) ObjectSetInteger(class="num">0,"Set SL/TP All Orders",OBJPROP_ZORDER,class="num">0); class=class="str">"cmt">//-- Priority of a graphical object for receiving events CreateManualPanel(); } class=class="str">"cmt">//--- if "Close All Order" button is click if(sparam=="Close All Order") { mc.CloseAllOrders(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Orders"); class=class="str">"cmt">//--- unpress the button ObjectSetInteger(class="num">0,"Close All Order",OBJPROP_STATE,false); class=class="str">"cmt">//-- Button state(depressed button) ObjectSetInteger(class="num">0,"Close All Order",OBJPROP_ZORDER,class="num">0); class=class="str">"cmt">//-- Priority of a graphical object for receiving events CreateManualPanel(); } class=class="str">"cmt">//--- if "Close All Profit" button is click if(sparam=="Close All Profit") { mc.ManualCloseAllProfit(); Alert("-- "+mc.expname+" -- ",Symbol()," -- Close All Profit"); class=class="str">"cmt">//--- unpress the button ObjectSetInteger(class="num">0,"Close All Profit",OBJPROP_STATE,false); class=class="str">"cmt">//-- Button state(depressed button)
◍ 面板按钮的事件分支怎么写
在 MT5 的 EA 里用图表按钮做交互,核心是在 OnChartEvent 里根据 sparam 判断哪个键被点。下面这段是实际处理 X / M / C / R 四个按钮被点击后的典型分支。 点击 X 时要清掉全部按钮、标签和矩形标签对象,并调用 DeleteButtonX() 收掉关闭键自身,再把 mc.PanelExtra 置 false 后重绘手动按钮。注意 ObjectsDeleteAll 三次分别传 OBJ_BUTTON、OBJ_LABEL、OBJ_RECTANGLE_LABEL,漏掉哪一种都可能留残影。 M 和 C 的逻辑类似:先把对应按钮的 OBJPROP_STATE 设回 false(弹起),ZORDER 归 0,再把 mc.PanelExtra 置 true,分别调 CreateManualPanel 或 CreateSymbolPanel 展开不同面板。R 则是弹 Alert 后直接 ExpertRemove 把 EA 从图卸掉。 别把 OBJPROP_ZORDER 随手设成 1,在高频点击场景里若多个对象抢事件,ZORDER 为 0 才能稳定收到 chart event。外汇和贵金属图表上跑这类面板属高风险操作,按钮误触可能瞬间清仓或卸 EA。
ObjectSetInteger(class="num">0,"Close All Profit",OBJPROP_ZORDER,class="num">0); class=class="str">"cmt">//-- Priority of a graphical object for receiving events CreateManualPanel(); } class=class="str">"cmt">//--- if "X" button is click if(sparam=="X") { ObjectsDeleteAll(class="num">0,class="num">0,OBJ_BUTTON); ObjectsDeleteAll(class="num">0,class="num">0,OBJ_LABEL); ObjectsDeleteAll(class="num">0,class="num">0,OBJ_RECTANGLE_LABEL); class=class="str">"cmt">//--- unpress the button ObjectSetInteger(class="num">0,"X",OBJPROP_STATE,false); class=class="str">"cmt">//-- Button state(depressed button) ObjectSetInteger(class="num">0,"X",OBJPROP_ZORDER,class="num">0); class=class="str">"cmt">//-- Priority of a graphical object for receiving events class=class="str">"cmt">//-- DeleteButtonX(); mc.PanelExtra=false; DisplayManualButton(); } class=class="str">"cmt">//--- if "M" button is click if(sparam=="M") { class=class="str">"cmt">//--- unpress the button ObjectSetInteger(class="num">0,"M",OBJPROP_STATE,false); class=class="str">"cmt">//-- Button state(depressed button) ObjectSetInteger(class="num">0,"M",OBJPROP_ZORDER,class="num">0); class=class="str">"cmt">//-- Priority of a graphical object for receiving events mc.PanelExtra=true; CreateManualPanel(); } class=class="str">"cmt">//--- if "C" button is click if(sparam=="C") { class=class="str">"cmt">//--- unpress the button ObjectSetInteger(class="num">0,"C",OBJPROP_STATE,false); class=class="str">"cmt">//-- Button state(depressed button) ObjectSetInteger(class="num">0,"C",OBJPROP_ZORDER,class="num">0); class=class="str">"cmt">//-- Priority of a graphical object for receiving events mc.PanelExtra=true; CreateSymbolPanel(); } class=class="str">"cmt">//--- if "R" button is click if(sparam=="R") { Alert("-- "+mc.expname+" -- ",Symbol()," -- expert advisor will be Remove from the chart."); ExpertRemove(); class=class="str">"cmt">//--- unpress the button ObjectSetInteger(class="num">0,"R",OBJPROP_STATE,false); class=class="str">"cmt">//-- Button state(depressed button)
图表对象点击后的事件收尾动作
在 MT5 的 OnChartEvent 里,处理完按钮点击后有几行容易被忽略但必须执行的收尾代码,直接决定面板交互是否卡顿。 先通过 ObjectSetInteger(0,"R",OBJPROP_ZORDER,0) 把名为 R 的图形对象事件接收优先级设为 0,避免它挡住后续鼠标事件穿透到按钮。 随后用 ChartSetSymbolPeriod(0,Symbol(),Period()) 尝试刷新当前图表品种与周期;若首次调用返回 false,就再调一次做兜底,防止极少数终端状态不同步导致图表不刷新。接着 DeletePanelButton() 清理旧面板按钮,ChartRedraw(0) 强制重绘,事件处理函数 return 退出。 若命中的是品种名按钮(lensparam==lensymbol),则先用 mc.PairsIdxArray(sparam) 取序号 sx,再 ChangeChartSymbol(mc.AS30[sx],CCS) 切换图表到对应货币对。外汇与贵金属品种切换属高风险操作,切换后报价跳动可能瞬间扩大点差。
ObjectSetInteger(class="num">0,"R",OBJPROP_ZORDER,class="num">0); class=class="str">"cmt">//-- Priority of a graphical object for receiving events if(!ChartSetSymbolPeriod(class="num">0,Symbol(),Period())) ChartSetSymbolPeriod(class="num">0,Symbol(),Period()); DeletePanelButton(); ChartRedraw(class="num">0); } class=class="str">"cmt">//--- if Symbol name button is click if(lensparam==lensymbol) { class="type">int sx=mc.PairsIdxArray(sparam); ChangeChartSymbol(mc.AS30[sx],CCS); } class=class="str">"cmt">//-- } class=class="str">"cmt">//-- class="kw">return; class=class="str">"cmt">//--- } class=class="str">"cmt">//-end OnChartEvent() class=class="str">"cmt">//---------//
「在 MT5 测试器里跑多币种 EA」
MT5 终端的策略测试器原生支持多品种同测,可在一次任务里覆盖多个交易标的,也能选择对所有可用品种跑自动交易,这对跨品种策略的验证效率提升明显。 以 ADXPSAR_MCEA 这套多币种智能交易系统为例,直接挂到策略测试器里选好多品种模式即可启动回测;外汇与贵金属品种波动属性差异大,多币种测试属于高风险验证,结果只代表历史样本下的概率表现,不代表未来实盘倾向。
◍ 把工具请下神坛
ADXPSAR_MCEA 这个多币种 EA 本质上只是思路演示,作者在策略测试器里的成绩并不好看,说明直接照搬很难稳定获利。它的价值不在收益数字,而在于证明用 MQL5 写多币种逻辑和写单币种差别不大,交易者不必为每个品种都开一张图表。 代码里那行 difDi(0.34) 是作者凭观察估出来的阈值:当 PLUSDI_LINE 与 MINUSDI_LINE 差值小于 0.34 时不认为趋势变动有效。你可以把 0.34 改成别的数值,在 MT5 里跑不同品种组合看信号触发频率怎么变。 多币种系统的意义是让亏损品种可能被其他品种盈利对冲,但外汇和贵金属杠杆高、滑点大,这种补偿只是概率上的倾向,不是兜底。拿这套框架做自己的策略验证,比信它的默认参数更实在。
difDi(class="num">0.34), class="type">bool gadxrise=(ADXDIp[class="num">2]<=ADXDIm[class="num">2] && ADXDIp[class="num">1]>ADXDIm[class="num">1]+difDi && ADXDIp[class="num">0]>ADXDIp[class="num">1] && GiADXc==rise); class="type">bool gadxdown=(ADXDIp[class="num">2]>=ADXDIm[class="num">2] && ADXDIp[class="num">1]<ADXDIm[class="num">1]-difDi && ADXDIp[class="num">0]<ADXDIp[class="num">1] && GiADXc==down);