DoEasy 库中的其他类(第六十九部分):图表对象集合类(基础篇)
📘

DoEasy 库中的其他类(第六十九部分):图表对象集合类(基础篇)

第 1/3 篇

「DoEasy 里的图表对象集合类怎么落地」

DoEasy 库在第六十九部分补上了图表对象集合类,把散落在各处的图表对象收进一个统一容器,方便按类型、按时间、按属性批量取用。这类封装对写 EA 或指标时反复遍历 OBJ_HLINE、OBJ_TREND 很有用,少写一层循环判断。 原文示例发布于 2021 年 6 月 28 日,在 MetaTrader 5 环境实测,示例帖文浏览量 1 664,说明这套集合类在老版本 MT5 上就能跑通。外汇与贵金属交易杠杆高、滑点跳空频繁,用集合类管理挂单线、止损线时仍要自己校验对象是否已被平台删除。 下一步是在此集合类之上接事件过滤,只处理被策略关心的那几类图表对象,降低 OnChartEvent 里的无效分支。

把分散的图表对象收拢成统一集合

在 MT5 里,单个图表窗口的对象列表和挂在对象上的指标列表通常是分开管理的。若手动逐个图表去翻,十几个窗口就能耗掉大半盯盘精力。 现在可以把这些分散结构合成一个图表对象集合:每个元素同时携带窗口对象与附加指标引用。集合建成后,按品种、周期或窗口句柄做筛选、排序都只需一次调用。 实际价值在于,你能用一段脚本批量读取当前终端所有已开图表的状态,而不是在图表间反复切换。外汇与贵金属杠杆高、跳空频繁,批量巡检有助于更快发现异常窗口。

◍ 给图表对象库打补丁

这次改动集中在 DoEasy 函数库的图表对象层。先往 \MQL5\Include\DoEasy\Data.mqh 里塞了一批新消息索引和对应文本,专门描述主窗口、子窗口、指标列表等状态,方便后续集合类直接调文案。 图表变化不靠 OnChartEvent() 硬盯。实测下来 CHARTEVENT_CHART_CHANGE 只告诉你“图变了”,但说不清哪变了。所以改成在程序计时器里每秒轮两次:比对当前图表数和窗口数跟上一拍的差值,没差就跳过,有差才刷集合。 整数型属性枚举里砍掉了 CHART_PROP_WINDOW_IS_VISIBLE,实际没派上用场,枚举总量从 67 掉到 66。顺手把排序用的 SORT_BY_CHART_WINDOW_IS_VISIBLE 也删了,ChartObj.mqh 里 SetVisible() 的声明和实现一并剔除——窗口可见性直接问运行环境更稳。 ChartWnd.mqh 公开段加了设窗口索引的新方法;ChartObj.mqh 私密段加了建子窗口列表的方法,公开段加了更新图表及子窗口属性的方法。WindowsTotal() 改成先调 SetWindowsTotal() 把数量写进对象属性再返回,避免反复问终端。 日志打印那块以前用 WindowsTotal() 当循环上限,容易因为列表和实际窗口数不一致而出错。现在改成取列表里窗口对象的实际数量来跑循环。更新方法里比一下终端窗口数和列表对象数的差,差为零就不动,否则整表重建——比增量增删更不容易漏。 集合计时器得有自己的计数器和 ID。在 Defines.mqh 的宏替换区加了图表对象集合计时器计数参数和列表 ID,每个集合靠 ID 区分归属。CSelect 类里挂了图表对象的搜索排序新方法,并把 ChartObj 类文件 include 进来。

MQL5 / C++
  MSG_CHART_OBJ_CHART_WINDOW,                                                                     class=class="str">"cmt">// Main chart window
  MSG_CHART_OBJ_CHART_SUBWINDOW,                                                                   class=class="str">"cmt">// Chart subwindow
  MSG_CHART_OBJ_CHART_SUBWINDOWS_NUM,                                                              class=class="str">"cmt">// Subwindows
  MSG_CHART_OBJ_INDICATORS_MW_NAME_LIST,                                                           class=class="str">"cmt">// Indicators in the main chart window
  MSG_CHART_OBJ_INDICATORS_SW_NAME_LIST,                                                           class=class="str">"cmt">// Indicators in the chart window
  MSG_CHART_OBJ_INDICATOR,                                                                         class=class="str">"cmt">// Indicator
  MSG_CHART_OBJ_INDICATORS_TOTAL,                                                                  class=class="str">"cmt">// Indicators
  MSG_CHART_OBJ_WINDOW_N,                                                                          class=class="str">"cmt">// Window
  MSG_CHART_OBJ_INDICATORS_NONE,                                                                   class=class="str">"cmt">// No indicators

