DoEasy 函数库中的图形(第九十二部分):标准图形对象记忆类。 对象属性变更历史记录(基础篇)
📘

DoEasy 函数库中的图形(第九十二部分):标准图形对象记忆类。 对象属性变更历史记录(基础篇)

第 1/3 篇

「给标准图形对象装上变更记忆」

在 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 里建几个箭头和线段就能复现。外汇与贵金属品种波动剧烈,这类对象状态管理只解决代码层级混乱,不预示任何价格方向。

MQL5 / C++
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,确认你的画线落在哪一组再动手。

MQL5 / C++
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 终端的对象事件开销偏高。

MQL5 / C++
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 即可,不用自己维护数组。

MQL5 / C++
  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

常见问题

给标准图形对象套一个变更记忆类,把每次属性改动写进历史数组,后面直接读数组就能看到前世。
按对象物种用 Species 枚举定型,排序分组只管显示层级,两者分开赋值就不会互相覆盖。
可以,小布能盯住品种页上的图形对象变动并生成变更记忆摘要,你打开对应页就能直接看。
物种字段定对象是什么类型,排序枚举剩余字段管在同物种里排第几,前者分类后者排队。
剩余字段可留作自定义排序位,但别和已有整数属性冲突,先打印枚举值确认没被占用再写。