DoEasy 函数库中的图形(第八十三部分):抽象标准图形对象类(基础篇)
📘

DoEasy 函数库中的图形(第八十三部分):抽象标准图形对象类(基础篇)

第 1/3 篇

◍ 给标准图形对象造一个抽象基类

在 DoEasy 函数库里,第八十三步是把 MT5 里几十种标准图形对象(趋势线、矩形、箭头等)收敛到一个抽象基类下面。原先每加一种对象就要重写一遍属性读写,现在统一接口后,上层策略只管调基类方法,不用关心具体是哪种图形。 抽象类的核心是把图形共有的坐标、颜色、宽度、可视状态做成受保护成员,派生类只补差异化字段。这样在 EA 里批量管理图表对象时,代码量能砍掉一大截,也方便后面接 AIGC 分析做自动标注。 这套改动已在 2021-11-01 的库版本落地,原文示例里附了基类头文件结构。开 MT5 把 DoEasy 拉进 include 目录,搜 CChartObject 派生关系就能看到这次抽象层的实际代码位置。外汇与贵金属图表对象自动化存在滑点与平面切换风险,回测与实盘对象行为可能不一致。

「从抽象基类接管所有图形对象的公共属性」

要把手工画在图表上的标准图形也纳入函数库管理,前提是先有一层能覆盖全部图形公共属性的抽象对象类。它不绑定某一种图形,只负责那些任意对象都能用 ObjectGetInteger、ObjectGetDouble、ObjectGetString 读到的字段。 具体做法是用一个通用抽象类承载公共属性与方法,再让箭头、趋势线等具体类继承它。终端里的箭头对象有箭头代码却没有向右射线,趋势线有向右射线却没有箭头代码——这类只属于单一图形的属性,要从基类下放到对应衍生类,避免不该访问的对象误用接口。 举例来说,箭头代码设置方法只进箭头图标类,向右射线设置方法只进趋势线类;衍生对象既能用自身专有方法,也能用抽象类里那套公用属性方法。 在真正写抽象类之前,还有前置活:往函数库枚举里补属性项、给每个图形属性写定义、再做返回部分属性定义的辅助函数。这部分准备量不小,因为图形对象属性种类本身很多。

给图形对象抽象层补上类型与属性骨架

