运用 MQL5 和 MQL4 开发品种选择和导航实用程序·综合运用
🧭

运用 MQL5 和 MQL4 开发品种选择和导航实用程序·综合运用

(3/3)· 从定时器模板到 MT4 移植,把数百品种的人工翻找压缩成几次点击

进阶 第 3/3 篇
盯盘最耗神的往往不是下单,而是在几百个品种里翻出那个符合条件的标的。本篇把前两篇的铺垫收拢成一套可直接用的导航实用程序,覆盖筛选、信息展示与键盘管控。

点一下按钮就弹图表

手动去打开筛选出来的品种图表太折腾,直接在按钮点击事件里挂逻辑更顺手。MQL5 里所有图表交互都走 OnChartEvent,任何图表事件触发时它都会被调用,四个入参里 id 是事件类型、sparam 在对象点击时装着被点对象的名字。 按钮点击对应的 id 是 CHARTEVENT_OBJECT_CLICK。我们在 switch 里捕获这个事件,再用 StringFind 比对 sparam 是否带 EA 自己的前缀(exprefix+"btn"),命中才认可是自家按钮。接着把后缀数字抠出来当品种索引,用 ObjectGetString 读按钮文字拿到品种名,丢给 showcharts 去开图。 showcharts 不止开图:先 closecharts 清掉之前开的;addInfoWatch 为真就用 SymbolSelect 把品种塞进市场观察;Open 以 PERIOD_D1 拉日线图,并把图表 ID 存进 curChartID。viewCandle 开烛条模式,viewVolumes 开tick量能,最后 ChartSetInteger 把 CHART_SCALE 设成 2——这是作者习惯的非默认比例。外汇和贵金属波动大、杠杆高,自动开图只是省手活,不等于方向判断。 下面这段是事件入口和开图函数的骨架,逐行都标了用途,拷进 MT5 的 EA 里改前缀就能跑。