class=class="str">"cmt">//--- CChartObjCollection
  MSG_CHART_COLLECTION_TEXT_CHART_COLLECTION,                                                      class=class="str">"cmt">// Chart collection
  MSG_CHART_COLLECTION_ERR_FAILED_CREATE_CHART_OBJ,                                                class=class="str">"cmt">// Failed to create a new chart object
  MSG_CHART_COLLECTION_ERR_FAILED_ADD_CHART,                                                       class=class="str">"cmt">// Failed to add a chart object to the collection

  };
class=class="str">"cmt">//+------------------------------------------------------------------+
  {"Главное окно графика","Main chart window"},
  {"Подокно графика","Chart subwindow"},
  {"Подокон","Subwindows"},
  {"Индикаторы в главном окне графика","Indicators in the main chart window"},

「库里的宏定义与多语字符串表」

这段代码片段来自一个 MT5 通用库的局部,前半部分是一组俄英双语的字符串映射,用于界面文本在不同终端语言下的切换。比如 "Индикаторов" 对应 "Indicators total","Отсутствуют" 对应 "No indicators",说明图表上无加载指标时的提示文案也纳入了本地化。 紧跟着的 CChartObjCollection 注释块里,定义了图表对象集合相关的报错文案:创建新图表对象失败、向集合添加图表对象失败,这类信息在调试自定义图表容器时直接可见。 下半部分是宏替换区,几个数值宏值得记住:TIMER_FREQUENCY 设为 16 毫秒,即库内定时器最低触发频率;TOTAL_TRADE_TRY 为 5,代表默认交易重试次数;IND_BUFFERS_MAX 给到 512,是一个指标最多能申请的缓冲区数量上限。 还有一组标准音效宏(SND_ALERT、SND_CONNECT 等),直接指向终端 Sounds 目录下的 wav 文件,写 EA 报警逻辑时可原样借用,不必自己再管路径。外汇与贵金属波动剧烈、杠杆风险高,调用这些宏做自动提醒时仍须人工复核信号。

MQL5 / C++
  {"Индикаторы в окне графика","Indicators in the chart window"},
  {"Индикатор","Indicator"},
  {"Индикаторов","Indicators total"},
  {"Окно","Window"},
  {"Отсутствуют","No indicators"},

class=class="str">"cmt">//--- CChartObjCollection
  {"Коллекция чартов","Chart collection"},
  {"Не удалось создать новый объект-чарт","Failed to create new chart object"},
  {"Не удалось добавить объект-чарт в коллекцию","Failed to add chart object to collection"},

  };
class=class="str">"cmt">//+---------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Macro substitutions                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Describe the function with the error line number
class="macro">#define DFUN_ERR_LINE(__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Page " : ", Line ")+(class="type">class="kw">string)__LINE__+": ")
class="macro">#define DFUN(__FUNCTION__+": ")     class=class="str">"cmt">// "Function description"
class="macro">#define COUNTRY_LANG("Russian")             class=class="str">"cmt">// Country language
class="macro">#define END_TIME(D&class="macro">#x27;class="num">31.12.class="num">3000 class="num">23:class="num">59:class="num">59&class="macro">#x27;)  class=class="str">"cmt">// End date for account history data requests
class="macro">#define TIMER_FREQUENCY(class="num">16)                   class=class="str">"cmt">// Minimal frequency of the library timer in milliseconds
class="macro">#define TOTAL_TRADE_TRY(class="num">5)                    class=class="str">"cmt">// Default number of trading attempts
class="macro">#define IND_COLORS_TOTAL(class="num">64)                   class=class="str">"cmt">// Maximum possible number of indicator buffer colors
class="macro">#define IND_BUFFERS_MAX(class="num">512)                  class=class="str">"cmt">// Maximum possible number of indicator buffers
class=class="str">"cmt">//--- Standard sounds
class="macro">#define SND_ALERT           "alert.wav"
class="macro">#define SND_ALERT2          "alert2.wav"
class="macro">#define SND_CONNECT         "connect.wav"
class="macro">#define SND_DISCONNECT      "disconnect.wav"

