DoEasy 函数库中的图形(第八十三部分):抽象标准图形对象类(基础篇)
◍ 给标准图形对象造一个抽象基类
在 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()——这套分层让后续写外汇画线工具或贵金属标记面板时,能直接继承拿现成属性管。
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 把下面这段枚举贴进头文件,编译后打印某个对象的枚举值,就能确认事件回调里拿到的是哪类图形元素。
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 循环里精准过滤,不会误删客户端默认对象。高风险品种上脚本权限出错可能瞬间清掉关键位,上线前务必在策略测试器跑一遍对象增删。
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 在真实波动中绘制失效。直接把下面代码贴进头文件,编译后用一个矩形标签验证句柄非零即可。
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