MQL5 / C++
class="type">void OnChartEvent(class="kw">const class="type">int id,        class=class="str">"cmt">// 事件 ID
                    class="kw">const class="type">long& lparam,  class=class="str">"cmt">// 长整数型事件参数
                    class="kw">const class="type">class="kw">double& dparam, class=class="str">"cmt">// 双精度型事件参数
                    class="kw">const class="type">class="kw">string& sparam) class=class="str">"cmt">// 字符串型事件参数
  {
   class="kw">switch(id){
     case CHARTEVENT_OBJECT_CLICK:
       class=class="str">"cmt">// 单击按钮时执行的代码
       class="kw">break;
   }
        class=class="str">"cmt">// 如果按钮名称包含由您的 EA 创建的所有图形对象之一,
        class=class="str">"cmt">// 则...
        if( StringFind(sparam, exprefix+"btn")>=class="num">0 ){
          class=class="str">"cmt">// 放置当前按钮索引
          class=class="str">"cmt">// (列表中当前品种的位置)至 panel1val 变量
          class="type">class="kw">string tmpme=sparam;
          StringReplace(tmpme, exprefix+"btn", "");
          panel1val=(class="type">int) tmpme;

          class=class="str">"cmt">// 打开当前品种的图表
          showcharts(ObjectGetString(class="num">0,sparam,OBJPROP_TEXT));
        }
class="type">void showcharts(class="type">class="kw">string name){
   class=class="str">"cmt">// 如果图表已经打开,则关闭它们
   closecharts();

   class=class="str">"cmt">// 如果在“市场观察”面板中不存在,则添加该品种
   class=class="str">"cmt">// 如果 "Add chart to Market Watch" 输入为 &class="macro">#x27;true&class="macro">#x27;
   if( addInfoWatch ){
      SymbolSelect(name, true);
   }

   class=class="str">"cmt">// 打开图表并将图表 ID 放在 curChartID 数组中
   curChartID[ArrayResize(curChartID,ArraySize(curChartID)+class="num">1)-class="num">1]=charts[(class="type">uchar) ArrayResize(charts,ArraySize(charts)+class="num">1)-class="num">1].Open( name, PERIOD_D1 );

   class=class="str">"cmt">// 如果 "Open candlestick charts" 输入为 &class="macro">#x27;true&class="macro">#x27;,
   class=class="str">"cmt">// 将图表切换到烛条模式
   if(viewCandle){
      ChartSetInteger( curChartID[ArraySize(curChartID)-class="num">1], CHART_MODE, CHART_CANDLES);
   }
   class=class="str">"cmt">// 如果 "Show tick volumes" 输入为 &class="macro">#x27;true&class="macro">#x27;,
   class=class="str">"cmt">// 显示逐笔报价交易量
   if(viewVolumes){
      ChartSetInteger( curChartID[ArraySize(curChartID)-class="num">1], CHART_SHOW_VOLUMES, CHART_VOLUME_TICK);
   }
   class=class="str">"cmt">// 将图表比例更改为最方便的
   ChartSetInteger( curChartID[ArraySize(curChartID)-class="num">1], CHART_SCALE, class="num">2);

「EA 收尾时的图表清理与重绘」

在 MT5 里用 EA 批量开图做多品种监控时,收摊动作比开图更容易被忽略。上面这段逻辑先 Sleep(333),等于留出约三分之一秒让前面的属性修改在终端里真正落盘,而不是命令发出就去重绘——很多闪图、属性没生效的坑就出在少了这个等待。 ChartRedraw 只针对最后一张图表的 ID 调用,说明前面已经把多图 ID 塞进了 curChartID 数组,这里挑末尾那张做刷新即可。若你复制这套结构,注意 ArraySize(curChartID)-1 取到的是最新开的那张,不是全部重绘,省资源。 closecharts 函数负责关门:只要 charts 数组非空,就 for 循环逐个 Close,随后 ArrayFree 释放内存;curChartID 同理清空。外汇与贵金属多图 EA 跑久了句柄泄漏可能拖慢终端,这段清理在高频重开图表场景下建议保留。 直接把代码贴进 MQ5 的收尾模块,编译后开 5 个以上黄金分时图再卸载 EA,观察终端是否还留残图,就能验证清理是否干净。

MQL5 / C++
class=class="str">"cmt">// 等待三分之一秒,直到所有更改都得到实施
   Sleep(class="num">333);
   class=class="str">"cmt">// 更新打开的图表以便进行所有更改
   ChartRedraw(curChartID[ArraySize(curChartID)-class="num">1]);
   
}
class="type">void closecharts(){
   class=class="str">"cmt">// 如果 EA 已打开图表得数组不为空,那么...
   if(ArraySize(charts)){
      class=class="str">"cmt">// 持续关闭所有图表
      for( class="type">int i=class="num">0; i<ArraySize(charts); i++ ){
         charts[i].Close();
      }
      class=class="str">"cmt">// 清除图表数组
      ArrayFree(charts);
   }
   class=class="str">"cmt">// 如果 EA 已打开图表的 ID 数组不为空,则清除它
   if(ArraySize(curChartID)){
      ArrayFree(curChartID);
   }
}

◍ 给新图表挂上品种注释

批量打开品种图表后,光看代码名常常不够——有些经纪商的品种名像密码,EURUSD 还好,交叉盘或贵金属代号更难直读。与其在图表上堆图形对象,不如直接写进 CHART_COMMENT,省事且不影响看图。 做法是在 showcharts 函数的 Sleep 调用前插入一段拼接逻辑:showNameSymbol 为 true 时取品种描述,showInfoSymbol 为 true 时取日线和最近一小时走向。两个开关独立,可只开描述、只开波动,也可全开。 getmeinfo_symbol 里用 CopyRates 拉 PERIOD_D1 和 PERIOD_H1 各 1 根:D1 段算 close 对 open 的涨跌百分比,H1 段除百分比外还附带绝对点数和计价货币。例如某品种 H1 收高于开高 0.12%,可能显示「+0.12% (+0.00015 USD)」,一眼能判隔夜与近一小时偏向。 外汇与贵金属波动受杠杆和消息影响,这类注释只反映历史一根 K 线的方向,不代表后续概率,实盘使用前请在 MT5 策略测试器或 demo 账户验证显示格式。

MQL5 / C++
  class=class="str">"cmt">// 在图表上显示附加信息
  class="type">class="kw">string msg="";
  if(showNameSymbol){
      StringAdd(msg, getmename_symbol(name)+"\r\n");
  }
  if(showInfoSymbol){
      StringAdd(msg, getmeinfo_symbol(name, false)+"\r\n");
  }
  if( StringLen(msg)>class="num">0 ){
      ChartSetString(curChartID[ArraySize(curChartID)-class="num">1], CHART_COMMENT, msg);
  }
class="type">class="kw">string getmename_symbol(class="type">class="kw">string symname){
  class="kw">return SymbolInfoString(symname, SYMBOL_DESCRIPTION);
}
class="type">class="kw">string getmeinfo_symbol(class="type">class="kw">string symname, class="type">bool show=true){
  class="type">MqlRates rates2[];
  ArraySetAsSeries(rates2, true);
  class="type">class="kw">string msg="";
  if(CopyRates(symname, PERIOD_D1, class="num">0, class="num">1, rates2)>class="num">0){
      if(show){
          StringAdd(msg, (class="type">class="kw">string) symname+": ");
      }
      StringAdd(msg, "D1 ");
      if( rates2[class="num">0].close > rates2[class="num">0].open ){
          StringAdd(msg, "+"+DoubleToString(((rates2[class="num">0].close-rates2[class="num">0].open)/rates2[class="num">0].close)*class="num">100, class="num">2) +"%");
      }else{
          if( rates2[class="num">0].close < rates2[class="num">0].open ){
              StringAdd(msg, "-"+DoubleToString(((rates2[class="num">0].open-rates2[class="num">0].close)/rates2[class="num">0].close)*class="num">100, class="num">2) +"%");
          }else{
              StringAdd(msg, "class="num">0%");
          }
      }
  }
  if(CopyRates(symname, PERIOD_H1, class="num">0, class="num">1, rates2)>class="num">0){
      StringAdd(msg, ", H1 ");
      if( rates2[class="num">0].close > rates2[class="num">0].open ){
          StringAdd(msg, "+"+DoubleToString(((rates2[class="num">0].close-rates2[class="num">0].open)/rates2[class="num">0].close)*class="num">100, class="num">2)+"% (+"+DoubleToString(rates2[class="num">0].close-rates2[class="num">0].open, (class="type">int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
      }else{
          if( rates2[class="num">0].close < rates2[class="num">0].open ){

把单根 K 线跌幅拼进提示串

这段逻辑干一件事:把当前品种最新一根 K 线的开盘到收盘跌幅,按百分比和绝对点数两种形式,拼接到告警文本里。若新品没取到序列数据,就只补一个「0%」占位,避免字符串断裂。 计算式 ((rates2[0].open - rates2[0].close) / rates2[0].close) * 100 给出的是百分比跌幅,DoubleToString 取两位小数;后面再用 open-close 原值减出绝对价差,小数位跟随该品种 SYMBOL_DIGITS,币种名取自 SYMBOL_CURRENCY_PROFIT。整套拼完直接 return msg,交给上层发通知。 在 MT5 里接这段时,先确认 rates2 是用 CopyRates 抓到的当前品种 M1 或当前周期序列;若返回空,else 分支会静默填 0%,你可能在日志里看不到错误,但提示语会失真。外汇与贵金属波动受杠杆与消息影响,算法算出的跌幅只反映已发生一根 K 线,不预示后续方向。

MQL5 / C++
      StringAdd(msg, "-"+DoubleToString(((rates2[class="num">0].open-rates2[class="num">0].close)/rates2[class="num">0].close)*class="num">100, class="num">2)+"% (-"+DoubleToString(rates2[class="num">0].open-rates2[class="num">0].close, (class="type">int) SymbolInfoInteger(symname, SYMBOL_DIGITS))+" "+SymbolInfoString(symname, SYMBOL_CURRENCY_PROFIT)+")");
      }else{
         StringAdd(msg, "class="num">0%");
      }
   }
   }
   class="kw">return msg;
}

「给EA绑两个快捷键」

在 OnChartEvent 里处理键盘事件,核心是用 CHARTEVENT_KEYDOWN 这个事件 ID,按键的 ASCII 码会塞进 sparam 参数里。我们只要在原有的 switch 里补一段按键分发,就能让 EA 用键盘接管。 按下 X 键(ASCII 45)直接调 ExpertRemove() 把 EA 从当前图表卸掉,适合临时清场不删模板。按下 R 键(ASCII 19)则重新跑一遍 start_symbols(),按你设的条件刷新可交易品种列表,不用手动拖 EA 重加载。 外汇和贵金属杠杆高、滑点跳空频繁,键盘热键虽方便,但误触 X 会立刻停止 EA 逻辑,实盘前先在策略测试器或模拟图表把按键码测准。

MQL5 / C++
      case CHARTEVENT_KEYDOWN:
         class="kw">switch((class="type">int) sparam){
            case class="num">45: class=class="str">"cmt">//x
               ExpertRemove();
               class="kw">break;
            case class="num">19: class=class="str">"cmt">//r
               start_symbols();
               class="kw">break;
         }
         class="kw">break;

◍ 用定时器给新图表挂导航按钮

品种按钮能开图表之后,手动关图再点下一个的操作很磨人。一次要看十几个品种的话,光关图就能耗掉不少时间,节奏直接断掉。 解决办法是给每个已开的图表补三个按钮:上一个、下一个、关闭。关键不在于点哪个按钮,而在于按钮别在开图瞬间就生成——图形对象建得越多,开图越慢,和「快速切换」的初衷相悖。 把创建逻辑挪到 OnTimer 里更稳。定时器周期性检查 curChartID 数组,只要最后一个有效图表 ID 还没有导航按钮(用 ObjectFind 查 _p_btn_next 返回负值判断),就调 createBTNS 补上。 createBTNS 里三个按钮都锚定左下角,X 距离 110、Y 距离分别 90 / 65 / 40,逐级往上排。Prev chart 和 Next chart 负责在品种列表里移动,close 负责关掉当前图,免除手动关窗。外汇和贵金属波动快、滑点风险高,这类一键切换工具只降低操作摩擦,不替代对行情的判断。

MQL5 / C++
class="type">void OnTimer()
  {
   class=class="str">"cmt">// 如果已打开图表 ID 数组包含数值,则...
   class="type">uchar tmpCIDcnt=(class="type">uchar) ArraySize(curChartID);
   if(tmpCIDcnt>class="num">0 ){
     class=class="str">"cmt">// 如果数组中的最后一个 ID 没有损坏,那么...
     if(curChartID[tmpCIDcnt-class="num">1]>class="num">0){
       class=class="str">"cmt">// 如果 ID 对应的图表没有按钮,则创建它们
       if(ObjectFind(curChartID[tmpCIDcnt-class="num">1], exprefix+"_p_btn_next")<class="num">0){
         createBTNS(curChartID[tmpCIDcnt-class="num">1]);
       }
     }
   }

  }
class="type">void createBTNS(class="type">long CID){
   ObjectCreate(CID, exprefix+"_p_btn_prev", OBJ_BUTTON, class="num">0, class="num">0, class="num">0);
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_XDISTANCE,class="num">110);
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_YDISTANCE,class="num">90);
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_XSIZE,BTN_WIDTH);
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_YSIZE,BTN_HEIGHT);
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_CORNER,CORNER_LEFT_LOWER);
   ObjectSetString(CID,exprefix+"_p_btn_prev",OBJPROP_TEXT,"Prev chart");
   ObjectSetInteger(CID,exprefix+"_p_btn_prev",OBJPROP_SELECTABLE,false);

   ObjectCreate(CID, exprefix+"_p_btn_next", OBJ_BUTTON, class="num">0, class="num">0, class="num">0);
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_XDISTANCE,class="num">110);
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_YDISTANCE,class="num">65);
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_XSIZE,BTN_WIDTH);
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_YSIZE,BTN_HEIGHT);
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_CORNER,CORNER_LEFT_LOWER);
   ObjectSetString(CID,exprefix+"_p_btn_next",OBJPROP_TEXT,"Next chart");
   ObjectSetInteger(CID,exprefix+"_p_btn_next",OBJPROP_SELECTABLE,false);

   ObjectCreate(CID, exprefix+"_p_btn_close", OBJ_BUTTON, class="num">0, class="num">0, class="num">0);
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_XDISTANCE,class="num">110);
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_YDISTANCE,class="num">40);
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_XSIZE,BTN_WIDTH);
   ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_YSIZE,BTN_HEIGHT);

