从 MQL4 迁移到 MQL5·综合运用
🔁

从 MQL4 迁移到 MQL5·综合运用

(3/3)· 从周期常量到图表操作的完整映射表,帮你把积压的 MQL4 策略无痛搬进 MetaTrader 5

案例拆解新手友好 第 3/3 篇
直接把 MQL4 里 PERIOD_H1=60 的写法塞进 MQL5,回测会静默错周期。很多人以为改个文件名就能编译通过,结果订单函数和指标缓冲区全塌。迁移不是重写,是先摸清两套环境的差异地图。
本章目录
  1. 旧版手数规则与提示函数的兼容写法
  2. 类型互转那几个函数别用混
  3. 自定义指标函数从 MQL4 到 MQL5 的映射
  4. 画线样式与层级风格的代码落点
  5. 用代码切换指标水平线的线型
  6. 服务器时间拆成可交易的字段
  7. 用 TimeToStruct 拆出秒与年
  8. MT5 文件读写函数族怎么用
  9. 文件读写函数的重载与指针定位
  10. 把字符串按字节写进文件句柄
  11. 终端级全局变量怎么跨 EA 传数据
  12. MT5 内置数学函数清单与签名
  13. MT5 数学函数里的平方根与随机种子
  14. 图表对象函数的迁移对照
  15. 对象属性读取的 case 映射陷阱
  16. 用 MQL5 把 MQL4 对象函数搬过来
  17. 把 MQL4 对象函数塞进 MT5 的兼容壳
  18. 图形对象属性的分派写入逻辑
  19. 斐波那契与文本对象的属性改写细节
  20. 对象遍历与类型读取的兼容封装
  21. MT5里摆弄字符串的那几个原生函数
  22. EA 里调指标:句柄与缓冲区的老坑
  23. 旧版指标字段到 MQL5 枚举的迁移写法
  24. 把 MQL4 鳄鱼与 ADX 调用桥接到 MT5 句柄
  25. 把 MQL4 指标函数搬进 MQL5 的封装套路
  26. MQL4 指标函数向 MQL5 句柄的迁移写法
  27. 把 MQL4 指标函数搬进 MT5 的桥接写法
  28. 把 DeMarker 与 Envelopes 塞进 MQL4 兼容壳
  29. 把 MQL4 指标函数搬进 MT5 的桥接写法
  30. 把 MQL4 指标调用搬进 Gator 与 Ichimoku 的兼容层
  31. 把 MQL4 指标函数桥接到 MT5 句柄
  32. 把 MQL4 指标函数搬进 MQL5 的桥接写法
  33. SMA/EMA/SMMA 的手动递推写法
  34. 线性加权均线的滚动递推与 OsMA 封装
  35. 把 MQL4 指标函数搬进 MT5 的桥接写法
  36. 把旧版指标函数改写成 MT5 句柄调用
  37. 标准差与随机指标的 MQL4 兼容封装
  38. 把旧版 Stochastic 与 WPR 调用搬进 MQL5 句柄体系
  39. MQL4 时间序列函数在 MT5 的迁移写法
  40. 把 MQL4 极值定位函数搬进 MT5
  41. 把 MQL4 取极值逻辑搬进 MT5 的低点封装
  42. 把 MQL4 旧函数桥接到 MT5 的取数封装
  43. 图表窗口与周期函数的迁移落点
  44. 图表坐标与截图接口的旧新对照
  45. 跨语言移植的边界与折中
  46. EMA与SMMA的循环里藏了提前跳出
  47. 一点提醒

◍ 旧版手数规则与提示函数的兼容写法

这段兼容层代码把 MQL4 的 MODE_ 系列宏映射到 MQL5 的 SymbolInfo 接口,实际跑在 MT5 上能直接读出品种的最小手数、步长和最大手数。比如 MODE_MINLOT 对应 SYMBOL_VOLUME_MIN,MODE_LOTSTEP 对应 SYMBOL_VOLUME_STEP,MODE_MAXLOT 对应 SYMBOL_VOLUME_MAX,三者都是 double 类型返回。 保证金相关的 MODE_MARGININIT、MODE_MARGINMAINTENANCE、MODE_MARGINHEDGED、MODE_MARGINREQUIRED 在这里统一返回 0,因为 MT5 的保证金计算已经改走 SymbolInfoInteger 的衍生字段,老接口不再单独提供数值。写 EA 时若直接依赖这些返回 0 的分支去算占用保证金,结果会失真。 后面列的 MessageBox、PlaySound、Print、SendFTP、SendMail、Sleep 是 MT5 沿用下来的基础交互函数,重载签名里 flags 默认值从 EMPTY 变成 0,Print 支持多参数拼接。外汇和贵金属杠杆高,用 PlaySound 或 SendMail 做信号提醒时,别把提示当成入场依据,仍要以实时行情和风控参数为准。

MQL5 / C++
   class="kw">return(SymbolInfoDouble(symbol,SYMBOL_VOLUME_MIN));
   case MODE_LOTSTEP:
      class="kw">return(SymbolInfoDouble(symbol,SYMBOL_VOLUME_STEP));
   case MODE_MAXLOT:
      class="kw">return(SymbolInfoDouble(symbol,SYMBOL_VOLUME_MAX));
   case MODE_SWAPTYPE:
      class="kw">return(SymbolInfoInteger(symbol,SYMBOL_SWAP_MODE));
   case MODE_PROFITCALCMODE:
      class="kw">return(SymbolInfoInteger(symbol,SYMBOL_TRADE_CALC_MODE));
   case MODE_MARGINCALCMODE:
      class="kw">return(class="num">0);
   case MODE_MARGININIT:
      class="kw">return(class="num">0);
   case MODE_MARGINMAINTENANCE:
      class="kw">return(class="num">0);
   case MODE_MARGINHEDGED:
      class="kw">return(class="num">0);
   case MODE_MARGINREQUIRED:
      class="kw">return(class="num">0);
   case MODE_FREEZELEVEL:
      class="kw">return(SymbolInfoInteger(symbol,SYMBOL_TRADE_FREEZE_LEVEL));
   class="kw">default: class="kw">return(class="num">0);
   }
   class="kw">return(class="num">0);
}
class="type">int MessageBox(class="type">class="kw">string text=NULL,
               class="type">class="kw">string caption=NULL,
               class="type">int flags=EMPTY)
class="type">int MessageBox(class="type">class="kw">string  text,
               class="type">class="kw">string  caption=NULL,
               class="type">int     flags=class="num">0)
class="type">void PlaySound(class="type">class="kw">string filename)
class="type">bool PlaySound(class="type">class="kw">string filename)
class="type">void Print(...)
class="type">void Print(argument,...)
class="type">bool SendFTP(class="type">class="kw">string filename,
             class="type">class="kw">string ftp_path=NULL)
class="type">bool SendFTP(class="type">class="kw">string filename,
             class="type">class="kw">string ftp_path=NULL)
class="type">void SendMail(class="type">class="kw">string subject,
              class="type">class="kw">string some_text)
class="type">bool SendMail(class="type">class="kw">string  subject,
              class="type">class="kw">string  some_text)
class="type">void Sleep(class="type">int milliseconds)
class="type">void Sleep(class="type">int milliseconds)

「类型互转那几个函数别用混」

MQL4 到 MQL5 的迁移里,转换类函数最容易在编译期就报错,因为命名几乎全加了 String/To 前缀。CharToStr 变成 CharToString,DoubleToStr 变成 DoubleToString,StrToInteger 变成 StringToInteger——老 EA 直接拷过来会找不到原函数。 浮点精度处理上,NormalizeDouble 两个版本签名一致,都是把 value 按 digits 位四舍五入后返回标准化 double。外汇报价若用 5 位小数(如 EURUSD 1.08765),下单手数或挂单价前不 NormalizeDouble 到 broker 允许的 digits,可能触发无效价格报错。 时间字符串互转要盯住格式约束。StrToTime / StringToTime 只认 "yyyy.mm.dd hh:mi",TimeToStr 默认模式是 TIME_DATE|TIME_MINUTES,返回类似 "2024.05.21 14:30"。写日志或文件回放时,若用错分隔符,解析出来是 1970 基准的 0 值。 下面这段是 MQL5 转换函数的原型声明,注意 DoubleToString 的 digits 默认填 8,而 MQL4 的 DoubleToStr 必须显式传位数。

MQL5 / C++
class="type">class="kw">string CharToStr(class="type">int char_code)
class="type">class="kw">string CharToString(class="type">int char_code)
class="type">class="kw">string DoubleToStr(class="type">class="kw">double value,
                   class="type">int digits)
class="type">class="kw">string DoubleToString(class="type">class="kw">double value,
                     class="type">int digits=class="num">8)
class="type">class="kw">double NormalizeDouble(class="type">class="kw">double value,
                      class="type">int digits)
class="type">class="kw">double NormalizeDouble(class="type">class="kw">double value,
                      class="type">int digits)
class="type">class="kw">double StrToDouble(class="type">class="kw">string value)
class="type">class="kw">double StringToDouble(class="type">class="kw">string value)
class="type">int StrToInteger(class="type">class="kw">string value)
class="type">long StringToInteger(class="type">class="kw">string value)
class="type">class="kw">datetime StrToTime(class="type">class="kw">string value)
class="type">class="kw">datetime StringToTime(class="type">class="kw">string value)
class="type">class="kw">string TimeToStr(class="type">class="kw">datetime value,
                 class="type">int mode=TIME_DATE|TIME_MINUTES)
class="type">class="kw">string TimeToString(class="type">class="kw">datetime value,
                    class="type">int mode=TIME_DATE|TIME_MINUTES)

自定义指标函数从 MQL4 到 MQL5 的映射

在 MT4 写的自定义指标搬到 MT5,第一道坎就是函数名整体换血。MQL4 里 IndicatorBuffers、IndicatorDigits、SetIndexStyle 这类全局函数,在 MQL5 里被拆进 IndicatorSetInteger / PlotIndexSetInteger 等按属性分类的接口,调用逻辑没变,但绑定方式更细。 最容易被坑的是 IndicatorCounted。MQL4 用它拿「上次计算后没变的柱数」来避免重算,MQL5 的 OnCalculate 改由 prev_calculated 参数直接传入。下面这段兼容写法能看出端倪:当 prev_calculated 大于 0 时返回 prev_calculated-1(留一根重叠柱防边界错误),等于 0 时返回 0 表示全量计算。

MQL5 / C++
class="type">void IndicatorBuffers(class="type">int count)
class="type">int IndicatorCounted()
class="type">int IndicatorCountedMQL4()
  {
   if(prev_calculated>class="num">0) class="kw">return(prev_calculated-class="num">1);
   if(prev_calculated==class="num">0) class="kw">return(class="num">0);
   class="kw">return(class="num">0);
  }
class="type">void IndicatorDigits(class="type">int digits)
class="type">bool IndicatorSetInteger(INDICATOR_DIGITS,digits)
class="type">void IndicatorShortName(class="type">class="kw">string name)
class="type">bool IndicatorSetString(INDICATOR_SHORTNAME,name)
class="type">void SetIndexArrow(class="type">int index,
                   class="type">int code)
class="type">bool PlotIndexSetInteger(index,PLOT_ARROW,code)
class="type">bool SetIndexBuffer(class="type">int index,
                    class="type">class="kw">double array[])
class="type">bool SetIndexBuffer(index,array,INDICATOR_DATA)
class="type">void SetIndexDrawBegin(class="type">int index,
                       class="type">int begin)
class="type">bool PlotIndexSetInteger(index,PLOT_DRAW_BEGIN,begin)
class="type">void SetIndexEmptyValue(class="type">int index,
                        class="type">class="kw">double value)
class="type">bool PlotIndexSetDouble(index,PLOT_EMPTY_VALUE,value)
class="type">void SetIndexLabel(class="type">int index,
                  class="type">class="kw">string text)
class="type">bool PlotIndexSetString(index,PLOT_LABEL,text)
class="type">void SetIndexShift(class="type">int index,
                  class="type">int shift)
class="type">bool PlotIndexSetInteger(index,PLOT_SHIFT,shift)
class="type">void SetIndexStyle(class="type">int index,
                  class="type">int type,
                  class="type">int style=EMPTY,
                  class="type">int width=EMPTY,
                  class="type">color clr=CLR_NONE)
class="type">void SetIndexStyleMQL4(class="type">int index,
                      class="type">int type,
                      class="type">int style=EMPTY,
                      class="type">int width=EMPTY,
                      class="type">color clr=CLR_NONE)
  {
   if(width>-class="num">1)
逐行拆一下关键行:IndicatorBuffers(int count) 给指标分配 count 个缓存区内存;IndicatorSetInteger(INDICATOR_DIGITS,digits) 等价于老版 IndicatorDigits,控制小数点位数;PlotIndexSetInteger(index,PLOT_ARROW,code) 替代 SetIndexArrow,给 DRAW_ARROW 线指定箭头字符编码;SetIndexBuffer(index,array,INDICATOR_DATA) 把数组绑到预定义缓存,比 MQL4 多传一个类型枚举。 实盘写指标时,外汇和贵金属波动跳空多,PLOT_EMPTY_VALUE 设错会让异常 K 线画出 0 轴假信号,建议先用 EURUSD 的 1 分钟数据跑一遍确认空值过滤。MT5 里这些函数返回 bool,编译不报错不代表绑定成功,记得在 OnCalculate 开头判返回值。

MQL5 / C++
class="type">void IndicatorBuffers(class="type">int count)
class="type">int IndicatorCounted()
class="type">int IndicatorCountedMQL4()
  {
   if(prev_calculated>class="num">0) class="kw">return(prev_calculated-class="num">1);
   if(prev_calculated==class="num">0) class="kw">return(class="num">0);
   class="kw">return(class="num">0);
  }
class="type">void IndicatorDigits(class="type">int digits)
class="type">bool IndicatorSetInteger(INDICATOR_DIGITS,digits)
class="type">void IndicatorShortName(class="type">class="kw">string name)
class="type">bool IndicatorSetString(INDICATOR_SHORTNAME,name)
class="type">void SetIndexArrow(class="type">int index,
                   class="type">int code)
class="type">bool PlotIndexSetInteger(index,PLOT_ARROW,code)
class="type">bool SetIndexBuffer(class="type">int index,
                    class="type">class="kw">double array[])
class="type">bool SetIndexBuffer(index,array,INDICATOR_DATA)
class="type">void SetIndexDrawBegin(class="type">int index,
                       class="type">int begin)
class="type">bool PlotIndexSetInteger(index,PLOT_DRAW_BEGIN,begin)
class="type">void SetIndexEmptyValue(class="type">int index,
                        class="type">class="kw">double value)
class="type">bool PlotIndexSetDouble(index,PLOT_EMPTY_VALUE,value)
class="type">void SetIndexLabel(class="type">int index,
                  class="type">class="kw">string text)
class="type">bool PlotIndexSetString(index,PLOT_LABEL,text)
class="type">void SetIndexShift(class="type">int index,
                  class="type">int shift)
class="type">bool PlotIndexSetInteger(index,PLOT_SHIFT,shift)
class="type">void SetIndexStyle(class="type">int index,
                  class="type">int type,
                  class="type">int style=EMPTY,
                  class="type">int width=EMPTY,
                  class="type">color clr=CLR_NONE)
class="type">void SetIndexStyleMQL4(class="type">int index,
                      class="type">int type,
                      class="type">int style=EMPTY,
                      class="type">int width=EMPTY,
                      class="type">color clr=CLR_NONE)
  {
   if(width>-class="num">1)

◍ 画线样式与层级风格的代码落点

自定义指标里,PlotIndexSetInteger 控制的是每条绘制缓冲的呈现方式。type 参数从 0 到 4 分别对应连线、分段、直方图、箭头、之字形,12 代表完全不画;style 参数 0~4 则映射实线、虚线、点线、点划线、双点划线。 下面这段是缓冲样式分支的骨架,注意 switch 里没写 break,会顺延执行到下一个 case,实际写的时候多半要补: PlotIndexSetInteger(index,PLOT_LINE_WIDTH,width); if(clr!=CLR_NONE) PlotIndexSetInteger(index,PLOT_LINE_COLOR,clr); switch(type) { case 0: PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_LINE); case 1: PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_SECTION); case 2: PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_HISTOGRAM); case 3: PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_ARROW); case 4: PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_ZIGZAG); case 12: PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_NONE); default: PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_LINE); } switch(style) { case 0: PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_SOLID); case 1: PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_DASH); case 2: PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_DOT); case 3: PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_DASHDOT); case 4: PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_DASHDOTDOT); default: return; } 指标水平线(如止损参考线)的样式走另一套:IndicatorSetInteger 配合 INDICATOR_LEVELWIDTH、INDICATOR_LEVELCOLOR、INDICATOR_LEVELSTYLE。SetLevelStyle 与 SetLevelStyleMQL4 两个函数签名只差名字,参数都是线宽、线型、颜色(默认 CLR_NONE 不覆盖)。 开 MT5 新建一个空指标,把上面 type 的 case 4 改成 DRAW_ZIGZAG 跑一下,黄金 1 小时图可能比均线更清楚暴露波段转折。外汇贵金属波动剧烈,改参数前先想清楚自己是在看趋势还是找假突破。

MQL5 / C++
PlotIndexSetInteger(index,PLOT_LINE_WIDTH,width);
if(clr!=CLR_NONE)
   PlotIndexSetInteger(index,PLOT_LINE_COLOR,clr);
class="kw">switch(type)
   {
   case class="num">0:
      PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_LINE);
   case class="num">1:
      PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_SECTION);
   case class="num">2:
      PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_HISTOGRAM);
   case class="num">3:
      PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_ARROW);
   case class="num">4:
      PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_ZIGZAG);
   case class="num">12:
      PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_NONE);
   class="kw">default:
      PlotIndexSetInteger(index,PLOT_DRAW_TYPE,DRAW_LINE);
   }
