将指标从 MQL4 迁移到 MQL5(基础篇)
📘

将指标从 MQL4 迁移到 MQL5(基础篇)

第 1/3 篇

◍ 把老 MQL4 指标搬进 MT5 的坑

MQL4 的指标代码不能直接在 MetaTrader 5 里编译运行,核心差异在指标缓冲区声明与 OnCalculate 接口。2013 年 12 月社区里一篇迁移示例帖被查看 4806 次、收藏 5 次,说明这是实盘老手常碰的硬需求。 MQL5 要求显式用 SetIndexBuffer 绑定缓冲区,且 OnCalculate 接收的是价格数组引用而非 MQL4 的预定义数组。开 MT5 新建指标时,先把 MQL4 里 IndicatorBuffers(N) 和 SetIndexStyle 的逻辑改写成缓冲区句柄 + 绘图属性设置,否则编译期就报数组越界。 外汇与贵金属品种波动连续,迁移后建议用 EURUSD 的 M1 数据跑一遍 OnCalculate,确认第 0 根 K 线不重复计算——这是 MQL4 转 MQL5 最容易漏的边界。

从 MQL4 价格计算迁到 MQL5 的底层差异

MQL4 与 MQL5 在价格数据访问上最直观的分歧是:MQL4 直接用 Close[0]、Open[1] 这类数组下标取价,而 MQL5 取消了全局预定义数组,必须显式调用 CopyClose()、CopyOpen() 等函数把行情拉进自定义缓冲区。 这种迁移不是单纯语法替换。MQL5 把历史数据与实时报价拆成了不同获取通道,若沿用 MQL4 的“数组即行情”思维,编译能通过但运行会得到空值或越界报错。 为降低迁移成本,作者封装了 mql4_2_mql5.mqh 函数库,把常用 MQL4 价格调用映射成 MQL5 等效实现。读完本篇后,你可以在 MT5 里 include 该库,直接以近似 MQL4 的写法做简单价格计算,省去重写数据获取层的麻烦。外汇与贵金属市场波动剧烈,此类迁移仅解决代码兼容,不预示任何收益。

「把 MACD 的输入与缓存改成引用传递」

做指标迁移,第一步是清掉 MQL4 里那些依赖终端全局环境的写法。如果原指标只算数值还好办;一旦挂了图形对象或自定义价格序列,后面会卡得很烦。 打开 MetaEditor 4 里的 MACD,先看输入参数和指标缓存的声明。原版用 extern 暴露 FastEMA=12、SlowEMA=26、SignalSMA=9,缓存是 MacdBuffer[] 和 SignalBuffer[] 两个普通数组。 关键改动在参数传递:缓存数组前必须加 & 符号,把数组引用塞进函数,而不是把数组整体拷贝过去。否则 MQL5 那边拿不到计算结果的回写通道,指标线画不出来。 下面这段就是改完后的核心骨架。注意 start() 在 MQL5 要接收 rates_total、prev_calculated,再接两个带 & 的缓存和三个周期参数;MACD 线用 12/26 双 EMA 差,信号线是对 MacdBuffer 跑 9 期 SMA。

MQL5 / C++
class=class="str">"cmt">//---- 指标参数
class="kw">extern class="type">int FastEMA=class="num">12;
class="kw">extern class="type">int SlowEMA=class="num">26;
class="kw">extern class="type">int SignalSMA=class="num">9;
class=class="str">"cmt">//---- 指标缓存
class="type">class="kw">double    MacdBuffer[];
class="type">class="kw">double    SignalBuffer[];
class="type">class="kw">double &MacdBuffer[],class="type">class="kw">double &SignalBuffer[],class="type">int FastEMA,class="type">int SlowEMA,class="type">int SignalSMA
class="type">int start()
class="type">int start(class="type">int rates_total,
         class="type">int prev_calculated,
         class="type">class="kw">double &MacdBuffer[],
         class="type">class="kw">double &SignalBuffer[],
         class="type">int FastEMA,
         class="type">int SlowEMA,
         class="type">int SignalSMA)
