DoEasy 价格库第六十五部分:DOM 集合与 MQL5 信号类的底层铺垫(基础篇)
📚

DoEasy 价格库第六十五部分:DOM 集合与 MQL5 信号类的底层铺垫(基础篇)

(1/3)· 从零搭起市场深度快照序列仓库,并接通 MQL5 信号数据源的第一块砖

实战向 第 1/3 篇
很多交易者把 DOM 当成图表里一闪而过的挂单墙,从不在代码层做序列留存,回测时根本看不到盘口演化。还有人想接 MQL5 信号做参考,却卡在数据结构零散、取数全靠手搓。本篇先把仓库和信号对象类立起来,后面的实战才有地基建。

◍ 给 DoEasy 套上市场深度与信号对象

在 DoEasy 函数库第六十五次迭代里,核心改动是新增了 DOM(市场深度)集合类,以及专门封装 MQL5 信号对象的类。这两块原本散落在脚本层,现在收进库体系,意味着后续 EA 可以直接用统一接口去读盘口挂单和订阅信号源。 从工程角度看,把 DOM 集合和信号对象做成独立类,好处是调用方不用关心底层报价刷新逻辑。MetaTrader 5 的 Market Book 事件和 SignalBase 接口都被包了一层,库使用者只需声明对象、调方法。 这一版发布于 2021 年 5 月 10 日,原文示例在终端内累计被查看 1736 次。外汇与贵金属品种用这套类做深度监控时,仍要清楚盘口薄时滑点风险偏高,信号跟单也可能因流动性断裂而偏离。

从抽象对象到快照序列集合

到目前为止,DOM 的底层结构已经搭好:抽象订单对象、它的派生类、DOM 快照类以及 DOM 快照序列类都已在前面实现。现在缺的只是最后一块拼图——一个能统管所有快照序列的容器。 这个容器叫 DOM 快照序列集合类,它把多个序列收进同一张列表,可以随时按索引或标识捞取任意一条历史快照。类内部会按预设的序列长度自动滚动:新快照进队,超出尺寸的旧快照出队,省去手动维护缓冲区。 顺带提一句,函数库要开新支线了——「其他库类」板块。第一刀落在 MQL5 信号服务上:建一个信号对象类,把信号广播里单个信号的全部字段(收益曲线、持仓、杠杆等)原样收进实例,后面做信号横向比对时直接读对象就行。 外汇与贵金属市场杠杆高、滑点突兀,这类 DOM 与信号数据只反映过往盘口与广播状态,据此交易仍可能触发超预期回撤,务必在 MT5 策略测试器里先跑一遍再上实盘。

「给 DOM 快照序列补上连字符描述开关」

要在 DoEasy 框架里新增 DOM 快照序列集合,先得把消息索引和文本补进 \MQL5\Include\DoEasy\Data.mqh,并在 Defines.mqh 的 ID 段给集合分配一个唯一标识。这一步不做,后面编译对象类时会直接报未定义符号。 MBookSeries.mqh 里有个实际痛点:单个序列描述有时要单独打印,有时要把整列快照序列一次性列出来。如果在每条描述前加个连字符,整块列表在终端里读起来层次更清楚。 做法是在类方法的声明和实现里都加一个布尔标志,用来控制「描述前是否显示连字符」。该标志默认 false,也就是平时单条描述干净输出,只有显式置 true 时才批量带前缀。 下方代码是 Data.mqh 中实际落地的消息枚举片段,高亮行为本次新增的 DOM 系列与信号扩展项。注意 MSG_MBOOK_SERIES_ERR_ADD_TO_LIST 和 CMBookSeriesCollection 文本都是这次配套加的,漏掉其一都会导致集合类日志报错。

MQL5 / C++
class=class="str">"cmt">//--- CMBookSeries
  MSG_MBOOK_SERIES_TEXT_MBOOKSERIES,                    class=class="str">"cmt">// DOM snapshot series
  MSG_MBOOK_SERIES_ERR_ADD_TO_LIST,                     class=class="str">"cmt">// Error. Failed to add DOM snapshot series to the list

class=class="str">"cmt">//--- CMBookSeriesCollection
  MSG_MB_COLLECTION_TEXT_MBCOLLECTION,                  class=class="str">"cmt">// DOM snapshot series collection
