将指标从 MQL4 迁移到 MQL5·综合运用
(3/3)·从模板拼装到缓存处理和设置改进,把老指标在 MT5 里跑稳的最后一块拼图
把 MQL4 指标逻辑塞进 OnCalculate
MT5 自定义指标跑起来靠的是 OnCalculate,而不是老 MT4 的 start。上面这段把 MQL4 习惯的 IndicatorCounted 映射成 prev_calculated,Bars 换成 rates_total,再交给 RSImql4 去算 RSI 缓冲区,返回 rates_total 表示全部柱形已处理。 在 RSImql4 里有个硬门槛:rates_total<=RSIPeriod 时直接 return(0),也就是说 EURUSD 这类品种若加载周期太短、总柱数不足 14(默认 RSI 周期),指标不画一根线。首次运行 counted_bars<1 会把末尾 RSIPeriod 根缓冲先清零,避免残留值污染。 循环起点也看 counted_bars:没历史就从头 rates_total-RSIPeriod-1 扫,有历史就只补 rates_total-counted_bars-1 之后的新柱。外汇与贵金属杠杆高、跳空频繁,这种增量计算能少算几百次价差滑点带来的重复开销,但信号滞后风险仍在。 下方那张对照表是实写代码时的速查:iMA 在 MT5 侧包了一层 iMAMql4,iMAOnArray 对应 iMAOnArrayMql4,直接照抄能省掉重写指标内核的时间。
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="type">int bars=MQL4Run(rates_total,prev_calculated); RSImql4(bars, CountedMQL4, RSIBuffer, PosBuffer, NegBuffer, RSIPeriod); class="kw">return(rates_total); } class="type">int RSImql4(class="type">int rates_total, class="type">int prev_calculated, class="type">class="kw">double &RSIBuffer[], class="type">class="kw">double &PosBuffer[], class="type">class="kw">double &NegBuffer[], class="type">int RSIPeriod) { class="type">int i,counted_bars=prev_calculated; class="type">class="kw">double rel,negative,positive; if(rates_total<=RSIPeriod) class="kw">return(class="num">0); if(counted_bars<class="num">1) for(i=class="num">1;i<=RSIPeriod;i++) RSIBuffer[rates_total-i]=class="num">0.0; i=rates_total-RSIPeriod-class="num">1; if(counted_bars>=RSIPeriod) i=rates_total-counted_bars-class="num">1; class="kw">while(i>=class="num">0) { class="type">class="kw">double sumn=class="num">0.0,sump=class="num">0.0; if(i==rates_total-RSIPeriod-class="num">1) {
「RSI 平滑递推的两种分支写法」
这段逻辑处理的是 RSI 计算中正/负向变动的平均值:首次触达时做全周期累加,后续柱用平滑递推。外汇与贵金属波动跳空频繁,负值缓冲为 0 时直接给 RSIBuffer 赋 0.0,避免除零崩指标。 初始段从 rates_total-2 倒序累加到 i,把每根 K 线的 Close[k]-Close[k+1] 按正负分别加进 sump / sumn;除以 RSIPeriod 得到首值 positive、negative。 非初始则走 Wilder 平滑:用上一根 PosBuffer[i+1] 乘以 (RSIPeriod-1) 再加当前变动,再除 RSIPeriod。RSIPeriod 取 14 时,新值约 93% 权重来自历史、7% 来自当下,反应偏慢但抗噪。 最后 RSIBuffer[i]=100-100/(1+positive/negative),若 negative 为 0 则记 0.0。开 MT5 把 RSIPeriod 改成 7 对比 14,能直观看到短周期 RSI 在黄金 1H 上假信号明显增多,属概率性现象而非必然。
class="type">int k=rates_total-class="num">2; class=class="str">"cmt">//---- 初始累加 class="kw">while(k>=i) { rel=Close[k]-Close[k+class="num">1]; if(rel>class="num">0) sump+=rel; else sumn-=rel; k--; } positive=sump/RSIPeriod; negative=sumn/RSIPeriod; } else { class=class="str">"cmt">//---- 平滑移动平均 rel=Close[i]-Close[i+class="num">1]; if(rel>class="num">0) sump=rel; else sumn=-rel; positive=(PosBuffer[i+class="num">1]*(RSIPeriod-class="num">1)+sump)/RSIPeriod; negative=(NegBuffer[i+class="num">1]*(RSIPeriod-class="num">1)+sumn)/RSIPeriod; } PosBuffer[i]=positive; NegBuffer[i]=negative; if(negative==class="num">0.0) RSIBuffer[i]=class="num">0.0; else RSIBuffer[i]=class="num">100.0-class="num">100.0/(class="num">1+positive/negative); i--; } class=class="str">"cmt">//---- class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------ class="type">int start() { class="type">int RSImql4(
◍ 把老 MQL4 头文件接进 MT5 的存放位置
想让这套兼容层跑起来,第一步是把 mql4_2_mql5.mqh 丢进 MQL5\Include\ 目录,编译器才能用 #include 找到它。 测试用的指标文件必须放在 MQL5\Indicators 文件夹里,否则 MT5 策略测试器根本扫不到,回测时会直接报找不到自定义指标。 这两步路径错了,后面代码写得再对也编译不过,开 MT5 数据文件夹确认一遍最实在。
接个兼容库扩功能
想在 MT5 里复用一部分 MQL4 的初始化习惯,不必重写整套逻辑。把 InitMQL4.mqh 丢进 MQL5\Include 目录,就能在 EA 或指标里直接挂上那层兼容封装。 具体做法是在输入参数声明之前,加一行包含指令。这样编译器会在预处理阶段把兼容层头文件并进来,老代码里常见的 MQL4 初始化写法大概率能少改甚至不改就过编。
class="macro">#include <InitMQL4.mqh>
class="macro">#include <InitMQL4.mqh>「迁移后的重算代价别低估」
用 mql4_2_mql5.mqh 把简单价格结构从 MQL4 搬进 MQL5,逻辑上只是换套运行时,但调试期大概率会卡在数据结构对齐上。熟悉 MQL4 的人照抄代码不算难,真正的坑在后面。 MQL5 存取缓存和 MQL4 不是一回事,指标首次加载要针对老环境的数据重建并重新计算,这段耗时随历史深度线性拉长。若要做成完全成熟的原生指标,重写时必须按 MQL5 的存储模型设计,而不是套一层兼容库就完事。 外汇与贵金属杠杆高、滑点跳空频繁,迁移后务必在 MT5 用真实品种多周期跑一遍,确认重算耗时和信号一致再上模拟。
◍ 句柄只建一次,等几秒很正常
在 MT5 里跨环境取数有个坑:指标句柄第一次创建时,客户端只在缓存里算一份。用相同参数再建,直接返回旧句柄,不会重算。 创建指标句柄后,若缓存里还没有这份数据,计算可能卡几秒才出图——空窗口先别慌,见图 10 未计算、图 11 有数据可用,等完就能用。 底层调用长这样,Symb 和 TimFram 决定品种周期,iMAPeriod 等参数一致才命中缓存: 外汇贵金属高波动,跨环境指标延迟可能放大滑点,验证前先开模拟盘跑一遍。
iMA(Symb,TimFram,iMAPeriod,ma_shift,ma_method,applied_price);