EA 里的声音与定时器常量怎么配

在 MQL5 的 EA 工程里,用预处理宏把系统提示音和各类采集定时器的参数固化下来,是降低硬编码、方便统一调参的常见做法。下面这段代码给出了 9 个提示音文件名映射,以及订单、账户、品种三类采集定时器的间隔与计数器步长。 提示音部分覆盖了邮件、专家信号、新闻、确认、请求、止损、心跳 tick、超时、等待共 9 种场景,分别对应 email.wav 到 wait.wav。实际跑 EA 时若听不到声,先确认 Terminal 目录下 Sounds 文件夹里有没有这 9 个文件。 定时器参数才是重点:订单与成交采集间隔设为 250 毫秒、计数器每次加 16、ID 为 1;账户采集间隔 1000 毫秒、步长 16、ID 为 2。品种扫描分两路,定时器 1 扫市场报价窗口品种,间隔 100 毫秒、ID 3;定时器 2 处理报价窗口品种事件,间隔 300 毫秒。外汇与贵金属波动快,这类毫秒级采集在重大数据行情中可能拖累主线程,实盘前建议在 MT5 策略测试器里改 COLLECTION_SYM_PAUSE1 到 200 看看 CPU 占用变化。

MQL5 / C++
class="macro">#define SND_EMAIL                    "email.wav"
class="macro">#define SND_EXPERT                   "expert.wav"
class="macro">#define SND_NEWS                     "news.wav"
class="macro">#define SND_OK                       "ok.wav"
class="macro">#define SND_REQUEST                  "request.wav"
class="macro">#define SND_STOPS                    "stops.wav"
class="macro">#define SND_TICK                     "tick.wav"
class="macro">#define SND_TIMEOUT                  "timeout.wav"
class="macro">#define SND_WAIT                     "wait.wav"
class=class="str">"cmt">//--- Parameters of the orders and deals collection timer
class="macro">#define COLLECTION_ORD_PAUSE(class="num">250)             class=class="str">"cmt">// Orders and deals collection timer pause in milliseconds
class="macro">#define COLLECTION_ORD_COUNTER_STEP(class="num">16)              class=class="str">"cmt">// Increment of the orders and deals collection timer counter
class="macro">#define COLLECTION_ORD_COUNTER_ID(class="num">1)               class=class="str">"cmt">// Orders and deals collection timer counter ID
class=class="str">"cmt">//--- Parameters of the account collection timer
class="macro">#define COLLECTION_ACC_PAUSE(class="num">1000)            class=class="str">"cmt">// Account collection timer pause in milliseconds
class="macro">#define COLLECTION_ACC_COUNTER_STEP(class="num">16)              class=class="str">"cmt">// Account timer counter increment
class="macro">#define COLLECTION_ACC_COUNTER_ID(class="num">2)               class=class="str">"cmt">// Account timer counter ID
class=class="str">"cmt">//--- Symbol collection timer class="num">1 parameters
class="macro">#define COLLECTION_SYM_PAUSE1(class="num">100)             class=class="str">"cmt">// Pause of the symbol collection timer class="num">1 in milliseconds(for scanning market watch symbols)
class="macro">#define COLLECTION_SYM_COUNTER_STEP1(class="num">16)              class=class="str">"cmt">// Increment of the symbol timer class="num">1 counter
class="macro">#define COLLECTION_SYM_COUNTER_ID1(class="num">3)               class=class="str">"cmt">// Symbol timer class="num">1 counter ID
class=class="str">"cmt">//--- Symbol collection timer class="num">2 parameters
class="macro">#define COLLECTION_SYM_PAUSE2(class="num">300)             class=class="str">"cmt">// Pause of the symbol collection timer class="num">2 in milliseconds(for events of the market watch symbol list)

◍ 多路采集定时器的节拍分配