class="type">int rates_total,class="type">int prev_calculated,
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| 移动平均的收敛/发散指标(MACD)                                  |
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int start(class="type">int rates_total
         ,class="type">int prev_calculated
         ,class="type">class="kw">double &MacdBuffer[]
         ,class="type">class="kw">double &SignalBuffer[]
         ,class="type">int FastEMA
         ,class="type">int SlowEMA
         ,class="type">int SignalSMA)
  {
   class="type">int limit;
   class="type">int counted_bars=IndicatorCounted();
class=class="str">"cmt">//---- 最近一次计算的柱形将被重算
   if(counted_bars>class="num">0) counted_bars--;
   limit=Bars-counted_bars;
class=class="str">"cmt">//---- macd的计算结果在第一个指标缓存中
   for(class="type">int i=class="num">0; i<limit; i++)
      MacdBuffer[i]=iMA(NULL,class="num">0,FastEMA,class="num">0,MODE_EMA,PRICE_CLOSE,i)
                     -iMA(NULL,class="num">0,SlowEMA,class="num">0,MODE_EMA,PRICE_CLOSE,i);
class=class="str">"cmt">//---- 信号线的计算结果在第二个指标缓存中
   for(i=class="num">0; i<limit; i++)
      SignalBuffer[i]=iMAOnArray(MacdBuffer,Bars,SignalSMA,class="num">0,MODE_SMA,i);
class=class="str">"cmt">//---- 完成
   class="kw">return(class="num">0);

  }
class=class="str">"cmt">//+------------------------------------------------------------------

◍ 在 MT5 里搭出 MQL4 指标的骨架

在 MetaEditor 5 里点「New」选「Custom indicator」,先按老 MQL4 指标的输入参数建好壳:FastEMA=12、SlowEMA=26、SignalSMA=9 这三个 extern/input 是 MACD 默认周期,直接照抄就能跑通大部分迁移场景。 指标缓存也要对齐——MacdBuffer 和 SignalBuffer 两个 double 数组对应 MQL4 里的主线与信号线,MQL5 里需用 SetIndexBuffer 映射到 INDICATOR_DATA,否则图窗不画东西。 关键一步是引入 mql4_2_mql5.mqh 包含文件,并在 OnInit 里调 InitMql4()。之后在程序主体用 MQL4Run(rates_total,prev_calculated) 拿到 bars——即 MQL4 环境可见的柱数,再用 CountedMQL4 变量承接已计算量,等价于 MQL4 的 prev_calculated。 把写好的 MQL4 逻辑块贴在模板末尾,最后在主体加一行把数据及缓存链接喂给 Start() 函数。到这步模板算立起来了,但只是迁移第一阶段,下一步要在 MT5 里实跑调试。

MQL5 / C++
class=class="str">"cmt">//---- 指标参数
class="kw">extern class="type">int FastEMA=class="num">12;
class="kw">extern class="type">int SlowEMA=class="num">26;
class="kw">extern class="type">int SignalSMA=class="num">9;
class=class="str">"cmt">//---- 指标缓存
class="type">class="kw">double    MacdBuffer[];
class="type">class="kw">double    SignalBuffer[];
class="macro">#include <mql4_2_mql5.mqh>
class=class="str">"cmt">//--- 输入参数
class="type">int OnInit()
  InitMql4();