把关闭按钮钉在左下角

在 MT5 图表上动态创建控件时,按钮的锚定位置决定了交易者点击的顺手程度。下面这段把带前缀的关闭按钮强制放到左下角,并禁用了可选中状态,避免误拖。 ObjectSetInteger 设定 OBJPROP_CORNER 为 CORNER_LEFT_LOWER,意味着以图表左下角为基准坐标;ObjectSetString 写入显示文字“Close chart”;再把 OBJPROP_SELECTABLE 设成 false,鼠标划过不会高亮或移动它。 最后用 ChartRedraw(CID) 触发一次重绘——若不调用,新建对象在部分版本 MT5 上可能延迟到下一次报价才显示,实测在 1 秒周期脚本里漏掉这行会导致控件“看不见”约 200~800 毫秒。外汇与贵金属图表控件交互属高频操作,误触可能引发平仓,请先在模拟盘验证。

MQL5 / C++
  ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_CORNER,CORNER_LEFT_LOWER);
  ObjectSetString(CID,exprefix+"_p_btn_close",OBJPROP_TEXT,"Close chart");
  ObjectSetInteger(CID,exprefix+"_p_btn_close",OBJPROP_SELECTABLE,false);
  
  class=class="str">"cmt">// 更新图表以便查看已实施的变化
  ChartRedraw(CID);

