扩展 MQL5 标准库和重用代码(基础篇)
把标准库当积木用
MQL5 自带的标准库(Standard Library)不是黑盒,而是一堆可继承、可改写的 C++ 风格类。想把别人写好的 CTrade、CIndicator 再包一层,直接 include 后派生子类即可,不必从零写下单逻辑。
- 年 5 月官方发布的这组示例,在站内累计被查看约 3485 次、引发 22 条讨论,说明「扩展而非重写」是社区早就在用的务实路线。外汇与贵金属杠杆高,复用代码前务必在策略测试器用历史数据跑一遍,参数错配可能放大回撤。
实际动手时,新建一个 .mqh 把标准库头文件引进来,只重写你需要的虚函数,比复制整段交易模板更利于后期维护。
「为什么先学会用标准库写代码」
MQL5 标准库本质是一套面向对象的现成类集合,目的是让开发者少造轮子:代码复用、可靠性、灵活性、易维护,这几条是它设计时就承诺的红利。但标准库不可能覆盖所有交易者的个性化需求,当你要做更复杂的工具——比如多货币 EA——如果连标准库的写法都没吃透,自己堆出来的项目很容易在后期崩维护。 原文给出的现实推力很直接:作者本人就是因为要开发复杂多货币 EA,才决定从根基上把项目按标准库理念重构。也就是说,框架思维不是学院派要求,而是复杂度逼出来的生存技能。 一个可验证的落点:打开 MT5 的 MetaEditor,在「文件 → 新建 → 指标」里选「使用标准库模板」,你能直接看到 CSymbolInfo、CIndicator 这类基类是怎么被继承的。先照着抄三遍,比直接手搓裸代码更能摸准这套框架的脉络。外汇与贵金属杠杆高、滑点诡异,复杂 EA 任何架构疏忽都可能放大实盘风险,按标准库规范写至少能把低级坑提前堵住。
◍ 把官方峰谷指标拉进 MT5
在 MT5 终端的 code base 里直接调取 MetaQuotes 发布的峰谷(ZigZag)指标,下载完成后会在 Indicators 目录下生成 zigzag.mq5 与编译好的 zigzag.ex5 两个文件,这是后续所有价格行为改写的基础件。 完整源文件有 298 行,这里只截取输入参数、全局变量与初始化段,方便你对着下文调参时不迷路。ExtDepth=12、ExtDeviation=5、ExtBackstep=3 是出厂默认值,分别对应回看深度、最小偏移点数与回溯步长。 三个缓冲区分工很硬:ZigzagBuffer 画主线,HighMapBuffer 和 LowMapBuffer 只做内部极值计算不显示。OnInit 里用 SetIndexBuffer 绑定索引,PlotIndexSetString 把参数拼进副图标签,便于多周期对照。 外汇与贵金属波动剧烈,ZigZag 仅作结构识别辅助,实际信号滞后可能性较高,切勿单独用作进出场依据。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| ZigZag.mq5 | class=class="str">"cmt">//| Copyright class="num">2009, MetaQuotes Software Corp. | class=class="str">"cmt">//| [MQL5官方文档] | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#class="kw">property copyright "class="num">2009, MetaQuotes Software Corp." class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property indicator_chart_window class="macro">#class="kw">property indicator_buffers class="num">3 class="macro">#class="kw">property indicator_plots class="num">1 class=class="str">"cmt">//---- plot Zigzag class="macro">#class="kw">property indicator_label1 "Zigzag" class="macro">#class="kw">property indicator_type1 DRAW_SECTION class="macro">#class="kw">property indicator_color1 Red class="macro">#class="kw">property indicator_style1 STYLE_SOLID class="macro">#class="kw">property indicator_width1 class="num">1 class=class="str">"cmt">//--- class="kw">input parameters class="kw">input class="type">int ExtDepth=class="num">12; class="kw">input class="type">int ExtDeviation=class="num">5; class="kw">input class="type">int ExtBackstep=class="num">3; class=class="str">"cmt">//--- indicator buffers class="type">class="kw">double ZigzagBuffer[]; class=class="str">"cmt">// main buffer class="type">class="kw">double HighMapBuffer[]; class=class="str">"cmt">// highs class="type">class="kw">double LowMapBuffer[]; class=class="str">"cmt">// lows class="type">int level=class="num">3; class=class="str">"cmt">// recounting depth class="type">class="kw">double deviation; class=class="str">"cmt">// deviation in points class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Custom indicator initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { class=class="str">"cmt">//--- indicator buffers mapping SetIndexBuffer(class="num">0,ZigzagBuffer,INDICATOR_DATA); SetIndexBuffer(class="num">1,HighMapBuffer,INDICATOR_CALCULATIONS); SetIndexBuffer(class="num">2,LowMapBuffer,INDICATOR_CALCULATIONS); class=class="str">"cmt">//--- set class="type">short name and digits PlotIndexSetString(class="num">0,PLOT_LABEL,"ZigZag("+(class="type">class="kw">string)ExtDepth+","+(class="type">class="kw">string)ExtDeviation+","+(class="type">class="kw">string)ExtBackstep+")"); IndicatorSetInteger(INDICATOR_DIGITS,_Digits); class=class="str">"cmt">//--- set empty value
把偏差换算成点数再初始化
指标初始化收尾时,先把空值画刷关掉:PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,0.0) 让第 0 号绘制缓冲在数值为 0.0 时不画任何东西,避免零值被当成有效线。 随后把用户输入的 ExtDeviation(以价格单位计)乘上 _Point,得到真实点数偏差 deviation。例如 EURUSD 的 _Point 是 0.00001,若 ExtDeviation=50,deviation 就是 0.00050,也就是 5 个点。这一步必须在循环前完成,否则后续逐根计算的容差会错位。 最后 return(INIT_SUCCEEDED) 告知终端初始化通过。MT5 里若漏掉这句或返回别的值,自定义指标不会加载到图表。
PlotIndexSetDouble(class="num">0,PLOT_EMPTY_VALUE,class="num">0.0); class=class="str">"cmt">//--- to use in cycle deviation=ExtDeviation*_Point; class=class="str">"cmt">//--- class="kw">return(INIT_SUCCEEDED); }
「从程序式到面向对象:峰谷指标的两种接法」
想把峰谷指标塞进 MQL5 标准库,先用自顶向下思路看全局:系统层面决定交互范式,再下沉到局部实现。中级开发者多半会直接写程序式 EA,用 iCustom 调原生 ZigZag;高级开发者则倾向做成标准库风格的面向对象类,用起来和内置指标无异。 写两个虚拟 EA 最能暴露差异。下面这段程序式版本在 H1 周期、EURUSD 上加载 ZigZag(参数 12/5/3),仅取最近 2 根缓冲做演示,足以让人在 MT5 里跑通验证。 面向对象方案胜在直观:类封装后,与峰谷功能交互比程序式版本轻松得多。OOP 让问题建模变简单,代码复用拉低维护成本、提升可靠性与灵活性;它还能定义 ADT(抽象数据类型),把传统类型概念从具体语言里抽出来。 别把正态当圣经 原文用正二十面体类比「基于立体理念构建应用」来保障设计持久性,这只是隐喻,不是回测结论。外汇与贵金属杠杆高、滑点随机,任何指标封装都只是降低编码熵,不预示胜率。
class=class="str">"cmt">//+----------------------------------------------------------------------+ class=class="str">"cmt">//| ExpertOriginalZigZag.mq5 | class=class="str">"cmt">//| Copyright © class="num">2013, Laplacianlab - Jordi Bassagañas | class=class="str">"cmt">//+----------------------------------------------------------------------+ class=class="str">"cmt">//--- EA properties class="macro">#class="kw">property copyright "Copyright © class="num">2013, Laplacianlab - Jordi Bassagañas" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property description "This dummy Expert Advisor is just for showing how to use the original MetaQuotes&class="macro">#x27; ZigZag indicator." class=class="str">"cmt">//--- EA inputs class="kw">input ENUM_TIMEFRAMES EAPeriod=PERIOD_H1; class="kw">input class="type">class="kw">string CurrencyPair="EURUSD"; class=class="str">"cmt">//--- global variables class="type">int zigZagHandle; class="type">class="kw">double zigZagBuffer[]; class="type">class="kw">double zigZagHigh[]; class="type">class="kw">double zigZagLow[]; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { zigZagHandle=iCustom(CurrencyPair,EAPeriod,"zigzag",class="num">12,class="num">5,class="num">3); ArraySetAsSeries(zigZagBuffer,true); ArraySetAsSeries(zigZagHigh,true); ArraySetAsSeries(zigZagLow,true); class="kw">return(class="num">0); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(class="kw">const class="type">int reason) { IndicatorRelease(zigZagHandle); ArrayFree(zigZagBuffer); ArrayFree(zigZagHigh); ArrayFree(zigZagLow); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert tick function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnTick() { class=class="str">"cmt">//--- refresh data if(CopyBuffer(zigZagHandle,class="num">0,class="num">0,class="num">2,zigZagBuffer)<class="num">0) { Print("Can&class="macro">#x27;t copy ZigZag buffer class="num">0!"); class="kw">return; }
◍ 用面向对象封装把 ZigZag 接进 EA
把经典 ZigZag 指标塞进 EA 时,直接用 iCustom 加 CopyBuffer 容易把缓冲区序号搞混。下面这段先拷缓冲区 1 和 2 分别存高低点,任何一次 CopyBuffer 返回值小于 0 就打印并报错退出,避免后续拿到空数组还硬算。 if(CopyBuffer(zigZagHandle,1,0,2,zigZagHigh)<0) { Print("Can't copy ZigZag buffer 1!"); return; } if(CopyBuffer(zigZagHandle,2,0,2,zigZagLow)<0) { Print("Can't copy ZigZag buffer 2!"); return; } //--- print values if(zigZagBuffer[0]!=0) Print("zigZagBuffer[0]: ", zigZagBuffer[0]); if(zigZagHigh[0]!=0) Print("zigZagHigh[0]: ", zigZagHigh[0]); if(zigZagLow[0]!=0) Print("zigZagLow[0]: ", zigZagLow[0]); } 更干净的做法是走标准库 CiZigZag 封装:OnInit 里 new 一个对象,Create 传品种、周期加三个参数 12、5、3(分别对应回调点数、最小幅度、拐点步长),EA 卸载时 delete 掉即可,不用自己管句柄。 #include <..\Include\Indicators\Custom\Trend.mqh> input ENUM_TIMEFRAMES EAPeriod=PERIOD_H1; input string CurrencyPair="EURUSD"; CiZigZag *ciZigZag; int OnInit() { ciZigZag=new CiZigZag; ciZigZag.Create(CurrencyPair,EAPeriod,12,5,3); return(0); } void OnDeinit(const int reason) { delete(ciZigZag); } 开 MT5 把这段贴进一个新 EA,把 CurrencyPair 改成你常看的 XAUUSD 或 EURUSD,H1 周期下加载,专家日志里就能看到最近两个拐点的高低值;外汇和贵金属杠杆高,ZigZag 只是结构参考,信号失效概率不低,别直接当入场依据。
if(CopyBuffer(zigZagHandle,class="num">1,class="num">0,class="num">2,zigZagHigh)<class="num">0) { Print("Can&class="macro">#x27;t copy ZigZag buffer class="num">1!"); class="kw">return; } if(CopyBuffer(zigZagHandle,class="num">2,class="num">0,class="num">2,zigZagLow)<class="num">0) { Print("Can&class="macro">#x27;t copy ZigZag buffer class="num">2!"); class="kw">return; } class=class="str">"cmt">//--- print values if(zigZagBuffer[class="num">0]!=class="num">0) Print("zigZagBuffer[class="num">0]: ", zigZagBuffer[class="num">0]); if(zigZagHigh[class="num">0]!=class="num">0) Print("zigZagHigh[class="num">0]: ", zigZagHigh[class="num">0]); if(zigZagLow[class="num">0]!=class="num">0) Print("zigZagLow[class="num">0]: ", zigZagLow[class="num">0]); } class="macro">#include <..\Include\Indicators\Custom\Trend.mqh> class="macro">#class="kw">property copyright "Copyright © class="num">2013, Laplacianlab - Jordi Bassagañas" class="macro">#class="kw">property link "[MQL5官方文档] class="macro">#class="kw">property version "class="num">1.00" class="macro">#class="kw">property description "This dummy Expert Advisor is just for showing how to use the object-oriented version of MetaQuotes&class="macro">#x27; ZigZag indicator." class="kw">input ENUM_TIMEFRAMES EAPeriod=PERIOD_H1; class="kw">input class="type">class="kw">string CurrencyPair="EURUSD"; CiZigZag *ciZigZag; class="type">int OnInit() { ciZigZag=new CiZigZag; ciZigZag.Create(CurrencyPair,EAPeriod,class="num">12,class="num">5,class="num">3); class="kw">return(class="num">0); } class="type">void OnDeinit(class="kw">const class="type">int reason) { class="kw">delete(ciZigZag); }