class=class="str">"cmt">//--- CMQLSignal
  MSG_SIGNAL_MQL5_TEXT_SIGNAL,                          class=class="str">"cmt">// Signal
  MSG_SIGNAL_MQL5_TEXT_SIGNAL_MQL5,                     class=class="str">"cmt">// MQL5.com Signals service signal
  MSG_SIGNAL_MQL5_TRADE_MODE,                           class=class="str">"cmt">// Account type
  MSG_SIGNAL_MQL5_DATE_PUBLISHED,                       class=class="str">"cmt">// Publication date
  MSG_SIGNAL_MQL5_DATE_STARTED,                         class=class="str">"cmt">// Monitoring start date
  MSG_SIGNAL_MQL5_DATE_UPDATED,                         class=class="str">"cmt">// Date of the latest update of the trading statistics 
  MSG_SIGNAL_MQL5_ID,                                   class=class="str">"cmt">// ID
  MSG_SIGNAL_MQL5_LEVERAGE,                             class=class="str">"cmt">// Trading account leverage
  MSG_SIGNAL_MQL5_PIPS,                                 class=class="str">"cmt">// Trading result in pips
  MSG_SIGNAL_MQL5_RATING,                               class=class="str">"cmt">// Position in the signal rating
  MSG_SIGNAL_MQL5_SUBSCRIBERS,                          class=class="str">"cmt">// Number of subscribers
  MSG_SIGNAL_MQL5_TRADES,                               class=class="str">"cmt">// Number of trades
  MSG_SIGNAL_MQL5_SUBSCRIPTION_STATUS,                  class=class="str">"cmt">// Status of account subscription to a signal

  MSG_SIGNAL_MQL5_EQUITY,                               class=class="str">"cmt">// Account equity
  MSG_SIGNAL_MQL5_GAIN,                                 class=class="str">"cmt">// Account growth in %
  MSG_SIGNAL_MQL5_MAX_DRAWDOWN,                         class=class="str">"cmt">// Maximum drawdown
  MSG_SIGNAL_MQL5_PRICE,                                class=class="str">"cmt">// Signal subscription price
  MSG_SIGNAL_MQL5_ROI,                                  class=class="str">"cmt">// Signal ROI(Return on Investment) in %

◍ 信号与DOM快照的多语言枚举映射

在 MQL5 的本地化消息结构里,信号服务相关的字段用一组枚举常量做索引,俄文与英文成对出现,方便终端内部做界面语言切换。从 MSG_SIGNAL_MQL5_AUTHOR_LOGINMSG_SIGNAL_MQL5_CURRENCY,覆盖了作者登录名、经纪商名称、服务器、信号名和账户币种这 5 个基础维度。 紧接着的 MSG_SIGNAL_MQL5_TEXT_GAINMSG_SIGNAL_MQL5_TEXT_DRAWDOWN 单独成组,对应增长和回撤的文本标签,说明这两者在前端展示时被当作独立指标处理,而非合并进通用信号属性。 往下翻到 CMQLSignal 的本地化块,字段密度明显更高:除基础信息外,还包含 Profit in pips( pip 收益)、Position in rating(评级位置)、Number of subscribers(订阅者数)、Account maximum drawdown(最大回撤)和 Return on Investment (%)(ROI%)。如果你要写跟单信号过滤器,直接复用这组枚举就能拿到 20 个以上的结构化字段。 外汇与贵金属信号跟单自带高杠杆与滑点风险,订阅前应当把 Account maximum drawdownNumber of trades 拉出来核对,避免被短期增益误导。

MQL5 / C++
MSG_SIGNAL_MQL5_AUTHOR_LOGIN,                                                                     class=class="str">"cmt">// Author login
 MSG_SIGNAL_MQL5_BROKER,                                                                              class=class="str">"cmt">// Broker(company) name
 MSG_SIGNAL_MQL5_BROKER_SERVER,                                                                       class=class="str">"cmt">// Broker server
 MSG_SIGNAL_MQL5_NAME,                                                                                class=class="str">"cmt">// Name
 MSG_SIGNAL_MQL5_CURRENCY,                                                                            class=class="str">"cmt">// Account currency

 MSG_SIGNAL_MQL5_TEXT_GAIN,                                                                           class=class="str">"cmt">// Growth
 MSG_SIGNAL_MQL5_TEXT_DRAWDOWN,                                                                       class=class="str">"cmt">// Drawdown

};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- CMBookSeries
  {"Серия снимков стакана цен","Series of shots of the Depth of Market"},
  {"Ошибка. Не удалось добавить серию снимков стакана цен в список","Error. Failed to add a shots series of the Depth of Market to the list"},

class=class="str">"cmt">//--- CMBookSeriesCollection
  {"Коллекция серий снимков стакана цен","Collection of series of the Depth of Market shot"},  