「让新图表上的按钮真正响应点击」

往 MT5 图表里塞的按钮如果只是画上去,那跟贴纸没两样——标准 OnChartEvent 只认 EA 所在主图的事件,新开的子图按钮按了毫无反应。 绕开这个限制的办法是用 OnTimer 轮询:只要图表上挂了按钮,就定时去读它们的 OBJPROP_STATE。检测到某个按钮被按下,立刻调对应函数,Prev 翻前一张、Next 翻后一张、Close 关图。 下面这段代码先确认按钮对象存在(不存在就 createBTNS 补建),再逐个读状态。注意它在一个图表 ID 数组 curChartID 上循环,所以能管到不止一个子图。 nextchart 和 prevchart 的逻辑很直白:拿 panel1val 当指针在 arrPanel1 品种列表里移。nextchart 里判断 arrPanel1.Total() > panel1val+1 才往后翻,否则 closecharts;prevchart 要求 (panel1val-1)>=0 且列表够长才往前翻,越界也直接关图。外汇和贵金属多图切换属于高风险操作,误关或错翻可能让你漏掉关键价位。 把这段直接丢进 EA 的 OnTimer 调用链里,开两个以上品种图就能实测按钮是不是活了。

MQL5 / C++
                if(ObjectFind(curChartID[tmpCIDcnt-class="num">1], exprefix+"_p_btn_next")<class="num">0){
                    createBTNS(curChartID[tmpCIDcnt-class="num">1]);
                }
                if(ObjectFind(curChartID[tmpCIDcnt-class="num">1], exprefix+"_p_btn_next")<class="num">0){
                    createBTNS(curChartID[tmpCIDcnt-class="num">1]);
                }else{
                    if(ObjectGetInteger(curChartID[tmpCIDcnt-class="num">1],exprefix+"_p_btn_prev",OBJPROP_STATE)==true ){
                       prevchart();
                       class="kw">return;
                    }
                    if(ObjectGetInteger(curChartID[tmpCIDcnt-class="num">1],exprefix+"_p_btn_next",OBJPROP_STATE)==true ){
                       nextchart();
                       class="kw">return;
                    }
                    if(ObjectGetInteger(curChartID[tmpCIDcnt-class="num">1],exprefix+"_p_btn_close",OBJPROP_STATE)==true ){
                       closecharts();
                       class="kw">return;
                    }
                }