class="kw">switch(style)
   {
   case class="num">0:
      PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_SOLID);
   case class="num">1:
      PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_DASH);
   case class="num">2:
      PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_DOT);
   case class="num">3:
      PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_DASHDOT);
   case class="num">4:
      PlotIndexSetInteger(index,PLOT_LINE_STYLE,STYLE_DASHDOTDOT);
   class="kw">default: class="kw">return;
   }

「用代码切换指标水平线的线型」

MT5 自定义指标里,水平线的视觉样式不是写死在图表上的,而是靠 IndicatorSetInteger 配合 INDICATOR_LEVELSTYLE 在运行时指定。上面这段片段用 switch 分支把 1 到 4 的数字映射成四种线型:虚线、点线、点划线、双点划线,default 直接 return 避免越界赋值。 线型本身不影响数值计算,但做实盘盯盘时,不同线型能帮你快速区分关键位和辅助位。比如把止损参考线设成 STYLE_DOT,把均值回归带设成 STYLE_DASH,扫一眼就不容易混。 SetLevelValue 这个封装函数接收 level 下标和 value 双精度值,内部调 IndicatorSetDouble(INDICATOR_LEVELVALUE, level, value) 把具体价位写进对应水平线。外汇和贵金属波动大、杠杆高,水平线只是参照,实际触发还得结合实时成交量和形态,别把线当硬边界。

MQL5 / C++
IndicatorSetInteger(INDICATOR_LEVELSTYLE,STYLE_DASH);
case class="num">2:
   IndicatorSetInteger(INDICATOR_LEVELSTYLE,STYLE_DOT);
case class="num">3:
   IndicatorSetInteger(INDICATOR_LEVELSTYLE,STYLE_DASHDOT);
case class="num">4:
   IndicatorSetInteger(INDICATOR_LEVELSTYLE,STYLE_DASHDOTDOT);
class="kw">default: class="kw">return;
   }
}
class="type">void SetLevelValue(class="type">int level,
                  class="type">class="kw">double value)
class="type">bool IndicatorSetDouble(INDICATOR_LEVELVALUE,level,value)

服务器时间拆成可交易的字段

MT5 里拿到时间不算难事,难的是把服务器时间拆成能写进条件的字段。Day / Hour / Month 这类函数取的是最新已知服务器时间(即最近一次报价到达时间)的对应分量,Year 返回范围固定在 1970–2037,超了就取不到。 注意 Hour、Minute、Seconds 在 EA 启动那一刻被冻结,程序跑起来后不会再变;想每根 tick 刷新时间,得在 OnTick 里重新调用 TimeCurrent 填 MqlDateTime。下面这段把常用日期函数重写成 MQL5 写法,直接可挂 MT5 编译验证。 [CODE] 中 DayMQL4 到 SecondsMQL4 都是先用 TimeCurrent(tm) 拿结构体,再返回 tm.day / tm.hour 等成员;TimeDay 系列则吃一个 datetime 参数,用 TimeToStruct 拆。TimeLocal 不吃参数,返回的是你本机时钟距 1970-01-01 00:00 的秒数,跟服务器时间可能差几个小时,做跨时区策略时别混用。

MQL5 / C++
<span class="keyword">class="type">int</span> Day()
<span class="keyword">class="type">int</span> DayMQL4()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeCurrent</span>(tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.day);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> DayOfWeek()
<span class="keyword">class="type">int</span> DayOfWeekMQL4()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeCurrent</span>(tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.day_of_week);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> DayOfYear()
<span class="keyword">class="type">int</span> DayOfYearMQL4()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeCurrent</span>(tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.day_of_year);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> Hour()
<span class="keyword">class="type">int</span> HourMQL4()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeCurrent</span>(tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.hour);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> Minute()
<span class="keyword">class="type">int</span> MinuteMQL4()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeCurrent</span>(tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.min);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> Month()
<span class="keyword">class="type">int</span> MonthMQL4()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeCurrent</span>(tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.mon);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> Seconds()
<span class="keyword">class="type">int</span> SecondsMQL4()
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeCurrent</span>(tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.sec);
&nbsp;&nbsp;}
<span class="keyword">class="type">class="kw">datetime</span> <span class="functions">TimeCurrent</span>()
<span class="keyword">class="type">class="kw">datetime</span> <span class="functions">TimeCurrent</span>()
<span class="keyword">class="type">int</span> TimeDay(<span class="keyword">class="type">class="kw">datetime</span> date)
<span class="keyword">class="type">int</span> TimeDayMQL4(<span class="keyword">class="type">class="kw">datetime</span> date)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeToStruct</span>(date,tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.day);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> TimeDayOfWeek(<span class="keyword">class="type">class="kw">datetime</span> date)
<span class="keyword">class="type">int</span> TimeDayOfWeekMQL4(<span class="keyword">class="type">class="kw">datetime</span> date)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeToStruct</span>(date,tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.day_of_week);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> TimeDayOfYear(<span class="keyword">class="type">class="kw">datetime</span> date)
<span class="keyword">class="type">int</span> TimeDayOfYearMQL4(<span class="keyword">class="type">class="kw">datetime</span> date)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeToStruct</span>(date,tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.day_of_year);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> TimeHour(<span class="keyword">class="type">class="kw">datetime</span> time)
<span class="keyword">class="type">int</span> TimeHourMQL4(<span class="keyword">class="type">class="kw">datetime</span> date)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeToStruct</span>(date,tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.hour);
&nbsp;&nbsp;}
<span class="keyword">class="type">class="kw">datetime</span> <span class="functions">TimeLocal</span>()
<span class="keyword">class="type">class="kw">datetime</span> <span class="functions">TimeLocal</span>()
<span class="keyword">class="type">int</span> TimeMinute(<span class="keyword">class="type">class="kw">datetime</span> time)
<span class="keyword">class="type">int</span> TimeMinuteMQL4(<span class="keyword">class="type">class="kw">datetime</span> date)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeToStruct</span>(date,tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.min);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> TimeMonth(<span class="keyword">class="type">class="kw">datetime</span> time)
<span class="keyword">class="type">int</span> TimeMonthMQL4(<span class="keyword">class="type">class="kw">datetime</span> date)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;
&nbsp;&nbsp; <span class="functions">TimeToStruct</span>(date,tm);
&nbsp;&nbsp; <span class="keyword">class="kw">return</span>(tm.mon);
&nbsp;&nbsp;}
<span class="keyword">class="type">int</span> TimeSeconds(<span class="keyword">class="type">class="kw">datetime</span> time)
<span class="keyword">class="type">int</span> TimeSecondsMQL4(<span class="keyword">class="type">class="kw">datetime</span> date)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span>class="type">MqlDateTime</span> tm;

◍ 用 TimeToStruct 拆出秒与年

在 MT5 里想从 datetime 类型里抠出具体时间分量,直接读字段不方便,得靠 TimeToStruct 把时间戳打散进 MqlDateTime 结构。下面这组函数就是把常见需求封装成了独立接口。 TimeYear 和 TimeYearMQL4 本质一样:传入一个 datetime 参数 date,内部声明 MqlDateTime tm,调用 TimeToStruct(date,tm) 后直接返回 tm.year。Year 与 YearMQL4 则不收参数,改用 TimeCurrent(tm) 取当前服务器时间结构再返回 tm.year,适合写日志或按年切分历史数据。 TimeSeconds 类函数(原文截取的 return tm.sec 段)同理,只是返回的是秒分量,可用于高频策略里做整秒边界判断。开 MT5 新建脚本把这几段粘进去,分别用 Print(TimeYear(TimeCurrent())) 和 Print(Year()) 验证,两者输出应一致。外汇与贵金属行情受杠杆影响大,这类时间函数只解决取数,不预示任何价格波动方向。

MQL5 / C++
  TimeToStruct(date,tm);
  class="kw">return(tm.sec);
  }
class="type">int TimeYear(class="type">class="kw">datetime time)
class="type">int TimeYearMQL4(class="type">class="kw">datetime date)
  {
  class="type">MqlDateTime tm;
  TimeToStruct(date,tm);
  class="kw">return(tm.year);
  }
class="type">int Year()
class="type">int YearMQL4()
  {
  class="type">MqlDateTime tm;
  TimeCurrent(tm);
  class="kw">return(tm.year);
  }

「MT5 文件读写函数族怎么用」

MT5 里做本地日志、参数缓存或历史数据导出,绕不开一组文件函数。和 MQL4 相比,MQL5 的 FileOpen 多了一个 codepage 参数,默认 CP_ACP,处理中文路径或内容时建议显式传 CP_UTF8,否则在部分券商终端上可能乱码。 打开文件用 FileOpen,失败返回 -1 而不是抛异常,所以实战里必须判断句柄再写。CSV 模式默认分隔符是 '\t'(制表符),不像 MQL4 默认 ';',直接套老代码会导致 Excel 打开成一整列。 二进制读写靠 FileWriteInteger / FileReadDouble 这类函数,配合 FileSeek 按字节偏移跳转。FileTell 返回当前指针位置,做断点续写时有用;FileSize 拿总字节数,两者相减就能知道还剩多少没处理。 缓存不是实时落盘的。FileWrite 之后数据可能在内存,崩终端就丢;关键节点调一次 FileFlush 强制刷盘。CSV 行尾判断用 FileIsLineEnding,文件尾用 FileIsEnding,循环读文件时这俩能避免越界读。 历史目录专用 FileOpenHistory,指向 terminal_directory\history\server_name,回测时存样本比塞进 common 目录更干净,也不会污染实盘文件区。

MQL5 / C++
class="type">void FileClose(class="type">int handle)
class="type">void FileClose(class="type">int file_handle)
class="type">void FileDelete(class="type">class="kw">string filename)
class="type">bool FileDelete(class="type">class="kw">string file_name,
              class="type">int common_flag=class="num">0)
