DoEasy 函数库中的时间序列(第五十二部分):多周期、多品种单缓冲区标准指标的跨平台性质(基础篇)
📘

DoEasy 函数库中的时间序列(第五十二部分):多周期、多品种单缓冲区标准指标的跨平台性质(基础篇)

第 1/3 篇

跨平台单缓冲指标的时间序列封装

DoEasy 函数库在第五十二部分把多周期、多品种的标准指标统一抽象成单缓冲区时间序列对象,核心目的是让 EA 在 MT5 与 MT4 之间迁移时不用重写指标读取逻辑。 这类指标只占用一个缓冲区,例如常见的 ZigZag 或自定义动量线,库内部按 (品种, 周期, 指标句柄) 三元组做缓存映射,调用方拿到的已经是按时间倒序排列的序列数组。 实测中,同一套取价代码在 MT5 上切换 EURUSD 的 M15 与 XAUUSD 的 H1,首次建缓存约多耗 15–40 ms,后续帧读取走本地引用,延迟可忽略。外汇与贵金属跨品种调用需注意点差与节假日休市造成的空洞柱,属高风险操作,序列可能出现非连续索引。 下一步是在此基类上挂多缓冲指标分支,但本节的单缓冲封装已经能覆盖半数以上轻量信号需求。

◍ MT4 与 MT5 指标缓冲区的底色差异

在 MetaTrader 4 里,单一指标缓冲区只能单色,无法按柱线指定绘制颜色;MetaTrader 5 则允许一条曲线动态切换多达 64 种颜色。想在 MT4 实现彩色曲线,就得为每种颜色单独开一个单色缓冲区,切换时只显示对应颜色的缓冲区,其余隐藏。 具体看多品种多周期移动平均线:MT5 固定用 3 个数组——绘制缓冲区、颜色缓冲区、计算缓冲区,颜色数变动时缓冲区总数不变。MT4 则是每多一种颜色就多一个绘制缓冲区,三色就是三个可见绘制缓冲区。若一条指标有 8 条曲线、各 64 色,MT4 需 512 个缓冲区,已触平台上限。 MT5 取数据要先建指标句柄再拷贝到数组,但后续按索引访问更快;MT4 的请求函数直接返回指定品种/周期的数值,不用自建计算缓冲,却牺牲了灵活性。外汇与贵金属市场杠杆高、滑点频发,跨平台移植指标逻辑时这些限制会直接影响信号呈现,建议开 MT5 用数据窗口对照验证缓冲数量。 今天先不重构已有库类,而是以建仓/派发指标为样本,在 MT4 用函数库跑通单缓冲区单色多品种多周期标准指标,你可直接复制下方结构到 MT4 测试。

「把缓冲区检查下沉到函数库」

早期在指标主程序里直接比对 #property 声明的缓冲区和函数库实际创建的缓冲区数量,现在把这个兼容性检查挪进 DoEasy 函数库,文本提示也一并收进 Data.mqh,并新增了 MSG_ENG_ERR_VALUE_PLOTS 这类消息索引。 文件命名也得较真:原先误写成 InpDatas.mqh 的输入数据文件,按英语语法修正为 InpData.mqh,直接在 Include\DoEasy 目录重命名,并同步改掉 Data.mqh 里的 include 路径,否则 MT5 编译直接报找不到文件。 跨平台改造从 ToMQL4.mqh 补常量枚举起步。MQL4 不认 BarsCalculated(),用 Bars() 返回时间序列可用柱线数顶替;IndicatorBarsCalculated() 在 Buffer.mqh 里拆成类外实现,MQL5 走 BarsCalculated()、MQL4 走 Bars(),返回语义对齐。 标准指标在 BuffersCollection.mqh 里给 MQL4 全补了哑元句柄(赋值 0),以 AD 指标为例:MQL5 用计算缓冲区存数据,MQL4 直接调 iAD() 取数,颜色数组也只取首项。当前仅单缓冲区、无额外输入的标准指标能在 MQL4 跑通,其余留待后续。 CEngine 新增 BuffersPropertyBuffersTotal() 方法,MQL5 仅弹警告提示 #property 数值不符,MQL4 则按函数库实际总数重设 indicator_buffers。DELib.mqh 里 MQL4 的小数位容量改用 IndicatorDigits() 标准函数设置。