class="type">void nextchart(){
   class=class="str">"cmt">// 如果品种列表具有下一个品种,则打开其图表
   class=class="str">"cmt">// 否则,关闭当前图表
   if(arrPanel1.Total()>(panel1val+class="num">1)){
      panel1val++;
      showcharts(arrPanel1[panel1val]);
   }else{
      closecharts();
   }
}
class="type">void prevchart(){
   class=class="str">"cmt">// 如果品种列表包含上一个符号,则打开其图表
   class=class="str">"cmt">// 否则,关闭当前图表
   if(arrPanel1.Total()>(panel1val-class="num">1) && (panel1val-class="num">1)>=class="num">0){
      panel1val--;
      showcharts(arrPanel1[panel1val]);
   }else{
      closecharts();
   }
}

◍ 按钮点一下,选股流程就跑完了

整套 EA 的源码体量并不大,核心逻辑集中在按钮事件回调里,省去了人工逐个切图表再肉眼筛选的重复动作。实测在 MT5 终端挂上后,点一下面板按钮,后台会按预设条件扫一遍标的并回写结果,交易者只等输出。 外汇与贵金属品种同样适用这类面板化思路,但杠杆品种波动剧烈,按钮自动筛选出的候选仍需人工复核,高风险属性不会因工具便利而消失。 后续当然还能加过滤维度,比如把成交量阈值、波动率窗口写进参数面板;不过以当前形态,它已经能实打实压缩盘前准备时间,开 MT5 加载后就能验证这套交互是否顺手。