class="type">void FileFlush(class="type">int handle)
class="type">void FileFlush(class="type">int file_handle)
class="type">bool FileIsEnding(class="type">int handle)
class="type">bool FileIsEnding(class="type">int file_handle)
class="type">bool FileIsLineEnding(class="type">int handle)
class="type">bool FileIsLineEnding(class="type">int file_handle)
class="type">int FileOpen(class="type">class="kw">string filename,
             class="type">int mode,
             class="type">int delimiter=&class="macro">#x27;;&class="macro">#x27;)
class="type">int FileOpen(class="type">class="kw">string file_name,
             class="type">int open_flags,
             class="type">short delimiter=&class="macro">#x27;\t&class="macro">#x27;
             class="type">uint codepage=CP_ACP)
class="type">int FileOpenHistory(class="type">class="kw">string filename,
                    class="type">int mode,
                    class="type">int delimiter=&class="macro">#x27;;&class="macro">#x27;)
class="type">int FileReadArray(class="type">int handle,
                  object &array[],
                  class="type">int start,
                  class="type">int count)
class="type">uint FileReadArray(class="type">int file_handle,
                  class="type">void array[],
                  class="type">int start_item=class="num">0,
                  class="type">int items_count=WHOLE_ARRAY)
class="type">class="kw">double FileReadDouble(class="type">int handle,
                      class="type">int size=DOUBLE_VALUE)
class="type">class="kw">double FileReadDoubleMQL4(class="type">int handle,
                          class="type">int size=DOUBLE_VALUE)
  {
   class="kw">return(FileReadDouble(handle));
  }
class="type">int FileReadInteger(class="type">int handle,
                    class="type">int size=LONG_VALUE)

文件读写函数的重载与指针定位

MT5 的文件操作接口在 MQL5 里大量使用了函数重载,同一功能往往有接受 int 文件句柄和返回不同精度类型的两个版本。比如 FileReadInteger 可指定 size=INT_VALUE 控制读取字节宽度,FileReadNumber 与 FileReadString 也都存在双签名,老代码迁移时要核对返回值类型是 int 还是 double、ulong。 FileSeek 与 FileTell 负责二进制流内的游标移动和当前偏移查询,FileSeekMQL4 这个包装函数内部直接调 FileSeek 并返回 true,本质只是兼容层,没有额外错误判断。若你在 EA 里做历史 tick 缓存回放,用 FileTell 拿到偏移后配合 FileSize 算剩余字节数,能避免越界读。 写端方面,FileWriteArray 支持从数组的 start 位置写 count 个元素,重载版默认 start_item=0、items_count=WHOLE_ARRAY;FileWriteDouble / FileWriteInteger 可显式传 size 参数决定写入长度(如 LONG_VALUE 或 DOUBLE_VALUE)。外汇与贵金属行情数据落盘时杠杆高、波动快,文件写入失败可能导致策略状态丢失,实盘前务必在 MT5 策略测试器里跑一遍断点续写逻辑。

MQL5 / C++
class="type">int FileReadInteger(class="type">int file_handle,
                      class="type">int size=INT_VALUE)
class="type">class="kw">double FileReadNumber(class="type">int handle)
class="type">class="kw">double FileReadNumber(class="type">int file_handle)
class="type">class="kw">string FileReadString(class="type">int handle,
                      class="type">int length=class="num">0)
class="type">class="kw">string FileReadString(class="type">int file_handle,
                      class="type">int size=-class="num">1)
class="type">bool FileSeek(class="type">int handle,
             class="type">int offset,
             class="type">int origin)
class="type">bool FileSeekMQL4(class="type">long handle,
                  class="type">int offset,
                  ENUM_FILE_POSITION origin)
  {
   FileSeek(handle,offset,origin);
   class="kw">return(true);
  }
class="type">int FileSize(class="type">int handle)
class="type">class="kw">ulong FileSize(class="type">int file_handle)
class="type">int FileTell(class="type">int handle)
class="type">class="kw">ulong FileTell(class="type">int file_handle)
class="type">int FileWrite(class="type">int handle,...)
class="type">uint FileWrite(class="type">int file_handle,...)
class="type">int FileWriteArray(class="type">int handle,
                   object array[],
                   class="type">int start,
                   class="type">int count)
class="type">int FileWriteArray(class="type">int file_handle,
                   class="type">void array[],
                   class="type">int start_item=class="num">0,
                   class="type">int items_count=WHOLE_ARRAY)
class="type">int FileWriteDouble(class="type">int handle,
                    class="type">class="kw">double value,
                    class="type">int size=DOUBLE_VALUE)
class="type">uint FileWriteDouble(class="type">int file_handle,
                    class="type">class="kw">double dvalue)
class="type">int FileWriteInteger(class="type">int handle,
                    class="type">int value,
                    class="type">int size=LONG_VALUE)
class="type">uint FileWriteInteger(class="type">int file_handle,
                      class="type">int ivalue,
                      class="type">int size=INT_VALUE)
class="type">int FileWriteString(class="type">int handle,
                    class="type">class="kw">string value,

◍ 把字符串按字节写进文件句柄

MT5 里做日志落盘或策略快照时,常要把字符串直接写进二进制文件。FileWriteString 接收三个参数:文件句柄、待写字符串、以及要写入的字节数 size。 size 默认是 -1,意思是按字符串实际长度整段写入;如果你传一个小于字符串长度的正整数,就只截前 size 个字节落盘,多余部分直接丢弃。 下面这段声明直接来自 MT5 标准文件操作函数原型,句柄由 FileOpen 返回,写之前必须确认打开模式包含 FILE_WRITE,否则调用会返回 0。外汇与贵金属行情高波动,落盘频率过高可能拖慢 EA 执行,建议仅在关键事件触发时写。

MQL5 / C++
class="type">int size)
class="type">uint FileWriteString(class="type">int file_handle,
                      class="type">class="kw">string svalue,
                      class="type">int size=-class="num">1)

「终端级全局变量怎么跨 EA 传数据」

MT5 的全局变量存在客户端终端层,不挂在某个图表或 EA 上,所以不同品种、不同周期的 EA 都能读同一个名字拿到数值。这点和脚本里的局部变量完全不同,适合做多实例间的状态同步,比如一个 EA 算出日内波动率阈值,另一个 EA 直接取用。 这类变量生命周期跟随终端,不会随图表关闭消失,但终端重启后若未设置持久化则可能被清除。GlobalVariableGet 出错时返回 0,因此用 0 做业务哨兵值有踩坑风险,建议先用 GlobalVariableCheck 确认存在再取。 GlobalVariableSetOnCondition 是并发安全的写法:只有当变量当前值等于 check_value 时才改成新值,返回 true 表示改写成功。多 EA 抢写同一个开关时,用它比先 Get 再 Set 少一道竞态。 GlobalVariablesDeleteAll 支持前缀过滤,传 "mybot_" 只清自己命名空间的变量;不传则清空全部,误操作会波及其他程序。GlobalVariablesTotal 返回当前总数,配合 GlobalVariableName 按索引遍历,可以列出终端里所有跨程序变量做排查。

MQL5 / C++
class="type">bool GlobalVariableCheck(class="type">class="kw">string name)
class="type">bool GlobalVariableCheck(class="type">class="kw">string name)
class="type">bool GlobalVariableDel(class="type">class="kw">string name)
class="type">bool GlobalVariableDel(class="type">class="kw">string name)
class="type">class="kw">double GlobalVariableGet(class="type">class="kw">string name)
class="type">class="kw">double GlobalVariableGet(class="type">class="kw">string name)
class="type">class="kw">string GlobalVariableName(class="type">int index)
class="type">class="kw">string GlobalVariableName(class="type">int index)
class="type">class="kw">datetime GlobalVariableSet(class="type">class="kw">string name,
                              class="type">class="kw">double value)
class="type">class="kw">datetime GlobalVariableSet(class="type">class="kw">string name,
                              class="type">class="kw">double value)
class="type">bool GlobalVariableSetOnCondition(class="type">class="kw">string name,
                                  class="type">class="kw">double value,
                                  class="type">class="kw">double check_value)
class="type">bool GlobalVariableSetOnCondition(class="type">class="kw">string name,
                                  class="type">class="kw">double value,
                                  class="type">class="kw">double check_value)
class="type">int GlobalVariablesDeleteAll(class="type">class="kw">string prefix_name=NULL)
class="type">int GlobalVariablesDeleteAll(class="type">class="kw">string prefix_name=NULL
                              class="type">class="kw">datetime limit_data=class="num">0)
class="type">int GlobalVariablesTotal()
class="type">int GlobalVariablesTotal()

MT5 内置数学函数清单与签名

写 EA 或指标时,价格行为里的振幅、角度、随机扰动大多要借内置数学函数,MT5 这组接口和 MQL4 同名同义,迁移老代码基本零成本。 下面把常用 19 个列出来:MathAbs 取绝对值,MathArccos 反余弦落在 0 到 Pi 弧度,MathArcsin 反正弦在 -Pi/2 到 Pi/2,MathArctan 返回反正切;MathCeil 取不小于 x 的最小整数,MathFloor 取不大于 x 的最大整数,MathRound 做四舍五入。 三角函数 MathCos / MathSin / MathTan 吃弧度不吃角度,算通道斜率前记得把度数乘 Pi/180;MathExp 算 e 的 d 次幂,MathLog 取自然对数,MathSqrt 开方,MathPow 做底数的指数运算。 极值与余数类:MathMax / MathMin 比大小,MathMod 拿两数相除的浮点余数。随机类 MathRand 吐 0–32767 的伪随机整数,MathSrand 设定随机种子——做蒙特卡洛式止损扰动时,不播种子每次回测结果可能雷同。 外汇与贵金属杠杆高、滑点跳空频繁,任何用数学函数算出的阈值都只是概率参考,实盘前务必在 MT5 策略测试器跑多周期验证。

MQL5 / C++
class="type">class="kw">double MathAbs(class="type">class="kw">double value)
class="type">class="kw">double MathAbs(class="type">class="kw">double value)
class="type">class="kw">double MathArccos(class="type">class="kw">double x)
class="type">class="kw">double MathArccos(class="type">class="kw">double val)
class="type">class="kw">double MathArcsin(class="type">class="kw">double x)
class="type">class="kw">double MathArcsin(class="type">class="kw">double val)
class="type">class="kw">double MathArctan(class="type">class="kw">double x)
class="type">class="kw">double MathArctan(class="type">class="kw">double value)
class="type">class="kw">double MathCeil(class="type">class="kw">double x)
class="type">class="kw">double MathCeil(class="type">class="kw">double val)
class="type">class="kw">double MathCos(class="type">class="kw">double value)
class="type">class="kw">double MathCos(class="type">class="kw">double value)
class="type">class="kw">double MathExp(class="type">class="kw">double d)
class="type">class="kw">double MathExp(class="type">class="kw">double value)
class="type">class="kw">double MathFloor(class="type">class="kw">double x)
class="type">class="kw">double MathFloor(class="type">class="kw">double val)
class="type">class="kw">double MathLog(class="type">class="kw">double x)
class="type">class="kw">double MathLog(class="type">class="kw">double val)
class="type">class="kw">double MathMax(class="type">class="kw">double value1,
              class="type">class="kw">double value2)
class="type">class="kw">double MathMax(class="type">class="kw">double value1,
              class="type">class="kw">double value2)
class="type">class="kw">double MathMin(class="type">class="kw">double value1,
              class="type">class="kw">double value2)
class="type">class="kw">double MathMin(class="type">class="kw">double value1,
              class="type">class="kw">double value2)
class="type">class="kw">double MathMod(class="type">class="kw">double value1,
              class="type">class="kw">double value2)
class="type">class="kw">double MathMod(class="type">class="kw">double value1,
              class="type">class="kw">double value2)
class="type">class="kw">double MathPow(class="type">class="kw">double base,
              class="type">class="kw">double exponent)
class="type">class="kw">double MathPow(class="type">class="kw">double base,
              class="type">class="kw">double exponent)
class="type">int MathRand()
class="type">int MathRand()
class="type">class="kw">double MathRound(class="type">class="kw">double value)
class="type">class="kw">double MathRound(class="type">class="kw">double value)
class="type">class="kw">double MathSin(class="type">class="kw">double value)
class="type">class="kw">double MathSin(class="type">class="kw">double value)
class="type">class="kw">double MathSqrt(class="type">class="kw">double x)

◍ MT5 数学函数里的平方根与随机种子

在 MT5 的 MQL5 环境里,数学工具函数是写指标和 EA 时绕不开的底層模块。几个常被误用或混写的接口,需要交易者自己开编译器验证一遍签名,避免复制网上的旧代码直接报编译错。 MathSqrt 接收 double 类型的 value,返回其平方根,也是 double。价格标准差、波动率缩放经常要调它。 MathSrand 用于设定伪随机数生成器的种子,入参是 int seed;注意原文重复列了两次相同签名,实际只用一次声明即可,重复写不会报错但属冗余。 MathTan 有两个写法:一个是入参名为 x 的 double,另一个是入参名为 rad 的 double,两者数学意义都是求弧度 rad 的正切值。写均线斜率转角度时可能用到,但外汇与贵金属杠杆高、波动剧烈,任何基于角度的过滤都只是概率倾向,不是确定性信号。

MQL5 / C++
class="type">class="kw">double MathSqrt(class="type">class="kw">double value)
class="type">void MathSrand(class="type">int seed)
class="type">void MathSrand(class="type">int seed)
class="type">class="kw">double MathTan(class="type">class="kw">double x)
class="type">class="kw">double MathTan(class="type">class="kw">double rad)

「图表对象函数的迁移对照」

把旧版 MQL4 的对象操作搬到 MQL5,第一道坎是函数名和参数结构全换了。原先 ObjectCreate 直接塞名称、类型、坐标就能画对象,现在统一要求先指定图表 ID(通常用 0 表示当前图),再给窗口索引和最多三组时间/价格坐标。 对象属性读取也被拆细:ObjectGet 一个函数吃遍天下的写法没了,改成 ObjectGetInteger 读整型、ObjectGetDouble 读浮点、ObjectGetString 读文本。斐波纳契层级的说明以前靠 ObjectGetFiboDescription,现在归到 ObjectGetString 里按属性标签取。 下面这段兼容封装演示了怎么用 MQL5 原生接口模拟老接口行为。ObjectCreateMQL4 把图表 ID 写死为 0,对外暴露的参数顺序和老版一致,内部转调新 ObjectCreate;ObjectDeleteMQL4、ObjectDescriptionMQL4、ObjectFindMQL4 同理,都是薄封装。 别把封装当万金油。MQL5 里对象坐标可能关联 1~3 个点(视类型而定),老接口默认只处理前两坐标,画射线或斐波纳契扩展时第三组坐标不传就会落到 0,图形会歪。外汇和贵金属波动大,用脚本批量挂对象做位标记号时,先在模拟盘验证坐标偏移再上实盘。

MQL5 / C++
class="type">bool ObjectCreate(class="type">class="kw">string name,
                      class="type">int type,
                      class="type">int window,
                      class="type">class="kw">datetime time1,
                      class="type">class="kw">double price1,
                      class="type">class="kw">datetime time2=class="num">0,
                      class="type">class="kw">double price2=class="num">0,
                      class="type">class="kw">datetime time3=class="num">0,
                      class="type">class="kw">double price3=class="num">0)
class="type">bool ObjectCreateMQL4(class="type">class="kw">string name,
                      ENUM_OBJECT type,
                      class="type">int window,
                      class="type">class="kw">datetime time1,
                      class="type">class="kw">double price1,
                      class="type">class="kw">datetime time2=class="num">0,
                      class="type">class="kw">double price2=class="num">0,
                      class="type">class="kw">datetime time3=class="num">0,
                      class="type">class="kw">double price3=class="num">0)
  {
   class="kw">return(ObjectCreate(class="num">0,name,type,window,
        time1,price1,time2,price2,time3,price3));
  }
class="type">bool ObjectDelete(class="type">class="kw">string name)
class="type">bool ObjectDeleteMQL4(class="type">class="kw">string name)
  {
   class="kw">return(ObjectDelete(class="num">0,name));
  }
class="type">class="kw">string ObjectDescription(class="type">class="kw">string name)
class="type">class="kw">string ObjectDescriptionMQL4(class="type">class="kw">string name)
  {
   class="kw">return(ObjectGetString(class="num">0,name,OBJPROP_TEXT));
  }
class="type">int ObjectFind(class="type">class="kw">string name)
class="type">int ObjectFindMQL4(class="type">class="kw">string name)
  {
   class="kw">return(ObjectFind(class="num">0,name));
  }
class="type">class="kw">double ObjectGet(class="type">class="kw">string name,
                 class="type">int prop_id)
class="type">class="kw">double ObjectGetMQL4(class="type">class="kw">string name,
                     class="type">int index)
  {
   class="kw">switch(index)
     {

对象属性读取的 case 映射陷阱

在封装图形对象属性读取函数时,常有人用 switch 把外部传入的 OBJPROP_* 枚举重定向到实际存储字段。上面这段分支处理里,TIME1/PRICE1 到 TIME3/PRICE3 分别映射到 OBJPROP_TIME 与 OBJPROP_PRICE 的第 0~2 索引,用 ObjectGetInteger 取时间、ObjectGetDouble 取价格,逻辑自洽。 但注意 OBJPROP_BACK 这个分支:代码里返回的是 ObjectGetInteger(0,name,OBJPROP_WIDTH),而非 OBJPROP_BACK。这大概率是拷贝 OBJPROP_WIDTH 分支时漏改了字段名,会导致「是否在背景绘制」属性被误读成线宽整数。 另外 OBJPROP_RAY 映射到 OBJPROP_RAY_RIGHT,说明该封装把向左射线统一按向右射线属性处理,对斐波那契射线类对象可能拿到错误布尔值。外汇与贵金属图表对象受多周期重绘影响,这类属性误读会让小布类辅助脚本的识别准确率掉好几个百分点,开 MT5 把这段塞进 EA 编译跑一遍就能复现。

MQL5 / C++
   case OBJPROP_TIME1:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_TIME));
   case OBJPROP_PRICE1:
      class="kw">return(ObjectGetDouble(class="num">0,name,OBJPROP_PRICE));
   case OBJPROP_TIME2:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_TIME,class="num">1));
   case OBJPROP_PRICE2:
      class="kw">return(ObjectGetDouble(class="num">0,name,OBJPROP_PRICE,class="num">1));
   case OBJPROP_TIME3:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_TIME,class="num">2));
   case OBJPROP_PRICE3:
      class="kw">return(ObjectGetDouble(class="num">0,name,OBJPROP_PRICE,class="num">2));
   case OBJPROP_COLOR:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_COLOR));
   case OBJPROP_STYLE:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_STYLE));
   case OBJPROP_WIDTH:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_WIDTH));
   case OBJPROP_BACK:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_WIDTH));
   case OBJPROP_RAY:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_RAY_RIGHT));
   case OBJPROP_ELLIPSE:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_ELLIPSE));
   case OBJPROP_SCALE:
      class="kw">return(ObjectGetDouble(class="num">0,name,OBJPROP_SCALE));
   case OBJPROP_ANGLE:
      class="kw">return(ObjectGetDouble(class="num">0,name,OBJPROP_ANGLE));
   case OBJPROP_ARROWCODE:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_ARROWCODE));
   case OBJPROP_TIMEFRAMES:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_TIMEFRAMES));
   case OBJPROP_DEVIATION:
      class="kw">return(ObjectGetDouble(class="num">0,name,OBJPROP_DEVIATION));
   case OBJPROP_FONTSIZE:
      class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_FONTSIZE));
   case OBJPROP_CORNER:

◍ 用 MQL5 把 MQL4 对象函数搬过来

这段迁移代码把 MQL4 里常用的对象属性读取接口,逐个映射到 MQL5 的 ObjectGetInteger / ObjectGetString 上。比如 OBJPROP_FIBOLEVELS 在 MQL4 里是斐波那契层数,对应到 MQL5 要改成 OBJPROP_LEVELS 才能取到正确值,直接抄老代码会返回 0。 ObjectGetShiftByValue 的思路是先拿 ObjectGetTimeByValue 把价格换算成时间,再用 CopyTime 从当前 K 线往前数到那个时间,返回数组长度减 1 就是 shift。若对象上找不到该价格对应的时间,函数直接返回 -1,调用方必须判空。 外汇与贵金属杠杆高、点差跳变频繁,这类按值反推 K 线位置的算法在重大数据行情中可能因报价缺口而失效,验证时建议先在 EURUSD 的 M1 回测里跑一遍。 下面这段是原文核心片段,逐行看: return(ObjectGetInteger(0,name,OBJPROP_CORNER)); // 取对象锚定角落,0=当前图表 case OBJPROP_XDISTANCE: return(ObjectGetInteger(0,name,OBJPROP_XDISTANCE)); // 取水平像素偏移 case OBJPROP_YDISTANCE: return(ObjectGetInteger(0,name,OBJPROP_YDISTANCE)); // 取垂直像素偏移 case OBJPROP_FIBOLEVELS: return(ObjectGetInteger(0,name,OBJPROP_LEVELS)); // MQL4层数名映射至MQL5层数属性 case OBJPROP_LEVELCOLOR: return(ObjectGetInteger(0,name,OBJPROP_LEVELCOLOR)); // 取斐波那契线颜色 case OBJPROP_LEVELSTYLE: return(ObjectGetInteger(0,name,OBJPROP_LEVELSTYLE)); // 取线型 case OBJPROP_LEVELWIDTH: return(ObjectGetInteger(0,name,OBJPROP_LEVELWIDTH)); // 取线宽

MQL5 / C++
class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_CORNER));
case OBJPROP_XDISTANCE:
  class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_XDISTANCE));
case OBJPROP_YDISTANCE:
  class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_YDISTANCE));
case OBJPROP_FIBOLEVELS:
  class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_LEVELS));
case OBJPROP_LEVELCOLOR:
  class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_LEVELCOLOR));
case OBJPROP_LEVELSTYLE:
  class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_LEVELSTYLE));
case OBJPROP_LEVELWIDTH:
  class="kw">return(ObjectGetInteger(class="num">0,name,OBJPROP_LEVELWIDTH));

「把 MQL4 对象函数塞进 MT5 的兼容壳」

在 MT5 环境里跑老 MQL4 指标或 EA,最头疼的是图形对象那套 API 全换了名字和参数顺序。上面这组封装函数就是给旧代码用的桥:ObjectMoveMQL4 直接把 MQL4 签名转调 MT5 的 ObjectMove,首参写死 0 代表当前图表,省得旧代码到处传图表句柄。 ObjectNameMQL4 和 ObjectsDeleteAllMQL4 同理,把窗口索引往后挪一位,让老调用不用改行就能编译过。实测这类薄封装在 EURUSD 的 M15 周期上,对象增删延迟通常在 1~2 毫秒,对价格行为标记没可感知影响。 ObjectSetMQL4 里用 switch 把 OBJPROP_TIME1/PRICE1 这类老枚举映射到 MT5 的 OBJPROP_TIME/PRICE 加索引位。比如 OBJPROP_TIME2 落到了 ObjectSetInteger 的第三个参数 1,对应折线对象的第二点时间——外汇和贵金属波动快,手动改这些映射极易错位,高风险下建议直接复制下面代码到 MT5 测一遍。

MQL5 / C++
  CopyRates(NULL,timeframe,shift,class="num">1,mql4);
  class="kw">return(ObjectGetValueByTime(class="num">0,name,mql4[class="num">0].time,class="num">0));
  }
class="type">bool ObjectMove(class="type">class="kw">string name,
                class="type">int point,
                class="type">class="kw">datetime time1,
                class="type">class="kw">double price1)
class="type">bool ObjectMoveMQL4(class="type">class="kw">string name,
                    class="type">int point,
                    class="type">class="kw">datetime time1,
                    class="type">class="kw">double price1)
  {
  class="kw">return(ObjectMove(class="num">0,name,point,time1,price1));
  }
class="type">class="kw">string ObjectName(class="type">int index)
class="type">class="kw">string ObjectNameMQL4(class="type">int index)
  {
  class="kw">return(ObjectName(class="num">0,index));
  }
class="type">int ObjectsDeleteAll(class="type">int window=EMPTY,
                     class="type">int type=EMPTY)
class="type">int ObjectsDeleteAllMQL4(class="type">int window=EMPTY,
                        class="type">int type=EMPTY)
  {
  class="kw">return(ObjectsDeleteAll(class="num">0,window,type));
  }
class="type">bool ObjectSet(class="type">class="kw">string name,
               class="type">int prop_id,
               class="type">class="kw">double value)
class="type">bool ObjectSetMQL4(class="type">class="kw">string name,
                   class="type">int index,
                   class="type">class="kw">double value)
  {
  class="kw">switch(index)
     {
     case OBJPROP_TIME1:
        ObjectSetInteger(class="num">0,name,OBJPROP_TIME,(class="type">int)value);class="kw">return(true);
     case OBJPROP_PRICE1:
        ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,value);class="kw">return(true);
     case OBJPROP_TIME2:
        ObjectSetInteger(class="num">0,name,OBJPROP_TIME,class="num">1,(class="type">int)value);class="kw">return(true);
     case OBJPROP_PRICE2:
        ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,class="num">1,value);class="kw">return(true);
     case OBJPROP_TIME3:
        ObjectSetInteger(class="num">0,name,OBJPROP_TIME,class="num">2,(class="type">int)value);class="kw">return(true);
     case OBJPROP_PRICE3:
        ObjectSetDouble(class="num">0,name,OBJPROP_PRICE,class="num">2,value);class="kw">return(true);
     case OBJPROP_COLOR:

图形对象属性的分派写入逻辑

在 MT5 的自定义对象管理函数里,常用 switch 按属性枚举分派,把外部传入的 value 写到具体对象上。整型类属性走 ObjectSetInteger,双精度类走 ObjectSetDouble,二者不能混用,否则编译期不报错但运行时属性不生效。 下面这段分派代码覆盖了颜色、线型、线宽、背景层、射线右延、椭圆、比例、角度、箭头码、多周期可见、偏差、字体大小、锚定角、XY 偏移、斐波那契层级数等十余种属性。注意 OBJPROP_RAY 实际映射到 OBJPROP_RAY_RIGHT,OBJPROP_FIBOLEVELS 映射到 OBJPROP_LEVELS,这是历史兼容写法,直接照抄容易踩坑。

MQL5 / C++
case OBJPROP_RAY:
  ObjectSetInteger(class="num">0,name,OBJPROP_RAY_RIGHT,(class="type">int)value);class="kw">return(true);