class="type">int bars=MQL4Run(rates_total,prev_calculated);
class=class="str">"cmt">// bars - MQL4程序可用的柱形数量
class="type">int CountedMQL4;
prev_calculated,
Start(bars,
CountedMQL4,
MacdBuffer,
SignalBuffer,
FastEMA,
SlowEMA,
SignalSMA);
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//|                      MACD_MQ4.mq5 |
class=class="str">"cmt">//|           Copyright class="num">2010, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                     [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------
class="macro">#class="kw">property copyright "Copyright class="num">2010, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property indicator_separate_window
class="macro">#class="kw">property indicator_buffers class="num">2
class="macro">#class="kw">property indicator_plots   class="num">2
class=class="str">"cmt">//---绘制 MacdBuffer
class="macro">#class="kw">property indicator_label1  "MacdBuffer"
class="macro">#class="kw">property indicator_type1   DRAW_LINE
class="macro">#class="kw">property indicator_color1  Red
class="macro">#class="kw">property indicator_style1  STYLE_SOLID
class="macro">#class="kw">property indicator_width1  class="num">1
class=class="str">"cmt">//--- 绘制 SignalBuffer
class="macro">#class="kw">property indicator_label2  "SignalBuffer"
class="macro">#class="kw">property indicator_type2   DRAW_LINE
class="macro">#class="kw">property indicator_color2  Red
class="macro">#class="kw">property indicator_style2  STYLE_SOLID
class="macro">#class="kw">property indicator_width2  class="num">1
class=class="str">"cmt">//--- 输入参数
class="macro">#include <mql4_2_mql5.mqh>
class="kw">input class="type">int      FastEMA=class="num">12;
class="kw">input class="type">int      SlowEMA=class="num">26;
class="kw">input class="type">int      SignalSMA=class="num">9;
class=class="str">"cmt">//--- 指标缓存
class="type">class="kw">double        MacdBuffer[];
class="type">class="kw">double        SignalBuffer[];
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| 自定义指标初始化函数                                |
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 指标缓存映射
   SetIndexBuffer(class="num">0,MacdBuffer,INDICATOR_DATA);
   SetIndexBuffer(class="num">1,SignalBuffer,INDICATOR_DATA);
class=class="str">"cmt">//---
InitMql4();
class=class="str">"cmt">//---
   class="kw">return(class="num">0);

}

把 MQL4 的 MACD 逻辑塞进 MT5 的 OnCalculate

在 MT5 自定义指标里跑老版 MQL4 的 MACD 逻辑,核心是把 OnCalculate 收到的全量报价数组转交给一个兼容层(这里叫 MQL4Run),再由 Start 函数按 MQL4 习惯去算。OnCalculate 只负责返回 rates_total,让终端知道本次处理了多少根 K 线。 下面这段是实际落地的迭代骨架。注意 MQL4Run 返回的 bars 是给旧代码『看得到』的柱数,不是直接拿来循环的上限。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| 自定义指标迭代函数                              | 
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime& time[],
                class="kw">const class="type">class="kw">double& open[],
                class="kw">const class="type">class="kw">double& high[],
                class="kw">const class="type">class="kw">double& low[],
                class="kw">const class="type">class="kw">double& close[],
                class="kw">const class="type">long& tick_volume[],
                class="kw">const class="type">long& volume[],
                class="kw">const class="type">int& spread[])
  {
class=class="str">"cmt">//---
   class="type">int bars=MQL4Run(rates_total,prev_calculated);
class=class="str">"cmt">// bars - MQL4程序可用的柱形数量
   Start(bars,
         CountedMQL4,
         MacdBuffer,
         SignalBuffer,
         FastEMA,
         SlowEMA,
         SignalSMA);
class=class="str">"cmt">//--- 返回prev_calculated的值用于下一次调用 
   class="kw">return(rates_total);
  }
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| 移动平均的收敛/发散指标(MACD)                                | 
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int Start(class="type">int rates_total,
          class="type">int prev_calculated,
          class="type">class="kw">double &MacdBuffer[],
          class="type">class="kw">double &SignalBuffer[],
          class="type">int FastEMA,
          class="type">int SlowEMA,
          class="type">int SignalSMA)
  {
   class="type">int limit;
   class="type">int counted_bars=IndicatorCounted();
class=class="str">"cmt">//---- 最近一次计算的柱形将被重算
   if(counted_bars>class="num">0) counted_bars--;
   limit=Bars-counted_bars;
class=class="str">"cmt">//---- macd的计算结果在第一个指标缓存中
   for(class="type">int i=class="num">0; i<limit; i++)
      MacdBuffer[i]=iMA(NULL,class="num">0,FastEMA,class="num">0,MODE_EMA,PRICE_CLOSE,i)
                  -iMA(NULL,class="num">0,SlowEMA,class="num">0,MODE_EMA,PRICE_CLOSE,i);
class=class="str">"cmt">//---- 信号线的计算结果在第二个指标缓存中
   for(i=class="num">0; i<limit; i++)
      SignalBuffer[i]=iMAOnArray(MacdBuffer,Bars,SignalSMA,class="num">0,MODE_SMA,i);
class=class="str">"cmt">//---- 完成
   class="kw">return(class="num">0);
  }
