如何利用 MQL5 创建简单的多币种智能交易系统(第 1 部分):基于 ADX 指标的信号,并结合抛物线 SAR·综合运用
📘

如何利用 MQL5 创建简单的多币种智能交易系统(第 1 部分):基于 ADX 指标的信号,并结合抛物线 SAR·综合运用

第 3/3 篇

「多品种遍历里的交易开关与信号落地」

这段逻辑跑在 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 调小验证。

MQL5 / C++
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 分支,意味着触顶场景下优先尝试反手,只有反手条件不成立才考虑批量止盈。

MQL5 / C++
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 策略测试器里把这组条件跑一遍,确认不会在限额临界值反复触发报警。

MQL5 / C++
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 确保空单未开,三者同时满足才触发 CloseBuyPositionsOpenSell。另一分支 SaveOnRev==Yes 则调用 CloseAllProfit 锁利,属于弱信号下的保守退出。 下方 CheckOpenPMx(symbol) 再次执行后,算法会扫描 iSAR 是否反转来确认信号弱化;若确认,则平掉亏损单并立刻反向开仓。把 1.02 改成你账户的可承受点数,能直接改变翻转频率。

MQL5 / C++
 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 用手动参数。建议黄金品种把自动档跑两周,对比滑点侵蚀再定档。

MQL5 / C++
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 跑一下,看黄金在亚盘突破时信号触发频率是变稀还是变密,外汇品种可能呈现不同过滤效果。这类多指标耦合逻辑在贵金属与外汇上都属于高风险策略,共振失效时回撤可能偏大,验证时先用策略测试器跑历史段。

MQL5 / C++
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) 在这段里算了却没用上,属于预留的防误触判断位,你抄代码时可以删掉,也可以拿它做品种前缀校验。

MQL5 / C++
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。

MQL5 / C++
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) 切换图表到对应货币对。外汇与贵金属品种切换属高风险操作,切换后报价跳动可能瞬间扩大点差。

MQL5 / C++
      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 里跑不同品种组合看信号触发频率怎么变。 多币种系统的意义是让亏损品种可能被其他品种盈利对冲,但外汇和贵金属杠杆高、滑点大,这种补偿只是概率上的倾向,不是兜底。拿这套框架做自己的策略验证,比信它的默认参数更实在。

MQL5 / C++
  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);

常见问题

给每个品种单独设交易开关,只在ADX与抛物线SAR共振时放行,其余弱信号直接跳过不开单。
触顶先平掉原仓,若反向SAR与ADX弱趋势确认再开反手单,避免无趋势期频繁翻转。
小布可实时汇总各品种ADX+SAR共振情况与交易开关,弱信号自动标灰,你只管看预警。
在下单前读持仓计数,达到预设上限就拒单,同时检查同品种已有卖单避免重复堆叠。
弱ADX下仅平亏损单并推保本止损,不新开仓;用最小波动阈值过滤杂讯减少被扫。