将指标从 MQL4 迁移到 MQL5(基础篇)
◍ 把老 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。
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 里实跑调试。
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 是给旧代码『看得到』的柱数,不是直接拿来循环的上限。
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); }
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 会整段反着走——一眼就能验证缓存顺序的权重。
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">//---