在 MT5 的行情与对象采集框架里,不同数据类型的刷新节奏被拆成了相互独立的定时器,各自带一个计数器 ID 和步进值。符号级二级计时器用 ID 2、步进 16;交易类对象计时器 ID 5、暂停 300 毫秒,步进同样是 16。 时间序列、指标序列、Tick 序列三组都设了 64 毫秒的暂停,计数器 ID 分别从 6、7、8 起算,步进统一为 16,说明这类高频数据倾向于更密地轮询。 图表集合定时器是这套配置里最慢的一路:暂停 500 毫秒、ID 未显式收尾但紧接 Ticks 的 8 之后、步进 16。把图表放在 500 毫秒节拍上,能在 CPU 占用和界面同步之间留出余量,外汇与贵金属品种在剧烈波动时仍可能感知到半秒级的滞后。 直接把上面这组宏拷进 EA 头文件,改 COLLECTION_CHARTS_PAUSE 到 250 试试,MT5 策略测试器里切到图表事件日志,能直观看到采集频率变化。

MQL5 / C++
class="macro">#define COLLECTION_SYM_COUNTER_STEP2(class="num">16)                 class=class="str">"cmt">// Increment of the symbol timer class="num">2 counter
class="macro">#define COLLECTION_SYM_COUNTER_ID2(class="num">4)                   class=class="str">"cmt">// Symbol timer class="num">2 counter ID
class=class="str">"cmt">//--- Trading class timer parameters
class="macro">#define COLLECTION_REQ_PAUSE(class="num">300)                 class=class="str">"cmt">// Trading class timer pause in milliseconds
class="macro">#define COLLECTION_REQ_COUNTER_STEP(class="num">16)                  class=class="str">"cmt">// Trading class timer counter increment
class="macro">#define COLLECTION_REQ_COUNTER_ID(class="num">5)                   class=class="str">"cmt">// Trading class timer counter ID
class=class="str">"cmt">//--- Parameters of the timeseries collection timer
class="macro">#define COLLECTION_TS_PAUSE(class="num">64)                  class=class="str">"cmt">// Timeseries collection timer pause in milliseconds
class="macro">#define COLLECTION_TS_COUNTER_STEP(class="num">16)                  class=class="str">"cmt">// Account timer counter increment
class="macro">#define COLLECTION_TS_COUNTER_ID(class="num">6)                   class=class="str">"cmt">// Timeseries timer counter ID
class=class="str">"cmt">//--- Parameters of the timer of indicator data timeseries collection
class="macro">#define COLLECTION_IND_TS_PAUSE(class="num">64)                  class=class="str">"cmt">// Pause of the timer of indicator data timeseries collection in milliseconds
class="macro">#define COLLECTION_IND_TS_COUNTER_STEP(class="num">16)                  class=class="str">"cmt">// Increment of indicator data timeseries timer counter
class="macro">#define COLLECTION_IND_TS_COUNTER_ID(class="num">7)                   class=class="str">"cmt">// ID of indicator data timeseries timer counter
class=class="str">"cmt">//--- Parameters of the tick series collection timer
class="macro">#define COLLECTION_TICKS_PAUSE(class="num">64)                  class=class="str">"cmt">// Tick series collection timer pause in milliseconds
class="macro">#define COLLECTION_TICKS_COUNTER_STEP(class="num">16)                  class=class="str">"cmt">// Tick series timer counter increment step
class="macro">#define COLLECTION_TICKS_COUNTER_ID(class="num">8)                   class=class="str">"cmt">// Tick series timer counter ID
class=class="str">"cmt">//--- Parameters of the chart collection timer
class="macro">#define COLLECTION_CHARTS_PAUSE(class="num">500)                 class=class="str">"cmt">// Chart collection timer pause in milliseconds
class="macro">#define COLLECTION_CHARTS_COUNTER_STEP(class="num">16)                  class=class="str">"cmt">// Chart timer counter increment

常见问题

它把散落在图表上的各类对象收进一个集合,用遍历就能批量改色、删对象,不用一个个手动点。
多半是字符串表 ID 和宏没对应上,检查头文件里声明和源文件里的赋值是否一致即可排错。
可以,小布能扫描当前图表对象数量与类型,乱了就提示你批量整理或删除无用对象。
把声音常量单独定义、定时器节拍用整除分配,避免同一秒触发多事件,就不会抢资源。
按采集频率给每路设不同余数偏移,主循环里用时间取模分发,就能均匀覆盖各路。