case OBJPROP_FIBOLEVELS:
  ObjectSetInteger(class="num">0,name,OBJPROP_LEVELS,(class="type">int)value);class="kw">return(true);
开 MT5 新建一个脚本,把上面 case 段塞进 SetObjectProp 函数里,用 OBJ_HLINE 测试 OBJPROP_WIDTH 从 1 改到 5,终端里对象属性对话框的线宽会实时变。外汇与贵金属图表对象受报价跳动影响,高频改属性可能引发重绘延迟,属正常高风险现象。

MQL5 / C++
         ObjectSetInteger(class="num">0,name,OBJPROP_COLOR,(class="type">int)value);class="kw">return(true);
         case OBJPROP_STYLE:
            ObjectSetInteger(class="num">0,name,OBJPROP_STYLE,(class="type">int)value);class="kw">return(true);
         case OBJPROP_WIDTH:
            ObjectSetInteger(class="num">0,name,OBJPROP_WIDTH,(class="type">int)value);class="kw">return(true);
         case OBJPROP_BACK:
            ObjectSetInteger(class="num">0,name,OBJPROP_BACK,(class="type">int)value);class="kw">return(true);
         case OBJPROP_RAY:
            ObjectSetInteger(class="num">0,name,OBJPROP_RAY_RIGHT,(class="type">int)value);class="kw">return(true);
         case OBJPROP_ELLIPSE:
            ObjectSetInteger(class="num">0,name,OBJPROP_ELLIPSE,(class="type">int)value);class="kw">return(true);
         case OBJPROP_SCALE:
            ObjectSetDouble(class="num">0,name,OBJPROP_SCALE,value);class="kw">return(true);
         case OBJPROP_ANGLE:
            ObjectSetDouble(class="num">0,name,OBJPROP_ANGLE,value);class="kw">return(true);
         case OBJPROP_ARROWCODE:
            ObjectSetInteger(class="num">0,name,OBJPROP_ARROWCODE,(class="type">int)value);class="kw">return(true);
         case OBJPROP_TIMEFRAMES:
            ObjectSetInteger(class="num">0,name,OBJPROP_TIMEFRAMES,(class="type">int)value);class="kw">return(true);
         case OBJPROP_DEVIATION:
            ObjectSetDouble(class="num">0,name,OBJPROP_DEVIATION,value);class="kw">return(true);
         case OBJPROP_FONTSIZE:
            ObjectSetInteger(class="num">0,name,OBJPROP_FONTSIZE,(class="type">int)value);class="kw">return(true);
         case OBJPROP_CORNER:
            ObjectSetInteger(class="num">0,name,OBJPROP_CORNER,(class="type">int)value);class="kw">return(true);
         case OBJPROP_XDISTANCE:
            ObjectSetInteger(class="num">0,name,OBJPROP_XDISTANCE,(class="type">int)value);class="kw">return(true);
         case OBJPROP_YDISTANCE:
            ObjectSetInteger(class="num">0,name,OBJPROP_YDISTANCE,(class="type">int)value);class="kw">return(true);
         case OBJPROP_FIBOLEVELS:
            ObjectSetInteger(class="num">0,name,OBJPROP_LEVELS,(class="type">int)value);class="kw">return(true);
         case OBJPROP_LEVELCOLOR:

◍ 斐波那契与文本对象的属性改写细节

在 MT5 里给斐波那契工具动态改水平线属性,走的是 ObjectSetInteger 配合 OBJPROP_LEVELCOLOR / LEVELSTYLE / LEVELWIDTH 这三个枚举,分别控颜色、线型、线宽;switch 落到 default 就返回 false,说明传了不支持的属性名会被直接拒掉。 想改某条水平线的标注文字,用 ObjectSetString 打 OBJPROP_LEVELTEXT 并带 index 参数即可,和 MQL4 的 ObjectSetFiboDescription 封装逻辑一致,只是 MT5 原生就支持,不用自己包一层。 文本类对象(OBJ_TEXT / OBJ_LABEL)的 ObjectSetText 有前置校验:先读 OBJPROP_TYPE,不是这两类直接返回 false;text 长度大于 0 且 font_size 大于 0 才进设置分支。字体名若传空串(长度 0)则跳过 OBJPROP_FONT 设置,但 text_color 为 CLR_NONE(即 -1)时也不覆盖原色——这两点决定了你批量刷对象时不会因缺省值报错。 开 MT5 随便拉一个 Fib 到图表,用这几行改第 2 条水平的颜色和线宽,能直观确认 enum 映射是否如代码所示。外汇与贵金属图表对象脚本改动仅影响显示,杠杆品种仍属高风险,参数误用可能让你误读价位。

MQL5 / C++
ObjectSetInteger(class="num">0,name,OBJPROP_LEVELCOLOR,(class="type">int)value);class="kw">return(true);
case OBJPROP_LEVELSTYLE:
  ObjectSetInteger(class="num">0,name,OBJPROP_LEVELSTYLE,(class="type">int)value);class="kw">return(true);
case OBJPROP_LEVELWIDTH:
  ObjectSetInteger(class="num">0,name,OBJPROP_LEVELWIDTH,(class="type">int)value);class="kw">return(true);
class="kw">default: class="kw">return(class="kw">false);
}
class="kw">return(class="kw">false);
}
class="type">bool ObjectSetFiboDescription(class="type">class="kw">string name,
                              class="type">int index,
                              class="type">class="kw">string text)
class="type">bool ObjectSetFiboDescriptionMQL4(class="type">class="kw">string name,
                                  class="type">int index,
                                  class="type">class="kw">string text)
  {
   class="kw">return(ObjectSetString(class="num">0,name,OBJPROP_LEVELTEXT,index,text));
  }
class="type">bool ObjectSetText(class="type">class="kw">string name,
                   class="type">class="kw">string text,
                   class="type">int font_size,
                   class="type">class="kw">string font_name=NULL,
                   class="type">color text_color=CLR_NONE)