class=class="str">"cmt">//--- CMQLSignal
  {"Сигнал","Signal"},
  {"Сигнал сервиса сигналов MQL5.com","Signal from MQL5.com signal service"},
  {"Тип счета","Account type"},
  {"Дата публикации","Publication date"},
  {"Дата начала мониторинга","Monitoring starting date"},
  {"Дата последнего обновления торговой статистики","The date of the last update of the signal&class="macro">#x27;s trading statistics"},
  {"ID","ID"},
  {"Плечо торгового счета","Account leverage"},
  {"Результат торговли в пипсах","Profit in pips"},
  {"Позиция в рейтинге сигналов","Position in rating"},
  {"Количество подписчиков","Number of subscribers"},
  {"Количество трейдов","Number of trades"},
  {"Состояние подписки счёта на этот сигнал","Account subscription status for this signal"},
  {"Средства на счете","Account equity"},
  {"Прирост счета в процентах","Account gain"},
  {"Максимальная просадка","Account maximum drawdown"},
  {"Цена подписки на сигнал","Signal subscription price"},
  {"Значение ROI(Return on Investment) сигнала в %","Return on Investment(%)"},

  {"Логин автора","Author login"},
  {"Наименование брокера (компании)","Broker name(company)"},
  {"Сервер брокера","Broker server"},
  {"Имя","Name"},
  {"Валюта счета","Base currency"},

  {"Прирост","Gain"},
  {"Просадка","Drawdown"},

};

DOM 快照序列的标识与日志输出

在 MT5 的行情深度(DOM)处理框架里,各类数据集合都用 16 进制常量做唯一 ID。DOM 快照序列被定为 0x7784,紧挨着 tick 序列的 0x7783,这种连续编号方便在调试时快速区分数组边界。 CMBookSeries 类提供了两个日志方法:Print 与 PrintShort,区别只在于输出详略。Print 会拼出「所需历史天数 + 实际深度」两段信息,调用 RequiredUsedDays() 和 DataTotal() 取数,再经 Header() 加前缀。 下面这段是 Print 的核心实现,dash 参数控制是否在前方补「- 」做缩进。外汇与贵金属的 DOM 数据受流动性影响极大,盘口深度可能在秒级剧烈变化,这类日志仅用于本地排查,不代表任何方向判断。

MQL5 / C++
class="macro">#define COLLECTION_MBOOKSERIES_ID(0x7784)           class=class="str">"cmt">// DOM series collection list ID
class=class="str">"cmt">//--- Display(class="num">1) description and(class="num">2) class="type">class="kw">short description of a DOM snapshot series
   class="type">void                Print(class="kw">const class="type">bool dash=class="kw">false);
   class="type">void                PrintShort(class="kw">const class="type">bool dash=class="kw">false);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Display the description of the DOM snapshot series in the journal|
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CMBookSeries::Print(class="kw">const class="type">bool dash=class="kw">false)
  {
   class="type">class="kw">string txt=
     (
       CMessage::Text(MSG_TICKSERIES_REQUIRED_HISTORY_DAYS)+(class="type">class="kw">string)this.RequiredUsedDays()+", "+
       CMessage::Text(MSG_LIB_TEXT_TS_ACTUAL_DEPTH)+(class="type">class="kw">string)this.DataTotal()
     );
   ::Print((dash ? "- " : ""),this.Header(),": ",txt);
  }

「给深度簿打印加个短横开关」

CMBookSeries 的 PrintShort 方法提供了一个轻量打印入口,适合在日志里快速扫一眼市场深度表头而不刷屏。 它接收一个 bool 参数 dash,默认 false;当传 true 时,输出前会拼一个 "- " 前缀,方便和多行日志做视觉分隔。 代码里用三元表达式 (dash ? "- " : "") 决定前缀,再叠 this.Header() 输出表头字符串,逻辑只有两行,开 MT5 把这类对象接进 OnTick 打印即可验证效果。 外汇与贵金属市场流动性瞬变,深度数据仅反映瞬时挂单,用于观察可能比用于下单依据更稳妥。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CMBookSeries::PrintShort(class="kw">const class="type">bool dash=class="kw">false)
  {
   ::Print((dash ? "- " : ""),this.Header());
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
把 DOM 序列监控交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到盘口快照序列的自动滚动与异常厚度提示,你只需在策略里调用结论。

常见问题

它把每个品种的 DOM 快照按时间存成序列,并限制长度自动淘汰旧数据,避免内存膨胀且能随时按索引取历史盘口。
该标志仅用于一次性打印全部序列时做视觉分隔,默认关闭,不影响对象内部逻辑,纯属日志可读性优化。
可以,小布的品种页已封装好快照序列的采集与展示,无需你自己写集合类就能看盘口深度变化。
本篇只定义单个信号的数据容器,还未拉取广播列表,后续篇会基于此做信号筛选与订阅。
新增 ID 与消息文本是追加式,不改动原有枚举值,旧程序编译不受影响,但需重新包含头文件。