逐行看:OnCalculate 前两句注释是分隔线,函数签名里的 rates_total 是终端当前总柱数,prev_calculated 是上回算到第几根;MQL4Run 把这两个值喂给兼容层,吐出 bars。Start 被调用时传入快慢 EMA 周期和信号 SMA 周期,MacdBuffer 与 SignalBuffer 是指标缓存引用,直接写进去终端就能画。 Start 里 IndicatorCounted 拿到已算过的柱,减 1 是为了把最后一根纳入重算(报价跳动时那根没闭合)。limit = Bars - counted_bars 决定循环上界。第一个 for 给 MacdBuffer[i] 赋快线减慢线之差;第二个 for 用 iMAOnArray 对 MACD 数组再做一次 SignalSMA 周期的 SMA,结果进 SignalBuffer。外汇与贵金属杠杆高,这类指标只反映概率,实盘前请在 MT5 用历史数据验证信号滞后。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| 自定义指标迭代函数                              | 
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int OnCalculate(class="kw">const class="type">int rates_total,
                class="kw">const class="type">int prev_calculated,
                class="kw">const class="type">class="kw">datetime& time[],
                class="kw">const class="type">class="kw">double& open[],
                class="kw">const class="type">class="kw">double& high[],
                class="kw">const class="type">class="kw">double& low[],
                class="kw">const class="type">class="kw">double& close[],
                class="kw">const class="type">long& tick_volume[],
                class="kw">const class="type">long& volume[],
                class="kw">const class="type">int& spread[])
  {
class=class="str">"cmt">//---
   class="type">int bars=MQL4Run(rates_total,prev_calculated);
class=class="str">"cmt">// bars - MQL4程序可用的柱形数量
   Start(bars,
         CountedMQL4,
         MacdBuffer,
         SignalBuffer,
         FastEMA,
         SlowEMA,
         SignalSMA);
class=class="str">"cmt">//--- 返回prev_calculated的值用于下一次调用 
   class="kw">return(rates_total);
  }
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| 移动平均的收敛/发散指标(MACD)                                | 
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int Start(class="type">int rates_total,
          class="type">int prev_calculated,
          class="type">class="kw">double &MacdBuffer[],
          class="type">class="kw">double &SignalBuffer[],
          class="type">int FastEMA,
          class="type">int SlowEMA,
          class="type">int SignalSMA)
  {
   class="type">int limit;
   class="type">int counted_bars=IndicatorCounted();
class=class="str">"cmt">//---- 最近一次计算的柱形将被重算
   if(counted_bars>class="num">0) counted_bars--;
   limit=Bars-counted_bars;
class=class="str">"cmt">//---- macd的计算结果在第一个指标缓存中
   for(class="type">int i=class="num">0; i<limit; i++)
      MacdBuffer[i]=iMA(NULL,class="num">0,FastEMA,class="num">0,MODE_EMA,PRICE_CLOSE,i)
                  -iMA(NULL,class="num">0,SlowEMA,class="num">0,MODE_EMA,PRICE_CLOSE,i);
class=class="str">"cmt">//---- 信号线的计算结果在第二个指标缓存中
   for(i=class="num">0; i<limit; i++)
      SignalBuffer[i]=iMAOnArray(MacdBuffer,Bars,SignalSMA,class="num">0,MODE_SMA,i);
class=class="str">"cmt">//---- 完成
   class="kw">return(class="num">0);
  }

「指标缓存绑定后记得翻转序列」