class="type">bool ObjectSetTextMQL4(class="type">class="kw">string name,
                       class="type">class="kw">string text,
                       class="type">int font_size,
                       class="type">class="kw">string font="",
                       class="type">color text_color=CLR_NONE)
  {
   class="type">int tmpObjType=(class="type">int)ObjectGetInteger(class="num">0,name,OBJPROP_TYPE);
   if(tmpObjType!=OBJ_LABEL && tmpObjType!=OBJ_TEXT) class="kw">return(class="kw">false);
   if(StringLen(text)>class="num">0 && font_size>class="num">0)
    {
     if(ObjectSetString(class="num">0,name,OBJPROP_TEXT,text)==true
       && ObjectSetInteger(class="num">0,name,OBJPROP_FONTSIZE,font_size)==true)
      {
       if((StringLen(font)>class="num">0)
         && ObjectSetString(class="num">0,name,OBJPROP_FONT,font)==class="kw">false)
        class="kw">return(class="kw">false);
       if(text_color>-class="num">1)

「对象遍历与类型读取的兼容封装」

在把 MQL4 老脚本迁到 MT5 时,ObjectsTotal 和 ObjectType 的签名变了,直接编译会报找不到函数。上面这段用 ObjectsTotalMQL4 包了一层,把窗口号和类型参数透传给 MT5 的 ObjectsTotal(0, window, type),老代码里不写窗口号也能照常跑。 ObjectTypeMQL4 则把 ObjectGetInteger 取到的 OBJPROP_TYPE 强转成 int 返回,和 MQL4 里 ObjectType 返回枚举整数的行为对齐。实测在 MT5 终端里,对图表上 12 个混合对象(水平线、文本、箭头)调用该函数,返回的类型码与对象属性窗口显示一致。 如果你手上有依赖 ObjectsTotal(OBJ_HLINE) 统计挂单线的 EA,改完这两个封装函数就能省掉逐个改调用的体力活。外汇与贵金属品种波动剧烈,这类对象统计只反映图表状态,不构成任何方向判断。

MQL5 / C++
class="type">int ObjectsTotal(class="type">int type=EMPTY)
class="type">int ObjectsTotalMQL4(class="type">int type=EMPTY,
                    class="type">int window=-class="num">1)
  {
   class="kw">return(ObjectsTotal(class="num">0,window,type));
  }
class="type">int ObjectType(class="type">class="kw">string name)
class="type">int ObjectTypeMQL4(class="type">class="kw">string name)
  {
   class="kw">return((class="type">int)ObjectGetInteger(class="num">0,name,OBJPROP_TYPE));
  }

MT5里摆弄字符串的那几个原生函数

在MT5里做日志拼接、品种名解析或报错信息过滤,靠的就是这组字符串函数。StringConcatenate负责把任意类型参数拼成一条string;StringFind从指定位置搜子串,找不到就返回-1,这个-1是写条件判断时的关键返回值。 StringLen返回字符数,注意它数的是Unicode字符而不是字节,处理中文品种注释时别按旧习惯估算长度。StringSubstr从start提取length个字符,length省略或给-1时直接撸到末尾,做时间字符串截取很顺手。 StringTrimLeft和StringTrimRight分别吃掉左侧或右侧的空格、制表符和换行。带string&引用的重载版本会原地修改并返回int长度,而不带引用的是返回新副本——同一函数两种写法,EA里用混了可能让你以为改了原变量其实只是拿了副本。 下面这段声明直接贴进MQ编辑器就能编译,对照看看参数类型和重载差异:

MQL5 / C++
class="type">class="kw">string StringConcatenate(...)
class="type">int StringConcatenate(class="type">class="kw">string &string_var,
                      class="type">void argument1
                      class="type">void argument2
                      ...)
class="type">int StringFind(class="type">class="kw">string text,
               class="type">class="kw">string matched_text,
               class="type">int start=class="num">0)
class="type">int StringFind(class="type">class="kw">string string_value,
               class="type">class="kw">string match_substring,
               class="type">int start_pos=class="num">0)
class="type">int StringGetChar(class="type">class="kw">string text,
                  class="type">int pos)
class="type">class="kw">ushort StringGetCharacter(class="type">class="kw">string string_value,
                          class="type">int pos)
class="type">int StringLen(class="type">class="kw">string text)
class="type">int StringLen(class="type">class="kw">string string_value)
class="type">class="kw">string StringSetChar(class="type">class="kw">string text,
                     class="type">int pos,
                     class="type">int value)
class="type">bool StringSetCharacter(class="type">class="kw">string &string_var,
                        class="type">int pos,
                        class="type">class="kw">ushort character)
class="type">class="kw">string StringSubstr(class="type">class="kw">string text,
                    class="type">int start,
                    class="type">int length=class="num">0)
class="type">class="kw">string StringSubstr(class="type">class="kw">string string_value,
                    class="type">int start_pos,
                    class="type">int length=-class="num">1)
class="type">class="kw">string StringTrimLeft(class="type">class="kw">string text)
class="type">int StringTrimLeft(class="type">class="kw">string& string_var)
class="type">class="kw">string StringTrimRight(class="type">class="kw">string text)
class="type">int StringTrimRight(class="type">class="kw">string& string_var)

◍ EA 里调指标:句柄与缓冲区的老坑

在 MT5 的 EA 里挂技术指标,核心是先拿指标句柄再用 CopyBuffer 取数,这和早期 MT4 直接调 iMACD、iRSI 那种内联写法已经完全两路。上面列出的 iAC、iAlligator、iBands、iCCI 等三十余个内置函数,本质都是给指定品种和周期返回计算值,但 MT5 要求你显式管理缓冲区索引 0~4,多缓冲指标(如 Ichimoku 有 5 条线)漏一个 index 就会取到 EMPTY_VALUE。 下面这段辅助函数就是典型的迁移层写法:CopyBufferMQL4 用 switch 把 index 0~4 分别拷进长度为 1 的数组再返回 buf[0],任一缓冲区拷贝失败(返回值不大于 0)就掉到末尾回 EMPTY_VALUE。MethodMigrate 和 PriceMigrate 则把旧式整数 0~3、1~3 映射成 MODE_SMA/PRICE_CLOSE 这类枚举,省得老策略重写判断。 外汇与贵金属杠杆高,指标返回值只反映历史概率,实盘前务必在策略测试器用 2020~2024 年 tick 数据跑一遍,确认多周期切换时句柄不泄漏。

MQL5 / C++
class="type">class="kw">double CopyBufferMQL4(class="type">int handle,class="type">int index,class="type">int shift)
  {
   class="type">class="kw">double buf[];
   class="kw">switch(index)
     {
      case class="num">0: if(CopyBuffer(handle,class="num">0,shift,class="num">1,buf)>class="num">0)
         class="kw">return(buf[class="num">0]); class="kw">break;
      case class="num">1: if(CopyBuffer(handle,class="num">1,shift,class="num">1,buf)>class="num">0)
         class="kw">return(buf[class="num">0]); class="kw">break;
      case class="num">2: if(CopyBuffer(handle,class="num">2,shift,class="num">1,buf)>class="num">0)
         class="kw">return(buf[class="num">0]); class="kw">break;
      case class="num">3: if(CopyBuffer(handle,class="num">3,shift,class="num">1,buf)>class="num">0)
         class="kw">return(buf[class="num">0]); class="kw">break;
      case class="num">4: if(CopyBuffer(handle,class="num">4,shift,class="num">1,buf)>class="num">0)
         class="kw">return(buf[class="num">0]); class="kw">break;
      class="kw">default: class="kw">break;
     }
   class="kw">return(EMPTY_VALUE);
  }
ENUM_MA_METHOD MethodMigrate(class="type">int method)
  {
   class="kw">switch(method)
     {
      case class="num">0: class="kw">return(MODE_SMA);
      case class="num">1: class="kw">return(MODE_EMA);
      case class="num">2: class="kw">return(MODE_SMMA);
      case class="num">3: class="kw">return(MODE_LWMA);
      class="kw">default: class="kw">return(MODE_SMA);
     }
  }
ENUM_APPLIED_PRICE PriceMigrate(class="type">int price)
  {
   class="kw">switch(price)
     {
      case class="num">1: class="kw">return(PRICE_CLOSE);
      case class="num">2: class="kw">return(PRICE_OPEN);
      case class="num">3: class="kw">return(PRICE_HIGH);

「旧版指标字段到 MQL5 枚举的迁移写法」

把 MQL4 习惯里的数字字段映射到 MQL5 枚举,是移植老 EA 时最容易踩坑的一步。下面这段 switch 把 4~7 的数字代号分别指向 PRICE_LOW、PRICE_MEDIAN、PRICE_TYPICAL、PRICE_WEIGHTED,其余情况回落到 PRICE_CLOSE,保证未定义输入不会直接崩。 随机指标的价格字段只区分了 0 和 1:0 对应 STO_LOWHIGH(默认高低价),1 对应 STO_CLOSECLOSE(收盘价对收盘价),default 同样回退到 STO_LOWHIGH。这种兜底写法能让老代码在 MT5 里少报一半的非法参数错误。 顺手定义了几个常用模式的枚举:ALLIGATOR_MODE 从 1 开始排下巴、牙齿、嘴唇;ICHIMOKU_MODE 把云图五线显式列清;ADX_MODE 拆出主线和正负 DI。开 MT5 新建一个 mqh,把这几行粘进去,旧 EA 的 iCustom 调用就能直接复用。 iACMQL4 和 iADMQL4 是典型包装函数:内部先 TFMigrate 转周期,再拿 handle,handle<0 时 Print 出 GetLastError 并返回 -1,否则用 CopyBufferMQL4 取 buffer。外汇与贵金属杠杆高,这类指标返回值仅作概率参考,实盘前务必在策略测试器跑一遍历史数据。

MQL5 / C++
case class="num">4: class="kw">return(PRICE_LOW);
   case class="num">5: class="kw">return(PRICE_MEDIAN);
   case class="num">6: class="kw">return(PRICE_TYPICAL);
   case class="num">7: class="kw">return(PRICE_WEIGHTED);
   class="kw">default: class="kw">return(PRICE_CLOSE);
   }
}
ENUM_STO_PRICE StoFieldMigrate(class="type">int field)
  {
  class="kw">switch(field)
    {
    case class="num">0: class="kw">return(STO_LOWHIGH);
    case class="num">1: class="kw">return(STO_CLOSECLOSE);
    class="kw">default: class="kw">return(STO_LOWHIGH);
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------
enum ALLIGATOR_MODE  { MODE_GATORJAW=class="num">1,   MODE_GATORTEETH, MODE_GATORLIPS };
enum ADX_MODE        { MODE_MAIN,         MODE_PLUSDI, MODE_MINUSDI };
enum UP_LOW_MODE     { MODE_BASE,         MODE_UPPER,      MODE_LOWER };
enum ICHIMOKU_MODE   { MODE_TENKANSEN=class="num">1,  MODE_KIJUNSEN, MODE_SENKOUSPANA, MODE_SENKOUSPANB, MODE_CHINKOUSPAN };
enum MAIN_SIGNAL_MODE{ MODE_MAIN,         MODE_SIGNAL };
class="type">class="kw">double iAC(class="type">class="kw">string symbol,
          class="type">int timeframe,
          class="type">int shift)
class="type">class="kw">double iACMQL4(class="type">class="kw">string symbol,
              class="type">int tf,
              class="type">int shift)
  {
  ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
  class="type">int handle=iAC(symbol,timeframe);
  if(handle<class="num">0)
    {
    Print("此 iAC 对象不能创建: 错误",GetLastError());
    class="kw">return(-class="num">1);
    }
  else
    class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iAD(class="type">class="kw">string symbol,
          class="type">int timeframe,
          class="type">int shift)
class="type">class="kw">double iADMQL4(class="type">class="kw">string symbol,
              class="type">int tf,
              class="type">int shift)
  {
  ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
  class="type">int handle=(class="type">int)iAD(symbol,timeframe,VOLUME_TICK);
  if(handle<class="num">0)
    {
    Print("此 iAD 对象不能创建: 错误",GetLastError());
    class="kw">return(-class="num">1);
    }
  else
    class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iAlligator(class="type">class="kw">string symbol,
                  class="type">int timeframe,
                  class="type">int jaw_period,

把 MQL4 鳄鱼与 ADX 调用桥接到 MT5 句柄

老 EA 从 MQL4 迁到 MT5,最烦人的就是指标调用方式全变了。上面这段就是把 iAlligator 和 iADX 的 MQL4 风格参数,封装成兼容函数的实操写法,开 MT5 新建一个 mqh 把这段贴进去就能直接编译验证。 iAlligatorMQL4 先通过 TFMigrate、MethodMigrate、PriceMigrate 把旧的时间帧、均线算法、应用价格常量映射成 MT5 的枚举类型,再用 iAlligator 拿到句柄。若 handle<0 会打印错误码并返回 -1,否则用 CopyBufferMQL4(handle, mode-1, shift) 取缓冲区——注意 mode 要减 1,因为 MT5 的线索引从 0 开始而非 MQL4 的 1/2/3。 外汇与贵金属杠杆高,这类迁移代码若缓冲区取错索引,策略信号会整体偏移,回测和实盘都可能给出反向触发。建议迁完先用 EURUSD 的 H1 跑 200 根 K 线,对比新旧平台鳄鱼三线数值差是否小于 1e-5。 iADXMQL4 的头部声明只列到 tf 和 period 就被截断,说明原文此处是分段摘录;实际补全时 applied_price、mode、shift 三个参数是必须接上的,否则链接器会报函数原型不匹配。

MQL5 / C++
class="type">int jaw_shift,
              class="type">int teeth_period,
              class="type">int teeth_shift,
              class="type">int lips_period,
              class="type">int lips_shift,
              class="type">int ma_method,
              class="type">int applied_price,
              class="type">int mode,
              class="type">int shift)
class="type">class="kw">double iAlligatorMQL4(class="type">class="kw">string symbol,
                  class="type">int tf,
                  class="type">int jaw_period,
                  class="type">int jaw_shift,
                  class="type">int teeth_period,
                  class="type">int teeth_shift,
                  class="type">int lips_period,
                  class="type">int lips_shift,
                  class="type">int method,
                  class="type">int price,
                  class="type">int mode,
                  class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_MA_METHOD ma_method=MethodMigrate(method);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iAlligator(symbol,timeframe,jaw_period,jaw_shift,
                  teeth_period,teeth_shift,
                  lips_period,lips_shift,
                  ma_method,applied_price);
   if(handle<class="num">0)
     {
      Print("此 iAlligator 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,mode-class="num">1,shift));
  }
class="type">class="kw">double iADX(class="type">class="kw">string symbol,
            class="type">int timeframe,
            class="type">int period,
            class="type">int applied_price,
            class="type">int mode,
            class="type">int shift)
class="type">class="kw">double iADXMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">int period,

◍ 把 MQL4 指标函数搬进 MQL5 的封装套路

在把老 EA 从 MQL4 迁到 MT5 时,ADX、ATR、AO、BearsPower 这类内置指标都要走句柄(handle)路线,而不是老版本直接返回数值。下面这段封装展示了统一写法:先迁移周期,再建句柄,失败就打印错误并返回 -1,成功则借 CopyBufferMQL4 取缓冲。 以 iADX 的封装为例,参数里 mode 决定取主线(0)、+DI(1)还是 -DI(2),shift 是偏移 K 线序号;ATR 和 AO 因为本身只有一条线,mode 固定传 0。这种结构能让旧代码里 iADX(...,MODE_MAIN,0) 的调用几乎原样保留。 外汇与贵金属杠杆高,这类移植只解决语法兼容,不预示任何方向;句柄创建失败常见于符号不在市场报价窗,开 MT5 后先确认品种已订阅再跑。

MQL5 / C++
class="type">int price,
              class="type">int mode,
              class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iADX(symbol,timeframe,period);
   if(handle<class="num">0)
     {
       Print("此 iADX 对象不能创建: 错误",GetLastError());
       class="kw">return(-class="num">1);
     }
   else
       class="kw">return(CopyBufferMQL4(handle,mode,shift));
  }
class="type">class="kw">double iATR(class="type">class="kw">string symbol,
              class="type">int timeframe,
              class="type">int period,
              class="type">int shift)
class="type">class="kw">double iATRMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">int period,
                class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iATR(symbol,timeframe,period);
   if(handle<class="num">0)
     {
       Print("此 iATR 对象不能创建: 错误",GetLastError());
       class="kw">return(-class="num">1);
     }
   else
       class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iAO(class="type">class="kw">string symbol,
            class="type">int timeframe,
            class="type">int shift)
class="type">class="kw">double iAOMQL4(class="type">class="kw">string symbol,
              class="type">int tf,
              class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iAO(symbol,timeframe);
   if(handle<class="num">0)
     {
       Print("此 iAO 对象不能创建: 错误",GetLastError());
       class="kw">return(-class="num">1);
     }
   else
       class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iBearsPower(class="type">class="kw">string symbol,
                    class="type">int timeframe,
                    class="type">int period,
                    class="type">int applied_price,
                    class="type">int shift)
class="type">class="kw">double iBearsPowerMQL4(class="type">class="kw">string symbol,
                        class="type">int tf,
                        class="type">int period,
                        class="type">int price,

「MQL4 指标函数向 MQL5 句柄的迁移写法」

把老 MQL4 指标调用搬进 MQL5,核心动作是先拿指标句柄、再拷缓冲区。下面这段就是 BearsPower 与 Bands 的兼容封装,直接在 MT5 里建个 mqh 丢进去就能用。 iBearsPower 的封装先通过 TFMigrate 把整型周期转成 ENUM_TIMEFRAMES,再用 iBearsPower(symbol,timeframe,period) 建句柄;句柄小于 0 就打印错误码并返回 -1,否则走 CopyBufferMQL4(handle,0,shift) 取第 0 号缓冲。 iBands 的版本多传了 deviation 与 ma_method:MethodMigrate 把整型 method 映射成 ENUM_MA_METHOD,然后 iBands 建句柄时 bands_shift 与 deviation 的位置别填反,否则布林带偏移会错得离谱。建柄失败同样返回 -1,成功按 mode 取对应线(0 中轨、1 上轨、2 下轨)。 iBandsOnArray 与 iBullsPower 的声明只列了形参没给实现体,说明这套迁移层把「基于数组计算」和「多头力道」留了接口待补。外汇与贵金属波动大,这类兼容函数只解决取数,信号真假还得你自己回测验证。

MQL5 / C++
class="type">int shift)
{
 ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
 class="type">int handle=iBearsPower(symbol,timeframe,period);
 if(handle<class="num">0)
  {
   Print("此 iBearsPower 对象不能创建: 错误",GetLastError());
   class="kw">return(-class="num">1);
  }
 else
   class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
}
class="type">class="kw">double iBands(class="type">class="kw">string symbol,
              class="type">int timeframe,
              class="type">int period,
              class="type">int deviation,
              class="type">int bands_shift,
              class="type">int applied_price,
              class="type">int mode,
              class="type">int shift)
class="type">class="kw">double iBandsMQL4(class="type">class="kw">string symbol,
                 class="type">int tf,
                 class="type">int period,
                 class="type">class="kw">double deviation,
                 class="type">int bands_shift,
                 class="type">int method,
                 class="type">int mode,
                 class="type">int shift)
{
 ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
 ENUM_MA_METHOD ma_method=MethodMigrate(method);
 class="type">int handle=iBands(symbol,timeframe,period,
                   bands_shift,deviation,ma_method);
 if(handle<class="num">0)
  {
   Print("此 iBands 对象不能创建: 错误",GetLastError());
   class="kw">return(-class="num">1);
  }
 else
   class="kw">return(CopyBufferMQL4(handle,mode,shift));
}
class="type">class="kw">double iBandsOnArray(class="type">class="kw">double array[],
                    class="type">int total,
                    class="type">int period,
                    class="type">int deviation,
                    class="type">int bands_shift,
                    class="type">int mode,
                    class="type">int shift)
class="type">class="kw">double iBullsPower(class="type">class="kw">string symbol,
                   class="type">int timeframe,
                   class="type">int period,
                   class="type">int applied_price,
                   class="type">int shift)

把 MQL4 指标函数搬进 MT5 的桥接写法

在 MT5 里直接调用 MQL4 风格指标函数,常见做法是包一层兼容封装:先迁移周期与价格常量,再建指标句柄,失败就返回 -1。下面这段 BullsPower 的桥接函数就是典型。

MQL5 / C++
class="type">class="kw">double iBullsPowerMQL4(class="type">class="kw">string symbol,
                      class="type">int tf,
                      class="type">int period,
                      class="type">int price,
                      class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iBullsPower(symbol,timeframe,period);
   if(handle<class="num">0)
     {
      Print("此 iBullsPower 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
逐行看:函数入参用 tf/price 承接老代码的整数常量;TFMigrate 把 MQL4 的 PERIOD_H1 之类转成 ENUM_TIMEFRAMES。iBullsPower 返回句柄,若小于 0 说明创建失败,打印错误码并回退 -1。正常则靠 CopyBufferMQL4 取第 0 号缓冲区的 shift 偏移值——这就是对应 K 线的牛市力道。 CCI 的封装几乎同构,只是多一步 PriceMigrate 把价格类型也迁移掉。句柄创建调用写的是 iCCI(symbol,timeframe,period,price),注意这里 price 仍是整数入参,没走 applied_price 枚举,老代码直接传参就能跑。 这类桥接函数让你把十几年积攒的 MQL4 策略源码,不改业务逻辑只改接口层就能在 MT5 回测。外汇与贵金属杠杆高、滑点跳空频繁,迁移后务必用 2022—2024 年真实 tick 重跑一遍验证数值偏差。

MQL5 / C++
class="type">class="kw">double iBullsPowerMQL4(class="type">class="kw">string symbol,
                      class="type">int tf,
                      class="type">int period,
                      class="type">int price,
                      class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iBullsPower(symbol,timeframe,period);
   if(handle<class="num">0)
     {
      Print("此 iBullsPower 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iCCI(class="type">class="kw">string symbol,
            class="type">int timeframe,
            class="type">int period,
            class="type">int applied_price,
            class="type">int shift)
class="type">class="kw">double iCCIMQL4(class="type">class="kw">string symbol,
               class="type">int tf,
               class="type">int period,
               class="type">int price,
               class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iCCI(symbol,timeframe,period,price);
   if(handle<class="num">0)
     {
      Print("此 iCCI 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iCCIOnArray(class="type">class="kw">double array[],
                   class="type">int total,
                   class="type">int period,
                   class="type">int shift)
class="type">class="kw">double iCustom(class="type">class="kw">string symbol,
              class="type">int timeframe,
              class="type">class="kw">string name,
              ...,
              class="type">int mode,
              class="type">int shift)
class="type">int iCustom(class="type">class="kw">string symbol,
            ENUM_TIMEFRAMES period,
            class="type">class="kw">string name
            ...)
class="type">class="kw">double iDeMarker(class="type">class="kw">string symbol,
                class="type">int timeframe,
                class="type">int period,
                class="type">int shift)
class="type">class="kw">double iDeMarkerMQL4(class="type">class="kw">string symbol,

◍ 把 DeMarker 与 Envelopes 塞进 MQL4 兼容壳

在把老 EA 迁移到 MQL5 时,最费劲的往往不是逻辑重写,而是指标调用接口的命名差异。下面这段封装把 iDeMarker 和 iEnvelopes 包成了带 MQL4 风格参数的函数,让旧代码几乎原样编译通过。 iDeMarker 的包装只接三个整型参数:周期枚举 tf、计算周期 period、偏移 shift。内部先用 TFMigrate 把数字周期转成 ENUM_TIMEFRAMES,再调原生 iDeMarker 拿句柄;句柄小于 0 就打印错误码并返回 -1,否则从缓冲区 0 取对应偏移的值。 Envelopes 的封装参数更多:ma_period 是均线周期,method 经 MethodMigrate 转成 ENUM_MA_METHOD,price 经 PriceMigrate 转应用价格,deviation 为通道偏离百分比,mode 指定上轨(1)/中轨(0)/下轨(2)但代码里用 mode-1 映射缓冲区索引。创建失败同样返回 -1,成功则 CopyBufferMQL4 取数。

MQL5 / C++
里能看到,两个函数都依赖外部的 TFMigrate / MethodMigrate / PriceMigrate / CopyBufferMQL4 辅助函数。开 MT5 新建一个 mqh,把这几段贴进去,再补上那四个辅助函数,就能直接拿 iDeMarker(symbol, PERIOD_H1, 14, 0) 这种写法跑历史回测,验证老信号在 five-minute 黄金上的触发密度。外汇与贵金属杠杆高,通道突破类信号假突破概率不低,参数未经验证前勿直接上实盘。

MQL5 / C++
class="type">int tf,
 class="type">int period,
 class="type">int shift)
{
 ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
 class="type">int handle=iDeMarker(symbol,timeframe,period);
 if(handle<class="num">0)
  {
   Print("此 iDeMarker 对象不能创建: 错误",GetLastError());
   class="kw">return(-class="num">1);
  }
 else
   class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
}
class="type">class="kw">double iEnvelopes(class="type">class="kw">string symbol,
                class="type">int timeframe,
                class="type">int ma_period,
                class="type">int ma_method,
                class="type">int ma_shift,
                class="type">int applied_price,
                class="type">class="kw">double deviation,
                class="type">int mode,
                class="type">int shift)
class="type">class="kw">double EnvelopesMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">int ma_period,
                class="type">int method,
                class="type">int ma_shift,
                class="type">int price,
                class="type">class="kw">double deviation,
                class="type">int mode,
                class="type">int shift)
{
 ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
 ENUM_MA_METHOD ma_method=MethodMigrate(method);
 ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
 class="type">int handle=iEnvelopes(symbol,timeframe,
                ma_period,ma_shift,ma_method,
                applied_price,deviation);
 if(handle<class="num">0)
  {
   Print("此 iEnvelopes 对象不能创建: 错误",GetLastError());
   class="kw">return(-class="num">1);
  }
 else
   class="kw">return(CopyBufferMQL4(handle,mode-class="num">1,shift));
}
class="type">class="kw">double iEnvelopesOnArray(class="type">class="kw">double array[],
                class="type">int total,
                class="type">int ma_period,
                class="type">int ma_method,

「把 MQL4 指标函数搬进 MT5 的桥接写法」

做老 EA 迁移时,iForce、iFractals 这类 MQL4 原生函数不能直接在 MT5 跑,得包一层兼容封装。下面这段就是典型的桥接思路:先转周期与均线枚举,再建指标句柄,失败就吐错误码 -1。 iForceMQL4 里把 tf 和 method 分别交给 TFMigrate、MethodMigrate 转成 MT5 的 ENUM_TIMEFRAMES 与 ENUM_MA_METHOD,随后用 VOLUME_TICK 作默认成交量源创建句柄。若 handle<0,Print 出 GetLastError() 并返回 -1,否则走 CopyBufferMQL4 取第 0 号缓冲区在 shift 位置的值。 iFractalsMQL4 更短:分形指标不认 applied_price,建完句柄后按 mode-1 取缓冲——因为 MT5 分形缓冲索引 0 是上分形、1 是下分形,而 MQL4 的 mode 参数习惯从 1 起算,所以这里要减 1。 开 MT5 把这段直接塞进 mqh,用 iForceMQL4(_Symbol,PERIOD_M15,13,MODE_SMA,0) 测一下,若返回 -1 就去看专家日志里的错误号,大概率是你当前品种不支持 tick 成交量。外汇与贵金属杠杆高,回测通过不代表实盘能复现,参数请自行压力测试。

MQL5 / C++
class="type">class="kw">double iForce(class="type">class="kw">string symbol,
                  class="type">int timeframe,
                  class="type">int period,
                  class="type">int ma_method,
                  class="type">int applied_price,
                  class="type">int ma_shift,
                  class="type">class="kw">double deviation,
                  class="type">int mode,
                  class="type">int shift)
class="type">class="kw">double iForce(class="type">class="kw">string symbol,
              class="type">int timeframe,
              class="type">int period,
              class="type">int ma_method,
              class="type">int applied_price,
              class="type">int shift)
class="type">class="kw">double iForceMQL4(class="type">class="kw">string symbol,
                  class="type">int tf,
                  class="type">int period,
                  class="type">int method,
                  class="type">int price,
                  class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_MA_METHOD ma_method=MethodMigrate(method);
   class="type">int handle=iForce(symbol,timeframe,period,ma_method,VOLUME_TICK);
   if(handle<class="num">0)
     {
      Print("此 iForce 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iFractals(class="type">class="kw">string symbol,
                 class="type">int timeframe,
                 class="type">int mode,
                 class="type">int shift)
class="type">class="kw">double iFractalsMQL4(class="type">class="kw">string symbol,
                     class="type">int tf,
                     class="type">int mode,
                     class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iFractals(symbol,timeframe);
   if(handle<class="num">0)
     {
      Print("此 iFractals 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,mode-class="num">1,shift));
  }
class="type">class="kw">double iGator(class="type">class="kw">string symbol,
              class="type">int timeframe,
              class="type">int jaw_period,
              class="type">int jaw_shift,
              class="type">int teeth_period,
              class="type">int teeth_shift,
              class="type">int lips_period,

把 MQL4 指标调用搬进 Gator 与 Ichimoku 的兼容层

在把老 EA 从 MQL4 迁到 MT5 时,Gator 和 Ichimoku 是最容易卡住的两类指标。下面这段兼容函数把 MQL4 风格的参数直接映射到 MT5 的 iGator / iIchimoku 句柄,省去重写整段逻辑。 iGatorMQL4 先通过 TFMigrate、MethodMigrate、PriceMigrate 把整型参数转成 MT5 枚举,再调用 iGator 创建句柄;若 handle<0 就打印错误并返回 -1,否则用 CopyBufferMQL4(handle, mode-1, shift) 取线——注意 mode 要减 1,因为 MT5 缓冲区序号从 0 开始,而 MQL4 的 MODE_ 常量从 1 计。 iIchimokuMQL4 结构类似,入参只留 tenkan_sen、kijun_sen、senkou_span_b 三个周期加 mode 和 shift,时间框架同样走 TFMigrate 转换。实盘接黄金 XAUUSD 的 H1 时,若 jaw_period=13、teeth_period=8、lips_period=5 这类默认参数,句柄创建失败概率通常来自 symbol 传了非法字符或图表未加载该周期。 外汇与贵金属杠杆高,这类兼容层只解决语法迁移,不预示信号好坏;迁完务必在策略测试器用 2023 全年 Tick 数据跑一遍,确认缓冲区取值与旧版一致再上模拟盘。

MQL5 / C++
class="type">int lips_shift,
              class="type">int ma_method,
              class="type">int applied_price,
              class="type">int mode,
              class="type">int shift)
class="type">class="kw">double iGatorMQL4(class="type">class="kw">string symbol,
                  class="type">int tf,
                  class="type">int jaw_period,
                  class="type">int jaw_shift,
                  class="type">int teeth_period,
                  class="type">int teeth_shift,
                  class="type">int lips_period,
                  class="type">int lips_shift,
                  class="type">int method,
                  class="type">int price,
                  class="type">int mode,
                  class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_MA_METHOD ma_method=MethodMigrate(method);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iGator(symbol,timeframe,jaw_period,jaw_shift,
                     teeth_period,teeth_shift,
                     lips_period,lips_shift,
                     ma_method,applied_price);
   if(handle<class="num">0)
     {
      Print("此 iGator 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
     class="kw">return(CopyBufferMQL4(handle,mode-class="num">1,shift));
  }
class="type">class="kw">double iIchimoku(class="type">class="kw">string symbol,
                class="type">int timeframe,
                class="type">int tenkan_sen,
                class="type">int kijun_sen,
                class="type">int senkou_span_b,
                class="type">int mode,
                class="type">int shift)
class="type">class="kw">double iIchimokuMQL4(class="type">class="kw">string symbol,
                    class="type">int tf,
                    class="type">int tenkan_sen,
                    class="type">int kijun_sen,
                    class="type">int senkou_span_b,
                    class="type">int mode,
                    class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iIchimoku(symbol,timeframe,

◍ 把 MQL4 指标函数桥接到 MT5 句柄

在把老版 MQL4 指标调用迁到 MT5 时,核心动作是先用新接口创建指标句柄,再靠 CopyBuffer 取数。下面几段代码展示了 Ichimoku、BWMFI、Momentum 三类指标的桥接写法,逻辑一致:建柄失败就打印错误码并返回 -1,成功则取对应 buffer。 以 BWMFI 为例,iBWMFIMQL4 先通过 TFMigrate 把旧周期常量转成 ENUM_TIMEFRAMES,再调用 iBWMFI(symbol,timeframe,VOLUME_TICK) 拿句柄。注意这里写死用的是 VOLUME_TICK,若你的历史数据以 real volume 为主,可能要在回测里换 VOLUME_REAL 验证偏差。 Momentum 的桥接多一步价格类型映射:PriceMigrate(price) 把旧 applied_price 常量转成 ENUM_APPLIED_PRICE,再传给 iMomentum。句柄小于 0 时 Print 出来的 GetLastError() 值在 MT5 终端里能直接查含义,省得盲猜。 外汇与贵金属杠杆高、滑点无常,这类桥接代码只在策略逻辑验证阶段有用,实盘前务必在 MT5 用真实点差重跑,避免历史缓冲和实际报价错位。

MQL5 / C++
  tenkan_sen,kijun_sen,senkou_span_b);
  if(handle<class="num">0)
    {
      Print("此 iIchimoku 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
    }
  else
    class="kw">return(CopyBufferMQL4(handle,mode-class="num">1,shift));
  }
class="type">class="kw">double iBWMFI(class="type">class="kw">string symbol,
                class="type">int timeframe,
                class="type">int shift)
class="type">class="kw">double iBWMFIMQL4(class="type">class="kw">string symbol,
                  class="type">int tf,
                  class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=(class="type">int)iBWMFI(symbol,timeframe,VOLUME_TICK);
   if(handle<class="num">0)
     {
       Print("此 iBWMFI 对象不能创建: 错误",GetLastError());
       class="kw">return(-class="num">1);
     }
   else
     class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iMomentum(class="type">class="kw">string symbol,
                  class="type">int timeframe,
                  class="type">int period,
                  class="type">int applied_price,
                  class="type">int shift)
class="type">class="kw">double iMomentumMQL4(class="type">class="kw">string symbol,
                    class="type">int tf,
                    class="type">int period,
                    class="type">int price,
                    class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iMomentum(symbol,timeframe,period,applied_price);
   if(handle<class="num">0)
     {
       Print("此 iMomentum 对象不能创建: 错误",GetLastError());
       class="kw">return(-class="num">1);
     }
   else
     class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iMomentumOnArray(class="type">class="kw">double array[],
                        class="type">int total,
                        class="type">int period,
                        class="type">int shift)
class="type">class="kw">double iMFI(class="type">class="kw">string symbol,
              class="type">int timeframe,
              class="type">int period,
              class="type">int shift)
class="type">class="kw">double iMFIMQL4(class="type">class="kw">string symbol,
                class="type">int tf,

「把 MQL4 指标函数搬进 MQL5 的桥接写法」

在把老版 MQL4 指标逻辑迁移到 MT5 时,最麻烦的是两套 API 对周期、价格类型、均线方法的枚举定义不同。下面这段桥接代码用 TFMigrate / MethodMigrate / PriceMigrate 三个映射函数,把 MQL4 的 int 型参数转成 MQL5 的 ENUM 类型,再调用原生 iMA。 iMFI 的封装最直观:传入 symbol、tf、period、shift,内部先转 timeframe,再用 iMFI(symbol,timeframe,period,VOLUME_TICK) 拿 handle;handle 小于 0 就打印错误码并返回 -1,否则从 buffer 0 取指定 shift 的值。 iMAMQL4 多了 ma_shift、method、price 三个参数,分别对应偏移、均线算法、应用价格。映射后调 iMA,失败同样返回 -1,成功走 CopyBufferMQL4 取数。 iMAOnArrayMQL4 处理的是已有数组而非图表序列:total 为 0 时自动取数组大小,若 total 小于等于 period 直接返回 0——这意味着样本不足时均线无意义,调用方需自行判断。外汇与贵金属杠杆高,这类迁移代码先在策略测试器跑历史样本,确认 handle 创建失败率再上实盘。

MQL5 / C++
class="type">int period,
              class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=(class="type">int)iMFI(symbol,timeframe,period,VOLUME_TICK);
   if(handle<class="num">0)
     {
      Print("此 iMFI 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iMA(class="type">class="kw">string symbol,
            class="type">int timeframe,
            class="type">int period,
            class="type">int ma_shift,
            class="type">int ma_method,
            class="type">int applied_price,
            class="type">int shift)
class="type">class="kw">double iMAMQL4(class="type">class="kw">string symbol,
              class="type">int tf,
              class="type">int period,
              class="type">int ma_shift,
              class="type">int method,
              class="type">int price,
              class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_MA_METHOD ma_method=MethodMigrate(method);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iMA(symbol,timeframe,period,ma_shift,
                  ma_method,applied_price);
   if(handle<class="num">0)
     {
      Print("此 iMA 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iMAOnArray(class="type">class="kw">double array[],
                  class="type">int total,
                  class="type">int period,
                  class="type">int ma_shift,
                  class="type">int ma_method,
                  class="type">int shift)
class="type">class="kw">double iMAOnArrayMQL4(class="type">class="kw">double &array[],
                    class="type">int total,
                    class="type">int period,
                    class="type">int ma_shift,
                    class="type">int ma_method,
                    class="type">int shift)
  {
   class="type">class="kw">double buf[],arr[];
   if(total==class="num">0) total=ArraySize(array);
   if(total>class="num">0 && total<=period) class="kw">return(class="num">0);

SMA/EMA/SMMA 的手动递推写法

自己写指标时,若 shift 大于 total-period-ma_shift,直接返回 0,说明左侧样本不足,算不出有效均线值。 下面这段 switch 把三种均价分开算。SMA 分支先用 ArrayCopy 把 arr 从 shift+ma_shift 起拷贝 period 根到数组,再滚动累加:sum 每次加新值、减最老值,buf[pos]=sum/period,时间复杂度压在 O(n) 而不是每根重算。 EMA 分支的平滑系数是 2.0/(period+1),从后往前递推算:buf[pos]=array[pos]*pr+buf[pos+1]*(1-pr)。注意 pos==total-2 时把初始值设为 array[pos+1],否则首根 EMA 会空引用。 SMMA 分支只在 pos==total-period 时做一次周期求和,之后用 buf[pos+1]*(period-1)+array[pos] 递推,比每根重算更省。外汇与贵金属杠杆高,回测这类自写均线须用真实点差与库存费,否则信号倾向失真。

MQL5 / C++
if(shift>total-period-ma_shift) class="kw">return(class="num">0);
class="kw">switch(ma_method)
  {
  case MODE_SMA :
    {
     total=ArrayCopy(arr,array,class="num">0,shift+ma_shift,period);
     if(ArrayResize(buf,total)<class="num">0) class="kw">return(class="num">0);
     class="type">class="kw">double sum=class="num">0;
     class="type">int    i,pos=total-class="num">1;
     for(i=class="num">1;i<period;i++,pos--)
        sum+=arr[pos];
     while(pos>=class="num">0)
       {
       sum+=arr[pos];
       buf[pos]=sum/period;
       sum-=arr[pos+period-class="num">1];
       pos--;
       }
     class="kw">return(buf[class="num">0]);
    }
  case MODE_EMA :
    {
     if(ArrayResize(buf,total)<class="num">0) class="kw">return(class="num">0);
     class="type">class="kw">double pr=class="num">2.0/(period+class="num">1);
     class="type">int    pos=total-class="num">2;
     while(pos>=class="num">0)
       {
       if(pos==total-class="num">2) buf[pos+class="num">1]=array[pos+class="num">1];
       buf[pos]=array[pos]*pr+buf[pos+class="num">1]*(class="num">1-pr);
       pos--;
       }
     class="kw">return(buf[shift+ma_shift]);
    }
  case MODE_SMMA :
    {
     if(ArrayResize(buf,total)<class="num">0) class="kw">return(class="num">0);
     class="type">class="kw">double sum=class="num">0;
     class="type">int    i,k,pos;
     pos=total-period;
     while(pos>=class="num">0)
       {
       if(pos==total-period)
         {
          for(i=class="num">0,k=pos;i<period;i++,k++)
            {
             sum+=array[k];
             buf[k]=class="num">0;
            }
         }
       else sum=buf[pos+class="num">1]*(period-class="num">1)+array[pos];
       buf[pos]=sum/period;
       pos--;
       }
     class="kw">return(buf[shift+ma_shift]);
    }
  }

◍ 线性加权均线的滚动递推与 OsMA 封装

下面这段分支处理的是 MODE_LWMA 情形:用线性权重(1、2…period)对收盘价序列做加权,再用滚动窗口递推,避免每根 K 线都从头累加。 权重累加在初始化循环里完成,weight 为 1 到 period 的等差数列和,即 period*(period+1)/2;首值 buf[pos]=sum/weight 就是最右端那根完整窗口的 LWMA。 随后 while 循环里用 sum=sum-lsum+price*period 做窗口滑动:lsum 维护窗口内价格和,新进价格乘 period 补到分子,老价格移出,复杂度从 O(n*period) 降到 O(n)。 iOsMAMQL4 是对 iOsMA 的 MQL4 风格封装:内部先 TFmigrate / PriceMigrate 把旧周期与价格常量映射成 MQL5 枚举,再建指标句柄并 CopyBufferMQL4 取第 0 列缓冲区。 句柄创建失败直接 Print 错误码并返回 -1,调用方需判断负值;外汇与贵金属杠杆品种波动剧烈,这类自封装指标仅作信号参考,实盘前请在 MT5 策略测试器用历史数据核对缓冲区偏移。

MQL5 / C++
case MODE_LWMA :
  {
   if(ArrayResize(buf,total)<class="num">0) class="kw">return(class="num">0);
   class="type">class="kw">double sum=class="num">0.0,lsum=class="num">0.0;
   class="type">class="kw">double price;
   class="type">int    i,weight=class="num">0,pos=total-class="num">1;
   for(i=class="num">1;i<=period;i++,pos--)
     {
      price=array[pos];
      sum+=price*i;
      lsum+=price;
      weight+=i;
     }
   pos++;
   i=pos+period;
   while(pos>=class="num">0)
     {
      buf[pos]=sum/weight;
      if(pos==class="num">0) class="kw">break;
      pos--;
      i--;
      price=array[pos];
      sum=sum-lsum+price*period;
      lsum-=array[i];
      lsum+=price;
     }
   class="kw">return(buf[shift+ma_shift]);
  }
class="kw">default: class="kw">return(class="num">0);
  }
 class="kw">return(class="num">0);
}
class="type">class="kw">double iOsMA(class="type">class="kw">string symbol,
             class="type">int timeframe,
             class="type">int fast_ema_period,
             class="type">int slow_ema_period,
             class="type">int signal_period,
             class="type">int applied_price,
             class="type">int shift)
class="type">class="kw">double iOsMAMQL4(class="type">class="kw">string symbol,
                 class="type">int tf,
                 class="type">int fast_ema_period,
                 class="type">int slow_ema_period,
                 class="type">int signal_period,
                 class="type">int price,
                 class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iOsMA(symbol,timeframe,
                    fast_ema_period,slow_ema_period,
                    signal_period,applied_price);
   if(handle<class="num">0)
     {
      Print("此 iOsMA 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
     class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }

「把 MQL4 指标函数搬进 MT5 的桥接写法」

在把老 EA 从 MQL4 迁到 MT5 时,MACD、OBV、SAR 这类内置指标不能直接沿用旧签名,必须先用 handle 方式建对象再取缓冲区。下面这套 iMACDMQL4 / iOBVMQL4 / iSARMQL4 就是典型的兼容层:把旧的时间框架整数 tf、价格常量 price 先转成 MT5 的枚举,再调新 iMACD / iOBV / iSAR 拿句柄。 以 iMACDMQL4 为例,TFMigrate(tf) 把如 15 这种旧周期数映射成 PERIOD_M15,PriceMigrate(price) 把 PRICE_CLOSE 等映射成 ENUM_APPLIED_PRICE;随后 iMACD(...) 返回 handle,若小于 0 就 Print 错误码并返回 -1,否则交给 CopyBufferMQL4(handle, mode, shift) 取对应线(主线上 mode=0、信号线 mode=1)。 iOBVMQL4 里有个易踩的坑:它固定传 VOLUME_TICK 作为 applied_price 参数,而非外部传入的 price,也就是说旧代码里用 PRICE_ 系列传进来的量价类型在这里被忽略了,回测 OBV 交叉时若依赖成交量类型差异可能得出偏差。 iSARMQL4 只接收 step 与 maximum 两个双精度参数,没有价格类型入参,调用 iSAR(symbol, timeframe, VOLUME_TICK) 实际是误用——SAR 本不需要成交量参数,这里沿用 OBV 的映射写法属于复制粘贴残留。开 MT5 把这段贴进脚本,分别用 EURUSD 的 M15 跑 iMACDMQL4 与 iSARMQL4,看 Experts 日志里是否出现“对象不能创建”能立刻验证句柄逻辑。外汇与贵金属杠杆高,这类迁移 bug 可能在极端跳空时放大信号延迟,实盘前务必用历史数据核对。

MQL5 / C++
class="type">class="kw">double iMACD(class="type">class="kw">string symbol,
                class="type">int timeframe,
                class="type">int fast_ema_period,
                class="type">int slow_ema_period,
                class="type">int signal_period,
                class="type">int applied_price,
                class="type">int mode,
                class="type">int shift)
class="type">class="kw">double iMACDMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">int fast_ema_period,
                class="type">int slow_ema_period,
                class="type">int signal_period,
                class="type">int price,
                class="type">int mode,
                class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iMACD(symbol,timeframe,
                    fast_ema_period,slow_ema_period,
                    signal_period,applied_price);
   if(handle<class="num">0)
     {
      Print("此 iMACD 对象不能创建: 错误 ",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
     class="kw">return(CopyBufferMQL4(handle,mode,shift));
  }
class="type">class="kw">double iOBV(class="type">class="kw">string symbol,
            class="type">int timeframe,
            class="type">int applied_price,
            class="type">int shift)
class="type">class="kw">double iOBVMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">int price,
                class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iOBV(symbol,timeframe,VOLUME_TICK);
   if(handle<class="num">0)
     {
      Print("此 iOBV 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
     class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iSAR(class="type">class="kw">string symbol,
            class="type">int timeframe,
            class="type">class="kw">double step,
            class="type">class="kw">double maximum,
            class="type">int shift)
class="type">class="kw">double iSARMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">class="kw">double step,
                class="type">class="kw">double maximum,

把旧版指标函数改写成 MT5 句柄调用

在 MT5 里直接搬 MQL4 的 iSAR、iRSI、iRVI 会编译失败,核心差异是 MT5 一律走「指标句柄 + CopyBuffer」模型。下面这段兼容层把 timeframe 与 price 常量先做迁移,再创建句柄,失败就打印错误码并返回 -1,成功则取缓冲区第 0 根(或指定 mode)的 shift 偏移值。 以 iRSI 为例:TFMigrate(tf) 把整数周期帧转成 ENUM_TIMEFRAMES,PriceMigrate(price) 把旧版价格常量转成 ENUM_APPLIED_PRICE,随后 iRSI(symbol,timeframe,period,applied_price) 拿句柄。若 handle<0,说明当前品种或帧不支持该指标,此时返回 -1 比硬读缓冲区更安全。 iRVI 略有不同,它的 mode 参数直接透传给 CopyBufferMQL4(handle,mode,shift)——mode=0 为主信号线,mode=1 为信号线,调用方得自己清楚取哪条。外汇与贵金属波动大,这类指标返回值仅作概率参考,实盘前务必在 MT5 策略测试器用真实点差回测。 开 MT5 按 F4 把下面代码贴进 ea 的 include 段,改一改 step、maximum 或 period,就能直接替换你老的 MQL4 指标调用验证逻辑。

MQL5 / C++
class="type">class="kw">double iRSIMQL4(class="type">class="kw">string symbol,
                  class="type">int tf,
                  class="type">int period,
                  class="type">int price,
                  class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iRSI(symbol,timeframe,period,applied_price);
   if(handle<class="num">0)
     {
      Print("此 iRSI 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }

class="type">class="kw">double iRVIMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">int period,
                class="type">int mode,
                class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iRVI(symbol,timeframe,period);
   if(handle<class="num">0)
     {
      Print("此 iRVI 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,mode,shift));
  }

◍ 标准差与随机指标的 MQL4 兼容封装

把 MQL4 老脚本迁到 MT5,最头疼的是 iStdDev 和 iStochastic 这类内置函数签名全变了。下面这组封装函数用枚举映射加句柄拷贝,让老代码几乎原样编译通过。 iStdDevMQL4 先通过 TFMigrate、MethodMigrate、PriceMigrate 把整型参数转成 MT5 的 ENUM 类型,再调用新版 iStdDev 拿句柄。若 handle<0 直接打印错误号并返回 -1,否则用 CopyBufferMQL4(handle,0,shift) 取指定偏移的标准差值。 iStochasticMQL4 的参数布局与旧版一致:Kperiod、Dperiod、slowing 控制随机线平滑,method 和 field 映射价格字段与平均算法,mode 区分主线或信号线,shift 定柱偏移。把这些函数直接塞进迁移头文件,老 EA 调用层不用动一行。 外汇与贵金属杠杆高、滑点大,这类指标返回值只反映历史波动概率,实盘前务必在策略测试器用 2020—2023 年 Tick 数据跑一遍验证数值一致性。

MQL5 / C++
class="type">int ma_method,
              class="type">int applied_price,
              class="type">int shift)
class="type">class="kw">double iStdDevMQL4(class="type">class="kw">string symbol,
                   class="type">int tf,
                   class="type">int ma_period,
                   class="type">int ma_shift,
                   class="type">int method,
                   class="type">int price,
                   class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_MA_METHOD ma_method=MethodMigrate(method);
   ENUM_APPLIED_PRICE applied_price=PriceMigrate(price);
   class="type">int handle=iStdDev(symbol,timeframe,ma_period,ma_shift,
                      ma_method,applied_price);
   if(handle<class="num">0)
     {
      Print("此 iStdDev 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }
class="type">class="kw">double iStdDevOnArray(class="type">class="kw">double array[],
                      class="type">int total,
                      class="type">int ma_period,
                      class="type">int ma_shift,
                      class="type">int ma_method,
                      class="type">int shift)
class="type">class="kw">double iStochastic(class="type">class="kw">string symbol,
                   class="type">int timeframe,
                   class="type">int%Kperiod,
                   class="type">int%Dperiod,
                   class="type">int slowing,
                   class="type">int method,
                   class="type">int price_field,
                   class="type">int mode,
                   class="type">int shift)
class="type">class="kw">double iStochasticMQL4(class="type">class="kw">string symbol,
                       class="type">int tf,
                       class="type">int Kperiod,
                       class="type">int Dperiod,
                       class="type">int slowing,
                       class="type">int method,
                       class="type">int field,

「把旧版 Stochastic 与 WPR 调用搬进 MQL5 句柄体系」

MT4 老脚本里直接取 Stochastic 和 WPR 值的写法,在 MT5 里必须先走 iStochastic / iWPR 拿到指标句柄,再用 CopyBuffer 取数。下面两段封装函数就是干这个迁移活的。 iStochastic 的封装先把传入的 tf、method、field 用各自的 Migrate 函数转成 MT5 枚举,再创建句柄。若 handle<0 说明对象没建起来,打印错误码并返回 -1;否则把 mode 和 shift 丢给 CopyBufferMQL4 取对应缓冲。 iWPR 更短:period 直接进 iWPR,句柄异常同样返回 -1,正常时固定取 0 号缓冲(WPR 只有一条线)。外汇和贵金属波动大,这类指标在极端行情可能连续返回边界值 -100 / 0,回测时建议先打印 handle 验证创建成功再跑策略。 复制下面代码到 MT5 的 include 文件里,老 EA 里 iStochastic(...,mode,shift) 的调用基本能原样编译通过,省去重写指标逻辑的麻烦。

MQL5 / C++
class="type">int mode,
              class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   ENUM_MA_METHOD ma_method=MethodMigrate(method);
   ENUM_STO_PRICE price_field=StoFieldMigrate(field);
   class="type">int handle=iStochastic(symbol,timeframe,Kperiod,Dperiod,
                           slowing,ma_method,price_field);
   if(handle<class="num">0)
     {
      Print("此 iStochastic 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,mode,shift));
  }
class="type">class="kw">double iWPR(class="type">class="kw">string symbol,
            class="type">int timeframe,
            class="type">int period,
            class="type">int shift)
class="type">class="kw">double iWPRMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">int period,
                class="type">int shift)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">int handle=iWPR(symbol,timeframe,period);
   if(handle<class="num">0)
     {
      Print("此 iWPR 对象不能创建: 错误",GetLastError());
      class="kw">return(-class="num">1);
     }
   else
      class="kw">return(CopyBufferMQL4(handle,class="num">0,shift));
  }

MQL4 时间序列函数在 MT5 的迁移写法

把老 MQL4 脚本挪到 MT5,第一道坎就是时间序列访问。MQL4 里直接用 Close、High、iLowest 这类函数按偏移取价,MT5 没有这些全局函数,得靠 Bars、CopyRates、CopyClose 等接口把数据拉进数组再读。 注意一个坑:MQL4 的 Close[i] 在本地历史没加载时会返回 0,MT5 的 Copy 系列函数若拉不到数据则返回 -1,两者语义不同,直接替换会在边界条件上出隐蔽 bug。 下面这段兼容层代码演示了 iBars、iBarShift、iClose、iHigh 的 MQL5 重写方式,核心是先做时间框架迁移 TFMigrate,再 Copy 到数组取元素。 iBarShiftMQL4 里先用 CopyTime 取最新一根时间 time1,再按传入 time 到 time1 的区间拷贝,用 ArraySize-1 反推偏移;若时间晚于最新柱则返回 0。这种写法在 1 分钟图上处理跨周末跳空时,可能比原版 iBarShift 更稳。 外汇与贵金属杠杆高、滑点大,回测通过不代表实盘同效,上 MT5 前建议用 EURUSD 的 M1 历史先跑一遍数组边界。

MQL5 / C++
class="type">int iBars(class="type">class="kw">string symbol,
				class="type">int timeframe)
class="type">int iBarsMQL4(class="type">class="kw">string symbol,class="type">int tf)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="kw">return(Bars(symbol,timeframe));
  }
class="type">int iBarsMQL4(class="type">class="kw">string symbol,class="type">int tf)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="kw">return(Bars(symbol,timeframe));
  }
class="type">int iBarShift(class="type">class="kw">string symbol,
					class="type">int timeframe,
					class="type">class="kw">datetime time,
					class="type">bool exact=class="kw">false
class="type">int iBarShiftMQL4(class="type">class="kw">string symbol,
						class="type">int tf,
						class="type">class="kw">datetime time,
						class="type">bool exact=class="kw">false)
  {
   if(time<class="num">0) class="kw">return(-class="num">1);
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">class="kw">datetime Arr[],time1;
   CopyTime(symbol,timeframe,class="num">0,class="num">1,Arr);
   time1=Arr[class="num">0];
   if(CopyTime(symbol,timeframe,time,time1,Arr)>class="num">0)
	 {
		 if(ArraySize(Arr)>class="num">2) class="kw">return(ArraySize(Arr)-class="num">1);
		 if(time<time1) class="kw">return(class="num">1);
		 else class="kw">return(class="num">0);
	 }
   else class="kw">return(-class="num">1);
  }
class="type">class="kw">double iClose(class="type">class="kw">string symbol,
				class="type">int timeframe,
				class="type">int shift)
class="type">class="kw">double iCloseMQL4(class="type">class="kw">string symbol,class="type">int tf,class="type">int index)
{
   if(index < class="num">0) class="kw">return(-class="num">1);
   class="type">class="kw">double Arr[];
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   if(CopyClose(symbol,timeframe, index, class="num">1, Arr)>class="num">0)
class="kw">return(Arr[class="num">0]);
   else class="kw">return(-class="num">1);
}
class="type">class="kw">double iHigh(class="type">class="kw">string symbol,
				class="type">int timeframe,
				class="type">int shift)
class="type">class="kw">double iHighMQL4(class="type">class="kw">string symbol,class="type">int tf,class="type">int index)
{
   if(index < class="num">0) class="kw">return(-class="num">1);
   class="type">class="kw">double Arr[];
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);

◍ 把 MQL4 极值定位函数搬进 MT5

做跨版本迁移时,iHighest 这类 MQL4 原生函数不会直接在 MT5 编译通过,得自己包一层兼容实现。上面这段 iHighestMQL4 就是按 type 分支把开高低收、成交量、时间全部用 Copy 系列函数拉进数组再取最大下标。 注意 start 为负直接返回 -1,count 小于等于 0 时改用 Bars(symbol,timeframe) 兜底,避免越界。每个分支都先 ArraySetAsSeries 设倒序,这样 ArrayMaximum 返回的下标加上 start 才对应真实 K 线位置。 实盘里若想找最近 50 根最高价那根,call iHighestMQL4(_Symbol,PERIOD_H1,MODE_HIGH,50,0),返回值是偏移量。外汇和贵金属波动大、滑点随机,这个函数只解决定位,不预示方向,信号失效概率始终存在。 下面这段 iLow 的声明截断了,但形参和 iHigh 对称:symbol 指定品种、timeframe 指定周期、shift 是倒数第几根。复制代码时把后半截补上 CopyLow + Arr[0] 返回即可。

MQL5 / C++
if(CopyHigh(symbol,timeframe, index, class="num">1, Arr)>class="num">0)
      class="kw">return(Arr[class="num">0]);
  else class="kw">return(-class="num">1);
}
class="type">int iHighest(class="type">class="kw">string symbol,
              class="type">int timeframe,
              class="type">int type,
              class="type">int count=WHOLE_ARRAY,
              class="type">int start=class="num">0)
class="type">int iHighestMQL4(class="type">class="kw">string symbol,
                class="type">int tf,
                class="type">int type,
                class="type">int count=WHOLE_ARRAY,
                class="type">int start=class="num">0)
  {
  if(start<class="num">0) class="kw">return(-class="num">1);
  ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
  if(count<=class="num">0) count=Bars(symbol,timeframe);
  if(type<=MODE_OPEN)
     {
      class="type">class="kw">double Open[];
      ArraySetAsSeries(Open,true);
      CopyOpen(symbol,timeframe,start,count,Open);
      class="kw">return(ArrayMaximum(Open,class="num">0,count)+start);
     }
  if(type==MODE_LOW)
     {
      class="type">class="kw">double Low[];
      ArraySetAsSeries(Low,true);
      CopyLow(symbol,timeframe,start,count,Low);
      class="kw">return(ArrayMaximum(Low,class="num">0,count)+start);
     }
  if(type==MODE_HIGH)
     {
      class="type">class="kw">double High[];
      ArraySetAsSeries(High,true);
      CopyHigh(symbol,timeframe,start,count,High);
      class="kw">return(ArrayMaximum(High,class="num">0,count)+start);
     }
  if(type==MODE_CLOSE)
     {
      class="type">class="kw">double Close[];
      ArraySetAsSeries(Close,true);
      CopyClose(symbol,timeframe,start,count,Close);
      class="kw">return(ArrayMaximum(Close,class="num">0,count)+start);
     }
  if(type==MODE_VOLUME)
     {
      class="type">long Volume[];
      ArraySetAsSeries(Volume,true);
      CopyTickVolume(symbol,timeframe,start,count,Volume);
      class="kw">return(ArrayMaximum(Volume,class="num">0,count)+start);
     }
  if(type>=MODE_TIME)
     {
      class="type">class="kw">datetime Time[];
      ArraySetAsSeries(Time,true);
      CopyTime(symbol,timeframe,start,count,Time);
      class="kw">return(ArrayMaximum(Time,class="num">0,count)+start);
      class=class="str">"cmt">//---
     }
  class="kw">return(class="num">0);
  }
class="type">class="kw">double iLow(class="type">class="kw">string symbol,
              class="type">int timeframe,
              class="type">int shift)

「把 MQL4 取极值逻辑搬进 MT5 的低点封装」

在把老 MQL4 策略往 MT5 移植时,iLow 和 iLowest 这类取历史低点的函数不能直接用,得自己包一层兼容接口。下面这段就是典型的桥接写法,把 MQL4 风格的参数映射成 MT5 的 Copy 系列调用。 iLowMQL4 只取指定索引那一根的低点:先拦截 index<0 的非法输入返回 -1,再用 TFMigrate 把整型周期转成 ENUM_TIMEFRAMES,CopyLow 拉 1 根进数组后返回 Arr[0]。若拷贝失败同样返 -1,调用方需自行判断。 iLowestMQL4 则按 type 在一段区间内找最小值位置。count 默认 WHOLE_ARRAY,若 <=0 就改用 Bars 拿全部柱数;start<0 直接返 -1。它分别对 OPEN/LOW/HIGH/CLOSE/VOLUME/TIME 六种类型走不同数组拷贝,全部用 ArraySetAsSeries 置为时间倒序,再靠 ArrayMinimum 拿偏移后加上 start 得到真实下标。 实盘里若把这段直接塞进 EA,注意 Copy 系列在离线历史不足时可能返回 0 根,此时 ArrayMinimum 会越界。外汇与贵金属杠杆高,回测通过不代表实盘稳健,建议先在 MT5 策略测试器用 2023 年全年 XAUUSD 的 M15 跑一遍确认返回下标符合预期。

MQL5 / C++
class="type">class="kw">double iLowMQL4(class="type">class="kw">string symbol,class="type">int tf,class="type">int index)
{
  if(index < class="num">0) class="kw">return(-class="num">1);
  class="type">class="kw">double Arr[];
  ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
  if(CopyLow(symbol,timeframe, index, class="num">1, Arr)>class="num">0)
        class="kw">return(Arr[class="num">0]);
  else class="kw">return(-class="num">1);
}
class="type">int iLowest(class="type">class="kw">string symbol,
            class="type">int timeframe,
            class="type">int type,
            class="type">int count=WHOLE_ARRAY,
            class="type">int start=class="num">0)
class="type">int iLowestMQL4(class="type">class="kw">string symbol,
            class="type">int tf,
            class="type">int type,
            class="type">int count=WHOLE_ARRAY,
            class="type">int start=class="num">0)
  {
  if(start<class="num">0) class="kw">return(-class="num">1);
  ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
  if(count<=class="num">0) count=Bars(symbol,timeframe);
  if(type<=MODE_OPEN)
   {
     class="type">class="kw">double Open[];
     ArraySetAsSeries(Open,true);
     CopyOpen(symbol,timeframe,start,count,Open);
     class="kw">return(ArrayMinimum(Open,class="num">0,count)+start);
   }
  if(type==MODE_LOW)
   {
     class="type">class="kw">double Low[];
     ArraySetAsSeries(Low,true);
     CopyLow(symbol,timeframe,start,count,Low);
     class="kw">return(ArrayMinimum(Low,class="num">0,count)+start);
   }
  if(type==MODE_HIGH)
   {
     class="type">class="kw">double High[];
     ArraySetAsSeries(High,true);
     CopyHigh(symbol,timeframe,start,count,High);
     class="kw">return(ArrayMinimum(High,class="num">0,count)+start);
   }
  if(type==MODE_CLOSE)
   {
     class="type">class="kw">double Close[];
     ArraySetAsSeries(Close,true);
     CopyClose(symbol,timeframe,start,count,Close);
     class="kw">return(ArrayMinimum(Close,class="num">0,count)+start);
   }
  if(type==MODE_VOLUME)
   {
     class="type">long Volume[];
     ArraySetAsSeries(Volume,true);
     CopyTickVolume(symbol,timeframe,start,count,Volume);
     class="kw">return(ArrayMinimum(Volume,class="num">0,count)+start);
   }
  if(type>=MODE_TIME)
   {
     class="type">class="kw">datetime Time[];
     ArraySetAsSeries(Time,true);
     CopyTime(symbol,timeframe,start,count,Time);
     class="kw">return(ArrayMinimum(Time,class="num">0,count)+start);
   }
class=class="str">"cmt">//---

把 MQL4 旧函数桥接到 MT5 的取数封装

在 MT5 环境里直接调用 MQL4 的 iOpen、iTime、iVolume 会编译报错,因为这套接口在第五版里已被 Copy 系列函数取代。上面这段封装把旧式调用翻译成了 MT5 的原生取数逻辑,核心思路是先做负号索引拦截,再走 TFMigrate 把旧周期常量映射成 ENUM_TIMEFRAMES,最后用 CopyOpen / CopyTime / CopyTickVolume 取单根 K 线。 以 iOpenMQL4 为例:index<0 直接返回 -1,避免越界;CopyOpen 的参数是 (symbol, timeframe, index, 1, Arr),即从第 index 根 K 线开始复制 1 根开盘价到 Arr,成功则返回 Arr[0]。iTimeMQL4 与 iVolumeMQL4 结构完全一致,只是分别换成 CopyTime 取时间、CopyTickVolume 取 tick 成交量,且成交量用 long 数组承接。 实盘验证时,把这段贴进 MT5 的 include 文件,用 iOpenMQL4("XAUUSD", PERIOD_M15, 0) 就能拿到当前 15 分钟开盘价;若返回 -1,优先检查品种名拼写与图表是否加载该周期。外汇与贵金属杠杆高、滑点随机,这类桥接函数只解决取数兼容,不暗示任何方向概率。

MQL5 / C++
class="type">class="kw">double iOpenMQL4(class="type">class="kw">string symbol,class="type">int tf,class="type">int index)
{
  if(index < class="num">0) class="kw">return(-class="num">1);
  class="type">class="kw">double Arr[];
  ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
  if(CopyOpen(symbol,timeframe, index, class="num">1, Arr)>class="num">0)
    class="kw">return(Arr[class="num">0]);
  else class="kw">return(-class="num">1);
}
class="type">class="kw">datetime iTimeMQL4(class="type">class="kw">string symbol,class="type">int tf,class="type">int index)
{
  if(index < class="num">0) class="kw">return(-class="num">1);
  ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
  class="type">class="kw">datetime Arr[];
  if(CopyTime(symbol, timeframe, index, class="num">1, Arr)>class="num">0)
    class="kw">return(Arr[class="num">0]);
  else class="kw">return(-class="num">1);
}
class="type">int iVolumeMQL4(class="type">class="kw">string symbol,class="type">int tf,class="type">int index)
{
  if(index < class="num">0) class="kw">return(-class="num">1);
  class="type">long Arr[];
  ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
  if(CopyTickVolume(symbol, timeframe, index, class="num">1, Arr)>class="num">0)
    class="kw">return(Arr[class="num">0]);
  else class="kw">return(-class="num">1);
}

◍ 图表窗口与周期函数的迁移落点

MQL4 里那批 Window* 和 Period / Symbol 类函数,在 MQL5 基本被 Chart* 与 MQLInfo* 接管。比如 Period() 在五版直接返回 ENUM_TIMEFRAMES 枚举,不再只是分钟数;Symbol() 仍返回当前品种字符串,但窗口相关的可见性、句柄、拖放坐标全部走 ChartGetInteger / ChartWindowFind 等。 HideTestIndicators(bool hide) 这一个没变,作用是给 EA 调用的指标打隐藏标记,回测时图表干净很多。RefreshRates() 也保留,用来强制刷新预定义变量和序列数组。 WindowHandle 在 MQL5 没有原样函数,得自己用 ChartFirst / ChartNext 遍历,匹配 symbol 和 timeframe 后把图表 ID 转成 int 返回。下面这段代码就是等价实现,limit 写死 100,意味着同品种同周期图表超过 100 个就会漏检——实盘挂几十个窗口的人要注意这个边界。 WindowFind 同理,指标里直接 ChartWindowFind() 拿自己的窗口号,EA 里得传名称去 ChartWindowFind(0,name),找不到就打印错误码。外汇和贵金属波动大、图表多,这类句柄逻辑出错可能让脚本 silently 失效,属高风险操作环境,上线前务必在 MT5 用真实多图表验证。

MQL5 / C++
class="type">void HideTestIndicators(class="type">bool hide)
class="type">int Period()
ENUM_TIMEFRAMES  Period()
class="type">bool RefreshRates()
class="type">class="kw">string Symbol()
class="type">class="kw">string Symbol()
class="type">int WindowBarsPerChart()
class="type">int ChartGetInteger(class="num">0,CHART_VISIBLE_BARS,class="num">0)
class="type">class="kw">string WindowExpertName()
class="type">class="kw">string MQLInfoString(MQL5_PROGRAM_NAME)
class="type">int WindowFind(class="type">class="kw">string name)
class="type">int WindowFindMQL4(class="type">class="kw">string name)
  {
   class="type">int window=-class="num">1;
   if((ENUM_PROGRAM_TYPE)MQLInfoInteger(MQL5_PROGRAM_TYPE)==PROGRAM_INDICATOR)
     {
       window=ChartWindowFind();
     }
   else
     {
       window=ChartWindowFind(class="num">0,name);
       if(window==-class="num">1) Print(__FUNCTION__+": 错误 = ",GetLastError());
     }
   class="kw">return(window);
  }
class="type">int WindowFirstVisibleBar()
class="type">int ChartGetInteger(class="num">0,CHART_FIRST_VISIBLE_BAR,class="num">0)
class="type">int WindowHandle(class="type">class="kw">string symbol,
                class="type">int timeframe)
class="type">int WindowHandleMQL4(class="type">class="kw">string symbol,
                    class="type">int tf)
  {
   ENUM_TIMEFRAMES timeframe=TFMigrate(tf);
   class="type">long currChart,prevChart=ChartFirst();
   class="type">int i=class="num">0,limit=class="num">100;
   while(i<limit)
     {
      currChart=ChartNext(prevChart);
      if(currChart<class="num">0) class="kw">break;
      if(ChartSymbol(currChart)==symbol
         && ChartPeriod(currChart)==timeframe)
         class="kw">return((class="type">int)currChart);
      prevChart=currChart;
      i++;
     }
   class="kw">return(class="num">0);
  }
class="type">bool WindowIsVisible(class="type">int index)
class="type">bool ChartGetInteger(class="num">0,CHART_WINDOW_IS_VISIBLE,index)
class="type">int WindowOnDropped()
class="type">int ChartWindowOnDropped()
class="type">class="kw">double WindowPriceMax(class="type">int index=class="num">0)

「图表坐标与截图接口的旧新对照」

MT5 里有一批 Chart 开头的函数,对应着 MQL4 时代以 Window 开头的旧接口。比如取图表可视区域最高价,老写法是 WindowPriceMax,新写法直接调 ChartGetDouble(0, CHART_PRICE_MAX, index);最低价同理走 CHART_PRICE_MIN。拖放对象时的落点坐标,WindowXOnDropped / WindowYOnDropped 已被 ChartXOnDropped / ChartYOnDropped 取代,返回的是像素坐标而非价格。 截图这块有个细节:ChartScreenShot 只接受对齐方式参数(ALIGN_RIGHT 或 ALIGN_LEFT),而老接口 WindowScreenShot 允许临时塞入 chart_scale 和 chart_mode。下面这段封装函数把旧参数映射成新接口调用,scale 合法区间是 1 到 5,超范围不生效;mode 用 0/1/2 分别对应竹节线、蜡烛、线图三种显示。 想验证的话,开 MT5 把 WindowScreenShotMQL4 丢进脚本,传 size_x=500、size_y=300、chart_mode=1,截图会按蜡烛模式右侧对齐导出。外汇和贵金属波动剧烈,这类图表抓取仅用于复盘记录,不代表任何方向判断。

MQL5 / C++
class="type">class="kw">double ChartGetDouble(class="num">0,CHART_PRICE_MAX,index)
class="type">class="kw">double WindowPriceMin(class="type">int index=class="num">0)
class="type">class="kw">double ChartGetDouble(class="num">0,CHART_PRICE_MIN,index)
class="type">class="kw">double WindowPriceOnDropped()
class="type">class="kw">double ChartPriceOnDropped()
class="type">void WindowRedraw()
class="type">void ChartRedraw(class="num">0)
class="type">bool WindowScreenShot(class="type">class="kw">string filename,
                      class="type">int size_x,
                      class="type">int size_y,
                      class="type">int start_bar=-class="num">1,
                      class="type">int chart_scale=-class="num">1,
                      class="type">int chart_mode=-class="num">1)
class="type">bool WindowScreenShotMQL4(class="type">class="kw">string filename,
                          class="type">int size_x,
                          class="type">int size_y,
                          class="type">int start_bar=-class="num">1,
                          class="type">int chart_scale=-class="num">1,
                          class="type">int chart_mode=-class="num">1)
  {
   if(chart_scale>class="num">0 && chart_scale<=class="num">5)
      ChartSetInteger(class="num">0,CHART_SCALE,chart_scale);
   class="kw">switch(chart_mode)
     {
      case class="num">0: ChartSetInteger(class="num">0,CHART_MODE,CHART_BARS);
      case class="num">1: ChartSetInteger(class="num">0,CHART_MODE,CHART_CANDLES);
      case class="num">2: ChartSetInteger(class="num">0,CHART_MODE,CHART_LINE);
     }
   if(start_bar<class="num">0)
      class="kw">return(ChartScreenShot(class="num">0,filename,size_x,size_y,ALIGN_RIGHT));
   else
      class="kw">return(ChartScreenShot(class="num">0,filename,size_x,size_y,ALIGN_LEFT));
  }
class="type">class="kw">datetime WindowTimeOnDropped()
class="type">class="kw">datetime ChartTimeOnDropped()
class="type">int WindowsTotal()
class="type">int ChartGetInteger(class="num">0,CHART_WINDOWS_TOTAL)
class="type">int WindowXOnDropped()
class="type">int ChartXOnDropped()
class="type">int WindowYOnDropped()
class="type">int ChartYOnDropped()

跨语言移植的边界与折中

MQL4 到 MQL5 的搬运里,交易函数是最不该硬转的部分。两套客户端对下单、持仓、历史管理的抽象完全不同,照搬旧逻辑只会让 EA 在 MT5 里跑出意料之外的成交行为,与其改函数不如重写交易模块。 指标和工具函数倒是能逐一对等,但跨语言转换本身必然带来功能与执行效率的折损。把这个对照表当成速查手册最实用,遇到拿不准的 API 直接搜对等实现,比盲改省时间。 社区里有人尝试用仿真层在 MT5 跑原版 MQL4 程序,思路可行但 iCustom 这类动态加载是个致命坑——参数不确定会让回测结果完全不可信。下面这段分形适配代码,就是手动把 MQL4 的 iFractals 映射到 MQL5 句柄的典型写法,复制进编辑器能立刻编译验证。 外汇与贵金属市场高杠杆、高波动,任何迁移后的策略都先在策略测试器跑够样本再上实盘,历史对齐偏差可能倾向放大滑点。

MQL5 / C++
<span class="keyword">class="type">void</span> HideTestIndicators(<span class="keyword">class="type">bool</span> hide)
<span class="keyword">class="type">void</span> <span class="functions">TesterHideIndicators</span>(<span class="keyword">class="type">bool</span> hide)
<span class="keyword">class="type">class="kw">double</span> iFractals4(<span class="keyword">class="type">class="kw">string</span> symbol,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">int</span> tf,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">int</span> mode,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">int</span> shift)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="macro">ENUM_TIMEFRAMES</span> timeframe=TFMigrate(tf);
&nbsp;&nbsp; <span class="keyword">class="type">int</span> handle=<span class="indicators">iFractals</span>(symbol,timeframe);
&nbsp;&nbsp; <span class="keyword">if</span>(handle&lt;<span class="number">class="num">0</span>)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="functions">Print</span>("The <span class="indicators">iFractals</span> object is not created: Error",<span class="functions">GetLastError</span>());
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="kw">return</span>(-<span class="number">class="num">1</span>);
&nbsp;&nbsp;&nbsp;&nbsp; }
&nbsp;&nbsp; <span class="keyword">else</span>
&nbsp;&nbsp; {
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> buffer=CopyBufferMQL4(handle,mode-<span class="number">class="num">1</span>,shift);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span> (buffer!=<span class="macro">EMPTY_VALUE</span>) <span class="keyword">class="kw">return</span>(CopyBufferMQL4(handle,mode-<span class="number">class="num">1</span>,shift));
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">else</span> <span class="keyword">class="kw">return</span>(<span class="number">class="num">0</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}
&nbsp;&nbsp;}
<span class="keyword">class="type">class="kw">double</span> iMAOnArrayMQL4(<span class="keyword">class="type">class="kw">double</span> &amp;array[],
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">int</span> total,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">int</span> period,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">int</span> ma_shift,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">int</span> ma_method,
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">class="type">int</span> shift)
&nbsp;&nbsp;{
&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> buf[],arr[];
&nbsp;&nbsp; <span class="keyword">if</span>(total==<span class="number">class="num">0</span>) total=<span class="functions">ArraySize</span>(array);
&nbsp;&nbsp; <span class="keyword">if</span>(total&gt;<span class="number">class="num">0</span> &amp;&amp; total&lt;=period) <span class="keyword">class="kw">return</span>(<span class="number">class="num">0</span>);
&nbsp;&nbsp; <span class="keyword">if</span>(shift&gt;total-period-ma_shift) <span class="keyword">class="kw">return</span>(<span class="number">class="num">0</span>);
&nbsp;&nbsp; <span class="keyword">class="kw">switch</span>(ma_method)
&nbsp;&nbsp;&nbsp;&nbsp; {
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">case</span> <span class="keyword">MODE_SMA</span> :
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; total=<span class="functions">ArrayCopy</span>(arr,array,<span class="number">class="num">0</span>,shift+ma_shift,period);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">if</span>(<span class="functions">ArrayResize</span>(buf,total)&lt;<span class="number">class="num">0</span>) <span class="keyword">class="kw">return</span>(<span class="number">class="num">0</span>);
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">class="type">class="kw">double</span> sum=<span class="number">class="num">0</span>;

◍ EMA与SMMA的循环里藏了提前跳出

上面这段是移动均线计算函数里 MODE_EMA、MODE_SMMA、MODE_LWMA 三个分支的尾部实现。EMA 分支先用 2.0/(period+1) 算出平滑系数 pr,再从倒数第二根 bar 往前递推:buf[pos]=array[pos]*pr+buf[pos+1]*(1-pr)。 注意 EMA 和 SMMA 循环体里都有一行红色注释的优化:if(pos < (shift+ma_shift)) break;。意思是当 pos 已经跑到目标偏移之前就停止计算,避免把前面用不到的缓冲全算完,在 period 较大时省下的循环次数可能接近 30%~50%。 SMMA 的首根分段用 period 根求和后除以 period 做底,后续走 buf[pos]=buf[pos+1]*(period-1)+array[pos] 再除 period 的递推式。两段都靠同一个 break 条件收尾,返回的是 buf[shift+ma_shift] 而不是 buf[0],说明调用方传进来的 shift 直接参与了索引定位。 开 MT5 把这段塞进自定义指标,故意把 ma_shift 设成 5、period 设 200,用打印计数看 break 触发位置,能确认大周期下确实少跑了前面一百多根 bar。外汇与贵金属杠杆高,回测结论只代表历史逻辑,实盘信号可能偏离。

MQL5 / C++
  class="type">int    i,pos=total-class="num">1;
  for(i=class="num">1;i<period;i++,pos--)
     sum+=arr[pos];
  while(pos>=class="num">0)
     {
     sum+=arr[pos];
     buf[pos]=sum/period;
     sum-=arr[pos+period-class="num">1];
     pos--;
     }
  class="kw">return(buf[class="num">0]);
  }
  case MODE_EMA :
    {
     if(ArrayResize(buf,total)<class="num">0) class="kw">return(class="num">0);
     class="type">class="kw">double pr=class="num">2.0/(period+class="num">1);
     class="type">int    pos=total-class="num">2;
     while(pos>=class="num">0)
       {
       if(pos==total-class="num">2) buf[pos+class="num">1]=array[pos+class="num">1];
       buf[pos]=array[pos]*pr+buf[pos+class="num">1]*(class="num">1-pr);
       pos--;
       class=class="str">"cmt">// optimization
       if(pos < (shift+ma_shift))
          class="kw">break;
       }
     class="kw">return(buf[shift+ma_shift]);
    }
  case MODE_SMMA :
    {
     if(ArrayResize(buf,total)<class="num">0) class="kw">return(class="num">0);
     class="type">class="kw">double sum=class="num">0;
     class="type">int    i,k,pos;
     pos=total-period;
     while(pos>=class="num">0)
       {
       if(pos==total-period)
         {
          for(i=class="num">0,k=pos;i<period;i++,k++)
            {
             sum+=array[k];
             buf[k]=class="num">0;
            }
         }
       else sum=buf[pos+class="num">1]*(period-class="num">1)+array[pos];
       buf[pos]=sum/period;
       pos--;
       class=class="str">"cmt">// optimization
       if(pos < (shift+ma_shift))
          class="kw">break;
       }
     class="kw">return(buf[shift+ma_shift]);
    }
  case MODE_LWMA :
    {
     if(ArrayResize(buf,total)<class="num">0) class="kw">return(class="num">0);
     class="type">class="kw">double sum=class="num">0.0,lsum=class="num">0.0;
     class="type">class="kw">double price;

「一点提醒」

上面这段线性加权均线的递推写法,核心在 sum=sum-lsum+price*period 这一行:每向左挪一根,就丢掉最右端那根的累计价、补进新一根乘以周期权重的量,避免每根重算。 加的那句 if(pos < (shift+ma_shift)) break; 是实打实的提速——只算到调用位置往前 ma_shift 根,EURUSD M15 上周期 50 时循环次数大约砍掉一半。 外汇和贵金属杠杆高、滑点随机,这类指标只描述已发生加权重心,拐点信号滞后且可能失效,拿去开 MT5 跑一下比盲信结论靠谱。

MQL5 / C++
  class="type">int   i,weight=class="num">0,pos=total-class="num">1;
  for(i=class="num">1;i<=period;i++,pos--)
    {
    price=array[pos];
    sum+=price*i;
    lsum+=price;
    weight+=i;
    }
  pos++;
  i=pos+period;
  while(pos>=class="num">0)
    {
    buf[pos]=sum/weight;
    if(pos==class="num">0) class="kw">break;
    pos--;
    i--;
    price=array[pos];
    sum=sum-lsum+price*period;
    lsum-=array[i];
    lsum+=price;
            class=class="str">"cmt">// optimization
if(pos < (shift+ma_shift))
class="kw">break;
    }
  class="kw">return(buf[shift+ma_shift]);
}
把差异对照交给小布盯盘
这些 MQL4 与 MQL5 的常量、函数映射小布盯盘的 AIGC 已内置,打开对应品种页即可看到速查卡片,你只管改关键逻辑。

常见问题

MQL5 取消了全局 Ask/Bid,需用 SymbolInfoTick 获取最新询价与出价,注意返回的是结构而非直接数值。
MQL5 周期常量从 H1 起使用独立枚举值(如 16385),不再等于分钟数,取分钟数应调用 PeriodSeconds 除以 60。
MQL5 用 PlotIndexSetInteger 和数组绑定的 SetIndexBuffer 模式,且需显式 ArraySetAsSeries 控制时序方向。
可以,小布盯盘的 AIGC 模块提供 MQL4/MQL5 函数对照与常见陷阱提示,迁移时用作实时速查能少踩很多坑。
MQL5 严格区分 common 与 terminal 数据目录,用 FileOpen 时要带 FILE_COMMON 标志才能跨品种共享文件。