做通用图形对象库时,光有标准终端对象类型不够,还得塞进一层“抽象图形元素”。在 Defines.mqh 的 ENUM_OBJECT_DE_TYPE 里,常量从 COLLECTION_ID_LIST_END+1 起算,新增了 GBASE、GELEMENT、GFORM、GSHADOW 及一组动画帧类型,这样单列表存的所有对象能按元素类型快速过滤——比如只拉“标准图形对象”就能剔除动画帧噪音。 隶属关系常量也从 GRAPH_OBJ_BELONG_TERMINAL 改成 GRAPH_OBJ_BELONG_NO_PROGRAM,语义上更准:对象可能归别的程序管,不一定绑在终端。配套要补三套属性枚举(整数/实数/字符串),加排序枚举,并在 Data.mqh 加消息索引、在 DELib.mqh 写 LineStyleDescription() 等返回枚举描述的服务函数,逻辑都是收一个类型变量、比对后调 CMessage::Text() 吐描述。 GBaseObj.mqh 是所有图表对象的基类。受保护区声明公用变量,公开区写 Set/Get 方法:普通属性直接存变量;图形对象自身属性要走异步调用 ObjectSetXXX(),命令进队列就立刻返回,不保证落地。帮助里说可用 ObjectGetXXX() 回读确认,但那会排到队列尾、拖慢大量对象处理,所以先假设成功,真碰到变量设了对象没动再补校验。 基类构造给所有变量赋默认,衍生类再按自身属性覆盖。GraphObjectType() 把类型值算成 ENUM_OBJECT_DE_TYPE,描述方法里标准对象调 StdGraphObjectTypeDescription(),其余调 TypeElementDescription()——这套分层让后续写外汇画线工具或贵金属标记面板时,能直接继承拿现成属性管。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| List of library object types                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_OBJECT_DE_TYPE
  {
class=class="str">"cmt">//--- Graphics
   OBJECT_DE_TYPE_GBASE =   COLLECTION_ID_LIST_END+class="num">1,                class=class="str">"cmt">// "Base object of all library graphical objects" object type
   OBJECT_DE_TYPE_GELEMENT,                                          class=class="str">"cmt">// "Graphical element" object type
   OBJECT_DE_TYPE_GFORM,                                             class=class="str">"cmt">// Form object type
   OBJECT_DE_TYPE_GSHADOW,                                           class=class="str">"cmt">// Shadow object type
class=class="str">"cmt">//--- Animation
   OBJECT_DE_TYPE_GFRAME,                                            class=class="str">"cmt">// "Single animation frame" object type
   OBJECT_DE_TYPE_GFRAME_TEXT,                                       class=class="str">"cmt">// "Single text animation frame" object type
   OBJECT_DE_TYPE_GFRAME_QUAD,                                       class=class="str">"cmt">// "Single rectangular animation frame" object type
   OBJECT_DE_TYPE_GFRAME_GEOMETRY,                                   class=class="str">"cmt">// "Single geometric animation frame" object type
   OBJECT_DE_TYPE_GANIMATIONS,                                       class=class="str">"cmt">// "Animations" object type
class=class="str">"cmt">//--- Managing graphical objects

◍ 图形对象类型的枚举偏移写法

在 MT5 自定义事件系统里,图形对象被归到 OBJECT_DE_TYPE_GELEMENT_CONTROL 这一大类之下,而标准图形对象单独用 OBJECT_DE_TYPE_GSTD_OBJ 做基类型。 具体每种画线对象并不是直接写死常量,而是以 OBJECT_DE_TYPE_GSTD_OBJ + 1 + OBJ_XXX 的形式做枚举偏移。比如垂直线就是 OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_VLINE,趋势线是 OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_TREND,标准差通道则是 OBJECT_DE_TYPE_GSTD_OBJ+1+OBJ_STDDEVCHANNEL。 这套偏移覆盖了从 VLINE、HLINE、TREND 到 CYCLES、CHANNEL、PITCHFORK、GANNFAN 等十余种标准对象。你在写对象管理 EA 时,直接拿这些复合枚举去区分事件来源,比判断对象名称字符串更稳。 开 MT5 把下面这段枚举贴进头文件,编译后打印某个对象的枚举值,就能确认事件回调里拿到的是哪类图形元素。

MQL5 / C++
OBJECT_DE_TYPE_GELEMENT_CONTROL,                                                                              class=class="str">"cmt">// "Managing graphical objects" object type
class=class="str">"cmt">//--- Standard graphical objects
  OBJECT_DE_TYPE_GSTD_OBJ,                                                                                        class=class="str">"cmt">// "Standard graphical object" object type
  OBJECT_DE_TYPE_GSTD_VLINE       =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_VLINE,                                          class=class="str">"cmt">// "Vertical line" object type
  OBJECT_DE_TYPE_GSTD_HLINE       =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_HLINE,                                          class=class="str">"cmt">// "Horizontal line" object type
  OBJECT_DE_TYPE_GSTD_TREND       =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_TREND,                                          class=class="str">"cmt">// "Trend line" object type
  OBJECT_DE_TYPE_GSTD_TRENDBYANGLE=  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_TRENDBYANGLE,                                   class=class="str">"cmt">// "Trend line by angle" object type
  OBJECT_DE_TYPE_GSTD_CYCLES      =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_CYCLES,                                         class=class="str">"cmt">// "Cyclic lines" object type
  OBJECT_DE_TYPE_GSTD_ARROWED_LINE=  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROWED_LINE,                                   class=class="str">"cmt">// "Arrowed line" object type
  OBJECT_DE_TYPE_GSTD_CHANNEL     =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_CHANNEL,                                        class=class="str">"cmt">// "Equidistant channel" object type
  OBJECT_DE_TYPE_GSTD_STDDEVCHANNEL= OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_STDDEVCHANNEL,                                  class=class="str">"cmt">// "Standard deviation channel" object type
  OBJECT_DE_TYPE_GSTD_REGRESSION  =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_REGRESSION,                                     class=class="str">"cmt">// "Linear regression channel" object type
  OBJECT_DE_TYPE_GSTD_PITCHFORK   =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_PITCHFORK,                                      class=class="str">"cmt">// "Andrews&class="macro">#x27; pitchfork" object type
  OBJECT_DE_TYPE_GSTD_GANNLINE    =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_GANNLINE,                                       class=class="str">"cmt">// "Gann line" object type
  OBJECT_DE_TYPE_GSTD_GANNFAN     =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_GANNFAN,                                         class=class="str">"cmt">// "Gann fan" object type
  OBJECT_DE_TYPE_GSTD_GANNGRID    =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_GANNGRID                                        class=class="str">"cmt">// "Gann grid" object type

「图形对象类型的枚举偏移写法」

在 MT5 自定义指标或 EA 里批量管理图表对象时,直接吃原生 OBJ_* 宏容易和标准图形类型撞车。上面这段把斐波那契、艾略特、几何框以及箭头类都统一挂到 OBJECT_DE_TYPE_GSTD_OBJ 基值上,再 +1 做区隔,等于自己划了一块类型私域。 以 OBJECT_DE_TYPE_GSTD_FIBO = OBJECT_DE_TYPE_GSTD_OBJ + 1 + OBJ_FIBO 为例,若基值为 1000、OBJ_FIBO 在终端里是 20,那该斐波那契级别对象类型就会被编成 1021。回测或日志里看到 1021,你就知道是自家脚本画的 Fibo 而非用户手动拉的。 这种写法对外汇、贵金属图表自动化有意义:XAUUSD 的 15 分钟图上常叠十几条 Fibo 和矩形区,用偏移枚举能在 ObjectDelete / ObjectFind 循环里精准过滤,不会误删客户端默认对象。高风险品种上脚本权限出错可能瞬间清掉关键位,上线前务必在策略测试器跑一遍对象增删。

MQL5 / C++
OBJECT_DE_TYPE_GSTD_FIBO                 =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_FIBO,          class=class="str">"cmt">// "Fibo levels" object type
OBJECT_DE_TYPE_GSTD_FIBOTIMES             =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_FIBOTIMES,      class=class="str">"cmt">// "Fibo time zones" object type
OBJECT_DE_TYPE_GSTD_FIBOFAN               =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_FIBOFAN,        class=class="str">"cmt">// "Fibo fan" object type
OBJECT_DE_TYPE_GSTD_FIBOARC               =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_FIBOARC,        class=class="str">"cmt">// "Fibo arcs" object type
OBJECT_DE_TYPE_GSTD_FIBOCHANNEL           =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_FIBOCHANNEL,    class=class="str">"cmt">// "Fibo channel" object type
OBJECT_DE_TYPE_GSTD_EXPANSION             =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_EXPANSION,      class=class="str">"cmt">// "Fibo expansion" object type
OBJECT_DE_TYPE_GSTD_ELLIOTWAVE5           =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ELLIOTWAVE5,    class=class="str">"cmt">// "Elliott class="num">5 waves" object type
OBJECT_DE_TYPE_GSTD_ELLIOTWAVE3           =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ELLIOTWAVE3,    class=class="str">"cmt">// "Elliott class="num">3 waves" object type
OBJECT_DE_TYPE_GSTD_RECTANGLE             =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_RECTANGLE,      class=class="str">"cmt">// "Rectangle" object type
OBJECT_DE_TYPE_GSTD_TRIANGLE              =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_TRIANGLE,       class=class="str">"cmt">// "Triangle" object type
OBJECT_DE_TYPE_GSTD_ELLIPSE               =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ELLIPSE,        class=class="str">"cmt">// "Ellipse" object type
OBJECT_DE_TYPE_GSTD_ARROW_THUMB_UP        =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_THUMB_UP, class=class="str">"cmt">// "Thumb up" object type
OBJECT_DE_TYPE_GSTD_ARROW_THUMB_DOWN      =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_THUMB_DOWN, class=class="str">"cmt">// "Thumb down" object type
OBJECT_DE_TYPE_GSTD_ARROW_UP              =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_UP,        class=class="str">"cmt">// "Arrow up" object type
OBJECT_DE_TYPE_GSTD_ARROW_DOWN            =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_DOWN,      class=class="str">"cmt">// "Arrow down" object type
OBJECT_DE_TYPE_GSTD_ARROW_STOP            =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_STOP,      class=class="str">"cmt">// "Stop sign" object type

图形对象枚举的收口定义

在自定义图形库里,标准对象类型用 OBJECT_DE_TYPE_GSTD_OBJ 基值加 1 再叠加 MQL5 原生宏来映射。上面这段把勾选箭头、左右价签、买卖箭头、文本、标签、按钮、子图、位图、编辑框、财经事件、矩形标签全部枚举完毕,共覆盖 15 类原生图形。 最后一行 OBJECT_DE_TYPE_BASE 取矩形标签枚举值再加 1,作为后续所有库对象的基类起点。你在 MT5 里改这套枚举时,注意基值偏移量写错会导致 ObjectCreate 返回无效类型句柄。 外汇与贵金属图表加载这类自定义 UI 属高风险操作,参数错配可能让 EA 在真实波动中绘制失效。直接把下面代码贴进头文件,编译后用一个矩形标签验证句柄非零即可。

MQL5 / C++
  OBJECT_DE_TYPE_GSTD_ARROW_CHECK        =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_CHECK,     class=class="str">"cmt">// "Check mark" object type
  OBJECT_DE_TYPE_GSTD_ARROW_LEFT_PRICE   =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_LEFT_PRICE, class=class="str">"cmt">// "Left price label" object type
  OBJECT_DE_TYPE_GSTD_ARROW_RIGHT_PRICE  =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_RIGHT_PRICE,class=class="str">"cmt">// "Right price label" object type
  OBJECT_DE_TYPE_GSTD_ARROW_BUY          =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_BUY,         class=class="str">"cmt">// "Buy sign" object type
  OBJECT_DE_TYPE_GSTD_ARROW_SELL         =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW_SELL,        class=class="str">"cmt">// "Sell sign" object type
  OBJECT_DE_TYPE_GSTD_ARROW              =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_ARROW,             class=class="str">"cmt">// "Arrow" object type
  OBJECT_DE_TYPE_GSTD_TEXT               =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_TEXT,             class=class="str">"cmt">// "Text" object type
  OBJECT_DE_TYPE_GSTD_LABEL              =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_LABEL,            class=class="str">"cmt">// "Text label" object type
  OBJECT_DE_TYPE_GSTD_BUTTON             =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_BUTTON,           class=class="str">"cmt">// "Button" object type
  OBJECT_DE_TYPE_GSTD_CHART              =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_CHART,            class=class="str">"cmt">// "Chart" object type
  OBJECT_DE_TYPE_GSTD_BITMAP             =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_BITMAP,           class=class="str">"cmt">// "Bitmap" object type
  OBJECT_DE_TYPE_GSTD_BITMAP_LABEL       =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_BITMAP_LABEL,     class=class="str">"cmt">// "Bitmap label" object type
  OBJECT_DE_TYPE_GSTD_EDIT               =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_EDIT,             class=class="str">"cmt">// "Input field" object type
  OBJECT_DE_TYPE_GSTD_EVENT              =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_EVENT,            class=class="str">"cmt">// "Event object which corresponds to an event in Economic Calendar" object type
  OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL    =  OBJECT_DE_TYPE_GSTD_OBJ+class="num">1+OBJ_RECTANGLE_LABEL,  class=class="str">"cmt">// "Rectangle Label object used to create and design the custom graphical interface" object type

class=class="str">"cmt">//--- Objects
  OBJECT_DE_TYPE_BASE  =  OBJECT_DE_TYPE_GSTD_RECTANGLE_LABEL+class="num">1, class=class="str">"cmt">// Base object for all library objects

常见问题

先抽象出一个标准图形对象的基类,把颜色、宽度、样式、可见性等公共属性都放进去,具体对象继承即可,避免重复代码。
用带偏移量的枚举写法,比如基础类型从固定值起跳,后续扩展只往后加,不改动已有偏移,保证旧编号稳定。
小布可以扫描当前品种图表上的标准图形对象,按抽象层定义核对类型与公共属性,把异常对象列出来给你看。
类型与属性骨架放在抽象层的收口定义里,具体对象类只实现自己独有的行为,这样类型判断和属性读写都不散落。
收口定义显式标出枚举边界,循环或校验时直接用边界判断,比依赖最后一个元素更安全,也方便以后插中间类型。