把 MQL4 指标搬进 MT5,第一道坎是指标缓存的索引逻辑。MQL4 里 IndicatorCounted()、Bars、iMA()、iMAOnArray() 分别对应 MQL5 的 prev_calculated、rates_total、iMAMql4()、iMAOnArrayMql4(),变量名换了但语义接近,照表改完只是过了编译关。 真正的坑在 SetIndexBuffer()。官方参考明确写:数组绑定后按普通数组从 0 正向索引,哪怕你之前给数组装了时间序列的逆向索引。也就是说,老 MQL4 代码默认缓存是时间倒序,迁移后若不处理,画出来的线和原生 MACD 会对不上。 解法很直接——绑定完立刻补一句 ArraySetAsSeries(buffer, true)。下面这段从重写 MACD 里摘的,MacdBuffer 和 SignalBuffer 都在 SetIndexBuffer 之后翻转了序列,图 3 的对比显示这样跑出来的曲线和标准 MACD 指标重合。外汇与贵金属杠杆高,指标错位可能直接带偏进出场,上机前先核对这句有没有漏。 让小布替你跑这套 开 MT5 新建指标,把下面代码贴进 OnInit,故意注释掉两行 ArraySetAsSeries 再编译,副图里 MACD 会整段反着走——一眼就能验证缓存顺序的权重。

MQL5 / C++
class=class="str">"cmt">//+--------------------+------------------+
class=class="str">"cmt">//|         MQL4    | MQL5          |
class=class="str">"cmt">//+--------------------+------------------+
class=class="str">"cmt">//|IndicatorCounted()  | prev_calculated  |
class=class="str">"cmt">//|         Bars   | rates_total      |
class=class="str">"cmt">//|         iMA(   | iMAMql4(        |
class=class="str">"cmt">//|    iMAOnArray(  | iMAOnArrayMql4( |
class=class="str">"cmt">//+--------------------+------------------+ 
   ArraySetAsSeries(MacdBuffer,true);
   ArraySetAsSeries(SignalBuffer,true);
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//|                            MACD_MQL4.mq5 |
class=class="str">"cmt">//|          Copyright class="num">2010, MetaQuotes Software Corp. |
class=class="str">"cmt">//|                 [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------
class="macro">#class="kw">property copyright "Copyright class="num">2010, MetaQuotes Software Corp."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property indicator_separate_window
class="macro">#class="kw">property indicator_buffers class="num">2
class="macro">#class="kw">property indicator_plots   class="num">2
class=class="str">"cmt">//---绘制 MacdBuffer
class="macro">#class="kw">property indicator_label1   "MacdBuffer"
class="macro">#class="kw">property indicator_type1   DRAW_LINE
class="macro">#class="kw">property indicator_color1   Red
class="macro">#class="kw">property indicator_style1   STYLE_SOLID
class="macro">#class="kw">property indicator_width1   class="num">1
class=class="str">"cmt">//--- 绘制 SignalBuffer
class="macro">#class="kw">property indicator_label2   "SignalBuffer"
class="macro">#class="kw">property indicator_type2   DRAW_LINE
class="macro">#class="kw">property indicator_color2   Red
class="macro">#class="kw">property indicator_style2   STYLE_SOLID
class="macro">#class="kw">property indicator_width2   class="num">1
class="macro">#include <mql4_2_mql5.mqh>
class=class="str">"cmt">//--- 输入参数
class="kw">input class="type">int      FastEMA=class="num">12;
class="kw">input class="type">int      SlowEMA=class="num">26;
class="kw">input class="type">int      SignalSMA=class="num">9;
class=class="str">"cmt">//--- 指标缓存
class="type">class="kw">double          MacdBuffer[];
class="type">class="kw">double          SignalBuffer[];
class=class="str">"cmt">//+------------------------------------------------------------------
class=class="str">"cmt">//| 自定义指标初始化函数                                 |
class=class="str">"cmt">//+------------------------------------------------------------------
class="type">int OnInit()
  {
class=class="str">"cmt">//--- 指标缓存映射
   SetIndexBuffer(class="num">0,MacdBuffer,INDICATOR_DATA);
   SetIndexBuffer(class="num">1,SignalBuffer,INDICATOR_DATA);
class=class="str">"cmt">//---
InitMql4();
class=class="str">"cmt">//---

常见问题

MT5时间序列默认倒序,迁移后需手动翻转指标缓存,否则显示左右颠倒。
新平台OnCalculate改用引用传递价格数组,老代码直接拷贝会编译报错或算错。
小布可加载你的指标做异动诊断,对比老平台信号偏差,省去手动逐根核对。
把输入改为引用传递并绑定缓存,避免每次刷新重算全量历史。
多半是指标缓存没绑定或序列没翻转,检查SetIndexBuffer与数组方向即可。