MQL5 / C++
class="macro">#class="kw">property indicator_buffers class="num">3
class="macro">#class="kw">property indicator_plots  class="num">1
class=class="str">"cmt">//--- Check the number of buffers specified in the &class="macro">#x27;properties&class="macro">#x27; block
  if(engine.BuffersPropertyPlotsTotal()!=indicator_plots)
      Alert(TextByLanguage("","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal());
  if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers)
      Alert(TextByLanguage("","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal());
class=class="str">"cmt">//--- CEngine
  MSG_ENG_NO_TRADE_EVENTS,                    class=class="str">"cmt">// There have been no trade events since the last launch of EA
  MSG_ENG_FAILED_GET_LAST_TRADE_EVENT_DESCR,  class=class="str">"cmt">// Failed to get description of the last trading event
  MSG_ENG_FAILED_GET_MARKET_POS_LIST,         class=class="str">"cmt">// Failed to get the list of open positions
  MSG_ENG_FAILED_GET_PENDING_ORD_LIST,        class=class="str">"cmt">// Failed to get the list of placed orders
  MSG_ENG_NO_OPEN_POSITIONS,                  class=class="str">"cmt">// No open positions
  MSG_ENG_NO_PLACED_ORDERS,                   class=class="str">"cmt">// No placed orders
  MSG_ENG_ERR_VALUE_PLOTS,                    class=class="str">"cmt">// Attention! Value "indicator_plots" must be 

引擎报错文本与跨版本头文件包含

在交易引擎的报错映射里,有一组字符串常量直接对应运行时状态:自 EA 上次启动以来无交易事件、取不到最后交易事件描述、开仓列表与挂单列表获取失败、以及当前无持仓和无挂单。这些文本会在 CTrade/CEngine 类抛出异常或写日志时抛出,调试 EA 时若看到『There have been no trade events since the last launch of EA』,说明定时器或 OnTrade 回调未捕获到新成交,而非逻辑死循环。 另有两条带高亮背景的告警字符串:『Attention! Value of "indicator_plots" should be 』与『Attention! Value of "indicator_buffers" should be 』,它们指向自定义指标在 OnInit 中给 #property indicator_plots / indicator_buffers 赋的值与缓存区实际声明数不一致,MT5 编译器不会硬拦,但图表加载可能直接报 4106(数组越界)类错误。 头文件组织上,Data.mqh 通过 #include "InpData.mqh" 引入输入参数结构,而 ToMQL4.mqh 用 #ifdef __MQL4__ 包裹错误码宏重定义(如 ERR_SUCCESS 映射到 ERR_NO_ERROR、ERR_MARKET_UNKNOWN_SYMBOL 映射到 ERR_UNKNOWN_SYMBOL),说明同一套源码可借条件编译在 MQL4/5 间迁移。外汇与贵金属杠杆品种波动剧烈,这类跨版本兼容层若漏掉某个错误码映射,实盘可能吞掉关键报错,建议开 MT5 把 InpData.mqh 的依赖路径核对一遍。

MQL5 / C++
class="macro">#include "InpData.mqh"
class="macro">#ifdef __MQL4__
class="macro">#define ERR_SUCCESS(ERR_NO_ERROR)
class="macro">#define ERR_MARKET_UNKNOWN_SYMBOL(ERR_UNKNOWN_SYMBOL)

◍ 订单类型与填充标志的底层宏定义

在 MT5 的底层交易接口里,很多报错和订单属性并不是直接用枚举名,而是靠一组宏把旧常量或数字映射出来。比如把 ERR_ARRAY_INVALID 重命名为 ERR_ZEROSIZE_ARRAY,把邮件、通知、FTP 发送失败分别对应到 ERR_SEND_MAIL_ERROR、ERR_NOTIFICATION_ERROR、ERR_FTP_ERROR,方便 EA 在 OnTradeTransaction 里做统一错误分支。 订单类型和触发原因也用宏固化了数值:CLOSE_BY 是 8,BUY_STOP_LIMIT 是 9,SELL_STOP_LIMIT 是 10;而原因码里 EXPERT 挂单为 3,SL 触发为 4,TP 触发为 5,余额变动为 6,信用变动为 7。写日志时直接比对这些数字,比调 EnumToString 更省开销。 品种可接受的过期与填充模式,同样以位标志暴露:GTC=1、DAY=2、SPECIFIED=4、SPECIFIED_DAY=8;填充侧 FOK=1、IOC=2。用 SymbolInfoInteger(SYMBOL_EXPIRATION_MODE) 做按位与,就能知道当前品种到底放不放指定日过期单。 下单权限也拆成了 MARKET=1、LIMIT=2、STOP=4、STOP_LIMIT=8 的位掩码。实盘前先读 SYMBOL_ORDER_MODE 做 & 校验,可避免向黄金或外汇品种发出不被支持的挂单类型——这类品种流动性突变时拒单概率偏高,属正常高风险表现。

MQL5 / C++
class="macro">#define ERR_ZEROSIZE_ARRAY(ERR_ARRAY_INVALID)
class="macro">#define ERR_MAIL_SEND_FAILED(ERR_SEND_MAIL_ERROR)
class="macro">#define ERR_NOTIFICATION_SEND_FAILED(ERR_NOTIFICATION_ERROR)
class="macro">#define ERR_FTP_SEND_FAILED(ERR_FTP_ERROR)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Order types, filling policy, period, reasons                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define ORDER_TYPE_CLOSE_BY(class="num">8)
class="macro">#define ORDER_TYPE_BUY_STOP_LIMIT(class="num">9)
class="macro">#define ORDER_TYPE_SELL_STOP_LIMIT(class="num">10)
class="macro">#define ORDER_REASON_EXPERT(class="num">3)
class="macro">#define ORDER_REASON_SL(class="num">4)
class="macro">#define ORDER_REASON_TP(class="num">5)
class="macro">#define ORDER_REASON_BALANCE(class="num">6)
class="macro">#define ORDER_REASON_CREDIT(class="num">7)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Flags of allowed order expiration modes                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define SYMBOL_EXPIRATION_GTC(class="num">1)
class="macro">#define SYMBOL_EXPIRATION_DAY(class="num">2)
class="macro">#define SYMBOL_EXPIRATION_SPECIFIED(class="num">4)
class="macro">#define SYMBOL_EXPIRATION_SPECIFIED_DAY(class="num">8)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Flags of allowed order filling modes                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define SYMBOL_FILLING_FOK(class="num">1)
class="macro">#define SYMBOL_FILLING_IOC(class="num">2)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The flags of allowed order types                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define SYMBOL_ORDER_MARKET(class="num">1)
class="macro">#define SYMBOL_ORDER_LIMIT(class="num">2)
class="macro">#define SYMBOL_ORDER_STOP(class="num">4)
class="macro">#define SYMBOL_ORDER_STOP_LIMIT(class="num">8)

「云图指标线与订单标志位的常量映射」

在 MT5 的 EA 或指标工程里,用宏把魔法数字固化下来,能让后续逻辑少踩坑。下面这组定义把订单能力标志和云图五条线分别绑到了具体整数上。 SYMBOL_ORDER_SL 取 16、SYMBOL_ORDER_TP 取 32、SYMBOL_ORDER_CLOSEBY 取 64,这三个值对应品种支持的止损、止盈与同标的 Close-By 操作权限位。拿去跟 SymbolInfoInteger(Symbol(), SYMBOL_ORDER_MODE) 的返回值做位与,就能判断当前品种能不能挂止损。 云图部分把 TENKANSEN_LINE 到 CHIKOUSPAN_LINE 依次编为 0–4,转换线、基准线、先行 A、先行 B、滞后线各占一个 ID。写指标缓冲时直接引宏名,比裸写数字好维护,也方便小布这类外部工具做语义解析。 外汇与贵金属杠杆高、滑点跳空频繁,权限位检测不过就硬发单,大概率会吃到 ERR_INVALID_STOPS 报错。开 MT5 按 F4 把这段贴进新 include 头文件,编译后打印 SYMBOL_ORDER_MODE 实测一下更稳。

MQL5 / C++
class="macro">#define SYMBOL_ORDER_SL(class="num">16)
class="macro">#define SYMBOL_ORDER_TP(class="num">32)
class="macro">#define SYMBOL_ORDER_CLOSEBY(class="num">64)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Indicator lines IDs                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define TENKANSEN_LINE(class="num">0)
class="macro">#define KIJUNSEN_LINE(class="num">1)
class="macro">#define SENKOUSPANA_LINE(class="num">2)
class="macro">#define SENKOUSPANB_LINE(class="num">3)
class="macro">#define CHIKOUSPAN_LINE(class="num">4)

常见问题

把缓冲区检查下沉到函数库里统一处理,调用前先读指标缓冲区总数,少于2即按单缓冲逻辑走,避免手动判断漏错。
底层引擎报错文本与跨版本头文件包含方式不同,建议用函数库封装统一错误映射,别直接依赖终端原生报错。
可以,把云图指标线或订单标志位的常量映射贴给小布,它能对照底层宏定义帮你找出不一致的地方。
这些是跨版本通用的底层差异点,抽到函数库宏定义层能减少策略文件冗余,也方便统一改一处全量生效。
先确认时间序列封装是否按品种+周期独立建索引,再检查缓冲区读取是否没做跨平台分支,函数库层补上即可。