DoEasy 函数库中的其他类(第七十一部分):图表对象集合事件(基础篇)
「给图表对象事件建一个集合类」
DoEasy 函数库在 2021-07-12 发布第七十一部分更新,核心动作是把图表对象相关事件收进一个独立的集合类里管理,而不是散落在各窗口回调中。 该版本示例帖当日浏览量为 1352,说明这类底层事件封装对实盘脚本编写者确有刚需:当你要在 MT5 里同时监控多个对象(趋势线、矩形、文本)的创建、移动、删除,原生 OnChartEvent 容易写成一坨 switch-case。 把事件抽象成集合后,外部调用只需遍历集合拿对象状态,逻辑更薄。外汇与贵金属图表对象受报价跳空影响大,这类封装能降低漏事件概率,但高频行情下仍可能丢事件,需自行压测。
◍ 事件功能要处理手动与程序化操控的差异
上篇已经把图表对象属性、指标窗口的开闭和指标增删改的自动更新跑通了。这一篇要给图表窗口里的图表对象、窗口对象和指标对象都加上事件功能,目的是在这些对象发生变化时,往控制程序的图表发一个自定义事件。 这里有个容易踩的坑:手动操控图表是逐对象进行的,一次只动一个;但如果在计时器一次 tick 里用代码批量改好几个对象,事件很可能被错误定义,最糟的情况是连“哪个对象变了”都识别错。稳妥做法是只记录最后一个事件。 我写代码时会预留批量改参数的接口,但不会去微调、测试那种程序化并发处理多个图表元素的情况。外汇和贵金属市场高波动、高杠杆,这类底层事件逻辑若误判,可能让自动化策略在极端行情下反应错位。如果后续库的用户真有并发需求,我再单独返工处理。
给已删指标留后路:窗口索引与只读列表指针
在 DoEasy 函数库里追踪图表事件,难点不在捕获,而在对象被删之后还能反查。MQL5 的图表子窗口和指标一旦移除,原生 API 很难再拿到它们原先所在的窗口索引,所以作者在 ChartWnd.mqh 的 CWndInd 类私密段加了整型变量存子窗口索引,并配套了 set/get 方法,构造时也强制传入该索引。 为了不在每次需要历史对象时遍历自定义列表,作者在 ChartObjCollection 集合类里声明了 CArrayObj 类型的已删除图表、已删除窗口、已变更指标三个列表,并把指针只读式传给每个图表/窗口/指标对象。整数型属性枚举因此从 66 扩到 67,排序标准也加了按窗口索引一项。 这套指针传递有硬约束:集合外不能删列表里的对象、不能重置列表、不能改指针指向,本质上当只读引用用。好处是任意一个图表对象都能直接读全局已删记录,不必层层回调。 顺带修了个资源泄漏误区:之前每次读完指标数据就 ReleaseIndicator,其实句柄应留到析构才释放。现在 CWndInd 析构里循环遍历窗口指标列表,取对象存的句柄并释放再移除对象,避免反复建句柄。 验证路径:开 MT5 进 \MQL5\Include\DoEasy\Objects\Chart\ChartWnd.mqh,搜 m_wnd_index 变量和类析构里的 ReleaseIndicator 循环,对照改前提交看句柄生命周期差异;外汇/贵金属图表多品种多窗口下,误删句柄会拖慢 EA,属高频实盘隐患。
「图表对象与集合的本地化消息枚举」
在 MT5 的图表管理封装里,模板保存、指标增删、子窗口变动这些动作都需要对应的提示文本,代码用枚举常量加双语字符串表来落地。 下面这段枚举定义了图表对象层级的关键事件 ID,高亮部分是运行时最常触发的指标与窗口类消息: MSG_CHART_OBJ_TEMPLATE_SAVED, // Chart template saved MSG_CHART_OBJ_TEMPLATE_APPLIED, // Template applied to chart MSG_CHART_OBJ_INDICATOR_ADDED, // Indicator added MSG_CHART_OBJ_INDICATOR_REMOVED, // Indicator removed MSG_CHART_OBJ_INDICATOR_CHANGED, // Indicator changed MSG_CHART_OBJ_WINDOW_ADDED, // Subwindow added MSG_CHART_OBJ_WINDOW_REMOVED, // Subwindow removed //--- CChartObjCollection MSG_CHART_COLLECTION_TEXT_CHART_COLLECTION, // Chart collection MSG_CHART_COLLECTION_ERR_FAILED_CREATE_CHART_OBJ, // Failed to create a new chart object MSG_CHART_COLLECTION_ERR_FAILED_ADD_CHART, // Failed to add a chart object to the collection MSG_CHART_COLLECTION_ERR_CHARTS_MAX, // Cannot open new chart. Number of open charts at maximum MSG_CHART_COLLECTION_CHART_OPENED, // Chart opened MSG_CHART_COLLECTION_CHART_CLOSED, // Chart closed }; 紧随其后的字符串数组把俄文界面词与英文做了映射,绿色高亮行对应前面指标和图表开关事件: {"Шаблон графика сохранён","Chart template saved"}, {"Шаблон применён к графику","Template applied to the chart"}, {"Добавлен индикатор","Added indicator"}, {"Удалён индикатор","Removed indicator"}, {"Изменён индикатор","Changed indicator"}, {"Добавлено подокно","Added subwindow"}, {"Удалено подокно","Removed subwindow"}, //--- CChartObjCollection {"Коллекция чартов","Chart collection"}, {"Не удалось создать новый объект-чарт","Failed to create new chart object"}, {"Не удалось добавить объект-чарт в коллекцию","Failed to add chart object to collection"}, {"Нельзя открыть новый график, так как количество открытых графиков уже максимальное","You cannot open a new chart, since the number of open charts is already maximum"}, {"Открыт график","Open chart"}, {"Закрыт график","Closed chart"}, }; 开 MT5 切到俄文终端时,若你写的 EA 调用了 CChartObjCollection,这些字符串就是用户实际看到的回显;改英文表不改枚举,界面可能错位。外汇与贵金属图表多开时,MSG_CHART_COLLECTION_ERR_CHARTS_MAX 会真实弹错,属终端硬限制,非代码 bug。
MSG_CHART_OBJ_TEMPLATE_SAVED, class=class="str">"cmt">// Chart class="kw">template saved MSG_CHART_OBJ_TEMPLATE_APPLIED, class=class="str">"cmt">// Template applied to chart MSG_CHART_OBJ_INDICATOR_ADDED, class=class="str">"cmt">// Indicator added MSG_CHART_OBJ_INDICATOR_REMOVED, class=class="str">"cmt">// Indicator removed MSG_CHART_OBJ_INDICATOR_CHANGED, class=class="str">"cmt">// Indicator changed MSG_CHART_OBJ_WINDOW_ADDED, class=class="str">"cmt">// Subwindow added MSG_CHART_OBJ_WINDOW_REMOVED, class=class="str">"cmt">// Subwindow removed 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 MSG_CHART_COLLECTION_ERR_CHARTS_MAX, class=class="str">"cmt">// Cannot open new chart. Number of open charts at maximum MSG_CHART_COLLECTION_CHART_OPENED, class=class="str">"cmt">// Chart opened MSG_CHART_COLLECTION_CHART_CLOSED, class=class="str">"cmt">// Chart closed }; class=class="str">"cmt">//+------------------------------------------------------------------+ {"Шаблон графика сохранён","Chart class="kw">template saved"}, {"Шаблон применён к графику","Template applied to the chart"}, {"Добавлен индикатор","Added indicator"}, {"Удалён индикатор","Removed indicator"}, {"Изменён индикатор","Changed indicator"}, {"Добавлено подокно","Added subwindow"}, {"Удалено подокно","Removed subwindow"}, class=class="str">"cmt">//--- CChartObjCollection {"Коллекция чартов","Chart collection"}, {"Не удалось создать новый объект-чарт","Failed to create new chart object"}, {"Не удалось добавить объект-чарт в коллекцию","Failed to add chart object to collection"}, {"Нельзя открыть новый график, так как количество открытых графиков уже максимальное","You cannot open a new chart, since the number of open charts is already maximum"}, {"Открыт график","Open chart"}, {"Закрыт график","Closed chart"}, };
◍ 把图表事件与整数属性做成可枚举常量
想在 EA 或指标里监听 MT5 图表自身的变化,第一步是把散落的事件码收拢成一个枚举。下面这段把图表开关、窗口增减、指标增删与参数改动都列进 ENUM_CHART_OBJ_EVENT,首成员从 SIGNAL_MQL5_EVENTS_NEXT_CODE 起算,避免和系统事件撞码。 末尾用宏 CHART_OBJ_EVENTS_NEXT_CODE 取最后一个成员加 1,作为自定义后续事件的起始偏移。实战中如果你要挂自己的图形交互事件,就从这个数往下排,能降低事件 ID 冲突概率。 图表整数属性另起一个枚举 ENUM_CHART_PROP_INTEGER,CHART_PROP_ID 固定为 0,紧接的是 TIMEFRAME、SHOW、IS_OBJECT 等。注意 CHART_PROP_EVENT_MOUSE_WHEEL 与 CHART_PROP_EVENT_MOUSE_MOVE 两个开关:不打开它们,鼠标滚轮和移动消息就不会广播给图表上的 MQL5 程序,回测里常被人漏掉导致画线工具没反应。外汇与贵金属杠杆高,这类监听逻辑若误触发批量下单,风险会被放大,参数务必先在模拟盘验证。
enum ENUM_CHART_OBJ_EVENT { CHART_OBJ_EVENT_NO_EVENT = SIGNAL_MQL5_EVENTS_NEXT_CODE, class=class="str">"cmt">// No event CHART_OBJ_EVENT_CHART_OPEN, class=class="str">"cmt">// "New chart opening" event CHART_OBJ_EVENT_CHART_CLOSE, class=class="str">"cmt">// "Chart closure" event CHART_OBJ_EVENT_CHART_WND_ADD, class=class="str">"cmt">// "Adding a new window on the chart" event CHART_OBJ_EVENT_CHART_WND_DEL, class=class="str">"cmt">// "Removing a window from the chart" event CHART_OBJ_EVENT_CHART_WND_IND_ADD, class=class="str">"cmt">// "Adding a new indicator to the chart window" event CHART_OBJ_EVENT_CHART_WND_IND_DEL, class=class="str">"cmt">// "Removing an indicator from the chart window" event CHART_OBJ_EVENT_CHART_WND_IND_CHANGE, class=class="str">"cmt">// "Changing indicator parameters in the chart window" event }; class="macro">#define CHART_OBJ_EVENTS_NEXT_CODE(CHART_OBJ_EVENT_CHART_WND_IND_CHANGE+class="num">1) class=class="str">"cmt">// The code of the next event after the last chart event code enum ENUM_CHART_PROP_INTEGER { CHART_PROP_ID = class="num">0, class=class="str">"cmt">// Chart ID CHART_PROP_TIMEFRAME, class=class="str">"cmt">// Chart timeframe CHART_PROP_SHOW, class=class="str">"cmt">// Price chart drawing CHART_PROP_IS_OBJECT, class=class="str">"cmt">// Chart object(OBJ_CHART) identification attribute CHART_PROP_BRING_TO_TOP, class=class="str">"cmt">// Show chart above all others CHART_PROP_CONTEXT_MENU, class=class="str">"cmt">// Enable/disable access to the context menu using the right click CHART_PROP_CROSSHAIR_TOOL, class=class="str">"cmt">// Enable/disable access to the Crosshair tool using the middle click CHART_PROP_MOUSE_SCROLL, class=class="str">"cmt">// Scroll the chart horizontally using the left mouse button CHART_PROP_EVENT_MOUSE_WHEEL, class=class="str">"cmt">// Send messages about mouse wheel events(CHARTEVENT_MOUSE_WHEEL) to all MQL5 programs on a chart CHART_PROP_EVENT_MOUSE_MOVE, class=class="str">"cmt">// Send messages about mouse button click and movement events(CHARTEVENT_MOUSE_MOVE) to all MQL5 programs on a chart };
图表属性枚举里藏着的事件与显示开关
在 MT5 的图表属性体系里,有一批以 CHART_PROP_ 开头的枚举值,专门控制图表的事件广播与视觉元素。上面这段罗列了从对象创建/删除事件到各种显示线条、网格、成交量等共 25 个属性常量,它们都能作为 ChartGetInteger / ChartSetInteger 的第二个参数直接读写。 比如 CHART_PROP_EVENT_OBJECT_CREATE 与 CHART_PROP_EVENT_OBJECT_DELETE,开启后图表上任意 EA 或指标画的对象被增删,都会向同图所有 MQL5 程序广播 CHARTEVENT_OBJECT_CREATE / DELETE 事件,多程序协作时靠这个互通有无。 CHART_PROP_SCALEFIX_11 是 1:1 比例模式,价格点数和屏幕像素一一对应,做精确形态测量时比普通缩放更不容易看走样;CHART_PROP_SHOW_BID_LINE 和 CHART_PROP_SHOW_ASK_LINE 则决定买卖价水平线是否常驻。外汇与贵金属杠杆高、滑点随机,这类显示开关只影响观察效率,不改变任何成交概率。 打开 MT5 按 F4 进 MetaEditor,随便建个脚本用 ChartSetInteger(0, CHART_PROP_SHOW_ASK_LINE, true) 跑一下,就能直接验证这些属性是否如枚举注释所说即时生效。
CHART_PROP_EVENT_OBJECT_CREATE, class=class="str">"cmt">// Send messages about the graphical object creation event(CHARTEVENT_OBJECT_CREATE) to all MQL5 programs on a chart CHART_PROP_EVENT_OBJECT_DELETE, class=class="str">"cmt">// Send messages about the graphical object destruction event(CHARTEVENT_OBJECT_DELETE) to all MQL5 programs on a chart CHART_PROP_MODE, class=class="str">"cmt">// Type of the chart(candlesticks, bars or line(ENUM_CHART_MODE)) CHART_PROP_FOREGROUND, class=class="str">"cmt">// Price chart in the foreground CHART_PROP_SHIFT, class=class="str">"cmt">// Mode of shift of the price chart from the right border CHART_PROP_AUTOSCROLL, class=class="str">"cmt">// The mode of automatic shift to the right border of the chart CHART_PROP_KEYBOARD_CONTROL, class=class="str">"cmt">// Allow managing the chart using a keyboard CHART_PROP_QUICK_NAVIGATION, class=class="str">"cmt">// Allow the chart to intercept Space and Enter key strokes to activate the quick navigation bar CHART_PROP_SCALE, class=class="str">"cmt">// Scale CHART_PROP_SCALEFIX, class=class="str">"cmt">// Fixed scale mode CHART_PROP_SCALEFIX_11, class=class="str">"cmt">// class="num">1:class="num">1 scale mode CHART_PROP_SCALE_PT_PER_BAR, class=class="str">"cmt">// The mode of specifying the scale in points per bar CHART_PROP_SHOW_TICKER, class=class="str">"cmt">// Display a symbol ticker in the upper left corner CHART_PROP_SHOW_OHLC, class=class="str">"cmt">// Display OHLC values in the upper left corner CHART_PROP_SHOW_BID_LINE, class=class="str">"cmt">// Display Bid value as a horizontal line on the chart CHART_PROP_SHOW_ASK_LINE, class=class="str">"cmt">// Display Ask value as a horizontal line on a chart CHART_PROP_SHOW_LAST_LINE, class=class="str">"cmt">// Display Last value as a horizontal line on a chart CHART_PROP_SHOW_PERIOD_SEP, class=class="str">"cmt">// Display vertical separators between adjacent periods CHART_PROP_SHOW_GRID, class=class="str">"cmt">// Display a grid on the chart CHART_PROP_SHOW_VOLUMES, class=class="str">"cmt">// Display volumes on a chart CHART_PROP_SHOW_OBJECT_DESCR class=class="str">"cmt">// Display text descriptions of objects