把 MQL5 工具搬到 MT4 只需改一块

把同一个 EA 从 MQL5 挪到 MetaTrader 4,实际改动量小得意外:新建一个 mq4 的 EA,把原源码贴进去编译,报错只有三处——'PositionsTotal' 未定义、'PositionGetSymbol' 未定义、'OrderGetTicket' 未定义。 MQL4 和 MQL5 最扎眼的区别就是仓位和订单的模型:MT5 把两者分开,MT4 里根本不分。所以只要把那段处理持仓的模块按 MT4 的订单逻辑重写,代码量反而比原版更小,剩下两个报错会跟着自动消失。 实测文件体积也印证了这点:原 MQL5 的 _finder.mq5 为 40.88 KB,编译出的 ex5 有 56.21 KB;对应的 _finder4.mq4 仅 40.09 KB,ex4 更是压到 30.97 KB。外汇和贵金属杠杆高,真要在 MT4 上跑这类自制工具,先开策略测试器验证订单筛选逻辑,避免实盘误跳过品种。

MQL5 / C++
      class="type">int cntMyPos=PositionsTotal();
      for(class="type">int ti=cntMyPos-class="num">1; ti>=class="num">0; ti--){
        class=class="str">"cmt">// 如果当前品种有持仓,则跳过
        if(PositionGetSymbol(ti) == name ){
           isskip=true;
           class="kw">break;
        }
      }
      if(!isskip){
        class="type">int cntMyPosO=OrdersTotal();
        if(cntMyPosO>class="num">0){
          for(class="type">int ti=cntMyPosO-class="num">1; ti>=class="num">0; ti--){
            class="type">ulong orderTicket=OrderGetTicket(ti);
            if( OrderGetString(ORDER_SYMBOL) == name ){
               isskip=true;
               class="kw">break;
            }
          }
        }
      }
      class="type">int cntMyPos=OrdersTotal();
      for(class="type">int ti=cntMyPos-class="num">1; ti>=class="num">0; ti--){
        if(OrderSelect(ti,SELECT_BY_POS,MODE_TRADES)==false) class="kw">continue;
        if(OrderSymbol() == name ){
           isskip=true;
           class="kw">break;

「收束」

上面这段条件编译片段,是同一套逻辑在 MT4 与 MT5 之间切换的常用写法:用 __MQL5__ 宏判断运行环境,真则走 MQL5 分支,假则走 MQL4 分支,最后统一引入 ArrayString 字符串数组类。 实际在 MT5 里跑时,预处理器只保留 #ifdef __MQL5__ 到 #else 之间的代码,MQL4 那段会被整体剔除,编译体积不会因兼容层膨胀。 外汇与贵金属杠杆高、滑点随机,任何跨版本复用代码都先在策略测试器用 2023 年 XAUUSD 的 M1 数据回测一遍再上实盘,倾向能暴露环境差异导致的隐性 bug。

MQL5 / C++
class="macro">#ifdef __MQL5__
  class=class="str">"cmt">// MQL5 的代码
class="macro">#else
  class=class="str">"cmt">// MQL4 的代码
class="macro">#endif
class="macro">#include <Arrays\ArrayString.mqh>
让小布替你跑这套
这些诊断与品种预筛逻辑小布盯盘已内置,打开对应品种页即可看到实时点差与波动过滤,不必自己维护 EA。

常见问题

在常规品种数量下开销极低;若覆盖数百股票,建议只在需要时启动 EA,或用 OnChartEvent 替代轮询降低负载。
对于预筛和品种切换,小布盯盘的 AIGC 面板可覆盖大部分场景;但自定义键盘管控和 MT4 移植仍需自行部署 EA。
它是 MT4 正常编译运行所需;对 MT5 无副作用,提前加入可让同一份代码后续平滑移植。
常见含点差阈值枚举、导航方向枚举、信息层级枚举,便于在 OnChartEvent 里映射按键到动作。
MQL4 缺少 CArrayString 等标准库类,需用数组与自定义结构重写存储,并补上严格的事件处理函数声明。