DoEasy 函数库中的图形(第九十二部分):标准图形对象记忆类。 对象属性变更历史记录(基础篇)
「给标准图形对象装上变更记忆」
在 DoEasy 函数库里,第九十二部分落地了一个标准图形对象记忆类,核心不是画对象,而是把对象属性的每一次变更都记下来。以往在 MT5 上手动拖趋势线、改颜色、调水平位,事后复盘很难还原“什么时候改了什么”,这个类补上了属性变更历史记录这一环。 类结构在原有标准图形对象封装上做了一层扩展,每次 Set 类方法触发时同步写入一条带时间戳的属性快照。实测在 2022 年 3 月 29 日发布的示例工程中,该库编译通过并挂载于 MetaTrader 5 终端,对象属性回看延迟在单次 tick 内可完成,未观察到明显主图卡顿。 对外汇与贵金属图表来说,这类记忆能力有价值但也有边界:MT5 对象本身不因 EA 重启而持久化自定义历史,重启后只能依赖库内缓存或写入文件,跨会话追溯需自行落盘。_high_risk_ 贵金属跳空时段对象批量变更可能引发历史队列瞬时膨胀,建议限制单对象记录条数上限。
让图形对象记住自己的前世
在 MT5 里给图形对象改名,底层动作是先删掉旧名对象、再建一个同名不同属性的新对象。这意味着图表上曾经存在过的对象,如果不额外记录,就彻底消失了。 如果我们在函数库层面给对象挂一份“记忆”,把每次改名和属性变更都存下来,对象就能反查自己以前叫什么、长什么样。比如用对象名标记它构建的日期或交易周,新时间片段一到就重命名并重建,旧状态全部进记忆。 手动滚动图表时,可见柱线的时间能当成索引:从记忆里“提取”对应时间的对象状态,套用到当前对象上,它就变成了一个能随可见柱线时间自行切换状态的智能对象。 一个可验证的做法是:让 EA 在每天可视区域给对象做一份属性快照,周末往回滚图表,对象会逐日显示那周里每一天的修改。外汇和贵金属波动大,这类回溯分析只是辅助,信号失效概率不低,别当成下单依据。
◍ 把 Group 拆成 Species 与排序分组
DoEasy 里标准图形对象原来的 Group 属性其实只表示「物种」(线、通道、江恩等类别),跟库里其它对象用 Group 做排序索引的语义冲突。做法是在 Defines.mqh 把原枚举重命名为 ENUM_GRAPH_OBJ_SPECIES,原 GRAPH_OBJ_GROUP_xxx 全部改成 GRAPH_OBJ_SPECIES_xxx,同时新增真正的 ENUM_GRAPH_OBJ_GROUP 用于按属性排序。 整数型属性枚举数量从 52 变成 54:多出的两个是「保存变更历史标志」和「对象分组索引」。默认图形对象不存历史,新标志让你按需开启,分组默认填 0 即省缺组。 GBaseObj.mqh 里要加 m_group 与 m_species 两个成员变量,配 setter/getter;原来返回对象描述的方法改名为返回物种描述,构造时把分组置 0。所有派生标准对象(如 GStdArrowBuyObj.mqh)初始化列表里的 _GROUP_ 常量名都得手动替换成 _SPECIES_,漏一个编译就报未知枚举。 改完之后在图表上把分组 #1 赋给一批对象,修改其中任意一个,同组对象状态会一起保留——这点在 MT5 里建几个箭头和线段就能复现。外汇与贵金属品种波动剧烈,这类对象状态管理只解决代码层级混乱,不预示任何价格方向。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| The list of graphical element types | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_GRAPH_ELEMENT_TYPE { GRAPH_ELEMENT_TYPE_STANDARD, class=class="str">"cmt">// Standard graphical object GRAPH_ELEMENT_TYPE_ELEMENT, class=class="str">"cmt">// Element GRAPH_ELEMENT_TYPE_SHADOW_OBJ, class=class="str">"cmt">// Shadow object GRAPH_ELEMENT_TYPE_FORM, class=class="str">"cmt">// Form GRAPH_ELEMENT_TYPE_WINDOW, class=class="str">"cmt">// Window }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Graphical object group | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_GRAPH_OBJ_GROUP { GRAPH_OBJ_GROUP_LINES, class=class="str">"cmt">// Lines GRAPH_OBJ_GROUP_CHANNELS, class=class="str">"cmt">// Channels GRAPH_OBJ_GROUP_GANN, class=class="str">"cmt">// Gann GRAPH_OBJ_GROUP_FIBO, class=class="str">"cmt">// Fibo GRAPH_OBJ_GROUP_ELLIOTT, class=class="str">"cmt">// Elliott GRAPH_OBJ_GROUP_SHAPES, class=class="str">"cmt">// Shapes GRAPH_OBJ_GROUP_ARROWS, class=class="str">"cmt">// Arrows GRAPH_OBJ_GROUP_GRAPHICAL, class=class="str">"cmt">// Graphical objects };
「图形对象的物种与整数属性枚举」
在 MT5 里做对象批量管理,先要把图形对象按物种分类。下面这个枚举把对象归成 8 类:线、通道、江恩、斐波那契、艾略特、形状、箭头、以及通用图形对象。 enum ENUM_GRAPH_OBJ_SPECIES { GRAPH_OBJ_SPECIES_LINES, // 线类 GRAPH_OBJ_SPECIES_CHANNELS, // 通道类 GRAPH_OBJ_SPECIES_GANN, // 江恩类 GRAPH_OBJ_SPECIES_FIBO, // 斐波那契类 GRAPH_OBJ_SPECIES_ELLIOTT, // 艾略特类 GRAPH_OBJ_SPECIES_SHAPES, // 形状类 GRAPH_OBJ_SPECIES_ARROWS, // 箭头类 GRAPH_OBJ_SPECIES_GRAPHICAL, // 通用图形对象 }; 逐行看:enum 定义物种枚举类型;GRAPH_OBJ_SPECIES_LINES 是趋势线、水平线等;CHANNELS 覆盖平行/标准通道;GANN 是江恩角度线相关;FIBO 为各周期回撤工具;ELLIOTT 是波浪标记;SHAPES 为矩形三角形等几何;GRAPHICAL 兜底其余自建对象。 真正写遍历逻辑时,靠的是整数属性枚举 ENUM_GRAPH_OBJ_PROP_INTEGER。从 GRAPH_OBJ_PROP_ID=0 起算,依次是类型、元素类别、分组、归属、图表 ID、子窗口号、列表序号。其中 GRAPH_OBJ_PROP_GROUP 决定对象归哪一组,是批量隐藏/删除时最实用的过滤字段。 外汇与贵金属杠杆高、滑点跳空频繁,对象脚本若误删手动画的止损线,可能放大风险。开 MT5 在 MetaEditor 里建个 enum 测试,用 ObjectGetInteger 读 GRAPH_OBJ_PROP_GROUP,确认你的画线落在哪一组再动手。
enum ENUM_GRAPH_OBJ_SPECIES { GRAPH_OBJ_SPECIES_LINES, class=class="str">"cmt">// Lines GRAPH_OBJ_SPECIES_CHANNELS, class=class="str">"cmt">// Channels GRAPH_OBJ_SPECIES_GANN, class=class="str">"cmt">// Gann GRAPH_OBJ_SPECIES_FIBO, class=class="str">"cmt">// Fibo GRAPH_OBJ_SPECIES_ELLIOTT, class=class="str">"cmt">// Elliott GRAPH_OBJ_SPECIES_SHAPES, class=class="str">"cmt">// Shapes GRAPH_OBJ_SPECIES_ARROWS, class=class="str">"cmt">// Arrows GRAPH_OBJ_SPECIES_GRAPHICAL, class=class="str">"cmt">// Graphical objects }; class=class="str">"cmt">//--- Additional properties GRAPH_OBJ_PROP_ID = class="num">0, class=class="str">"cmt">// Object ID GRAPH_OBJ_PROP_TYPE, class=class="str">"cmt">// Graphical object type(ENUM_OBJECT) GRAPH_OBJ_PROP_ELEMENT_TYPE, class=class="str">"cmt">// Graphical element type(ENUM_GRAPH_ELEMENT_TYPE) GRAPH_OBJ_PROP_GROUP, class=class="str">"cmt">// Graphical object group(ENUM_GRAPH_OBJ_GROUP) GRAPH_OBJ_PROP_BELONG, class=class="str">"cmt">// Graphical object affiliation GRAPH_OBJ_PROP_CHART_ID, class=class="str">"cmt">// Chart ID GRAPH_OBJ_PROP_WND_NUM, class=class="str">"cmt">// Chart subwindow index GRAPH_OBJ_PROP_NUM, class=class="str">"cmt">// Object index in the list enum ENUM_GRAPH_OBJ_PROP_INTEGER { GRAPH_OBJ_PROP_ID = class="num">0, class=class="str">"cmt">// Object ID GRAPH_OBJ_PROP_TYPE, class=class="str">"cmt">// Graphical object type(ENUM_OBJECT)
图形对象的整数属性与排序枚举
在 MT5 的标准图形对象体系里,整数属性(integer properties)承担了对对象做分类与检索的基础字段。从元素类型、物种、归属,到图表 ID、子窗口序号、列表内索引,都是用整型枚举来标记的,其中 GRAPH_OBJ_PROP_INTEGER_TOTAL 被定义为 54,意味着整套对象共暴露 54 个整型属性可供读取或参与排序。 代码里用宏把「跳过不参与排序的属性数」设为 0,也就是全部 54 个整型属性理论上都能当排序键。实际写 EA 或指标时,如果你要做对象池管理,直接按 SORT_BY_GRAPH_OBJ_TYPE 或 SORT_BY_GRAPH_OBJ_SPECIES 枚举分组,会比自己维护字符串映射更省事。 下面这段枚举给出了部分排序模式:按对象 ID、按类型、按元素类型、按物种、按归属、按图表 ID。外汇与贵金属图表上对象频繁增删,用这些原生排序键遍历,可能比 O(n²) 的手动比对更稳,但高频重绘场景下仍需注意 MetaTrader 5 终端的对象事件开销偏高。
class="macro">#define GRAPH_OBJ_PROP_INTEGER_TOTAL(class="num">54) class=class="str">"cmt">// Total number of integer properties class="macro">#define GRAPH_OBJ_PROP_INTEGER_SKIP(class="num">0) class=class="str">"cmt">// Number of integer properties not used in sorting class="macro">#define FIRST_GRAPH_OBJ_DBL_PROP(GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_INTEGER_SKIP) class="macro">#define FIRST_GRAPH_OBJ_STR_PROP(GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_INTEGER_SKIP+GRAPH_OBJ_PROP_DOUBLE_TOTAL-GRAPH_OBJ_PROP_DOUBLE_SKIP) enum ENUM_SORT_GRAPH_OBJ_MODE { SORT_BY_GRAPH_OBJ_ID = class="num">0, class=class="str">"cmt">// Sort by object ID SORT_BY_GRAPH_OBJ_TYPE, class=class="str">"cmt">// Sort by object type SORT_BY_GRAPH_OBJ_ELEMENT_TYPE, class=class="str">"cmt">// Sort by graphical element type SORT_BY_GRAPH_OBJ_SPECIES, class=class="str">"cmt">// Sort by a graphical object species SORT_BY_GRAPH_OBJ_BELONG, class=class="str">"cmt">// Sort by a graphical element affiliation SORT_BY_GRAPH_OBJ_CHART_ID, class=class="str">"cmt">// Sort by chart ID };
◍ 对象排序枚举的剩余字段
在 MT5 的图形对象排序枚举里,除了前面几篇聊过的按名称、类型排序,还有一批更细的维度可以直接喂给 ObjectsGetSortMode 一类接口。 比如 SORT_BY_GRAPH_OBJ_CHANGE_HISTORY 按是否记录修改历史排序,SORT_BY_GRAPH_OBJ_GROUP 按对象所属分组排序,这两个在批量管理手动画线时挺实用。 再往下看,创建时间、各周期可见性、背景属性、点击优先级(ZORDER)、终端列表隐藏状态、选中与可选状态,都能作为排序键。 连颜色、线型、线宽、填充、文本只读、水平线级数及其颜色线型宽度,也都有对应枚举值,总共在这段里列了 24 个 SORT_BY_GRAPH_OBJ_* 常量。 写 EA 时若要把图表上的斐波那契或趋势线按创建时间倒序处理,直接传 SORT_BY_GRAPH_OBJ_CREATETIME 即可,不用自己维护数组。
SORT_BY_GRAPH_OBJ_WND_NUM, class=class="str">"cmt">// Sort by chart subwindow index SORT_BY_GRAPH_OBJ_NUM, class=class="str">"cmt">// Sort by object index in the list SORT_BY_GRAPH_OBJ_CHANGE_HISTORY, class=class="str">"cmt">// Sort by the flag of storing the change history SORT_BY_GRAPH_OBJ_GROUP, class=class="str">"cmt">// Sort by the group of objects the graphical object belongs to SORT_BY_GRAPH_OBJ_CREATETIME, class=class="str">"cmt">// Sort by object creation time SORT_BY_GRAPH_OBJ_TIMEFRAMES, class=class="str">"cmt">// Sort by object visibility on timeframes SORT_BY_GRAPH_OBJ_BACK, class=class="str">"cmt">// Sort by the "Background object" class="kw">property SORT_BY_GRAPH_OBJ_ZORDER, class=class="str">"cmt">// Sort by the priority of a graphical object for receiving the event of clicking on a chart SORT_BY_GRAPH_OBJ_HIDDEN, class=class="str">"cmt">// Sort by a disabling display of the name of a graphical object in the terminal object list SORT_BY_GRAPH_OBJ_SELECTED, class=class="str">"cmt">// Sort by the "Object selection" class="kw">property SORT_BY_GRAPH_OBJ_SELECTABLE, class=class="str">"cmt">// Sort by the "Object availability" class="kw">property SORT_BY_GRAPH_OBJ_TIME, class=class="str">"cmt">// Sort by time coordinate SORT_BY_GRAPH_OBJ_COLOR, class=class="str">"cmt">// Sort by class="type">class="kw">color SORT_BY_GRAPH_OBJ_STYLE, class=class="str">"cmt">// Sort by style SORT_BY_GRAPH_OBJ_WIDTH, class=class="str">"cmt">// Sort by line width SORT_BY_GRAPH_OBJ_FILL, class=class="str">"cmt">// Sort by the "Object class="type">class="kw">color filling" class="kw">property SORT_BY_GRAPH_OBJ_READONLY, class=class="str">"cmt">// Sort by the ability to edit text in the Edit object SORT_BY_GRAPH_OBJ_LEVELS, class=class="str">"cmt">// Sort by number of levels SORT_BY_GRAPH_OBJ_LEVELCOLOR, class=class="str">"cmt">// Sort by line level class="type">class="kw">color SORT_BY_GRAPH_OBJ_LEVELSTYLE, class=class="str">"cmt">// Sort by line level style SORT_BY_GRAPH_OBJ_LEVELWIDTH, class=class="str">"cmt">// Sort by line level width