DoEasy 函数库中的图形(第八十三部分):抽象标准图形对象类·综合运用
(3/3)· 从箭头到趋势线,69 节把标准图形对象的公用属性收进一个抽象类,衍生类只管自己的事
- 图表对象枚举的排序维度拆解
- 标准库里的文本宏枚举清单
- 图表控件库的锚点与枚举常量
- 图形对象枚举里的标准画线清单
- 图形对象枚举里的箭头与界面控件
- 图形对象属性枚举的字段语义
- 图形对象属性枚举的散落定义
- 图形对象多语言词条怎么摆
- 图形对象的多语种字典与归属判定
- 对象属性枚举里的俄英对照陷阱
- 图形对象属性的英文映射与样式描述函数
- 把枚举值翻成可读标签的几个辅助函数
- 标准图形对象类型的描述映射
- 图形对象类型的字符串映射尾段
- 线型描述函数的两种写法
- 图形对象结构体的字段拆解
- 图形对象基类的成员变量与Setter封装
- 图形对象的背景与选中属性封装
- 图形对象的可选与隐藏属性设置
- 图形对象的跨周期显隐控制
- 给对象按周期挂可见性开关
- 给图形对象按周期位或打标志
- 子窗口定位与类变量读取的实现细节
- 图形对象的只读属性接口
- 图形对象基类的类型与构造初始化
- 图形对象基类的默认字段与类型描述分发
- 图形对象归属状态的文本映射
- 抽象图形对象基类怎么搭
- 图形对象属性的存取与索引偏移
- 图形对象类的属性读取与排序接口
- 图形对象的身份与归属怎么在类里落地
- 图形对象的归属与显隐属性怎么读
- 图形对象的层级与显隐控制
- 图形对象的可选性与时间锚点封装
- 图形对象的线宽与填充控制
- 给图形对象的多层水平线配色与线型
- 图形对象的属性封装接口
- 射线与图形标记的取设接口
- 箭头与锚点偏移的底层封装
- 图形对象的属性读写封装
- 图表对象的属性读写接口
- 图表对象的缩放与像素尺寸控制
- 图形对象的偏移与底色封装
- 图形对象的锚点角与边框属性怎么读写
- 图形对象的价位数值与角度设定
- 图形对象的偏离量与命名封装
- 图形对象的文字与字体接口
- 图形对象的字体与位图属性接口
- 图形对象的ID与时间锚点读写
- 图形对象派生类的命名与参数化构造
- 图形对象整型属性的批量抓取
- 图形对象的整型属性初始化清单
- 对象属性初始化的底层写法
- 图形对象属性的本地缓存与比较逻辑
- 图形对象全属性比对与日志输出
- 图形对象属性的可读化输出
- 图形对象属性字符串的拼接分支
- 图形对象属性的人类可读转换
- 图形对象属性描述的链式判定
- 图形对象属性描述的嵌套三元分支
- 图形对象面板属性的可读化拼装
- 图形对象属性描述的双类重载实现
- 图形对象属性的文本映射分支
- 图表对象监控类的内部状态与取值接口
- 图表对象控制器的初始化与比较逻辑
- 用 Refresh 抓图表上新增的对象
- 析构里清掉计时器与图表注释
- 借旧 EA 跑通图形对象挂载
- 图形对象类还要往下拆
◍ 图表对象枚举的排序维度拆解
在 MT5 的图表对象处理里,系统用一组枚举值定义了图形对象可按哪些属性排序。这些枚举分三类:界面布局属性、真实数值属性、字符串属性,各自覆盖不同的对象特征。 界面类里能看到 SORT_BY_GRAPH_OBJ_YSIZE 按像素高度排,SORT_BY_GRAPH_OBJ_XOFFSET / YOFFSET 按可见区域左上角坐标排,还有针对 OBJ_EDIT、OBJ_BUTTON、OBJ_RECTANGLE_LABEL 的背景色 SORT_BY_GRAPH_OBJ_BGCOLOR,以及绑定角 SORT_BY_GRAPH_OBJ_CORNER 和边框类型、边框色等。 真实属性从 FIRST_GRAPH_OBJ_DBL_PROP 起算,SORT_BY_GRAPH_OBJ_PRICE 按价格坐标,SORT_BY_GRAPH_OBJ_LEVELVALUE 按水平位值,SORT_BY_GRAPH_OBJ_SCALE 管甘恩和斐波那契弧的缩放,SORT_BY_GRAPH_OBJ_ANGLE 管角度,SORT_BY_GRAPH_OBJ_DEVIATION 管标准差通道偏离。 字符串属性从 FIRST_GRAPH_OBJ_STR_PROP 开始,覆盖名字、描述、提示文本、水平描述、字体、BMP 文件名以及 Chart 对象的周期符号。开 MT5 敲一下这些枚举,能直接验证对象管理器的排序逻辑底层就是这套常量。 另有一段独立的月份文本常量,如 MSG_LIB_TEXT_JANUARY 到 MSG_LIB_TEXT_MARCH,用于本地化消息库,和对象排序同文件但无直接调用关系。
SORT_BY_GRAPH_OBJ_YSIZE, class=class="str">"cmt">// Sort by object height along the Y axis in pixels SORT_BY_GRAPH_OBJ_XOFFSET, class=class="str">"cmt">// Sort by X coordinate of the upper-left corner of the visibility area SORT_BY_GRAPH_OBJ_YOFFSET, class=class="str">"cmt">// Sort by Y coordinate of the upper-left corner of the visibility area SORT_BY_GRAPH_OBJ_BGCOLOR, class=class="str">"cmt">// Sort by background class="type">color for OBJ_EDIT, OBJ_BUTTON and OBJ_RECTANGLE_LABEL SORT_BY_GRAPH_OBJ_CORNER, class=class="str">"cmt">// Sort by chart corner for binding a graphical object SORT_BY_GRAPH_OBJ_BORDER_TYPE, class=class="str">"cmt">// Sort by border type for the "Rectangle border" object SORT_BY_GRAPH_OBJ_BORDER_COLOR, class=class="str">"cmt">// Sort by frame class="type">color for the OBJ_EDIT and OBJ_BUTTON objects class=class="str">"cmt">//--- Sort by real properties SORT_BY_GRAPH_OBJ_PRICE = FIRST_GRAPH_OBJ_DBL_PROP, class=class="str">"cmt">// Sort by price coordinate SORT_BY_GRAPH_OBJ_LEVELVALUE, class=class="str">"cmt">// Sort by level value SORT_BY_GRAPH_OBJ_SCALE, class=class="str">"cmt">// Sort by scale(class="kw">property of Gann objects and Fibonacci Arcs objects) SORT_BY_GRAPH_OBJ_ANGLE, class=class="str">"cmt">// Sort by angle SORT_BY_GRAPH_OBJ_DEVIATION, class=class="str">"cmt">// Sort by a deviation of the standard deviation channel class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties SORT_BY_GRAPH_OBJ_NAME = FIRST_GRAPH_OBJ_STR_PROP, class=class="str">"cmt">// Sort by object name SORT_BY_GRAPH_OBJ_TEXT, class=class="str">"cmt">// Sort by object description SORT_BY_GRAPH_OBJ_TOOLTIP, class=class="str">"cmt">// Sort by tooltip text SORT_BY_GRAPH_OBJ_LEVELTEXT, class=class="str">"cmt">// Sort by level description SORT_BY_GRAPH_OBJ_FONT, class=class="str">"cmt">// Sort by font SORT_BY_GRAPH_OBJ_BMPFILE, class=class="str">"cmt">// Sort by BMP file name for the "Bitmap Level" object SORT_BY_GRAPH_OBJ_CHART_OBJ_SYMBOL, class=class="str">"cmt">// Sort by Chart object period symbol }; class=class="str">"cmt">//+------------------------------------------------------------------+ MSG_LIB_TEXT_JANUARY, class=class="str">"cmt">// January MSG_LIB_TEXT_FEBRUARY, class=class="str">"cmt">// February MSG_LIB_TEXT_MARCH, class=class="str">"cmt">// March
「标准库里的文本宏枚举清单」
在 MT5 标准库的文本消息模块中,月份、对齐方式、甘氏线与艾略特波浪层级、按钮状态都被定义成一组 MSG_LIB_TEXT_* 枚举常量,方便图形对象与面板直接调用本地化字符串。 从四月到十二月共 9 个月份宏,对齐方式另有左/中/右 3 个宏;甘氏线区分上升与下降趋势各 1 个宏。 艾略特波浪部分覆盖了从 Grand Supercycle 到 Subminuette 共 9 个层级宏,按钮状态则只有 Pressed 与 Depressed 两个宏——总共 24 个文本标识常量。 开 MT5 在 MetaEditor 里搜 MSG_LIB_TEXT_DECEMBER,能直接跳转标准库头文件,确认这些宏的真实定义与注释,避免自己手写字符串出错。
MSG_LIB_TEXT_APRIL, class=class="str">"cmt">// April MSG_LIB_TEXT_MAY, class=class="str">"cmt">// May MSG_LIB_TEXT_JUNE, class=class="str">"cmt">// June MSG_LIB_TEXT_JULY, class=class="str">"cmt">// July MSG_LIB_TEXT_AUGUST, class=class="str">"cmt">// August MSG_LIB_TEXT_SEPTEMBER, class=class="str">"cmt">// September MSG_LIB_TEXT_OCTOBER, class=class="str">"cmt">// October MSG_LIB_TEXT_NOVEMBER, class=class="str">"cmt">// November MSG_LIB_TEXT_DECEMBER, class=class="str">"cmt">// December MSG_LIB_TEXT_ALIGN_LEFT, class=class="str">"cmt">// Left alignment MSG_LIB_TEXT_ALIGN_CENTER, class=class="str">"cmt">// Center alignment MSG_LIB_TEXT_ALIGN_RIGHT, class=class="str">"cmt">// Right alignment MSG_LIB_TEXT_GANN_UP_TREND, class=class="str">"cmt">// Line corresponds to an uptrend MSG_LIB_TEXT_GANN_DOWN_TREND, class=class="str">"cmt">// Line corresponds to a downtrend MSG_LIB_TEXT_ELLIOTT_GRAND_SUPERCYCLE, class=class="str">"cmt">// Grand Supercycle MSG_LIB_TEXT_ELLIOTT_SUPERCYCLE, class=class="str">"cmt">// Supercycle MSG_LIB_TEXT_ELLIOTT_CYCLE, class=class="str">"cmt">// Cycle MSG_LIB_TEXT_ELLIOTT_PRIMARY, class=class="str">"cmt">// Primary cycle MSG_LIB_TEXT_ELLIOTT_INTERMEDIATE, class=class="str">"cmt">// Intermediate MSG_LIB_TEXT_ELLIOTT_MINOR, class=class="str">"cmt">// Minor cycle MSG_LIB_TEXT_ELLIOTT_MINUTE, class=class="str">"cmt">// Minute MSG_LIB_TEXT_ELLIOTT_MINUETTE, class=class="str">"cmt">// Second(Minuette) MSG_LIB_TEXT_ELLIOTT_SUBMINUETTE, class=class="str">"cmt">// Subsecond(Subminuette) MSG_LIB_TEXT_BUTTON_STATE_PRESSED, class=class="str">"cmt">// Pressed MSG_LIB_TEXT_BUTTON_STATE_DEPRESSED, class=class="str">"cmt">// Released
图表控件库的锚点与枚举常量
在 MT5 的图表控件封装库里,文本类元素的坐标原点并不是写死在左上角的,而是通过一组 MSG_LIB_TEXT_CORNER_* 枚举来切换。左上、左下、右下、右上四种角点对应不同的坐标系基准,写面板类 EA 时若错配,标签会直接飞出可视区。 边框样式也有三档:MSG_LIB_TEXT_BORDER_FLAT 是平面、RAISED 是凸起、SUNKEN 是凹陷。实盘里用凹陷边框做背景框,能在黄金 1 分钟图密集 K 线中把提示文字和蜡烛拉开层次。 星期枚举从 SUNDAY 到 SATURDAY 共 7 个,做跨周期时间过滤器时直接引用即可,不用自己拼字符串。 图形对象归属上,MSG_GRAPH_OBJ_BELONG_PROGRAM 与 NO_PROGRAM 区分了对象是不是当前 EA 创建的——删对象前先判这个,能避免把手动画的趋势线误清掉。标准对象类型枚举里 STANDART、ELEMENT、SHADOW_OBJ、FORM、WINDOW 五类,决定了后续事件响应和重绘逻辑的分支走向。
MSG_LIB_TEXT_CORNER_LEFT_UPPER, class=class="str">"cmt">// Center of coordinates at the upper left corner of the chart MSG_LIB_TEXT_CORNER_LEFT_LOWER, class=class="str">"cmt">// Center of coordinates at the lower left corner of the chart MSG_LIB_TEXT_CORNER_RIGHT_LOWER, class=class="str">"cmt">// Center of coordinates at the lower right corner of the chart MSG_LIB_TEXT_CORNER_RIGHT_UPPER, class=class="str">"cmt">// Center of coordinates at the upper right corner of the chart MSG_LIB_TEXT_BORDER_FLAT, class=class="str">"cmt">// Flat MSG_LIB_TEXT_BORDER_RAISED, class=class="str">"cmt">// Raised MSG_LIB_TEXT_BORDER_SUNKEN, class=class="str">"cmt">// Sunken MSG_LIB_TEXT_SUNDAY, class=class="str">"cmt">// Sunday MSG_LIB_TEXT_MONDAY, class=class="str">"cmt">// Monday MSG_LIB_TEXT_TUESDAY, class=class="str">"cmt">// Tuesday MSG_LIB_TEXT_WEDNESDAY, class=class="str">"cmt">// Wednesday MSG_LIB_TEXT_THURSDAY, class=class="str">"cmt">// Thursday MSG_LIB_TEXT_FRIDAY, class=class="str">"cmt">// Friday MSG_LIB_TEXT_SATURDAY, class=class="str">"cmt">// Saturday class=class="str">"cmt">//--- CGraphElementsCollection MSG_GRAPH_ELM_COLLECTION_ERR_OBJ_ALREADY_EXISTS, class=class="str">"cmt">// Error. A chart control object already exists with chart id MSG_GRAPH_ELM_COLLECTION_ERR_FAILED_CREATE_CTRL_OBJ,class=class="str">"cmt">// Failed to create chart control object with chart ID class=class="str">"cmt">//--- GStdGraphObj MSG_GRAPH_STD_OBJ_ERR_NOT_FIND_SUBWINDOW, class=class="str">"cmt">// Failed to find the chart subwindow MSG_GRAPH_ELEMENT_TYPE_STANDART, class=class="str">"cmt">// Standard graphical object MSG_GRAPH_ELEMENT_TYPE_ELEMENT, class=class="str">"cmt">// Element MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ, class=class="str">"cmt">// Shadow object MSG_GRAPH_ELEMENT_TYPE_FORM, class=class="str">"cmt">// Form MSG_GRAPH_ELEMENT_TYPE_WINDOW, class=class="str">"cmt">// Window MSG_GRAPH_OBJ_BELONG_PROGRAM, class=class="str">"cmt">// Graphical object belongs to a program MSG_GRAPH_OBJ_BELONG_NO_PROGRAM, class=class="str">"cmt">// Graphical object does not belong to a program class=class="str">"cmt">//--- MSG_GRAPH_STD_OBJ_VLINE, class=class="str">"cmt">// Vertical line
◍ 图形对象枚举里的标准画线清单
在 MT5 的图表对象体系里,标准图形对象靠一组枚举常量来标识,上面这段就是其中覆盖画线、通道与斐波那契系列的核心片段。 从水平线、趋势线到按角度画的趋势线,再到循环线,都属于最基础的手工标注类对象; equidistant channel 与 standard deviation channel 则分别对应等距通道和标准差通道,后者在波动率扩张时容易把边界拉得很宽。 斐波那契家族在这里一口气列了 7 种:水平位、时间区、扇形、弧线、通道、扩展以及普通回撤,实战中黄金 XAUUSD 的 H1 图上用 Fibo levels 卡 0.618 回撤是常见动作,但外汇与贵金属杠杆高,信号失效概率不低。 剩下还有 Gann 系列、Andrews pitchfork、Elliott 波浪模板(5 浪与 3 浪),以及矩形、三角形、椭圆和四种箭头标记;开 MT5 按 F4 进 metaeditor,搜 MSG_GRAPH_STD_OBJ 前缀就能看到完整列表,复制进自己的指标面板做对象类型映射很直接。
MSG_GRAPH_STD_OBJ_HLINE, class=class="str">"cmt">// Horizontal line MSG_GRAPH_STD_OBJ_TREND, class=class="str">"cmt">// Trend line MSG_GRAPH_STD_OBJ_TRENDBYANGLE, class=class="str">"cmt">// Trend line by angle MSG_GRAPH_STD_OBJ_CYCLES, class=class="str">"cmt">// Cyclic lines MSG_GRAPH_STD_OBJ_ARROWED_LINE, class=class="str">"cmt">// Arrowed line object MSG_GRAPH_STD_OBJ_CHANNEL, class=class="str">"cmt">// Equidistant channel MSG_GRAPH_STD_OBJ_STDDEVCHANNEL, class=class="str">"cmt">// Standard deviation channel MSG_GRAPH_STD_OBJ_REGRESSION, class=class="str">"cmt">// Linear regression channel MSG_GRAPH_STD_OBJ_PITCHFORK, class=class="str">"cmt">// Andrews&class="macro">#x27; pitchfork MSG_GRAPH_STD_OBJ_GANNLINE, class=class="str">"cmt">// Gann line MSG_GRAPH_STD_OBJ_GANNFAN, class=class="str">"cmt">// Gann fan MSG_GRAPH_STD_OBJ_GANNGRID, class=class="str">"cmt">// Gann grid MSG_GRAPH_STD_OBJ_FIBO, class=class="str">"cmt">// Fibo levels MSG_GRAPH_STD_OBJ_FIBOTIMES, class=class="str">"cmt">// Fibo time zones MSG_GRAPH_STD_OBJ_FIBOFAN, class=class="str">"cmt">// Fibo fan MSG_GRAPH_STD_OBJ_FIBOARC, class=class="str">"cmt">// Fibo arcs MSG_GRAPH_STD_OBJ_FIBOCHANNEL, class=class="str">"cmt">// Fibo channel MSG_GRAPH_STD_OBJ_EXPANSION, class=class="str">"cmt">// Fibo expansion MSG_GRAPH_STD_OBJ_ELLIOTWAVE5, class=class="str">"cmt">// Elliott class="num">5 waves MSG_GRAPH_STD_OBJ_ELLIOTWAVE3, class=class="str">"cmt">// Elliott class="num">3 waves MSG_GRAPH_STD_OBJ_RECTANGLE, class=class="str">"cmt">// Rectangle MSG_GRAPH_STD_OBJ_TRIANGLE, class=class="str">"cmt">// Triangle MSG_GRAPH_STD_OBJ_ELLIPSE, class=class="str">"cmt">// Ellipse MSG_GRAPH_STD_OBJ_ARROW_THUMB_UP, class=class="str">"cmt">// Thumb up MSG_GRAPH_STD_OBJ_ARROW_THUMB_DOWN, class=class="str">"cmt">// Thumb down MSG_GRAPH_STD_OBJ_ARROW_UP, class=class="str">"cmt">// Arrow up MSG_GRAPH_STD_OBJ_ARROW_DOWN class=class="str">"cmt">// Arrow down
「图形对象枚举里的箭头与界面控件」
在 MT5 的标准图形对象体系里,箭头类对象被拆得很细:从 MSG_GRAPH_STD_OBJ_ARROW_STOP 到 MSG_GRAPH_STD_OBJ_ARROW_SELL,覆盖了止损标记、勾选符、左右价签以及方向箭头(Buy/Sell/Arrow 本身)。这些枚举值直接对应 ObjectCreate() 第二个参数所用的类型常量,调试时若对象画不出来,先核对用的到底是 OBJ_ARROW 还是 OBJ_ARROW_SELL。 文本与控件类则包括 MSG_GRAPH_STD_OBJ_TEXT、LABEL、BUTTON、EDIT、BITMAP_LABEL 以及 RECTANGLE_LABEL。RECTANGLE_LABEL 的注释明确写着用于搭建自定义图形界面,这意味着它和普通的 OBJ_RECTANGLE 不同,不参与价格锚定,只活在像素坐标层。 对象属性侧有一组 MSG_GRAPH_OBJ_PROP_* 枚举,例如 PROP_CHART_ID 取图表 ID、PROP_WND_NUM 取子窗口序号、PROP_CREATETIME 取创建时间。实盘里用 ObjectGetInteger(0, name, OBJPROP_CREATETIME) 能拿到毫秒时间戳,用来筛掉超过 N 根 K 线前的旧标记,避免图面堆积。外汇与贵金属波动剧烈,这类自动清理逻辑在高杠杆品种上更显必要。
MSG_GRAPH_STD_OBJ_ARROW_STOP, class=class="str">"cmt">// Stop MSG_GRAPH_STD_OBJ_ARROW_CHECK, class=class="str">"cmt">// Check mark MSG_GRAPH_STD_OBJ_ARROW_LEFT_PRICE, class=class="str">"cmt">// Left price label MSG_GRAPH_STD_OBJ_ARROW_RIGHT_PRICE, class=class="str">"cmt">// Right price label MSG_GRAPH_STD_OBJ_ARROW_BUY, class=class="str">"cmt">// Buy MSG_GRAPH_STD_OBJ_ARROW_SELL, class=class="str">"cmt">// Sell MSG_GRAPH_STD_OBJ_ARROW, class=class="str">"cmt">// Arrow MSG_GRAPH_STD_OBJ_TEXT, class=class="str">"cmt">// Text MSG_GRAPH_STD_OBJ_LABEL, class=class="str">"cmt">// Text label MSG_GRAPH_STD_OBJ_BUTTON, class=class="str">"cmt">// Button MSG_GRAPH_STD_OBJ_CHART, class=class="str">"cmt">// Chart MSG_GRAPH_STD_OBJ_BITMAP, class=class="str">"cmt">// Bitmap MSG_GRAPH_STD_OBJ_BITMAP_LABEL, class=class="str">"cmt">// Bitmap label MSG_GRAPH_STD_OBJ_EDIT, class=class="str">"cmt">// Edit MSG_GRAPH_STD_OBJ_EVENT, class=class="str">"cmt">// Event object which corresponds to an event in Economic Calendar MSG_GRAPH_STD_OBJ_RECTANGLE_LABEL, class=class="str">"cmt">// Rectangle Label object used to create and design the custom graphical interface MSG_GRAPH_OBJ_PROP_ID, class=class="str">"cmt">// Object ID MSG_GRAPH_OBJ_PROP_TYPE, class=class="str">"cmt">// Graphical object type(ENUM_OBJECT) MSG_GRAPH_OBJ_PROP_ELEMENT_TYPE, class=class="str">"cmt">// Graphical element type(ENUM_GRAPH_ELEMENT_TYPE) MSG_GRAPH_OBJ_PROP_BELONG, class=class="str">"cmt">// Graphical object affiliation MSG_GRAPH_OBJ_PROP_CHART_ID, class=class="str">"cmt">// Chart ID MSG_GRAPH_OBJ_PROP_WND_NUM, class=class="str">"cmt">// Chart subwindow index MSG_GRAPH_OBJ_PROP_CREATETIME, class=class="str">"cmt">// Creation time MSG_GRAPH_OBJ_PROP_TIMEFRAMES, class=class="str">"cmt">// Object visibility on timeframes MSG_GRAPH_OBJ_PROP_BACK, class=class="str">"cmt">// Background object
图形对象属性枚举的字段语义
在 MT5 的图表对象消息体系里,MSG_GRAPH_OBJ_PROP_* 这组枚举定义了可读取或设置的图形对象属性。搞自动标注或点击事件处理时,认全这些字段能少踩很多坑。 比如 MSG_GRAPH_OBJ_PROP_ZORDER 控制对象接收图表点击事件的优先级,MSG_GRAPH_OBJ_PROP_HIDDEN 决定对象名是否出现在终端对象列表里,MSG_GRAPH_OBJ_PROP_SELECTABLE 与 SELECTED 则分别管“能否被选”和“当前是否被选中”。 画线类对象有一组专属属性:MSG_GRAPH_OBJ_PROP_RAY_LEFT / RAY_RIGHT 控制射线左右延伸,MSG_GRAPH_OBJ_PROP_RAY 让竖线穿透图表所有子窗口;斐波那契弧的 MSG_GRAPH_OBJ_PROP_ELLIPSE 决定是否画完整椭圆。 文本与 Edit 类对象涉及 MSG_GRAPH_OBJ_PROP_READONLY(编辑权限)、ALIGN(水平对齐)、FONTSIZE(字号)、XDISTANCE(距基准角像素偏移)。外汇与贵金属图表加载这类对象属高风险环境操作,参数误设可能导致图形层叠错乱,建议开 MT5 在 EA 里逐项打印验证。
MSG_GRAPH_OBJ_PROP_ZORDER, class=class="str">"cmt">// Priority of a graphical object for receiving the event of clicking on a chart MSG_GRAPH_OBJ_PROP_HIDDEN, class=class="str">"cmt">// Disable displaying the name of a graphical object in the terminal object list MSG_GRAPH_OBJ_PROP_SELECTED, class=class="str">"cmt">// Object selection MSG_GRAPH_OBJ_PROP_SELECTABLE, class=class="str">"cmt">// Object availability MSG_GRAPH_OBJ_PROP_NUM, class=class="str">"cmt">// Object index in the list MSG_GRAPH_OBJ_PROP_TIME, class=class="str">"cmt">// Time coordinate MSG_GRAPH_OBJ_PROP_COLOR, class=class="str">"cmt">// Color MSG_GRAPH_OBJ_PROP_STYLE, class=class="str">"cmt">// Style MSG_GRAPH_OBJ_PROP_WIDTH, class=class="str">"cmt">// Line width MSG_GRAPH_OBJ_PROP_FILL, class=class="str">"cmt">// Object class="type">color filling MSG_GRAPH_OBJ_PROP_READONLY, class=class="str">"cmt">// Ability to edit text in the Edit object MSG_GRAPH_OBJ_PROP_LEVELS, class=class="str">"cmt">// Number of levels MSG_GRAPH_OBJ_PROP_LEVELCOLOR, class=class="str">"cmt">// Level line class="type">color MSG_GRAPH_OBJ_PROP_LEVELSTYLE, class=class="str">"cmt">// Level line style MSG_GRAPH_OBJ_PROP_LEVELWIDTH, class=class="str">"cmt">// Level line width MSG_GRAPH_OBJ_PROP_ALIGN, class=class="str">"cmt">// Horizontal text alignment in the Edit object(OBJ_EDIT) MSG_GRAPH_OBJ_PROP_FONTSIZE, class=class="str">"cmt">// Font size MSG_GRAPH_OBJ_PROP_RAY_LEFT, class=class="str">"cmt">// Ray goes to the left MSG_GRAPH_OBJ_PROP_RAY_RIGHT, class=class="str">"cmt">// Ray goes to the right MSG_GRAPH_OBJ_PROP_RAY, class=class="str">"cmt">// Vertical line goes through all windows of a chart MSG_GRAPH_OBJ_PROP_ELLIPSE, class=class="str">"cmt">// Display the full ellipse of the Fibonacci Arc object MSG_GRAPH_OBJ_PROP_ARROWCODE, class=class="str">"cmt">// Arrow code for the "Arrow" object MSG_GRAPH_OBJ_PROP_ANCHOR, class=class="str">"cmt">// Position of the binding point of the graphical object MSG_GRAPH_OBJ_PROP_XDISTANCE, class=class="str">"cmt">// Distance from the base corner along the X axis in pixels
◍ 图形对象属性枚举的散落定义
在 MT5 的标准库头文件里,图形对象属性消息并非集中声明,而是按功能被切成了几段枚举常量。上面这段就覆盖了从 Y 轴像素距离、甘恩线方向,到埃利奥特波浪标记层级、按钮状态等 20 余个 MSG_GRAPH_OBJ_PROP_* 常量。 比如 MSG_GRAPH_OBJ_PROP_YDISTANCE 注释写明是「base corner 沿 Y 轴的像素距离」,MSG_GRAPH_OBJ_PROP_STATE 对应按钮按下/弹起,MSG_GRAPH_OBJ_PROP_XSIZE / YSIZE 则直接以像素定义对象宽高。这些常量在调用 GraphObj 相关消息时当作属性 ID 传入,改一个面板标签位置就得动 YOFFSET 和 XOFFSET 两个值。 中间用 //--- 隔开了三组:第一组偏 UI 与绑定(角点、边框色、背景色),第二组回到价格类(PRICE、LEVELVALUE、SCALE、ANGLE、DEVIATION),第三组只剩 NAME 与 TEXT 两个描述性字段。 开 MT5 随便拖一个 OBJ_BUTTON 到图表,用 ObjectGetInteger(0,name,OBJPROP_YDISTANCE) 读一下,就能验证这些枚举背后绑定的就是屏幕像素而不是价格坐标,外汇和贵金属图表上做悬浮控件时这点偏差会被放大,属于高风险界面的细节坑。
MSG_GRAPH_OBJ_PROP_YDISTANCE, class=class="str">"cmt">// Distance from the base corner along the Y axis in pixels MSG_GRAPH_OBJ_PROP_DIRECTION, class=class="str">"cmt">// Gann object trend MSG_GRAPH_OBJ_PROP_DEGREE, class=class="str">"cmt">// Elliott wave marking level MSG_GRAPH_OBJ_PROP_DRAWLINES, class=class="str">"cmt">// Display lines for Elliott wave marking MSG_GRAPH_OBJ_PROP_STATE, class=class="str">"cmt">// Button state(pressed/released) MSG_GRAPH_OBJ_PROP_CHART_OBJ_PERIOD, class=class="str">"cmt">// Chart object period MSG_GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE, class=class="str">"cmt">// Time scale display flag for the Chart object MSG_GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE, class=class="str">"cmt">// Price scale display flag for the Chart object MSG_GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE, class=class="str">"cmt">// Chart object scale MSG_GRAPH_OBJ_PROP_XSIZE, class=class="str">"cmt">// Object width along the X axis in pixels. MSG_GRAPH_OBJ_PROP_YSIZE, class=class="str">"cmt">// Object height along the Y axis in pixels. MSG_GRAPH_OBJ_PROP_XOFFSET, class=class="str">"cmt">// X coordinate of the upper-left corner of the visibility area. MSG_GRAPH_OBJ_PROP_YOFFSET, class=class="str">"cmt">// Y coordinate of the upper-left corner of the visibility area. MSG_GRAPH_OBJ_PROP_BGCOLOR, class=class="str">"cmt">// Background class="type">color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL MSG_GRAPH_OBJ_PROP_CORNER, class=class="str">"cmt">// Chart corner for binding a graphical object MSG_GRAPH_OBJ_PROP_BORDER_TYPE, class=class="str">"cmt">// Border type for "Rectangle border" MSG_GRAPH_OBJ_PROP_BORDER_COLOR, class=class="str">"cmt">// Border class="type">color for OBJ_EDIT and OBJ_BUTTON class=class="str">"cmt">//--- MSG_GRAPH_OBJ_PROP_PRICE, class=class="str">"cmt">// Price coordinate MSG_GRAPH_OBJ_PROP_LEVELVALUE, class=class="str">"cmt">// Level value MSG_GRAPH_OBJ_PROP_SCALE, class=class="str">"cmt">// Scale(class="kw">property of Gann objects and Fibonacci Arcs objects) MSG_GRAPH_OBJ_PROP_ANGLE, class=class="str">"cmt">// Angle MSG_GRAPH_OBJ_PROP_DEVIATION, class=class="str">"cmt">// Deviation of the standard deviation channel class=class="str">"cmt">//--- MSG_GRAPH_OBJ_PROP_NAME, class=class="str">"cmt">// Object name MSG_GRAPH_OBJ_PROP_TEXT, class=class="str">"cmt">// Object description(text contained in the object)
「图形对象多语言词条怎么摆」
在 MT5 的图表对象本地化模块里,属性名和界面文案是成对出现的枚举映射。上面这段截取了对象属性提示、层级描述、字体与位图文件等 5 个 MSG_GRAPH_OBJ_PROP_* 常量,分别对应 Tooltip text、Level description、Font、BMP file name、Symbol for Chart object。 真正落地的多语料不是只译属性,还包含月份、对齐方式、趋势线语义、艾略特波浪级别、按钮状态、坐标锚点、星期等。例如 12 个月从 Январь→January 到 Декабрь→December 连续铺开,波浪级别从 Grand Supercycle 到 Subminuette 共 9 档,星期从 Воскресение→Sunday 到 Пятница→Friday 共 7 条。 对齐方式那组有个细节:俄语「Выравнивание по правой границе」对应 «Right alignment» 的行尾多了一个逗号,编译时 MT5 通常容错但通过不了严格静态检查。开 MT5 把这段塞进你的 LangTable 结构体,搜 Right alignment 就能定位那行尾逗号。
MSG_GRAPH_OBJ_PROP_TOOLTIP, class=class="str">"cmt">// Tooltip text MSG_GRAPH_OBJ_PROP_LEVELTEXT, class=class="str">"cmt">// Level description MSG_GRAPH_OBJ_PROP_FONT, class=class="str">"cmt">// Font MSG_GRAPH_OBJ_PROP_BMPFILE, class=class="str">"cmt">// BMP file name for the "Bitmap Level" object MSG_GRAPH_OBJ_PROP_SYMBOL, class=class="str">"cmt">// Symbol for the Chart object }; class=class="str">"cmt">//+------------------------------------------------------------------+ {"Январь","January"}, {"Февраль","February"}, {"Март","March"}, {"Апрель","April"}, {"Май","May"}, {"Июнь","June"}, {"Июль","July"}, {"Август","August"}, {"Сентябрь","September"}, {"Октябрь","October"}, {"Ноябрь","November"}, {"Декабрь","December"}, {"Выравнивание по левой границе","Left alignment"}, {"Выравнивание по центру","Centered"}, {"Выравнивание по правой границе","Right alignment",}, {"Линия соответствует восходящему тренду","Line corresponding to the uptrend line"}, {"Линия соответствует нисходящему тренду","Line corresponding to the downward trend"}, {"Главный Суперцикл (Grand Supercycle)","Grand Supercycle"}, {"Суперцикл (Supercycle)","Supercycle"}, {"Цикл (Cycle)","Cycle"}, {"Первичный цикл (Primary)","Primary"}, {"Промежуточное звено (Intermediate)","Intermediate"}, {"Второстепенный цикл (Minor)","Minor"}, {"Минута (Minute)","Minute"}, {"Секунда (Minuette)","Minuette"}, {"Субсекунда (Subminuette)","Subminuette"}, {"Нажата","Pressed"}, {"Отжата","Depressed"}, {"Центр координат в левом верхнем углу графика","Center of coordinates is in the upper left corner of the chart"}, {"Центр координат в левом нижнем углу графика","Center of coordinates is in the lower left corner of the chart"}, {"Центр координат в правом нижнем углу графика","Center of coordinates is in the lower right corner of the chart"}, {"Центр координат в правом верхнем углу графика","Center of coordinates is in the upper right corner of the chart"}, {"Плоский вид","Flat form"}, {"Выпуклый вид","Prominent form"}, {"Вогнутый вид","Concave form"}, {"Воскресение","Sunday"}, {"Понедельник","Monday"}, {"Вторник","Tuesday"}, {"Среда","Wednesday"}, {"Четверг","Thursday"}, {"Пятница","Friday"},
图形对象的多语种字典与归属判定
在 MT5 的标准图形库里,对象名称与提示文本常以俄英双字符串数组形式集中定义,方便界面本地化与跨语言调试。上面这段初始化数据就列出了从垂直线、趋势线到斐波那契各类工具、 Elliott 波浪以及 Buy/Sell 标记等约 40 种图形对象的双语映射。 其中 "The graphic object belongs to the program" 与 "does not belong to the program" 这组状态串很关键:它对应运行时判定某个图表对象是否由当前 EA 或指标创建的逻辑。若对象非本程序所属,后续修改或删除操作会直接失败,这是很多自行封装绘图模块时踩过的坑。 实际验证时,可在 MT5 用 ObjectGetInteger(0,name,OBJPROP_CREATED_BY) 配合程序 ID 比对,确认归属后再调用 ObjectDelete,避免误删手动画线。外汇与贵金属图表上手动对象混杂,这类判定失误可能让你丢掉关键支撑参考位,属高风险操作环节。
{"Суббота","Saturday"},
class=class="str">"cmt">//--- CGraphElementsCollection
{"Ошибка. Уже существует объект управления чартами с идентификатором чарта ","Error. A chart control object already exists with chart id "},
{"Не удалось создать объект управления чартами с идентификатором чарта ","Failed to create chart control object with chart id "},
class=class="str">"cmt">//--- GStdGraphObj
{"Не удалось найти подокно графика","Could not find chart subwindow"},
{"Стандартный графический объект","Standard graphic object"},
{"Элемент","Element"},
{"Объект тени","Shadow object"},
{"Форма","Form"},
{"Окно","Window"},
{"Графический объект принадлежит программе","The graphic object belongs to the program"},
{"Графический объект не принадлежит программе","The graphic object does not belong to the program"},
{"Вертикальная линия","Vertical Line"},
{"Горизонтальная линия","Horizontal Line"},
{"Трендовая линия","Trend Line"},
{"Трендовая линия по углу","Trend Line By Angle"},
{"Циклические линии","Cycle Lines"},
{"Линия со стрелкой","Arrowed Line"},
{"Равноудаленный канал","Equidistant Channel"},
{"Канал стандартного отклонения","Standard Deviation Channel"},
{"Канал на линейной регрессии","Linear Regression Channel"},
{"Вилы Эндрюса","Andrews’ Pitchfork"},
{"Линия Ганна","Gann Line"},
{"Веер Ганна","Gann Fan"},
{"Сетка Ганна","Gann Grid"},
{"Уровни Фибоначчи","Fibonacci Retracement"},
{"Временные зоны Фибоначчи","Fibonacci Time Zones"},
{"Веер Фибоначчи","Fibonacci Fan"},
{"Дуги Фибоначчи","Fibonacci Arcs"},
{"Канал Фибоначчи","Fibonacci Channel"},
{"Расширение Фибоначчи","Fibonacci Expansion"},
{"class="num">5-волновка Эллиотта","Elliott Motive Wave"},
{"class="num">3-волновка Эллиотта","Elliott Correction Wave"},
{"Прямоугольник","Rectangle"},
{"Треугольник","Triangle"},
{"Эллипс","Ellipse"},
{"Знак \"Хорошо\"","Thumbs Up"},
{"Знак \"Плохо\"","Thumbs Down"},
{"Знак \"Стрелка вверх\"","Arrow Up"},
{"Знак \"Стрелка вниз\"","Arrow Down"},
{"Знак \"Стоп\"","Stop Sign"},
{"Знак \"Птичка\"","Check Sign"},
{"Левая ценовая метка","Left Price Label"},
{"Правая ценовая метка","Right Price Label"},
{"Знак \"Buy\"","Buy Sign"},
{"Знак \"Sell\"","Sell Sign"},
{"Стрелка","Arrow"},
{"Текст","Text"},
{"Текстовая метка","Label"},
{"Кнопка","Button"},
{"График","Chart"},
{"Рисунок","Bitmap"},
{"Графическая метка","Bitmap Label"}◍ 对象属性枚举里的俄英对照陷阱
在 MT5 标准库的资源描述文件里,图形对象与经济日历事件的属性常以俄语字符串作键、英语作值成对出现。上面这段节选就包含了从「Поле ввода」(输入字段) 到「Высота по оси Y в пикселях」(Y轴像素高度) 共 49 组映射,覆盖 Edit 控件、Rectangle label、Event 对象及通用对象坐标、颜色、线宽等字段。 直接抄这类映射表进自己的面板代码有个坑:俄语键只是源码注释用途,实际 ObjectGetInteger / ObjectGetString 调用时认的是 ENUM_OBJECT_PROPERTY_INTEGER 里的英文枚举,比如「Object chart ID」对应 OBJPROP_CHART_ID,「Chart subwindow number」对应 OBJPROP_SUBWINDOW。 如果你在做小布盯盘式的自定义 GUI,建议只保留英文侧做索引,俄语部分当人肉词典。外汇与贵金属图表对象受多品种多周期切换影响,属性读取可能返回空值,属正常高风险环境现象,验证时记得在 EURUSD 与 XAUUSD 各挂一个对象跑一遍。
「图形对象属性的英文映射与样式描述函数」
在 MT5 自定义指标或 EA 里操控图形对象时,有一批属性名需要先完成俄文到英文的映射,例如可见区域左上角坐标(X/Y)、背景色、图表绑定角、边框类型与颜色、价格坐标、水平值、缩放、角度、标准差通道偏离、名称、描述、悬浮提示文本、水平描述、字体、BMP 文件名、图表品种等,共 19 项键值对,缺一项都可能导致 ObjectSetXXX 调用时属性名不匹配。 下面这段函数把线型枚举转成可读文本,用的是三元表达式嵌套,覆盖 STYLE_SOLID、STYLE_DASH、STYLE_DOT、STYLE_DASHDOT、STYLE_DASHDOTDOT 五种,未命中返回 "Unknown"。实盘里若你写面板显示对象样式,直接复用可省去硬编中文。 对齐方式描述函数同理,处理 ALIGN_LEFT / ALIGN_CENTER / ALIGN_RIGHT 三种,用于标签类对象水平锚定。外汇与贵金属图表对象受报价跳空影响,锚点漂移概率偏高,建议回测时打印 align 与 style 描述做视觉核对。 甘氏网格方向描述函数入口已给出,仅声明未实现返回体,开 MT5 在对应库文件补全 GANN_UP / GANN_DOWN 等分支即可验证网格绘制倾向。
class="type">class="kw">string LineStyleDescription(class="kw">const ENUM_LINE_STYLE style) { class="kw">return ( style==STYLE_SOLID ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID) : style==STYLE_DASH ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH) : style==STYLE_DOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT) : style==STYLE_DASHDOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT) : style==STYLE_DASHDOTDOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT) : "Unknown" ); } class="type">class="kw">string AlignModeDescription(ENUM_ALIGN_MODE align) { class="kw">return ( align==ALIGN_LEFT ? CMessage::Text(MSG_LIB_TEXT_ALIGN_LEFT) : align==ALIGN_CENTER ? CMessage::Text(MSG_LIB_TEXT_ALIGN_CENTER): align==ALIGN_RIGHT ? CMessage::Text(MSG_LIB_TEXT_ALIGN_RIGHT) : "Unknown" ); } class="type">class="kw">string GannDirectDescription(class="kw">const ENUM_GANN_DIRECTION direction) { class="kw">return
把枚举值翻成可读标签的几个辅助函数
在 MT5 标准库里,甘氏方向、艾略特波浪级别、锚点角落、边框类型这些枚举,直接打印就是整数或宏名,复盘时很不直观。下面几个函数就是把这些枚举映射成界面文字,未命中任何分支时统一回退到 "Unknown"。 甘氏方向只分上行与下行两种趋势态:GANN_UP_TREND 与 GANN_DOWN_TREND 各自取对应的多语言文本,其余情况落 "Unknown"。艾略特波浪级别覆盖得最细,从 ELLIOTT_GRAND_SUPERCYCLE 到 ELLIOTT_SUBMINUETTE 共 9 级,对应周线以上超大周期到分时亚微浪,缺一级就会显示 Unknown,写面板时得留意别漏判。 BaseCornerDescription 处理像素坐标的参考角,四个 CORNER_* 宏分别对应左上、左下、右下、右上;BorderTypeDescription 目前只列了 BORDER_FLAT 与 BORDER_RAISED 两个分支,代码段在原文里被截断,实际库文件应还有 BORDER_SUNKEN 等后续项。 开 MT5 按 F4 进标准库找这些函数,把返回值接到你自己的 ObjectCreate 标签上,能少写一半中文硬编码,也方便后续切语种。外汇与贵金属波动剧烈,这类界面映射只解决可读性问题,不构成任何方向判断。
(
direction==GANN_UP_TREND ? CMessage::Text(MSG_LIB_TEXT_GANN_UP_TREND) :
direction==GANN_DOWN_TREND ? CMessage::Text(MSG_LIB_TEXT_GANN_DOWN_TREND) :
"Unknown"
);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of the Elliott wave marking level |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string ElliotWaveDegreeDescription(class="kw">const ENUM_ELLIOT_WAVE_DEGREE degree)
{
class="kw">return
(
degree==ELLIOTT_GRAND_SUPERCYCLE ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_GRAND_SUPERCYCLE):
degree==ELLIOTT_SUPERCYCLE ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_SUPERCYCLE) :
degree==ELLIOTT_CYCLE ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_CYCLE) :
degree==ELLIOTT_PRIMARY ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_PRIMARY) :
degree==ELLIOTT_INTERMEDIATE ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_INTERMEDIATE) :
degree==ELLIOTT_MINOR ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_MINOR) :
degree==ELLIOTT_MINUTE ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_MINUTE) :
degree==ELLIOTT_MINUETTE ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_MINUETTE) :
degree==ELLIOTT_SUBMINUETTE ? CMessage::Text(MSG_LIB_TEXT_ELLIOTT_SUBMINUETTE) :
"Unknown"
);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of the chart corner, relative to |
class=class="str">"cmt">//| which the coordinates in pixels are specified |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string BaseCornerDescription(class="kw">const ENUM_BASE_CORNER corner)
{
class="kw">return
(
corner==CORNER_LEFT_UPPER ? CMessage::Text(MSG_LIB_TEXT_CORNER_LEFT_UPPER) :
corner==CORNER_LEFT_LOWER ? CMessage::Text(MSG_LIB_TEXT_CORNER_LEFT_LOWER) :
corner==CORNER_RIGHT_LOWER ? CMessage::Text(MSG_LIB_TEXT_CORNER_RIGHT_LOWER) :
corner==CORNER_RIGHT_UPPER ? CMessage::Text(MSG_LIB_TEXT_CORNER_RIGHT_UPPER) :
"Unknown"
);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of the graphical object frame look |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string BorderTypeDescription(class="kw">const ENUM_BORDER_TYPE border_type)
{
class="kw">return
(
border_type==BORDER_FLAT ? CMessage::Text(MSG_LIB_TEXT_BORDER_FLAT) :
border_type==BORDER_RAISED ? CMessage::Text(MSG_LIB_TEXT_BORDER_RAISED) :◍ 标准图形对象类型的描述映射
在 MT5 的图表对象封装里,常需要一个函数把 ENUM_OBJECT 枚举翻译成可读中文描述,方便在日志或面板里直接显示。下面这个函数用三元嵌套把 24 种标准对象一一对应到消息宏。 实际跑起来,OBJ_FIBO 到 OBJ_ELLIOTWAVE5 这一段覆盖了 14 种斐波那契与艾略特工具,占整个标准对象类型的半数以上。如果你自己做对象管理器,直接抄这套映射能省掉大量硬编码字符串。 外汇与贵金属图表上对象类型繁杂,调用前务必确认 type 落在已知枚举内,否则会落入末尾的 Unknown 分支,调试时容易误判为对象创建失败。
class="type">class="kw">string StdGraphObjectTypeDescription(class="kw">const ENUM_OBJECT type) { class="kw">return ( type==OBJ_VLINE ? CMessage::Text(MSG_GRAPH_STD_OBJ_VLINE) : type==OBJ_HLINE ? CMessage::Text(MSG_GRAPH_STD_OBJ_HLINE) : type==OBJ_TREND ? CMessage::Text(MSG_GRAPH_STD_OBJ_TREND) : type==OBJ_TRENDBYANGLE ? CMessage::Text(MSG_GRAPH_STD_OBJ_TRENDBYANGLE) : type==OBJ_CYCLES ? CMessage::Text(MSG_GRAPH_STD_OBJ_CYCLES) : type==OBJ_ARROWED_LINE ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROWED_LINE) : type==OBJ_CHANNEL ? CMessage::Text(MSG_GRAPH_STD_OBJ_CHANNEL) : type==OBJ_STDDEVCHANNEL ? CMessage::Text(MSG_GRAPH_STD_OBJ_STDDEVCHANNEL) : type==OBJ_REGRESSION ? CMessage::Text(MSG_GRAPH_STD_OBJ_REGRESSION) : type==OBJ_PITCHFORK ? CMessage::Text(MSG_GRAPH_STD_OBJ_PITCHFORK) : type==OBJ_GANNLINE ? CMessage::Text(MSG_GRAPH_STD_OBJ_GANNLINE) : type==OBJ_GANNFAN ? CMessage::Text(MSG_GRAPH_STD_OBJ_GANNFAN) : type==OBJ_GANNGRID ? CMessage::Text(MSG_GRAPH_STD_OBJ_GANNGRID) : type==OBJ_FIBO ? CMessage::Text(MSG_GRAPH_STD_OBJ_FIBO) : type==OBJ_FIBOTIMES ? CMessage::Text(MSG_GRAPH_STD_OBJ_FIBOTIMES) : type==OBJ_FIBOFAN ? CMessage::Text(MSG_GRAPH_STD_OBJ_FIBOFAN) : type==OBJ_FIBOARC ? CMessage::Text(MSG_GRAPH_STD_OBJ_FIBOARC) : type==OBJ_FIBOCHANNEL ? CMessage::Text(MSG_GRAPH_STD_OBJ_FIBOCHANNEL) : type==OBJ_EXPANSION ? CMessage::Text(MSG_GRAPH_STD_OBJ_EXPANSION) : type==OBJ_ELLIOTWAVE5 ? CMessage::Text(MSG_GRAPH_STD_OBJ_ELLIOTWAVE5) :
「图形对象类型的字符串映射尾段」
这段三元表达式链是标准图形对象类型枚举向本地化描述文本转换的收尾部分,覆盖了从艾略特三波线到矩形标签等二十余种对象类型。 若传入的 type 不匹配前面已列出的任何宏常量,运算符会落到最末的 "Unknown" 分支,返回未识别标记,便于在日志里快速揪出异常对象。 在 MT5 的 ObjectsTotal() 遍历中,若某对象返回 Unknown,多半是自定义指标用了非标准枚举或版本差异,建议直接打印 objtype 数值核对。 外汇与贵金属图表上叠加这类对象时波动剧烈,误判类型可能导致脚本批量删除误操作,执行前务必在策略测试器用小周期样本验证。
type==OBJ_ELLIOTWAVE3 ? CMessage::Text(MSG_GRAPH_STD_OBJ_ELLIOTWAVE3) : type==OBJ_RECTANGLE ? CMessage::Text(MSG_GRAPH_STD_OBJ_RECTANGLE) : type==OBJ_TRIANGLE ? CMessage::Text(MSG_GRAPH_STD_OBJ_TRIANGLE) : type==OBJ_ELLIPSE ? CMessage::Text(MSG_GRAPH_STD_OBJ_ELLIPSE) : type==OBJ_ARROW_THUMB_UP ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_THUMB_UP) : type==OBJ_ARROW_THUMB_DOWN ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_THUMB_DOWN) : type==OBJ_ARROW_UP ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_UP) : type==OBJ_ARROW_DOWN ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_DOWN) : type==OBJ_ARROW_STOP ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_STOP) : type==OBJ_ARROW_CHECK ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_CHECK) : type==OBJ_ARROW_LEFT_PRICE ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_LEFT_PRICE) : type==OBJ_ARROW_RIGHT_PRICE ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_RIGHT_PRICE) : type==OBJ_ARROW_BUY ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_BUY) : type==OBJ_ARROW_SELL ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW_SELL) : type==OBJ_ARROW ? CMessage::Text(MSG_GRAPH_STD_OBJ_ARROW) : type==OBJ_TEXT ? CMessage::Text(MSG_GRAPH_STD_OBJ_TEXT) : type==OBJ_LABEL ? CMessage::Text(MSG_GRAPH_STD_OBJ_LABEL) : type==OBJ_BUTTON ? CMessage::Text(MSG_GRAPH_STD_OBJ_BUTTON) : type==OBJ_CHART ? CMessage::Text(MSG_GRAPH_STD_OBJ_CHART) : type==OBJ_BITMAP ? CMessage::Text(MSG_GRAPH_STD_OBJ_BITMAP) : type==OBJ_BITMAP_LABEL ? CMessage::Text(MSG_GRAPH_STD_OBJ_BITMAP_LABEL) : type==OBJ_EDIT ? CMessage::Text(MSG_GRAPH_STD_OBJ_EDIT) : type==OBJ_EVENT ? CMessage::Text(MSG_GRAPH_STD_OBJ_EVENT) : type==OBJ_RECTANGLE_LABEL ? CMessage::Text(MSG_GRAPH_STD_OBJ_RECTANGLE_LABEL) : "Unknown" );
线型描述函数的两种写法
在 MT5 自定义指标里,给缓冲区的画线样式做文字说明,早期写法是用一长串三元运算符硬匹配。下面这段把 STYLE_SOLID 到 STYLE_DASHDOTDOT 五个枚举逐个比一遍,匹配不到就返回 "Unknown",代码行数多但逻辑直白。 后续版本把同样功能收成一行:直接调 LineStyleDescription(this.LineStyle()) 做转发。少写 10 行以上条件分支,维护时改样式映射只动被调函数一处即可。 外汇与贵金属图表上画线样式只是视觉区分,不改变信号逻辑;但滥用 DASHDOTDOT 这类密点线在 1 分钟周期可能显脏,倾向用 SOLID 或 DASH 做主信号线。开 MT5 把两段分别塞进 CBuffer 类编译,能直观看到后者省出的空间。
class="type">class="kw">string CBuffer::GetLineStyleDescription(class="type">void) class="kw">const { class="kw">return ( this.LineStyle()==STYLE_SOLID ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID) : this.LineStyle()==STYLE_DASH ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH) : this.LineStyle()==STYLE_DOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT) : this.LineStyle()==STYLE_DASHDOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT) : this.LineStyle()==STYLE_DASHDOTDOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT) : "Unknown" ); } class="type">class="kw">string CBuffer::GetLineStyleDescription(class="type">void) class="kw">const { class="kw">return LineStyleDescription(this.LineStyle()); }
◍ 图形对象结构体的字段拆解
在 MT5 自定义指标或 EA 里接管图形对象,第一步是把对象的元信息塞进一个结构体。下面这段声明覆盖了从类型枚举到显示标志的全部基础属性,缺一个都可能导致鼠标事件收不到或对象在切换周期时消失。 m_type_graph_obj 用 ENUM_OBJECT 记录对象大类(如 OBJ_HLINE、OBJ_TREND),m_type_element 与 m_belong 则区分它是独立图形还是附属程序元素。m_name_prefix 和 m_name 配合可避免多实例对象重名,m_chart_id 与 m_object_id 是后续用 ObjectGetInteger 定位的唯一钥匙。 m_zorder 控制叠放优先级,值大的优先接收点击事件;m_subwindow 与 m_shift_y 决定对象落在哪个子窗口及纵向偏移,写画线指标时漏掉 m_subwindow 会直接画到主图。 m_timeframes_visible 是周期可见性的标志位集合,用 0 表示全周期显示;m_digits 约束报价小数位。剩下的 bool 字段里,m_back 决定是否在背景层,m_hidden 为真时终端对象列表不显示该对象名称——调试阶段建议保持 false 方便排查。 开 MT5 按 F4 建个空指标,把下面结构体贴进头文件,编译通过即说明字段声明无误,可在此基础上写自己的图形管理类。
ENUM_OBJECT m_type_graph_obj; class=class="str">"cmt">// Graphical object type ENUM_GRAPH_ELEMENT_TYPE m_type_element; class=class="str">"cmt">// Graphical element type ENUM_GRAPH_OBJ_BELONG m_belong; class=class="str">"cmt">// Program affiliation class="type">class="kw">string m_name_prefix; class=class="str">"cmt">// Object name prefix class="type">class="kw">string m_name; class=class="str">"cmt">// Object name class="type">long m_chart_id; class=class="str">"cmt">// Object chart ID class="type">long m_object_id; class=class="str">"cmt">// Object ID class="type">long m_zorder; class=class="str">"cmt">// Priority of a graphical object for receiving the mouse click event class="type">int m_subwindow; class=class="str">"cmt">// Subwindow index class="type">int m_shift_y; class=class="str">"cmt">// Subwindow Y coordinate shift class="type">int m_type; class=class="str">"cmt">// Object type class="type">int m_timeframes_visible; class=class="str">"cmt">// Visibility of an object on timeframes(a set of flags) class="type">int m_digits; class=class="str">"cmt">// Number of decimal places in a quote class="type">bool m_visible; class=class="str">"cmt">// Object visibility class="type">bool m_back; class=class="str">"cmt">// "Background object" flag class="type">bool m_selected; class=class="str">"cmt">// "Object selection" flag class="type">bool m_selectable; class=class="str">"cmt">// "Object availability" flag class="type">bool m_hidden; class=class="str">"cmt">// "Disable displaying the name of a graphical object in the terminal object list" flag
「图形对象基类的成员变量与Setter封装」
在 MT5 自定义图形对象体系里,基类通常会先落几个核心私有字段:m_create_time 记录对象被创建的时间戳,m_object_id、m_chart_id、m_name 分别绑定对象 ID、图表 ID 和对象名,方便后续在多个图表实例里精准寻址。 虚拟方法 ObjectToStruct 与 StructToObject 留给子类去填——前者把运行对象压回结构体,默认返回 true;后者从结构体还原对象,默认空实现。这意味着你写趋势线、矩形这类派生类时,必须自己覆盖这两处,否则存取会丢字段。 公开区是一组轻量 Setter:SetBelong 接 ENUM_GRAPH_OBJ_BELONG 枚举标定归属,SetTypeGraphObject 接 ENUM_OBJECT 设定 MT5 原生对象类型,SetDigits 控小数位。直接改 this 成员,没有容错,调用前自己保证枚举合法。 SetBack 是唯一带错误处理的 Setter:先 ResetLastError,再调 ObjectSetInteger 写 OBJPROP_BACK 标志。若返回 false,成员不更新且函数返回 false,调用方应检查 ::GetLastError 排查图表 ID 或对象名无效的问题。外汇与贵金属图表对象受品种波动影响,对象属性设置失败概率随报价中断而上升,属正常高风险现象。
class="type">class="kw">datetime m_create_time; class=class="str">"cmt">// Object creation time class=class="str">"cmt">//--- Create(class="num">1) the object structure and(class="num">2) the object from the structure class="kw">virtual class="type">bool ObjectToStruct(class="type">void) { class="kw">return true; } class="kw">virtual class="type">void StructToObject(class="type">void){;} class="kw">public: class="kw">public: class=class="str">"cmt">//--- Set the values of the class variables class="type">void SetObjectID(class="kw">const class="type">long value) { this.m_object_id=value; } class="type">void SetBelong(class="kw">const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong; } class="type">void SetTypeGraphObject(class="kw">const ENUM_OBJECT obj) { this.m_type_graph_obj=obj; } class="type">void SetTypeElement(class="kw">const ENUM_GRAPH_ELEMENT_TYPE type) { this.m_type_element=type;} class="type">void SetName(class="kw">const class="type">class="kw">string name) { this.m_name=name; } class="type">void SetChartID(class="kw">const class="type">long chart_id) { this.m_chart_id=chart_id; } class="type">void SetDigits(class="kw">const class="type">int value) { this.m_digits=value; } class=class="str">"cmt">//--- Set the "Background object" flag class="type">bool SetBack(class="kw">const class="type">bool flag) { ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) { this.m_back=flag; class="kw">return true; } else
图形对象的背景与选中属性封装
在 MT5 自定义对象类里,SetBack、SetSelected、SetSelectable 这类方法本质是对 ObjectSetInteger 的薄封装,用来切换对象是否置于背景、是否被选中、是否允许用户点选。 以 SetBack 为例,进函数先调 ::ResetLastError() 清掉旧错误码,再对当前图表 ID 与对象名写 OBJPROP_BACK。成功就把成员变量 m_back 同步成传入值并返回 true,失败则通过 CMessage::ToLog 把 GetLastError() 打进日志并返回 false。 SetSelected 逻辑完全一致,只是属性换成 OBJPROP_SELECTED,同步的是 m_selected 成员。这类写法保证对象状态在内存与图表两端不脱节,调试时若发现背景层对象突然跑到 K 线上方,优先查 SetBack(false) 是否漏调用。 外汇与贵金属图表上叠加自绘对象时,背景标志设错可能遮住价格行为结构,属高频低级坑,建议在 MT5 里挂一段测试 EA 验证返回码。
CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return class="kw">false; } class=class="str">"cmt">//--- Set the "Background object" flag class="type">bool SetBack(class="kw">const class="type">bool flag) { ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_BACK,flag)) { this.m_back=flag; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return class="kw">false; } class=class="str">"cmt">//--- Set the "Object selection" flag class="type">bool SetSelected(class="kw">const class="type">bool flag) { ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTED,flag)) { this.m_selected=flag; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return class="kw">false; } class=class="str">"cmt">//--- Set the "Object selection" flag class="type">bool SetSelectable(class="kw">const class="type">bool flag)
◍ 图形对象的可选与隐藏属性设置
在 MT5 自定义指标或 EA 里封装图形对象时,可选中(selectable)与隐藏(hidden)是两个常被忽略但影响交互的细节。下面这段类方法演示了如何通过 ObjectSetInteger 去改 OBJPROP_SELECTABLE 标志,并在失败时用 ResetLastError + GetLastError 把错误抛进日志。 SetHidden 方法的逻辑与 SetSelectable 几乎对称,只是它把成员变量写成 m_hidden,注释里写的是“终端对象列表里不显示名称”,但代码实际仍传 OBJPROP_SELECTABLE——这是个明显的源码笔误,真要隐藏对象列表名称应该用 OBJPROP_HIDDEN。 SetZorder 接收 long 型 value,用来设定对象接收图表点击事件的优先级,同样先清错误码再调 ObjectSetInteger 写 OBJPROP_ZORDER。若返回 false,CMessage::ToLog 会带上 DFUN 函数名和错误号输出,方便在专家日志里定位是哪一行对象属性没写进去。 开 MT5 建个脚本把这些方法挂到画线对象上,分别传 true/false 看对象列表和鼠标点选表现,能直接验证上面说的隐藏标志错位问题。
{
::ResetLastError();
if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag))
{
this.m_selectable=flag;
class="kw">return true;
}
else
CMessage::ToLog(DFUN,::GetLastError(),true);
class="kw">return class="kw">false;
}
class=class="str">"cmt">//--- Set the "Disable displaying the name of a graphical object in the terminal object list" flag
class="type">bool SetHidden(class="kw">const class="type">bool flag)
{
::ResetLastError();
if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_SELECTABLE,flag))
{
this.m_hidden=flag;
class="kw">return true;
}
else
CMessage::ToLog(DFUN,::GetLastError(),true);
class="kw">return class="kw">false;
}
class=class="str">"cmt">//--- Set the priority of a graphical object for receiving the event of clicking on a chart
class="type">bool SetZorder(class="kw">const class="type">long value)
{
::ResetLastError();
if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_ZORDER,value))
{「图形对象的跨周期显隐控制」
在 MT5 自定义指标或 EA 里封装图形对象时,显隐不能只靠画图那一下,得用 OBJPROP_TIMEFRAMES 属性单独控。下面这段封装逻辑把「全周期显示 / 全隐藏 / 指定周期显示」拆成了三个入口,调用失败就走日志返回 false,避免静默出错。 SetVisible 里用三元把 bool 翻成 OBJ_ALL_PERIODS 或 OBJ_NO_PERIODS:传 true 对象在所有周期露脸,传 false 则任何周期都不画。SetVisibleOnTimeframe 更细,接收 int 位标 flags,比如只让 H1+H4 显示就传对应周期宏的按位或,适合做多周期共振标记时隔离噪音。 实盘接这类封装要注意:外汇和贵金属杠杆高、跳空多,对象若在错周期残留可能造成误读。跑之前在策略测试器切两个周期确认显隐符合预期再上真仓。
this.m_zorder=value; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return class="kw">false; } class=class="str">"cmt">//--- Set object visibility on all timeframes class="type">bool SetVisible(class="kw">const class="type">bool flag) { class="type">long value=(flag ? OBJ_ALL_PERIODS : OBJ_NO_PERIODS); ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,value)) { this.m_visible=flag; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return class="kw">false; } class=class="str">"cmt">//--- Set visibility flags on timeframes specified as flags class="type">bool SetVisibleOnTimeframe(class="kw">const class="type">int flags) { ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) { this.m_timeframes_visible=flags; class="kw">return true; }
给对象按周期挂可见性开关
图形对象在 MT5 里不是天然全周期显示的,想让某条线只在 M5、H1 出现,就得手动往可见性标志位里写值。下面这段是类方法里的一段,负责把一个指定周期并入对象的可见周期集合。
先取出对象已有的可见周期标志 m_timeframes_visible,再用 switch 把入参 timeframe 映射成对应的 OBJ_PERIOD_* 宏,通过 |= 按位或写回 flags。这样原本不可见的周期就被打开了,比如传 PERIOD_M5 就并上 OBJ_PERIOD_M5。
外汇与贵金属杠杆高、滑点随机,这类对象控制逻辑只解决“画在哪”的问题,不涉及任何方向判断,实盘前请在策略测试器里跑一遍确认周期切换符合预期。
else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return class="kw">false; } class=class="str">"cmt">//--- Add the visibility flag on a specified timeframe class="type">bool SetVisibleOnTimeframe(class="kw">const ENUM_TIMEFRAMES timeframe) { class="type">int flags=this.m_timeframes_visible; class="kw">switch(timeframe) { case PERIOD_M1 : flags |= OBJ_PERIOD_M1; class="kw">break; case PERIOD_M2 : flags |= OBJ_PERIOD_M2; class="kw">break; case PERIOD_M3 : flags |= OBJ_PERIOD_M3; class="kw">break; case PERIOD_M4 : flags |= OBJ_PERIOD_M4; class="kw">break; case PERIOD_M5 : flags |= OBJ_PERIOD_M5; class="kw">break; case PERIOD_M6 : flags |= OBJ_PERIOD_M6; class="kw">break; case PERIOD_M10 : flags |= OBJ_PERIOD_M10; class="kw">break; case PERIOD_M12 : flags |= OBJ_PERIOD_M12; class="kw">break; case PERIOD_M15 : flags |= OBJ_PERIOD_M15; class="kw">break; case PERIOD_M20 : flags |= OBJ_PERIOD_M20; class="kw">break; case PERIOD_M30 : flags |= OBJ_PERIOD_M30; class="kw">break; case PERIOD_H1 : flags |= OBJ_PERIOD_H1; class="kw">break;
◍ 给图形对象按周期位或打标志
这段逻辑出现在自定义图形类的周期可见性设置函数里,用 switch 把传入的 ENUM_TIMEFRAMES 逐个映射成 OBJ_PERIOD_* 标志位,再用 |= 累加到 flags 变量。
H2 到 MN1 共 10 个周期常量都被覆盖,default 分支直接返回 true 而不报错,意味着未列举的周期(如 M2、M3)会被静默放行。
映射完之后调用 ObjectSetInteger 写 OBJPROP_TIMEFRAMES,成功就把 flags 存进 m_timeframes_visible 成员;失败则走 CMessage::ToLog 打错误码并返回 false。
开 MT5 自己写类时,若发现某对象在部分周期不显示,先 Print 一下 flags 的十进制值,对照位或结果就能定位是哪个周期标志没挂上。外汇与贵金属图表对象受多周期切换影响,实操中需注意跨周期显示异常带来的误判风险。
case PERIOD_H2 : flags |= OBJ_PERIOD_H2; class="kw">break; case PERIOD_H3 : flags |= OBJ_PERIOD_H3; class="kw">break; case PERIOD_H4 : flags |= OBJ_PERIOD_H4; class="kw">break; case PERIOD_H6 : flags |= OBJ_PERIOD_H6; class="kw">break; case PERIOD_H8 : flags |= OBJ_PERIOD_H8; class="kw">break; case PERIOD_H12 : flags |= OBJ_PERIOD_H12; class="kw">break; case PERIOD_D1 : flags |= OBJ_PERIOD_D1; class="kw">break; case PERIOD_W1 : flags |= OBJ_PERIOD_W1; class="kw">break; case PERIOD_MN1 : flags |= OBJ_PERIOD_MN1; class="kw">break; class="kw">default : class="kw">return true; } ::ResetLastError(); if(::ObjectSetInteger(this.m_chart_id,this.m_name,OBJPROP_TIMEFRAMES,flags)) { this.m_timeframes_visible=flags; class="kw">return true; } else CMessage::ToLog(DFUN,::GetLastError(),true); class="kw">return class="kw">false; class=class="str">"cmt">//--- Set a subwindow index
「子窗口定位与类变量读取的实现细节」
在自定义图形类里,把指标或对象挂到正确的子窗口是第一道关。下面这组 SetSubwindow 重载就是干这个的:先清错误码,再用 ObjectFind 按名字查图表里的子窗口索引,查不到就写日志,返回时用 WRONG_VALUE 做边界判断。 bool SetSubwindow(const long chart_id,const string name) { ::ResetLastError(); this.m_subwindow=::ObjectFind(chart_id,name); if(this.m_subwindow<0) CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_NOT_FIND_SUBWINDOW); return(this.m_subwindow>WRONG_VALUE); } bool SetSubwindow(void) { return this.SetSubwindow(this.m_chart_id,this.m_name); } 逐行看:ResetLastError 清空上一次系统错误,避免误判;ObjectFind 返回负值代表没找到,此时 m_subwindow 保持 -1 并记一条日志;返回值大于 WRONG_VALUE 才视为有效,通常 WRONG_VALUE 定义为 -1,所以只有 >=0 的子窗口号能过关。无参版本只是用类内已有的 chart_id 和 name 调了一次带参版本,省得每次手填。 后面那一串 const 内联函数才是日常调用的主力:TypeGraphElement 吐出元素类型枚举,Belong 说明对象归属,TypeGraphObject 给具体图形对象类型,TimeCreate 返回创建时间,Name 和 ChartID、ObjectID 直接把私有成员暴露出来只读。 ENUM_GRAPH_ELEMENT_TYPE TypeGraphElement(void) const { return this.m_type_element; } ENUM_GRAPH_OBJ_BELONG Belong(void) const { return this.m_belong; } ENUM_OBJECT TypeGraphObject(void) const { return this.m_type_graph_obj; } datetime TimeCreate(void) const { return this.m_create_time; } string Name(void) const { return this.m_name; } long ChartID(void) const { return this.m_chart_id; } long ObjectID(void) const { return this.m_object_id; } 在 MT5 里开个 EA 把这类封装继承一下,打印 ChartID() 和 ObjectID() 就能确认对象到底落在哪个子窗口。外汇和贵金属图表多子窗口叠加指标很常见,这种定位失手会直接让后续绘图偏到主图,高风险环境下建议每次 SetSubwindow 后断言 m_subwindow>=0 再往下走。
class="type">bool SetSubwindow(class="kw">const class="type">long chart_id,class="kw">const class="type">class="kw">string name) { ::ResetLastError(); this.m_subwindow=::ObjectFind(chart_id,name); if(this.m_subwindow<class="num">0) CMessage::ToLog(DFUN,MSG_GRAPH_STD_OBJ_ERR_NOT_FIND_SUBWINDOW); class="kw">return(this.m_subwindow>WRONG_VALUE); } class="type">bool SetSubwindow(class="type">void) { class="kw">return this.SetSubwindow(this.m_chart_id,this.m_name); } class=class="str">"cmt">//--- Return the values of class variables class=class="str">"cmt">//--- Return the values of class variables ENUM_GRAPH_ELEMENT_TYPE TypeGraphElement(class="type">void) class="kw">const { class="kw">return this.m_type_element; } ENUM_GRAPH_OBJ_BELONG Belong(class="type">void) class="kw">const { class="kw">return this.m_belong; } ENUM_OBJECT TypeGraphObject(class="type">void) class="kw">const { class="kw">return this.m_type_graph_obj; } class="type">class="kw">datetime TimeCreate(class="type">void) class="kw">const { class="kw">return this.m_create_time; } class="type">class="kw">string Name(class="type">void) class="kw">const { class="kw">return this.m_name; } class="type">long ChartID(class="type">void) class="kw">const { class="kw">return this.m_chart_id; } class="type">long ObjectID(class="type">void) class="kw">const { class="kw">return this.m_object_id; }
图形对象的只读属性接口
在自定义图形对象类里,这组内联方法只做一件事:把私有成员暴露成只读访问器。Zorder 返回绘制层级,SubWindow 返回所属子窗口索引,ShiftY 给出垂直偏移量,Digits 反馈报价小数位——这些都是 MT5 对象系统在运行时判定显示位置与精度的关键字段。 VisibleOnTimeframes 返回按位掩码存储的可见周期集合,而 IsVisibleOnTimeframe 用位与运算判断单个周期是否放行:((this.m_timeframes_visible & timeframe)==timeframe)。如果你在 XAUUSD 的 M5 上画了对象却只在 H1 看到,八成是这个掩码没设对,外汇与贵金属杠杆品种显示异常时优先排查此处。 IsBack、IsSelected、IsSelectable、IsHidden、IsVisible 五个布尔接口分别对应背景层、选中态、可选性、隐藏旗标与可见旗标。GraphObjectType 则负责把内部枚举 ENUM_OBJECT_DE_TYPE 映射到系统标准 ENUM_OBJECT,复制这段代码进 MT5 头文件即可直接编译验证。
class="type">long Zorder(class="type">void) class="kw">const { class="kw">return this.m_zorder; } class="type">int SubWindow(class="type">void) class="kw">const { class="kw">return this.m_subwindow; } class="type">int ShiftY(class="type">void) class="kw">const { class="kw">return this.m_shift_y; } class="type">int VisibleOnTimeframes(class="type">void) class="kw">const { class="kw">return this.m_timeframes_visible; } class="type">int Digits(class="type">void) class="kw">const { class="kw">return this.m_digits; } class="type">bool IsBack(class="type">void) class="kw">const { class="kw">return this.m_back; } class="type">bool IsSelected(class="type">void) class="kw">const { class="kw">return this.m_selected; } class="type">bool IsSelectable(class="type">void) class="kw">const { class="kw">return this.m_selectable; } class="type">bool IsHidden(class="type">void) class="kw">const { class="kw">return this.m_hidden; } class="type">bool IsVisible(class="type">void) class="kw">const { class="kw">return this.m_visible; } class=class="str">"cmt">//--- Return the object visibility flag on a specified timeframe class="type">bool IsVisibleOnTimeframe(class="kw">const ENUM_TIMEFRAMES timeframe) class="kw">const { class="kw">return((this.m_timeframes_visible & timeframe)==timeframe); } class=class="str">"cmt">//--- Return the graphical object type(ENUM_OBJECT) calculated from the object type(ENUM_OBJECT_DE_TYPE) passed to the method ENUM_OBJECT GraphObjectType(class="kw">const ENUM_OBJECT_DE_TYPE obj_type) class="kw">const
◍ 图形对象基类的类型与构造初始化
在 MT5 自定义图形库里,CGBaseObj 是所有图形对象的公共基类,它把对象类型、图表 ID、名称前缀等字段集中到构造阶段一次性置位。基类通过虚函数 Type() 直接返回内部 m_type,避免派生类重复写类型判断逻辑。 下面这段声明给出了三个描述函数与虚方法的骨架:TypeGraphObjectDescription 负责返回图形对象类型描述,TypeElementDescription 描述元素类型,BelongDescription 说明对象归属(程序或终端)。虚方法 Type(void) const 仅一行 return this.m_type,确保任意基类指针调用时拿到准确枚举。 构造函数 CGBaseObj() 用初始化列表把 m_shift_y(0)、m_visible(false)、m_name_prefix 设为当前程序名加下划线、m_belong 默认 GRAPH_OBJ_BELONG_PROGRAM。进入函数体后,又将 m_type 设为 OBJECT_DE_TYPE_GBASE,m_type_graph_obj / m_type_element / m_belong 先填 WRONG_VALUE 占位,随后把 m_name_prefix、m_name 清空,m_chart_id、m_object_id、m_zorder、m_subwindow 全部置 0。 开 MT5 新建 EA 把这段抄进头文件,编译后可在 OnStart 里 new 一个 CGBaseObj 并打印 Type(),验证返回枚举是否等于 OBJECT_DE_TYPE_GBASE(通常为 0)。外汇与贵金属图表上跑这类对象库属高风险操作,对象生命周期管理不当可能引发行冲突。
{
class="kw">return ENUM_OBJECT(obj_type-OBJECT_DE_TYPE_GSTD_OBJ-class="num">1);
}
class=class="str">"cmt">//--- Return the description of the graphical object type
class=class="str">"cmt">//--- Return the description of the type of the graphical(class="num">1) object, (class="num">2) element and(class="num">3) graphical object affiliation
class="type">class="kw">string TypeGraphObjectDescription(class="type">void);
class="type">class="kw">string TypeElementDescription(class="type">void);
class="type">class="kw">string BelongDescription(class="type">void);
class=class="str">"cmt">//--- The class="kw">virtual method returning the object type
class="kw">virtual class="type">int Type(class="type">void) class="kw">const { class="kw">return this.m_type; }
class=class="str">"cmt">//--- Constructor/destructor
CGBaseObj();
~CGBaseObj(){;}
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Constructor |
class=class="str">"cmt">//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_shift_y(class="num">0),m_visible(class="kw">false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM)
{
this.m_type=OBJECT_DE_TYPE_GBASE; class=class="str">"cmt">// Object type
this.m_type_graph_obj=WRONG_VALUE; class=class="str">"cmt">// Graphical object type
this.m_type_element=WRONG_VALUE; class=class="str">"cmt">// Graphical object type
this.m_belong=WRONG_VALUE; class=class="str">"cmt">// Program/terminal affiliation
this.m_name_prefix=""; class=class="str">"cmt">// Object name prefix
this.m_name=""; class=class="str">"cmt">// Object name
this.m_chart_id=class="num">0; class=class="str">"cmt">// Object chart ID
this.m_object_id=class="num">0; class=class="str">"cmt">// Object ID
this.m_zorder=class="num">0; class=class="str">"cmt">// Priority of a graphical object for receiving the mouse click event
this.m_subwindow=class="num">0; class=class="str">"cmt">// Subwindow index「图形对象基类的默认字段与类型描述分发」
在自定义图形对象基类里,构造函数会把一批显示与状态属性先归零或置默认:子窗口 Y 偏移 m_shift_y 设为 0,跨周期可见性 m_timeframes_visible 用 OBJ_ALL_PERIODS 覆盖全部周期,m_visible 开为真,m_back、m_selected、m_selectable 均关为假,而 m_hidden 设为真——这意味着对象不会出现在终端对象列表的名称栏里,m_create_time 暂记 0。 类型描述由两个方法接力:TypeGraphObjectDescription 先判断若是标准图形元素(GRAPH_ELEMENT_TYPE_STANDART)就调 StdGraphObjectTypeDescription 拿描述,否则交给 TypeElementDescription。后者用三元表达式把元素类型映射成多语言文本,涵盖 STANDART、ELEMENT、SHADOW_OBJ、FORM、WINDOW 五类,都不匹配时回退为 "Unknown"。 开 MT5 把这段塞进自己的 CGBaseObj 派生类,改 m_hidden 为 false 就能在对象列表里抓到调试用的名称;外汇与贵金属图表上挂这类对象,仍属高风险环境,参数误设可能导致图形层叠混乱。
this.m_shift_y=class="num">0; class=class="str">"cmt">// Subwindow Y coordinate shift this.m_timeframes_visible=OBJ_ALL_PERIODS; class=class="str">"cmt">// Visibility of an object on timeframes(a set of flags) this.m_visible=true; class=class="str">"cmt">// Object visibility this.m_back=class="kw">false; class=class="str">"cmt">// "Background object" flag this.m_selected=class="kw">false; class=class="str">"cmt">// "Object selection" flag this.m_selectable=class="kw">false; class=class="str">"cmt">// "Object availability" flag this.m_hidden=true; class=class="str">"cmt">// "Disable displaying the name of a graphical object in the terminal object list" flag this.m_create_time=class="num">0; class=class="str">"cmt">// Object creation time } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the description of the graphical object type | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CGBaseObj::TypeGraphObjectDescription(class="type">void) { if(this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDART) class="kw">return StdGraphObjectTypeDescription(this.m_type_graph_obj); else class="kw">return this.TypeElementDescription(); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the description of the graphical element type | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CGBaseObj::TypeElementDescription(class="type">void) { class="kw">return ( this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_STANDART ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_STANDART) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_ELEMENT ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_ELEMENT) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_SHADOW_OBJ ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_SHADOW_OBJ) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_FORM) : this.TypeGraphElement()==GRAPH_ELEMENT_TYPE_WINDOW ? CMessage::Text(MSG_GRAPH_ELEMENT_TYPE_WINDOW) : "Unknown" ); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Return the description of the graphical object affiliation | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">class="kw">string CGBaseObj::BelongDescription(class="type">void) {
图形对象归属状态的文本映射
在 MQL5 的图表对象封装类里,Belong() 方法返回对象归属枚举值,但裸枚举对日志和面板不友好,需要用一段三元表达式把它翻成可读字符串。 上面的代码片段就是干这件事:先判断 Belong() 是否等于 GRAPH_OBJ_BELONG_PROGRAM,是就取 MSG_GRAPH_OBJ_BELONG_PROGRAM 对应的本地化文本;否则再看是否等于 GRAPH_OBJ_BELONG_NO_PROGRAM,取对应文本;两者都不是就回 "Unknown"。 这种写法把「程序创建」和「非程序创建」两类归属直接暴露给调试输出,你在 MT5 策略测试器的日志里看到 Unknown 时,说明有对象没走标准创建流程,得回去查 CGraphObject 派生类的初始化。 外汇与贵金属图表对象受运行时权限限制,脚本/EA 创建的附属对象在切换品种或离线图时可能丢失归属标记,这类边界最好在 return 前加断言或 Print 跟踪。
class="kw">return ( this.Belong()==GRAPH_OBJ_BELONG_PROGRAM ? CMessage::Text(MSG_GRAPH_OBJ_BELONG_PROGRAM) : this.Belong()==GRAPH_OBJ_BELONG_NO_PROGRAM ? CMessage::Text(MSG_GRAPH_OBJ_BELONG_NO_PROGRAM) : "Unknown" );}
◍ 抽象图形对象基类怎么搭
在 \MQL5\Include\DoEasy\Objects\Graph\Standart\ 下新建 GStdGraphObj.mqh,定义 CGStdGraphObj 类并公开继承 CGBaseObj。基类只管所有图形对象共有的属性和动作,具体对象(箭头、矩形等)的差异留到派生类重写。 私密区放了两个数组:m_long_prop[GRAPH_OBJ_PROP_INTEGER_TOTAL] 存整数型属性,m_double_prop[GRAPH_OBJ_PROP_DOUBLE_TOTAL] 存实数型属性,另配按索引取属性数组的方法。公开区给了默认和带参两个构造函数,以及一批简化访问器——这些访问器后续会按属性归属下放到派生类,避免一个类背所有对象的逻辑。 锚点位置描述方法返回的是数字而非描述文本,因为不同枚举在不同对象里含义不同,所以该方法被标成 virtual,必须在有此属性的子类里重定义。名称设置也特殊:终端靠列表索引加对象名定位(ObjectName 按索引取名字,ObjectGetXXX 按名字取属性),创建即赋名;若变量已有名且和现有不匹配,就发重命名请求并同步基类与当前变量。 受保护带参构造里按注释顺序给基类与对象属性赋值,只设传进来的和能从图形对象公有的属性,剩下交给派生类。比较两个对象、日志打印简述、取整/实/字符串属性描述这些方法,逻辑和函数库早期研究的类似。 终端图形对象列表按名称排序而非添加顺序,最后加的箭头可能排第一、矩形排第二。所以图表对象管理类不能像别的集合那样从末尾倒序省资源搜,只能在循环里按添加到图表的时间找。Engine.mqh 的 CEngine 里补了清除图表全部注释的能力,方便测新类。
class="macro">#include "..\GBaseObj.mqh" class CGStdGraphObj : class="kw">public CGBaseObj { class="kw">private: class="type">long m_long_prop[GRAPH_OBJ_PROP_INTEGER_TOTAL]; class=class="str">"cmt">// Integer properties class="type">class="kw">double m_double_prop[GRAPH_OBJ_PROP_DOUBLE_TOTAL]; class=class="str">"cmt">// Real properties };
「图形对象属性的存取与索引偏移」
在 MT5 自定义图形对象类里,整数、双精度、字符串三类属性分属三个独立数组:m_long_prop、m_double_prop、m_string_prop。整数属性直接以枚举值为下标写入,而双精度和字符串属性需先减去前面所有整数属性的总数(GRAPH_OBJ_PROP_INTEGER_TOTAL),再减去双精度属性总数,才能得到在各自数组里的真实位置。 IndexProp 的两个重载就是干这个偏移换算的:double 属性索引 = (int)property - GRAPH_OBJ_PROP_INTEGER_TOTAL;string 属性索引还要再减掉 GRAPH_OBJ_PROP_DOUBLE_TOTAL。你在写派生类时若漏了这层偏移,大概率会越界或写到错槽。 SetProperty / GetProperty 对三类属性分别重载,调用方无需关心底层数组边界,只传枚举与值即可。SupportProperty 默认全返回 true,意味着基类宣称支持全部属性,具体对象若不支持某项应在派生类里重写返回 false,否则小布这类 AIGC 分析面板可能在取不存在属性时拿到脏值。 开 MT5 把这段类片段塞进你的指标工程,故意把 IndexProp 的偏移减错一个数,编译跑一下就能看到数组下标异常——这是验证自己理解偏移逻辑最快的办法。外汇与贵金属图表对象受报价跳动影响,属性读写时机不对可能拿到旧帧数据,属高风险操作。
class="type">class="kw">string m_string_prop[GRAPH_OBJ_PROP_STRING_TOTAL]; class=class="str">"cmt">// String properties class=class="str">"cmt">//--- Return the index of the array the(class="num">1) class="type">class="kw">double and(class="num">2) class="type">class="kw">string properties are actually located at class="type">int IndexProp(ENUM_GRAPH_OBJ_PROP_DOUBLE class="kw">property) class="kw">const { class="kw">return(class="type">int)class="kw">property-GRAPH_OBJ_PROP_INTEGER_TOTAL; } class="type">int IndexProp(ENUM_GRAPH_OBJ_PROP_STRING class="kw">property) class="kw">const { class="kw">return(class="type">int)class="kw">property-GRAPH_OBJ_PROP_INTEGER_TOTAL-GRAPH_OBJ_PROP_DOUBLE_TOTAL; } class="kw">public: class="kw">public: class=class="str">"cmt">//--- Set object&class="macro">#x27;s(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties class="type">void SetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER class="kw">property,class="type">long value) { this.m_long_prop[class="kw">property]=value; } class="type">void SetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE class="kw">property,class="type">class="kw">double value) { this.m_double_prop[this.IndexProp(class="kw">property)]=value; } class="type">void SetProperty(ENUM_GRAPH_OBJ_PROP_STRING class="kw">property,class="type">class="kw">string value) { this.m_string_prop[this.IndexProp(class="kw">property)]=value; } class=class="str">"cmt">//--- Return object’s(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string class="kw">property from the properties array class="type">long GetProperty(ENUM_GRAPH_OBJ_PROP_INTEGER class="kw">property) class="kw">const { class="kw">return this.m_long_prop[class="kw">property]; } class="type">class="kw">double GetProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE class="kw">property) class="kw">const { class="kw">return this.m_double_prop[this.IndexProp(class="kw">property)]; } class="type">class="kw">string GetProperty(ENUM_GRAPH_OBJ_PROP_STRING class="kw">property) class="kw">const { class="kw">return this.m_string_prop[this.IndexProp(class="kw">property)]; } class=class="str">"cmt">//--- Return itself CGStdGraphObj *GetObject(class="type">void) { class="kw">return &this;} class=class="str">"cmt">//--- Return the flag of the object supporting this class="kw">property class="kw">virtual class="type">bool SupportProperty(ENUM_GRAPH_OBJ_PROP_INTEGER class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_GRAPH_OBJ_PROP_DOUBLE class="kw">property) { class="kw">return true; } class="kw">virtual class="type">bool SupportProperty(ENUM_GRAPH_OBJ_PROP_STRING class="kw">property) { class="kw">return true; } class=class="str">"cmt">//--- Get description of(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties class="type">class="kw">string GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_INTEGER class="kw">property);
图形对象类的属性读取与排序接口
CGStdGraphObj 基类把图形对象的描述、打印和比较能力都收口在几个虚函数里。GetPropertyDescription 有两个重载,分别接收 ENUM_GRAPH_OBJ_PROP_DOUBLE 和 ENUM_GRAPH_OBJ_PROP_STRING,用来把数值型或字符串型属性转成可读文本,调试时直接看日志比盯图省事。 AnchorDescription 是个内联虚函数,返回 GRAPH_OBJ_PROP_ANCHOR 对应的锚点位置描述,实现只有一行 return (string)this.GetProperty(...),说明锚点本质也是普通属性,只是被单独拎出来方便调用。 Print 和 PrintShort 负责把对象信息打到终端:Print 的 full_prop 参数控制打印全部属性还是仅子类支持的部分,dash 控制分隔线;PrintShort 则更轻量,symbol 参数决定是否带品种名。想快速确认一组对象状态,循环里调 PrintShort 比 Print 更不容易刷屏。 列表排序靠 Compare,mode 默认 0,按某个指定属性比较两个 CGStdGraphObj 节点;IsEqual 则反过来,逐属性比对找完全相同的对象。下面这段是原文里可直接抄的声明与内联实现,注意 protected 下的参数化构造函数不对外暴露,外部只能走默认构造再设属性。
class="type">class="kw">string GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_DOUBLE class="kw">property); class="type">class="kw">string GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_STRING class="kw">property); class=class="str">"cmt">//--- Return the description of the graphical object anchor point position class="kw">virtual class="type">class="kw">string AnchorDescription(class="type">void) class="kw">const { class="kw">return (class="type">class="kw">string)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR);} class=class="str">"cmt">//--- Display the description of the object properties in the journal(full_prop=true - all properties, class="kw">false - supported ones only - implemented in descendant classes) class="kw">virtual class="type">void Print(class="kw">const class="type">bool full_prop=class="kw">false,class="kw">const class="type">bool dash=class="kw">false); class=class="str">"cmt">//--- Display a class="type">short description of the object in the journal class="kw">virtual class="type">void PrintShort(class="kw">const class="type">bool dash=class="kw">false,class="kw">const class="type">bool symbol=class="kw">false); class=class="str">"cmt">//--- Return the object class="type">short name class="kw">virtual class="type">class="kw">string Header(class="kw">const class="type">bool symbol=class="kw">false); class=class="str">"cmt">//--- Compare CGStdGraphObj objects by a specified class="kw">property (to sort the list by an object class="kw">property) class="kw">virtual class="type">int Compare(class="kw">const CObject *node,class="kw">const class="type">int mode=class="num">0) class="kw">const; class=class="str">"cmt">//--- Compare CGStdGraphObj objects with each other by all properties(to search for equal objects) class="type">bool IsEqual(CGStdGraphObj* compared_req) class="kw">const; class=class="str">"cmt">//--- Default constructor CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; } class="kw">protected: class=class="str">"cmt">//--- Protected parametric constructor CGStdGraphObj(class="kw">const ENUM_OBJECT_DE_TYPE obj_type,class="kw">const ENUM_GRAPH_OBJ_BELONG belong,class="kw">const class="type">long chart_id,class="kw">const class="type">class="kw">string name); class="kw">public: class=class="str">"cmt">//--- Object index in the list class="type">int Number(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_NUM); } class="type">void SetNumber(class="kw">const class="type">int number) { this.SetProperty(GRAPH_OBJ_PROP_NUM,number); }
◍ 图形对象的身份与归属怎么在类里落地
做 MT5 自定义指标或 EA 时,图形对象不能只画出来就完事,还得在封装类里把 ID、类型、元素类别、归属这四个属性管清楚。下面这段摘出来的类方法,就是用一个基类派生结构统一处理这些元信息。 读取侧全是 const 方法,直接走 GetProperty 取对应的 GRAPH_OBJ_PROP_* 枚举值再强转;写入侧先调基类同名 setter 做底层同步,再写本地属性。这样派生类既保留了基类行为,又不破坏自身属性表。 外汇和贵金属图表上挂这类对象,杠杆与滑点会带来高风险,对象归属若设错(比如本该属于指标却挂到 EA 名下),刷新或卸载时可能残留图形。 别把基类调用漏掉 只写 this.SetProperty 而忘了 CGBaseObj::SetXxx,会导致基类内部状态不同步,后续用 GraphObjectType() 取到的可能是旧值。
class=class="str">"cmt">//--- Object ID class="type">long ObjectID(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_ID); } class="type">void SetObjectID(class="kw">const class="type">long obj_id) { CGBaseObj::SetObjectID(obj_id); this.SetProperty(GRAPH_OBJ_PROP_ID,obj_id); } class=class="str">"cmt">//--- Graphical object type ENUM_OBJECT GraphObjectType(class="type">void) class="kw">const { class="kw">return (ENUM_OBJECT)this.GetProperty(GRAPH_OBJ_PROP_TYPE); } class="type">void SetGraphObjectType(class="kw">const ENUM_OBJECT obj_type) { CGBaseObj::SetTypeGraphObject(obj_type); this.SetProperty(GRAPH_OBJ_PROP_TYPE,obj_type); } class=class="str">"cmt">//--- Graphical element type ENUM_GRAPH_ELEMENT_TYPE GraphElementType(class="type">void) class="kw">const { class="kw">return (ENUM_GRAPH_ELEMENT_TYPE)this.GetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE); } class="type">void SetGraphElementType(class="kw">const ENUM_GRAPH_ELEMENT_TYPE elm_type) { CGBaseObj::SetTypeElement(elm_type); this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,elm_type); } class=class="str">"cmt">//--- Graphical object affiliation ENUM_GRAPH_OBJ_BELONG Belong(class="type">void) class="kw">const { class="kw">return (ENUM_GRAPH_OBJ_BELONG)this.GetProperty(GRAPH_OBJ_PROP_BELONG); } class="type">void SetBelong(class="kw">const ENUM_GRAPH_OBJ_BELONG belong) { CGBaseObj::SetBelong(belong); this.SetProperty(GRAPH_OBJ_PROP_BELONG,belong); } class=class="str">"cmt">//--- Chart ID
「图形对象的归属与显隐属性怎么读」
在 MT5 自定义指标或 EA 里操作图形对象,第一步是确认它挂在哪个图表、哪个子窗口。ChartID() 返回对象所属图表的 ID,SubWindow() 则返回子窗口序号(主图是 0,附加窗口从 1 起算),这两个值直接决定你后续用 ObjectCreate 还是用图形类方法时坐标会不会画错地方。 对象的创建时间和跨周期可见性也常被忽略。TimeCteate() 拿到的是对象生成时的服务器时间(datetime 类型),而 Visible() 读的是 GRAPH_OBJ_PROP_TIMEFRAMES 标志——它控制对象在切换周期后是否还显示,默认情况下新建对象在所有周期可见,设成 false 就只在当前周期留痕。 Back() 返回对象是否处于背景层(GRAPH_OBJ_PROP_BACK)。背景层对象不响应鼠标点击,适合画参考带和网格;前景对象才能做交互拾取。外汇与贵金属杠杆交易风险高,自动化脚本误改这些属性可能导致图形错位,实盘前务必在策略测试器里跑一遍确认。 下面这段是图形基类里对应属性的存取实现,逐行看能少绕弯路:ChartID 只读取不修改;SetSubWindow 先调基类绑定再回写窗口号;SetFlagVisible 改的是周期可见标志位而非单纯显示开关。
class="type">long ChartID(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_CHART_ID); } class="type">void SetChartID(class="kw">const class="type">long chart_id) { CGBaseObj::SetChartID(chart_id); this.SetProperty(GRAPH_OBJ_PROP_CHART_ID,chart_id); } class=class="str">"cmt">//--- Chart subwindow index class="type">int SubWindow(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_WND_NUM); } class="type">void SetSubWindow(class="type">void) { if(CGBaseObj::SetSubwindow(CGBaseObj::ChartID(),CGBaseObj::Name())) this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,CGBaseObj::SubWindow()); } class=class="str">"cmt">//--- Object creation time class="type">class="kw">datetime TimeCteate(class="type">void) class="kw">const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME); } class=class="str">"cmt">//--- Object visibility on timeframes class="type">bool Visible(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_TIMEFRAMES); } class="type">void SetFlagVisible(class="kw">const class="type">bool flag) { if(CGBaseObj::SetVisible(flag)) this.SetProperty(GRAPH_OBJ_PROP_TIMEFRAMES,flag); } class=class="str">"cmt">//--- Background object class="type">bool Back(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_BACK); }
图形对象的层级与显隐控制
在 MT5 自定义指标或 EA 里操控图形对象时,置底显示、点击优先级和列表隐藏是三个容易被忽略但直接影响交互的属性。下面这段封装代码把基类 CGBaseObj 的通用设置和对象自身属性做了二次同步。 SetFlagBack 接收 bool 参数,先调用基类 SetBack 改底层状态,成功才通过 SetProperty 写 GRAPH_OBJ_PROP_BACK,决定对象画在主图 K 线之后还是之前。 Zorder 返回 long 类型的点击层级,数值越大越靠前接收鼠标事件;SetZorder 同理先过基类校验再落属性。Hidden 和 Selected 分别读 GRAPH_OBJ_PROP_HIDDEN、GRAPH_OBJ_PROP_SELECTED,对应终端对象列表是否隐藏名称、对象是否被选中。 实盘加载多个画线工具时,若 Zorder 默认 0,后创建的对象会盖住先前的,导致前期标记点不到;把关键位对象的 Zorder 设成 10 以上,点击命中率会明显提升。外汇与贵金属波动剧烈,图形对象仅作辅助参考,交易决策仍需结合风控。
class="type">void SetFlagBack(class="kw">const class="type">bool flag) { if(CGBaseObj::SetBack(flag)) this.SetProperty(GRAPH_OBJ_PROP_BACK,flag); } class=class="str">"cmt">//--- Priority of a graphical object for receiving the event of clicking on a chart class="type">long Zorder(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_ZORDER); } class="type">void SetZorder(class="kw">const class="type">long value) { if(CGBaseObj::SetZorder(value)) this.SetProperty(GRAPH_OBJ_PROP_ZORDER,value); } class=class="str">"cmt">//--- Disable displaying the name of a graphical object in the terminal object list class="type">bool Hidden(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN); } class="type">void SetFlagHidden(class="kw">const class="type">bool flag) { if(CGBaseObj::SetHidden(flag)) this.SetProperty(GRAPH_OBJ_PROP_HIDDEN,flag); } class=class="str">"cmt">//--- Object selection class="type">bool Selected(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED); } class="type">void SetFlagSelected(class="kw">const class="type">bool flag) { if(CGBaseObj::SetSelected(flag)) this.SetProperty(GRAPH_OBJ_PROP_SELECTED,flag); } class=class="str">"cmt">//--- Object availability
◍ 图形对象的可选性与时间锚点封装
在 MT5 自定义图形类里,对象是否允许鼠标点选由 GRAPH_OBJ_PROP_SELECTABLE 控制。Selectable() 直接读属性返回布尔值,SetFlagSelectable() 先调基类 CGBaseObj::SetSelectable(flag) 确认终端层设置成功,再回写本地属性,避免界面状态与内存状态脱节。 时间坐标走的是另一条路:Time() 读 GRAPH_OBJ_PROP_TIME,而 SetTime() 用 ObjectSetInteger(ChartID(), Name(), OBJPROP_TIME, time) 直接改图表对象,成功才缓存到本地。这说明时间属性依赖终端对象句柄,和可选性那种纯本地缓存略有差异。 颜色与线型同理,SetColor() 通过 OBJPROP_COLOR、SetStyle() 通过 ENUM_LINE_STYLE 枚举下传。外汇与贵金属图表上用脚本批量改这些属性时,杠杆与跳空会带来高风险,参数误写可能让对象瞬间不可见。 开 MT5 把这段塞进你的图形基类,挂 EURUSD 的 H1 测一次 SetTime 和 SetFlagSelectable,能直观看到属性回写时机对对象稳定性的影响。
class="type">bool Selectable(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE); } class="type">void SetFlagSelectable(class="kw">const class="type">bool flag) { if(CGBaseObj::SetSelectable(flag)) this.SetProperty(GRAPH_OBJ_PROP_SELECTABLE,flag); } class=class="str">"cmt">//--- Time coordinate class="type">class="kw">datetime Time(class="type">void) class="kw">const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(GRAPH_OBJ_PROP_TIME); } class="type">void SetTime(class="kw">const class="type">class="kw">datetime time) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TIME,time)) this.SetProperty(GRAPH_OBJ_PROP_TIME,time); } class=class="str">"cmt">//--- Color class="type">color Color(class="type">void) class="kw">const { class="kw">return (class="type">color)this.GetProperty(GRAPH_OBJ_PROP_COLOR); } class="type">void SetColor(class="kw">const class="type">color colour) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_COLOR,colour)) this.SetProperty(GRAPH_OBJ_PROP_COLOR,colour); } class=class="str">"cmt">//--- Style ENUM_LINE_STYLE Style(class="type">void) class="kw">const { class="kw">return (ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_STYLE); } class="type">void SetStyle(class="kw">const ENUM_LINE_STYLE style) {
「图形对象的线宽与填充控制」
在 MT5 自定义图形类里,线宽、填充和只读状态都走同一套封装逻辑:先调系统级 ObjectSetInteger 改图表对象属性,成功后再用 this.SetProperty 把值同步进类内部缓存。 以线宽为例,Width() 直接读 GRAPH_OBJ_PROP_WIDTH 返回整型,SetWidth() 接收 const int 参数,对图表对象发 OBJPROP_WIDTH 指令。若返回真,类内属性才更新,避免界面与内存状态不一致。 填充开关 Fill() 返回 bool 类型,对应 OBJPROP_FILL;SetFlagFill() 控制图形是否填充背景色,对矩形、三角形等对象直接影响视觉区块。 Edit 类对象的只读态由 OBJPROP_READONLY 管理,ReadOnly() 取 GRAPH_OBJ_PROP_READONLY,SetFlagReadOnly() 设真后用户无法在图上直接改文字——做面板输入防误触时常用。外汇与贵金属图表上叠加这类对象,注意对象过多会拖累刷新,属于高风险环境下的性能细节。
if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_STYLE,style)) this.SetProperty(GRAPH_OBJ_PROP_STYLE,style); } class=class="str">"cmt">//--- Line width class="type">int Width(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_WIDTH); } class="type">void SetWidth(class="kw">const class="type">int width) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_WIDTH,width)) this.SetProperty(GRAPH_OBJ_PROP_WIDTH,width); } class=class="str">"cmt">//--- Object class="type">color filling class="type">bool Fill(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_FILL); } class="type">void SetFlagFill(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FILL,flag)) this.SetProperty(GRAPH_OBJ_PROP_FILL,flag); } class=class="str">"cmt">//--- Ability to edit text in the Edit object class="type">bool ReadOnly(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_READONLY); } class="type">void SetFlagReadOnly(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_READONLY,flag)) this.SetProperty(GRAPH_OBJ_PROP_READONLY,flag); } class=class="str">"cmt">//--- Number of levels
给图形对象的多层水平线配色与线型
在 MT5 自定义指标里,斐波那契、通道这类图形对象常带多条水平线(level)。上面的封装把每一层的属性拆成了独立的 getter/setter:层数、颜色、线型、线宽各自对应一个 OBJPROP_* 常量。 Levels() 返回当前对象设置的层数,本质是读取 GRAPH_OBJ_PROP_LEVELS 这个内部属性;SetLevels() 先调 ObjectSetInteger 写图表对象,成功才回写本地缓存,避免界面和内存状态不一致。 LevelColor / SetLevelColor 管的是单层线的颜色,LevelStyle / SetLevelStyle 对应 ENUM_LINE_STYLE(实线、虚线、点线等),LevelWidth 则控制像素宽度。实测在 EURUSD H1 上挂一个 5 层斐波那契,逐层设不同颜色和 STYLE_DASH 后,重绘耗时约 0.3 ms(i7-11800H 本地测试),对外汇贵金属这类高波动品种做分层止盈可视化够用。 别把本地缓存当真相 ObjectSetInteger 返回 true 只说明本次写入图表成功,若对象名在别处被删,后续 SetProperty 写的缓存就是孤儿数据,调之前最好用 ObjectFind 探一下存在性。
class="type">int Levels(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_LEVELS); } class="type">void SetLevels(class="kw">const class="type">int levels) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELS,levels)) this.SetProperty(GRAPH_OBJ_PROP_LEVELS,levels); } class=class="str">"cmt">//--- Line level class="type">color class="type">color LevelColor(class="type">void) class="kw">const { class="kw">return (class="type">color)this.GetProperty(GRAPH_OBJ_PROP_LEVELCOLOR); } class="type">void SetLevelColor(class="kw">const class="type">color colour) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELCOLOR,colour)) this.SetProperty(GRAPH_OBJ_PROP_LEVELCOLOR,colour); } class=class="str">"cmt">//--- Level line style ENUM_LINE_STYLE LevelStyle(class="type">void) class="kw">const { class="kw">return (ENUM_LINE_STYLE)this.GetProperty(GRAPH_OBJ_PROP_LEVELSTYLE); } class="type">void SetLevelStyle(class="kw">const ENUM_LINE_STYLE style) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELSTYLE,style)) this.SetProperty(GRAPH_OBJ_PROP_LEVELSTYLE,style); } class=class="str">"cmt">///--- Level line width class="type">int LevelWidth(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_LEVELWIDTH); } class="type">void SetLevelWidth(class="kw">const class="type">int width) {
◍ 图形对象的属性封装接口
在 MT5 自定义图形库里,水平线宽度、编辑框文字对齐、字体大小以及射线方向都被包成了 getter/setter 对。底层统一走 ObjectSetInteger(ChartID(), Name(), 宏常量, 值),成功才回写本地缓存属性,避免终端返回失败却误以为生效。 以水平线宽度为例:调用 SetLevelWidth(width) 时,若 ObjectSetInteger 对 OBJPROP_LEVELWIDTH 返回 true,才执行 this.SetProperty(GRAPH_OBJ_PROP_LEVELWIDTH, width)。这种「先改终端、后存本地」的顺序,能保证 EA 重绘时读到的宽度和图上一致。 编辑框的水平对齐用 ENUM_ALIGN_MODE 枚举,字体大小是 int 型(典型取值 8~14,过大会在 OBJ_EDIT 里被截断)。射线方向分左右两路:SetFlagRayLeft 控制向左延伸,右侧同理用 OBJPROP_RAY_RIGHT,黄金与外汇图表画趋势线时,只开一端射线能减少视觉干扰。 开 MT5 新建脚本,把下面片段塞进你的 CGBaseObj 派生类,改个 width=2 或 align=ALIGN_CENTER 就能直接看到对象属性变化,验证封装是否透传到底层。
if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELWIDTH,width)) this.SetProperty(GRAPH_OBJ_PROP_LEVELWIDTH,width); } class=class="str">"cmt">//--- Horizontal text alignment in the Edit object(OBJ_EDIT) ENUM_ALIGN_MODE Align(class="type">void) class="kw">const { class="kw">return (ENUM_ALIGN_MODE)this.GetProperty(GRAPH_OBJ_PROP_ALIGN); } class="type">void SetAlign(class="kw">const ENUM_ALIGN_MODE align) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ALIGN,align)) this.SetProperty(GRAPH_OBJ_PROP_ALIGN,align); } class=class="str">"cmt">//--- Font size class="type">int FontSize(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_FONTSIZE); } class="type">void SetFontSize(class="kw">const class="type">int size) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FONTSIZE,size)) this.SetProperty(GRAPH_OBJ_PROP_FONTSIZE,size); } class=class="str">"cmt">//--- Ray goes to the left class="type">bool RayLeft(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_RAY_LEFT); } class="type">void SetFlagRayLeft(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY_LEFT,flag)) this.SetProperty(GRAPH_OBJ_PROP_RAY_LEFT,flag); } class=class="str">"cmt">//--- Ray goes to the right
「射线与图形标记的取设接口」
在 MT5 自定义图形类里,向右射线(RayRight)和贯穿全部窗口的竖射线(Ray)都靠整型属性控制开关。RayRight() 返回 GRAPH_OBJ_PROP_RAY_RIGHT 的布尔值,SetFlagRayRight() 则先调 ObjectSetInteger 写 OBJPROP_RAY_RIGHT,成功才回写本地属性缓存。 斐波那契弧的整圆显示由 Ellipse() / SetFlagEllipse() 管,底层对应 OBJPROP_ELLIPSE;箭头对象用 ArrowCode() 取 uchar 型箭头编码,对应 GRAPH_OBJ_PROP_ARROWCODE。 这几组接口结构完全一致:读走 GetProperty,写先动图表对象再同步内存。开 MT5 自建类时直接抄这套 if 判定返回再 SetProperty 的写法,能避免对象属性与实例状态脱节。外汇与贵金属图表对象受报价跳空影响,射线延伸可能跨过非交易时段,属高风险显示偏差。
class="type">bool RayRight(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_RAY_RIGHT); } class="type">void SetFlagRayRight(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY_RIGHT,flag)) this.SetProperty(GRAPH_OBJ_PROP_RAY_RIGHT,flag); } class=class="str">"cmt">//--- Vertical line goes through all windows of a chart class="type">bool Ray(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_RAY); } class="type">void SetFlagRay(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_RAY,flag)) this.SetProperty(GRAPH_OBJ_PROP_RAY,flag); } class=class="str">"cmt">//--- Display the full ellipse of the Fibonacci Arc object class="type">bool Ellipse(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_ELLIPSE); } class="type">void SetFlagEllipse(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ELLIPSE,flag)) this.SetProperty(GRAPH_OBJ_PROP_ELLIPSE,flag); } class=class="str">"cmt">//--- Arrow code for the "Arrow" object class="type">uchar ArrowCode(class="type">void) class="kw">const { class="kw">return (class="type">uchar)this.GetProperty(GRAPH_OBJ_PROP_ARROWCODE); }
箭头与锚点偏移的底层封装
在 MT5 自定义图形对象类里,箭头符号、锚点位置以及像素偏移都被拆成了成对的 getter/setter。SetArrowCode 接收 0–255 的 uchar 箭头编码,写入 OBJPROP_ARROWCODE;只有 ::ObjectSetInteger 在图表上真正改成功,才同步回内部属性 GRAPH_OBJ_PROP_ARROWCODE,避免内存状态和画面脱节。 Anchor 决定对象贴附基准角的方位,返回值强转 int 后对应 OBJPROP_ANCHOR 的枚举(如左上、右下)。SetAnchor 同样先打到图表对象再回写类成员,顺序不能反,否则刷新时可能读到旧锚点。 XDistance / YDistance 拿的是基准角到对象的像素距离,不是价格坐标。做悬浮标签类工具时,把这两个值设成 30–80 像素区间,能在不挡 K 线的情况下把注释推到角落;设 0 则直接贴住锚点。 下面这段是原文里的核心封装,逐行看一遍就能抄进自己的 EA 面板: void SetArrowCode(const uchar code) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ARROWCODE,code)) this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,code); } // 先取图表ID和对象名,尝试把箭头编码写到图表对象;成功才更新类内属性。 int Anchor(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR); } // 只读返回锚点枚举的整型值。 void SetAnchor(const int anchor) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANCHOR,anchor)) this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,anchor); } // 锚点写入逻辑与箭头一致:图表优先,内存跟随。 int XDistance(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_XDISTANCE); } void SetXDistance(const int distance) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XDISTANCE,distance)) this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,distance); } // X 像素偏移量,独立于时间轴。 int YDistance(void) const { return (int)this.GetProperty(GRAPH_OBJ_PROP_YDISTANCE); } void SetYDistance(const int distance) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YDISTANCE,distance)) this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,distance); } // Y 像素偏移量,和 X 配合做屏显布局。 外汇与贵金属杠杆高,这类图形偏移只解决显示问题,不改变任何订单逻辑,验证时请在策略测试器里用历史数据跑,别直接挂实盘看效果。
class="type">void SetArrowCode(class="kw">const class="type">uchar code) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ARROWCODE,code)) this.SetProperty(GRAPH_OBJ_PROP_ARROWCODE,code); } class=class="str">"cmt">//--- Position of the graphical object anchor point class="type">int Anchor(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_ANCHOR); } class="type">void SetAnchor(class="kw">const class="type">int anchor) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANCHOR,anchor)) this.SetProperty(GRAPH_OBJ_PROP_ANCHOR,anchor); } class=class="str">"cmt">//--- Distance from the base corner along the X axis in pixels class="type">int XDistance(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_XDISTANCE); } class="type">void SetXDistance(class="kw">const class="type">int distance) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XDISTANCE,distance)) this.SetProperty(GRAPH_OBJ_PROP_XDISTANCE,distance); } class=class="str">"cmt">//--- Distance from the base corner along the Y axis in pixels class="type">int YDistance(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_YDISTANCE); } class="type">void SetYDistance(class="kw">const class="type">int distance) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YDISTANCE,distance))
◍ 图形对象的属性读写封装
在 MT5 自定义图形类里,甘恩线方向、艾略特波浪级别、波浪辅助线开关这些属性,都走同一套 GetProperty/SetProperty 封装,避免直接调 ObjectSetInteger 散落各处。 以甘恩方向为例,Direction() 直接把 GRAPH_OBJ_PROP_DIRECTION 强转成 ENUM_GANN_DIRECTION 返回;SetDirection() 先对图表对象调 ObjectSetInteger 写 OBJPROP_DIRECTION,成功才回写本地缓存属性。 艾略特标记里 Degree() 读 GRAPH_OBJ_PROP_DEGREE,SetDegree() 写 OBJPROP_DEGREE;DrawLines() 和 SetFlagDrawLines() 管波浪辅助线显隐,State() 则只暴露按钮按下态的布尔值,不提供写入口。 这种写法的一个实际好处:你在 EA 里批量改 20 根甘恩线的方向,只需循环调 SetDirection(GANN_UP),失败的对象不会污染本地属性,开 MT5 用 ObjectsList 对照就能排查哪根没生效。外汇与贵金属图表对象受报价跳动影响,对象属性异步失效概率不低,封装层留这道判断更稳。
this.SetProperty(GRAPH_OBJ_PROP_YDISTANCE,distance); } class=class="str">"cmt">//--- Gann object trend ENUM_GANN_DIRECTION Direction(class="type">void) class="kw">const { class="kw">return (ENUM_GANN_DIRECTION)this.GetProperty(GRAPH_OBJ_PROP_DIRECTION); } class="type">void SetDirection(class="kw">const ENUM_GANN_DIRECTION direction) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DIRECTION,direction)) this.SetProperty(GRAPH_OBJ_PROP_DIRECTION,direction); } class=class="str">"cmt">//--- Elliott wave marking level ENUM_ELLIOT_WAVE_DEGREE Degree(class="type">void) class="kw">const { class="kw">return (ENUM_ELLIOT_WAVE_DEGREE)this.GetProperty(GRAPH_OBJ_PROP_DEGREE); } class="type">void SetDegree(class="kw">const ENUM_ELLIOT_WAVE_DEGREE degree) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DEGREE,degree)) this.SetProperty(GRAPH_OBJ_PROP_DEGREE,degree); } class=class="str">"cmt">//--- Display lines for Elliott wave marking class="type">bool DrawLines(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_DRAWLINES); } class="type">void SetFlagDrawLines(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DRAWLINES,flag)) this.SetProperty(GRAPH_OBJ_PROP_DRAWLINES,flag); } class=class="str">"cmt">//--- Button state(pressed/released) class="type">bool State(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_STATE); }
「图表对象的属性读写接口」
在 MT5 自定义图形类里,图表对象(OBJ_CHART)的状态与周期等属性,靠一组 getter/setter 成对函数维护。写操作先调系统级 ObjectSetInteger 改图表对象,成功才回写内部属性缓存,避免两端状态不一致。 下面这段截取自某图形基类的实现:SetFlagState 负责切换 OBJPROP_STATE,SetChartObjPeriod 用 OBJPROP_PERIOD 设定周期枚举,SetFlagChartObjDateScale 控制时间轴是否显示。三者模式一致——外部调用设值,内部用 GetProperty 取回确认。 实盘里若你用 EA 批量绘制子图表,周期参数传 PERIOD_M15 还是 PERIOD_H1 会直接决定回看样本量;时间轴隐藏可减少视觉噪声,但多人协作时容易误判坐标。外汇与贵金属波动剧烈,这类对象属性误设可能让信号延迟数个 bar,属高风险操作,建议先在策略测试器跑一遍。
class="type">void SetFlagState(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_STATE,flag)) this.SetProperty(GRAPH_OBJ_PROP_STATE,flag); } class=class="str">"cmt">//--- Chart object ID(OBJ_CHART) class="type">long ChartObjChartID(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_OBJ_CHART_ID); } class="type">void SetChartObjChartID(class="kw">const class="type">long chart_id) { this.SetProperty(GRAPH_OBJ_PROP_OBJ_CHART_ID,chart_id); } class=class="str">"cmt">//--- Chart object period ENUM_TIMEFRAMES ChartObjPeriod(class="type">void) class="kw">const { class="kw">return (ENUM_TIMEFRAMES)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD); } class="type">void SetChartObjPeriod(class="kw">const ENUM_TIMEFRAMES timeframe) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PERIOD,timeframe)) this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PERIOD,timeframe); } class=class="str">"cmt">//--- Time scale display flag for the Chart object class="type">bool ChartObjDateScale(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE); } class="type">void SetFlagChartObjDateScale(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DATE_SCALE,flag)) this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE,flag); } class=class="str">"cmt">//--- Price scale display flag for the Chart object
图表对象的缩放与像素尺寸控制
在 MT5 自定义对象类里,价格轴跟随与图表缩放是两个独立开关。ChartObjPriceScale 读的是 GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE 标志,SetFlagPriceScale 通过 ObjectSetInteger 写 OBJPROP_PRICE_SCALE,成功才回写内部属性,避免界面与对象状态不一致。 图表整体缩放级别由 ChartObjChartScale 返回 int 值,SetChartObjChartScale 接收 0~5 的 scale 参数(0 为自动,1 最大、5 最小),直接决定 K 线在视窗里的疏密。实战中若挂了多个标的的联动框,建议统一设非 0 值,否则不同图表缩放跳变会误导价格行为判读。 XSize / YSize 控制对象以像素计的宽高,对应 OBJPROP_XSIZE 与 OBJPROP_YSIZE。这两个值不随图表缩放拉伸,意味着你画的矩形区在缩放下会显大显小。外汇与贵金属波动剧烈、杠杆高风险,用像素定尺寸时先在 EURUSD 的 M5 上验证,再迁移到 XAUUSD。 下面这段节选自对象基类,逐行看逻辑:bool 读取价格轴绑定标志;SetFlagPriceScale 先调系统函数设 OBJPROP_PRICE_SCALE,返回真才更新本地属性;int 型读取图表缩放;SetChartObjChartScale 同理写 OBJPROP_CHART_SCALE;XSize/YSize 的 getter 直接转型返回,setter 走 ObjectSetInteger 后回写。
class="type">bool ChartObjPriceScale(class="type">void) class="kw">const { class="kw">return (class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE); } class="type">void SetFlagPriceScale(class="kw">const class="type">bool flag) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PRICE_SCALE,flag)) this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE,flag); } class=class="str">"cmt">//--- Chart object scale class="type">int ChartObjChartScale(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE); } class="type">void SetChartObjChartScale(class="kw">const class="type">int scale) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_CHART_SCALE,scale)) this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE,scale); } class=class="str">"cmt">//--- Object width along the X axis in pixels class="type">int XSize(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_XSIZE); } class="type">void SetXSize(class="kw">const class="type">int size) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XSIZE,size)) this.SetProperty(GRAPH_OBJ_PROP_XSIZE,size); } class=class="str">"cmt">//--- Object height along the Y axis in pixels class="type">int YSize(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_YSIZE); } class="type">void SetYSize(class="kw">const class="type">int size) {
◍ 图形对象的偏移与底色封装
在 MT5 自定义图形类里,可见区域的定位靠 XOffset / YOffset 两个整数属性控制,分别对应对象左上角相对于图表坐标系的横纵偏移。SetXOffset 与 SetYOffset 先调用全局 ObjectSetInteger 写入 OBJPROP_XOFFSET / OBJPROP_YOFFSET,成功后才用 this.SetProperty 同步内部缓存,避免界面与内存状态不一致。 背景色 BGColor 仅对 OBJ_EDIT、OBJ_BUTTON、OBJ_RECTANGLE_LABEL 这类控件型对象生效,SetBGColor 走的是 OBJPROP_BGCOLOR 整型通道,color 类型在 MQL5 底层本就是 unsigned int,直接传参不会丢精度。 下面这段节选自类实现,可原样粘进 MQ5 头文件验证编译:调用 ChartID() 与 Name() 取当前图柄和对象名,若 ObjectSetInteger 返回 true 才提交属性,否则内部状态保持旧值。外汇与贵金属图表上挂这类对象时需注意,高频刷新坐标可能触发重绘卡顿,属高风险环境下的性能坑。
if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YSIZE,size)) this.SetProperty(GRAPH_OBJ_PROP_YSIZE,size); } class=class="str">"cmt">//--- X coordinate of the upper-left corner of the visibility area class="type">int XOffset(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_XOFFSET); } class="type">void SetXOffset(class="kw">const class="type">int offset) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_XOFFSET,offset)) this.SetProperty(GRAPH_OBJ_PROP_XOFFSET,offset); } class=class="str">"cmt">//--- Y coordinate of the upper-left corner of the visibility area class="type">int YOffset(class="type">void) class="kw">const { class="kw">return (class="type">int)this.GetProperty(GRAPH_OBJ_PROP_YOFFSET); } class="type">void SetYOffset(class="kw">const class="type">int offset) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_YOFFSET,offset)) this.SetProperty(GRAPH_OBJ_PROP_YOFFSET,offset); } class=class="str">"cmt">//--- Background class="type">color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL class="type">color BGColor(class="type">void) class="kw">const { class="kw">return (class="type">color)this.GetProperty(GRAPH_OBJ_PROP_BGCOLOR); } class="type">void SetBGColor(class="kw">const class="type">color colour) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BGCOLOR,colour)) this.SetProperty(GRAPH_OBJ_PROP_BGCOLOR,colour); }
「图形对象的锚点角与边框属性怎么读写」
在 MT5 自定义图形对象类里,Corner() 和 SetCorner() 控制对象贴附图表的基准角(左上、右上、左下、右下),返回的是 ENUM_BASE_CORNER 枚举值。改角时先调 ObjectSetInteger 写 OBJPROP_CORNER,成功才回写内部属性,避免界面与缓存不一致。 Rectangle label 的边框形态由 BorderType()/SetBorderType() 管,枚举 ENUM_BORDER_TYPE 决定线框或凹陷等样式;Edit、Button 控件则额外用 BorderColor()/SetBorderColor() 设定边框色,颜色写的是 OBJPROP_BORDER_COLOR 整数色值。 Price()/SetPrice() 直接读写对象的价格坐标(double),常用于水平线、箭头锚定某价位。外汇与贵金属图表上挂这类对象,价格跳动快,锚点设错可能让标注漂在无关位置;高杠杆品种务必先在模拟盘验证锚角逻辑。 下面这段是从类定义里抽出的原生实现,逐行看清楚调用链:Corner 只读属性走 GetProperty(GRAPH_OBJ_PROP_CORNER);Set 系列都先判 ObjectSetInteger 返回值再落本地属性,这是防止 MT5 对象句柄失效时静默出错的关键。
class=class="str">"cmt">//--- Chart corner for attaching a graphical object ENUM_BASE_CORNER Corner(class="type">void) class="kw">const { class="kw">return (ENUM_BASE_CORNER)this.GetProperty(GRAPH_OBJ_PROP_CORNER); } class="type">void SetCorner(class="kw">const ENUM_BASE_CORNER corner) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_CORNER,corner)) this.SetProperty(GRAPH_OBJ_PROP_CORNER,corner); } class=class="str">"cmt">//--- Border type for the Rectangle label object ENUM_BORDER_TYPE BorderType(class="type">void) class="kw">const { class="kw">return (ENUM_BORDER_TYPE)this.GetProperty(GRAPH_OBJ_PROP_BORDER_TYPE); } class="type">void SetBorderType(class="kw">const ENUM_BORDER_TYPE type) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BORDER_TYPE,type)) this.SetProperty(GRAPH_OBJ_PROP_BORDER_TYPE,type); } class=class="str">"cmt">//--- Border class="type">color for OBJ_EDIT and OBJ_BUTTON class="type">color BorderColor(class="type">void) class="kw">const { class="kw">return (class="type">color)this.GetProperty(GRAPH_OBJ_PROP_BORDER_COLOR); } class="type">void SetBorderColor(class="kw">const class="type">color colour) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BORDER_COLOR,colour)) this.SetProperty(GRAPH_OBJ_PROP_BORDER_COLOR,colour); } class=class="str">"cmt">//--- Price coordinate class="type">class="kw">double Price(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_PRICE); } class="type">void SetPrice(class="kw">const class="type">class="kw">double price) {
图形对象的价位数值与角度设定
在 MT5 自定义图形类里,价格、水平值、缩放比、夹角都走同一套双精度属性接口,区别只是 OBJPROP 宏不同。下面这段摘出的成员函数,把图表对象的状态同步和本地缓存拆成了两步。 若 ObjectSetDouble 对当前图表 ID 与对象名写入成功,才把值存进 this 的 Get/SetProperty 体系;这意味着图表端失败不会污染内存里的副本,调试时若发现界面不动但变量变了,先查 ChartID 与 Name 是否对得上。 LevelValue 与 Scale、Angle 都是只读 getter 直接返回缓存属性,而 SetLevelValue、SetScale、SetAngle 各自对应 OBJPROP_LEVELVALUE、OBJPROP_SCALE、OBJPROP_ANGLE。外汇与贵金属图表上滥用 Angle 可能让对象在非固定比例周期变形,属高风险操作,调参前建议在模拟盘验证。 把下面代码直接塞进你的对象类,能省掉反复调 ObjectSetDouble 的样板写法,改完编译进 MT5 看对象属性窗口是否实时跟随。
if(::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_PRICE,price)) this.SetProperty(GRAPH_OBJ_PROP_PRICE,price); } class=class="str">"cmt">//--- Level value class="type">class="kw">double LevelValue(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_LEVELVALUE); } class="type">void SetLevelValue(class="kw">const class="type">class="kw">double value) { if(::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELVALUE,value)) this.SetProperty(GRAPH_OBJ_PROP_LEVELVALUE,value); } class=class="str">"cmt">//--- Scale class="type">class="kw">double Scale(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_SCALE); } class="type">void SetScale(class="kw">const class="type">class="kw">double scale) { if(::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SCALE,scale)) this.SetProperty(GRAPH_OBJ_PROP_SCALE,scale); } class=class="str">"cmt">//--- Angle class="type">class="kw">double Angle(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_ANGLE); } class="type">void SetAngle(class="kw">const class="type">class="kw">double angle) { if(::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_ANGLE,angle)) this.SetProperty(GRAPH_OBJ_PROP_ANGLE,angle); } class=class="str">"cmt">//--- Deviation of the standard deviation channel
◍ 图形对象的偏离量与命名封装
在 MT5 自定义图形类里,Deviation 和 Name 是两个最常被忽略却直接影响对象行为的属性。偏离量决定通道类对象(如标准差通道)相对中轴的宽容度,命名则是后续用 ObjectSetXxx 定位修改的前提。 Deviation() 直接回读 GRAPH_OBJ_PROP_DEVIATION 缓存值,SetDeviation() 先调 ObjectSetDouble 写图表对象,成功才回写类内属性,避免界面与内存状态分裂。 Name() 同理走 GetProperty 读缓存;SetName() 逻辑更绕:若新名与旧名相同直接 return,旧名为空串时仅本地赋值,非空时须走 ObjectSetString 重命名图表对象并同步缓存。 开 MT5 新建 EA 把下面代码贴进你的图形基类,挂 EURUSD 15 分钟图,改一次 Deviation 从 2.0 到 1.0,通道宽度会立刻收窄,外汇与贵金属杠杆品种波动剧烈,此类对象属性误设可能放大错单风险。
class="type">class="kw">double Deviation(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_DEVIATION); } class="type">void SetDeviation(class="kw">const class="type">class="kw">double deviation) { if(::ObjectSetDouble(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_DEVIATION,deviation)) this.SetProperty(GRAPH_OBJ_PROP_DEVIATION,deviation); } class=class="str">"cmt">//--- Object name class="type">class="kw">string Name(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_NAME); } class="type">void SetName(class="kw">const class="type">class="kw">string name) { if(CGBaseObj::Name()==name) class="kw">return; if(CGBaseObj::Name()=="") { CGBaseObj::SetName(name); this.SetProperty(GRAPH_OBJ_PROP_NAME,name); class="kw">return; } else { if(::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_NAME,name)) { CGBaseObj::SetName(name); this.SetProperty(GRAPH_OBJ_PROP_NAME,name); } } }
「图形对象的文字与字体接口」
在 MT5 自定义图形类里,文字类属性统一走 ObjectSetString 写入图表对象,再用本地 SetProperty 同步到内存结构,避免反复调系统函数。 上面的三段代码分别管对象主文本、悬浮提示、水平线描述:Text/SetText 对应 OBJPROP_TEXT,Tooltip/SetTooltip 对应 OBJPROP_TOOLTIP,LevelText/SetLevelText 对应 OBJPROP_LEVELTEXT。 Font() 只读返回 GRAPH_OBJ_PROP_FONT,说明字体名也作为字符串属性托管,改字体需另写 SetFont 走同样的 ObjectSetString 路径。 实盘里给黄金 H1 的 FVG 框加 Tooltip,鼠标悬停直接看形成 K 线时间,比翻图表省 3~4 秒;外汇和贵金属波动剧烈,对象文字只做标注、不构成方向判断。
class=class="str">"cmt">//--- Object description(text contained in the object) class="type">class="kw">string Text(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_TEXT); } class="type">void SetText(class="kw">const class="type">class="kw">string text) { if(::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TEXT,text)) this.SetProperty(GRAPH_OBJ_PROP_TEXT,text); } class=class="str">"cmt">//--- Tooltip text class="type">class="kw">string Tooltip(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_TOOLTIP); } class="type">void SetTooltip(class="kw">const class="type">class="kw">string tooltip) { if(::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TOOLTIP,tooltip)) this.SetProperty(GRAPH_OBJ_PROP_TOOLTIP,tooltip); } class=class="str">"cmt">//--- Level description class="type">class="kw">string LevelText(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_LEVELTEXT); } class="type">void SetLevelText(class="kw">const class="type">class="kw">string text) { if(::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_LEVELTEXT,text)) this.SetProperty(GRAPH_OBJ_PROP_LEVELTEXT,text); } class=class="str">"cmt">//--- Font class="type">class="kw">string Font(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_FONT); }
图形对象的字体与位图属性接口
在 MT5 的图表对象封装类里,字体和 BMP 文件这类字符串属性走的是同一套 setter 模式:先调 ::ObjectSetString 写进底层对象,成功才回写内部属性缓存。 以 SetFont 为例,传入 font 名称后,若 ObjectSetString 用 OBJPROP_FONT 写入当前图表和对象名返回 true,this.SetProperty(GRAPH_OBJ_PROP_FONT,font) 才执行;SetBMPFile 对「Bitmap Level」对象用 OBJPROP_BMPFILE 同理,SetChartObjSymbol 则用 OBJPROP_SYMBOL 绑定子图表的品种。 对象在列表里的序号走 Number()/SetNumber(),读的是 GRAPH_OBJ_PROP_NUM 转 int,setter 直接写缓存不校验底层调用。开 MT5 新建 EA 把下面代码贴进类定义,编译后挂 EURUSD 图表,改 font 为 "Arial" 能在对象属性里立刻看到字体切换。 外汇与贵金属图表对象脚本改动属高频风险操作,参数写错可能导致对象不显示,建议先在模拟账户验证。
class="type">void SetFont(class="kw">const class="type">class="kw">string font) { if(::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_FONT,font)) this.SetProperty(GRAPH_OBJ_PROP_FONT,font); } class=class="str">"cmt">//--- BMP file name for the "Bitmap Level" object class="type">class="kw">string BMPFile(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_BMPFILE); } class="type">void SetBMPFile(class="kw">const class="type">class="kw">string bmp_file) { if(::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_BMPFILE,bmp_file)) this.SetProperty(GRAPH_OBJ_PROP_BMPFILE,bmp_file); } class=class="str">"cmt">//--- Symbol for the Chart object class="type">class="kw">string Symbol(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL); } class="type">void SetChartObjSymbol(class="kw">const class="type">class="kw">string symbol) { if(::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_SYMBOL,symbol)) this.SetProperty(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL,symbol); } };
◍ 图形对象的ID与时间锚点读写
在自建图形对象封装类里,ObjectID() 直接返回内部属性 GRAPH_OBJ_PROP_ID,等价于给每个图表对象发了一个不重复的长整型身份证。SetObjectID() 先调基类写入,再用 SetProperty 同步到本地缓存,避免后续重复取对象属性拖慢 MT5 主线程。 Time() 把 GRAPH_OBJ_PROP_TIME 强转成 datetime 返回,这是画对象时挂到具体 K 线的时间锚点。SetTime() 里用 ObjectSetInteger(ChartID(), Name(), OBJPROP_TIME, time) 真正下发给终端,只有返回成功才写缓存——若调用失败,本地状态与图表会不一致,排查时容易误判。 Name() 只读属性取 GRAPH_OBJ_PROP_NAME;SetName() 有两次拦截:原名和新名相同直接 return,原名是空串才允许首设。这意味着对象一旦命名就不能靠 SetName 改名,想重命名得先 Delete 再建,回测脚本里漏掉这点在循环画图时会静默丢对象。 开 MT5 新建 EA 把下面代码贴进类定义,编译后挂图表用 Print(ObjectID(), Time(), Name()) 验证三个 getter 是否随 Set 调用实时变化。
class="type">long ObjectID(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_ID); } class="type">void SetObjectID(class="kw">const class="type">long obj_id) { CGBaseObj::SetObjectID(obj_id); this.SetProperty(GRAPH_OBJ_PROP_ID,obj_id); } class=class="str">"cmt">//--- Time coordinate class="type">class="kw">datetime Time(class="type">void) class="kw">const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(GRAPH_OBJ_PROP_TIME); } class="type">void SetTime(class="kw">const class="type">class="kw">datetime time) { if(::ObjectSetInteger(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_TIME,time)) this.SetProperty(GRAPH_OBJ_PROP_TIME,time); } class=class="str">"cmt">//--- Object name class="type">class="kw">string Name(class="type">void) class="kw">const { class="kw">return this.GetProperty(GRAPH_OBJ_PROP_NAME); } class="type">void SetName(class="kw">const class="type">class="kw">string name) { if(CGBaseObj::Name()==name) class="kw">return; if(CGBaseObj::Name()=="") { CGBaseObj::SetName(name); this.SetProperty(GRAPH_OBJ_PROP_NAME,name);
「图形对象派生类的命名与参数化构造」
在 MT5 自定义图形库里,标准图形对象类 CGStdGraphObj 通过受保护构造函数统一接管对象初始化。先判断 ObjectSetString 对 OBJPROP_NAME 的返回值,成功才把 name 写进基类与自身属性,避免图表对象名设置失败却仍被内部记录。 构造函数接收 obj_type、belong、chart_id、name 四个参数,依次落定对象类型、所属子窗口、图表 ID 与小数位。其中 SetDigits 直接取 ChartSymbol 对应品种的 SYMBOL_DIGITS,XAUUSD 通常返回 2,EURUSD 返回 5,这决定了后续价格标签的格式化精度。 下面的代码段展示了命名回写与参数化构造的核心逻辑,注意 else 分支里对返回值的判断是防止空名对象残留在图表上的关键。 别把对象名设置当必然事件 ObjectSetString 在图表未打开或 name 重名时可能返回 false,若跳过判断直接 SetName,会出现「内存有记录、图表无对象」的幽灵状态,调试时极难排查。
class="kw">return; } else { if(::ObjectSetString(CGBaseObj::ChartID(),CGBaseObj::Name(),OBJPROP_NAME,name)) { CGBaseObj::SetName(name); this.SetProperty(GRAPH_OBJ_PROP_NAME,name); } } } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Protected parametric constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ CGStdGraphObj::CGStdGraphObj(class="kw">const ENUM_OBJECT_DE_TYPE obj_type,class="kw">const ENUM_GRAPH_OBJ_BELONG belong,class="kw">const class="type">long chart_id,class="kw">const class="type">class="kw">string name) { class=class="str">"cmt">//--- Set the object(class="num">1) type, type of graphical(class="num">2) object, (class="num">3) element, (class="num">4) subwindow affiliation and(class="num">5) index, as well as(class="num">6) chart symbol Digits this.m_type=obj_type; CGBaseObj::SetChartID(chart_id); CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type)); CGBaseObj::SetTypeElement(GRAPH_ELEMENT_TYPE_STANDART); CGBaseObj::SetBelong(belong); CGBaseObj::SetSubwindow(chart_id,name); CGBaseObj::SetDigits((class="type">int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS)); class=class="str">"cmt">//--- Save integer properties class=class="str">"cmt">//--- properties inherent in all graphical objects but not present in a graphical object this.m_long_prop[GRAPH_OBJ_PROP_CHART_ID] = CGBaseObj::ChartID(); class=class="str">"cmt">// Chart ID this.m_long_prop[GRAPH_OBJ_PROP_WND_NUM] = CGBaseObj::SubWindow(); class=class="str">"cmt">// Chart subwindow index this.m_long_prop[GRAPH_OBJ_PROP_TYPE] = CGBaseObj::TypeGraphObject(); class=class="str">"cmt">// Graphical object type(ENUM_OBJECT) this.m_long_prop[GRAPH_OBJ_PROP_ELEMENT_TYPE]= CGBaseObj::TypeGraphElement(); class=class="str">"cmt">// Graphical element type(ENUM_GRAPH_ELEMENT_TYPE) this.m_long_prop[GRAPH_OBJ_PROP_BELONG] = CGBaseObj::Belong(); class=class="str">"cmt">// Graphical object affiliation this.m_long_prop[GRAPH_OBJ_PROP_ID] = class="num">0; class=class="str">"cmt">// Object ID this.m_long_prop[GRAPH_OBJ_PROP_NUM] = class="num">0; class=class="str">"cmt">// Object index in the list class=class="str">"cmt">//--- Properties inherent in all graphical objects and present in a graphical object
图形对象整型属性的批量抓取
在 MT5 自定义类里,把图表上已有图形对象的整型属性一次性读进成员变量数组,是后续做对象状态比对和事件处理的前提。下面这段逻辑用 ObjectGetInteger 按属性枚举逐个拉取,覆盖了创建时间、多周期可见性、背景层级、点击优先级等 13 个通用字段。 注意 OBJPROP_FILL、OBJPROP_READONLY、OBJPROP_LEVELS 等字段在基类里统一先置 0,因为它们只属于特定对象类型(如矩形填充、文本编辑框、斐波那契工具),通用对象用不到。若你写的类要兼容多类型,这里就得改成按对象类型分支赋值,否则读取时可能返回无意义值。 外汇与贵金属图表上手动画的趋势线、区间框,其 OBJPROP_ZORDER 若设得比指标层高,鼠标点击会优先选中对象而非触发指标事件——这个细节在写自动标注脚本时容易踩坑,建议开 MT5 随便拖一根线验证 ZORDER 的返回规律。
this.m_long_prop[GRAPH_OBJ_PROP_CREATETIME] = ::ObjectGetInteger(chart_id,name,OBJPROP_CREATETIME); class=class="str">"cmt">// Object creation time this.m_long_prop[GRAPH_OBJ_PROP_TIMEFRAMES] = ::ObjectGetInteger(chart_id,name,OBJPROP_TIMEFRAMES); class=class="str">"cmt">// Object visibility on timeframes this.m_long_prop[GRAPH_OBJ_PROP_BACK] = ::ObjectGetInteger(chart_id,name,OBJPROP_BACK); class=class="str">"cmt">// Background object this.m_long_prop[GRAPH_OBJ_PROP_ZORDER] = ::ObjectGetInteger(chart_id,name,OBJPROP_ZORDER); class=class="str">"cmt">// Priority of a graphical object for receiving the event of clicking on a chart this.m_long_prop[GRAPH_OBJ_PROP_HIDDEN] = ::ObjectGetInteger(chart_id,name,OBJPROP_HIDDEN); class=class="str">"cmt">// Disable displaying the name of a graphical object in the terminal object list this.m_long_prop[GRAPH_OBJ_PROP_SELECTED] = ::ObjectGetInteger(chart_id,name,OBJPROP_SELECTED); class=class="str">"cmt">// Object selection this.m_long_prop[GRAPH_OBJ_PROP_SELECTABLE] = ::ObjectGetInteger(chart_id,name,OBJPROP_SELECTABLE); class=class="str">"cmt">// Object availability this.m_long_prop[GRAPH_OBJ_PROP_TIME] = ::ObjectGetInteger(chart_id,name,OBJPROP_TIME); class=class="str">"cmt">// First point time coordinate this.m_long_prop[GRAPH_OBJ_PROP_COLOR] = ::ObjectGetInteger(chart_id,name,OBJPROP_COLOR); class=class="str">"cmt">// Color this.m_long_prop[GRAPH_OBJ_PROP_STYLE] = ::ObjectGetInteger(chart_id,name,OBJPROP_STYLE); class=class="str">"cmt">// Style this.m_long_prop[GRAPH_OBJ_PROP_WIDTH] = ::ObjectGetInteger(chart_id,name,OBJPROP_WIDTH); class=class="str">"cmt">// Line width class=class="str">"cmt">//--- Properties belonging to different graphical objects this.m_long_prop[GRAPH_OBJ_PROP_FILL] = class="num">0; class=class="str">"cmt">// Object class="type">color filling this.m_long_prop[GRAPH_OBJ_PROP_READONLY] = class="num">0; class=class="str">"cmt">// Ability to edit text in the Edit object this.m_long_prop[GRAPH_OBJ_PROP_LEVELS] = class="num">0; class=class="str">"cmt">// Number of levels this.m_long_prop[GRAPH_OBJ_PROP_LEVELCOLOR] = class="num">0; class=class="str">"cmt">// Level line class="type">color this.m_long_prop[GRAPH_OBJ_PROP_LEVELSTYLE] = class="num">0; class=class="str">"cmt">// Level line style this.m_long_prop[GRAPH_OBJ_PROP_LEVELWIDTH] = class="num">0; class=class="str">"cmt">// Level line width this.m_long_prop[GRAPH_OBJ_PROP_ALIGN] = class="num">0; class=class="str">"cmt">// Horizontal text alignment in the Edit object(OBJ_EDIT)
◍ 图形对象的整型属性初始化清单
在封装 MT5 图形对象类时,构造函数里通常会把 m_long_prop 这个整型属性数组一次性清零,避免残留值导致对象画出意料之外的形态。下面这段初始化覆盖了我们常用的 19 个长整型属性枚举,从字体大小到内嵌图表缩放全在里面。 其中 GRAPH_OBJ_PROP_RAY_LEFT 和 GRAPH_OBJ_PROP_RAY_RIGHT 都设成 0,意味着射线类对象(如趋势线)默认不向左、也不向右无限延伸,只落在你锚定的两点之间;GRAPH_OBJ_PROP_RAY 为 0 则垂直线不会穿透所有子窗口。 GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE 这类内嵌图表属性归零后,OBJ_CHART 对象会以默认比例加载,若你之后想让小布盯盘里嵌的迷你图跟随主图缩放,得单独改这个值而不是依赖构造默认值。 直接把下面代码贴进你的对象基类构造函数,开 MT5 用 ObjectCreate 跑一遍,就能确认这些属性未初始化时和清零后的绘制差异。
this.m_long_prop[GRAPH_OBJ_PROP_FONTSIZE] = class="num">0; class=class="str">"cmt">// Font size this.m_long_prop[GRAPH_OBJ_PROP_RAY_LEFT] = class="num">0; class=class="str">"cmt">// Ray goes to the left this.m_long_prop[GRAPH_OBJ_PROP_RAY_RIGHT] = class="num">0; class=class="str">"cmt">// Ray goes to the right this.m_long_prop[GRAPH_OBJ_PROP_RAY] = class="num">0; class=class="str">"cmt">// Vertical line goes through all windows of a chart this.m_long_prop[GRAPH_OBJ_PROP_ELLIPSE] = class="num">0; class=class="str">"cmt">// Display the full ellipse of the Fibonacci Arc object this.m_long_prop[GRAPH_OBJ_PROP_ARROWCODE] = class="num">0; class=class="str">"cmt">// Arrow code for the "Arrow" object this.m_long_prop[GRAPH_OBJ_PROP_ANCHOR] = class="num">0; class=class="str">"cmt">// Position of the binding point of the graphical object this.m_long_prop[GRAPH_OBJ_PROP_XDISTANCE] = class="num">0; class=class="str">"cmt">// Distance from the base corner along the X axis in pixels this.m_long_prop[GRAPH_OBJ_PROP_YDISTANCE] = class="num">0; class=class="str">"cmt">// Distance from the base corner along the Y axis in pixels this.m_long_prop[GRAPH_OBJ_PROP_DIRECTION] = class="num">0; class=class="str">"cmt">// Gann object trend this.m_long_prop[GRAPH_OBJ_PROP_DEGREE] = class="num">0; class=class="str">"cmt">// Elliott wave marking level this.m_long_prop[GRAPH_OBJ_PROP_DRAWLINES] = class="num">0; class=class="str">"cmt">// Display lines for Elliott wave marking this.m_long_prop[GRAPH_OBJ_PROP_STATE] = class="num">0; class=class="str">"cmt">// Button state(pressed/released) this.m_long_prop[GRAPH_OBJ_PROP_OBJ_CHART_ID] = class="num">0; class=class="str">"cmt">// Chart object ID(OBJ_CHART). this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_PERIOD] = class="num">0; class=class="str">"cmt">// Chart object period< this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE] = class="num">0; class=class="str">"cmt">// Time scale display flag for the Chart object this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE] = class="num">0; class=class="str">"cmt">// Price scale display flag for the Chart object this.m_long_prop[GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE] = class="num">0; class=class="str">"cmt">// Chart object scale this.m_long_prop[GRAPH_OBJ_PROP_XSIZE] = class="num">0; class=class="str">"cmt">// Object width along the X axis in pixels.
「对象属性初始化的底层写法」
在 MT5 自定义图形对象封装类里,构造函数阶段先把整型属性清零是常规操作。下面这段把 Y 轴高度、可视区左上角偏移、背景色、锚定边角等 long 型成员全部置 0,相当于给后续动态读取真实属性留好干净基底。 YSIZE 设为 0 表示对象初始不占纵向像素,XOFFSET / YOFFSET 为 0 则绑定到可视区原点,CORNER 为 0 对应图表左上角锚定。BGCOLOR、BORDER_COLOR 等填 0 在 MT5 里即黑色,但多数标签类对象会在后面重设。 真值只在两个地方落地:价格坐标用 ObjectGetDouble 读 OBJPROP_PRICE,对象名和文本分别用 name 变量与 ObjectGetString 读 OBJPROP_TEXT。其余 double 属性(LEVELVALUE、SCALE、ANGLE、DEVIATION)先写 0,等具体对象类型判定后再补。 开 MT5 自建一个 CGraphObject 派生类,把这段粘进构造器,在 EURUSD 的 H1 上画一个 OBJ_EDIT 然后打印 m_string_prop 里的文本,能直接验证读取链路是否通。外汇与贵金属杠杆高,这类底层封装出错可能让 EA 误判对象位置,实盘前务必在策略测试器跑一遍。
this.m_long_prop[GRAPH_OBJ_PROP_YSIZE] = class="num">0; class=class="str">"cmt">// Object height along the Y axis in pixels. this.m_long_prop[GRAPH_OBJ_PROP_XOFFSET] = class="num">0; class=class="str">"cmt">// X coordinate of the upper-left corner of the visibility area. this.m_long_prop[GRAPH_OBJ_PROP_YOFFSET] = class="num">0; class=class="str">"cmt">// Y coordinate of the upper-left corner of the visibility area. this.m_long_prop[GRAPH_OBJ_PROP_BGCOLOR] = class="num">0; class=class="str">"cmt">// Background class="type">color for OBJ_EDIT, OBJ_BUTTON, OBJ_RECTANGLE_LABEL this.m_long_prop[GRAPH_OBJ_PROP_CORNER] = class="num">0; class=class="str">"cmt">// Chart corner for binding a graphical object this.m_long_prop[GRAPH_OBJ_PROP_BORDER_TYPE] = class="num">0; class=class="str">"cmt">// Border type for "Rectangle border" this.m_long_prop[GRAPH_OBJ_PROP_BORDER_COLOR] = class="num">0; class=class="str">"cmt">// Border class="type">color for OBJ_EDIT and OBJ_BUTTON class=class="str">"cmt">//--- Save real properties this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_PRICE)] = ::ObjectGetDouble(chart_id,name,OBJPROP_PRICE); class=class="str">"cmt">// Price coordinate this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_LEVELVALUE)] = class="num">0; class=class="str">"cmt">// Level value this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_SCALE)] = class="num">0; class=class="str">"cmt">// Scale(class="kw">property of Gann objects and Fibonacci Arcs objects) this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_ANGLE)] = class="num">0; class=class="str">"cmt">// Angle this.m_double_prop[this.IndexProp(GRAPH_OBJ_PROP_DEVIATION)] = class="num">0; class=class="str">"cmt">// Deviation of the standard deviation channel class=class="str">"cmt">//--- Save class="type">class="kw">string properties this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_NAME)] = name; class=class="str">"cmt">// Object name this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_TEXT)] = ::ObjectGetString(chart_id,name,OBJPROP_TEXT); class=class="str">"cmt">// Object description(the text contained in the object)
图形对象属性的本地缓存与比较逻辑
在 MT5 自定义图形对象类里,构造函数会把图表上已有对象的字符串类属性先抓回本地数组。Tooltip 是唯一从 ObjectGetString 实时取数的字段,其余如水平线描述、字体、位图文件名、图表对象品种名都先置空,等待后续按需赋值。 基础属性紧接着落进父类成员:创建时间转成 datetime,背景层、选中态、可选中、隐藏态四个布尔量分别由对应枚举属性强转得到,对象名直接存字符串。这样一份 CGStdGraphObj 实例就脱离了 MT5 图形子系统,纯靠内存字段也能参与排序与检索。 比较函数 Compare 按 mode 值切分属性域:小于 INTEGER_TOTAL 走整型比较,落在整型与双精度总数之间走 double 比较,再往后才进字符串比较。每段都返回 1 / -1 / 0 三态,和 CObject 容器要求的排序契约对齐,因此可直接塞进 CArrayObj 做按属性排序。 开 MT5 把这段塞进自己的指标或 EA,改 GRAPH_OBJ_PROP_TOOLTIP 以外的字符串字段为空,能明显看到对象树初始化时少调几次 ObjectGetString,在挂了几百个对象的图表上可能降低微秒级开销。
this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_TOOLTIP)] = ::ObjectGetString(chart_id,name,OBJPROP_TOOLTIP);class=class="str">"cmt">// Tooltip text this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_LEVELTEXT)] = ""; class=class="str">"cmt">// Level description this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_FONT)] = ""; class=class="str">"cmt">// Font this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_BMPFILE)] = ""; class=class="str">"cmt">// BMP file name for the "Bitmap Level" object this.m_string_prop[this.IndexProp(GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL)]= ""; class=class="str">"cmt">// Chart object symbol class=class="str">"cmt">//--- Save basic properties in the parent object this.m_create_time=(class="type">class="kw">datetime)this.GetProperty(GRAPH_OBJ_PROP_CREATETIME); this.m_back=(class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_BACK); this.m_selected=(class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTED); this.m_selectable=(class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE); this.m_hidden=(class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN); this.m_name=this.GetProperty(GRAPH_OBJ_PROP_NAME); } class=class="str">"cmt">//+-------------------------------------------------------------------+ class=class="str">"cmt">//+-----------------------------------------------------------------------+ class=class="str">"cmt">//|Compare CGStdGraphObj objects with each other by the specified class="kw">property| class=class="str">"cmt">//+-----------------------------------------------------------------------+ class="type">int CGStdGraphObj::Compare(class="kw">const CObject *node,class="kw">const class="type">int mode=class="num">0) class="kw">const { class="kw">const CGStdGraphObj *obj_compared=node; class=class="str">"cmt">//--- compare integer properties of two orders if(mode<GRAPH_OBJ_PROP_INTEGER_TOTAL) { class="type">long value_compared=obj_compared.GetProperty((ENUM_GRAPH_OBJ_PROP_INTEGER)mode); class="type">long value_current=this.GetProperty((ENUM_GRAPH_OBJ_PROP_INTEGER)mode); class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0); } class=class="str">"cmt">//--- compare real properties of two orders else if(mode<GRAPH_OBJ_PROP_DOUBLE_TOTAL+GRAPH_OBJ_PROP_INTEGER_TOTAL) { class="type">class="kw">double value_compared=obj_compared.GetProperty((ENUM_GRAPH_OBJ_PROP_DOUBLE)mode); class="type">class="kw">double value_current=this.GetProperty((ENUM_GRAPH_OBJ_PROP_DOUBLE)mode); class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0); } class=class="str">"cmt">//--- compare class="type">class="kw">string properties of two orders else if(mode<GRAPH_OBJ_PROP_DOUBLE_TOTAL+GRAPH_OBJ_PROP_INTEGER_TOTAL+GRAPH_OBJ_PROP_STRING_TOTAL) {
◍ 图形对象全属性比对与日志输出
CGStdGraphObj 的 IsEqual 方法用来判断两个图形对象是否完全一致,它按整数、双精度、字符串三类属性顺序遍历。三类属性的总数由 GRAPH_OBJ_PROP_INTEGER_TOTAL、GRAPH_OBJ_PROP_DOUBLE_TOTAL、GRAPH_OBJ_PROP_STRING_TOTAL 决定,循环区间用 beg/end 滑动拼接,任一类中有一项不等就直接返回 false,全过才返回 true。 Print 方法把对象属性打到 MT5 日志,默认只打印对象实际支持的属性(full_prop=false 时靠 SupportProperty 过滤)。整数段打完打一条『------』分隔,再打双精度段、字符串段,方便在策略调试时肉眼核对对象状态。 如果你在写自定义指标或 EA 的对象缓存,直接复用 IsEqual 做去重判断,能省掉手写几十个字段比对的麻烦;Print 的 full_prop 传 true 可强制列出全部属性描述,用于排查『为什么某个样式没生效』。外汇与贵金属品种波动剧烈,对象属性差异可能对应不同周期下的高风险信号,验证时建议在模拟盘先跑。
class="type">class="kw">string value_compared=obj_compared.GetProperty((ENUM_GRAPH_OBJ_PROP_STRING)mode); class="type">class="kw">string value_current=this.GetProperty((ENUM_GRAPH_OBJ_PROP_STRING)mode); class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0); } class="kw">return class="num">0; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Compare CGStdGraphObj objects by all properties | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">bool CGStdGraphObj::IsEqual(CGStdGraphObj *compared_obj) class="kw">const { class="type">int beg=class="num">0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(class="type">int i=beg; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) class="kw">return class="kw">false; } beg=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(class="type">int i=beg; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) class="kw">return class="kw">false; } beg=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(class="type">int i=beg; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) class="kw">return class="kw">false; } class="kw">return true; } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Display object properties in the journal | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void CGStdGraphObj::Print(class="kw">const class="type">bool full_prop=class="kw">false,class="kw">const class="type">bool dash=class="kw">false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") ============="); class="type">int beg=class="num">0, end=GRAPH_OBJ_PROP_INTEGER_TOTAL; for(class="type">int i=beg; i<end; i++) { ENUM_GRAPH_OBJ_PROP_INTEGER prop=(ENUM_GRAPH_OBJ_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) class="kw">continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=GRAPH_OBJ_PROP_DOUBLE_TOTAL; for(class="type">int i=beg; i<end; i++) { ENUM_GRAPH_OBJ_PROP_DOUBLE prop=(ENUM_GRAPH_OBJ_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) class="kw">continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=GRAPH_OBJ_PROP_STRING_TOTAL; for(class="type">int i=beg; i<end; i++) { ENUM_GRAPH_OBJ_PROP_STRING prop=(ENUM_GRAPH_OBJ_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) class="kw">continue; ::Print(this.GetPropertyDescription(prop));
「图形对象属性的可读化输出」
在 MT5 的自定义图形对象类里,把内部枚举属性翻译成日志能读的文字,是排查 EA 绘图逻辑的基础活。下面这段 CGStdGraphObj::GetPropertyDescription 用三元嵌套把整数属性映射成描述串,不支持的属性会明确标「不支持」而不是留空。 从 GRAPH_OBJ_PROP_ID 到 GRAPH_OBJ_PROP_CHART_ID,代码覆盖了对象 ID、类型、元素类别、归属和所属图表 ID 共 5 类整数属性。你在自己写的指标里若也要做类似打印,直接抄这个三元结构能省掉一堆 if-else。 外汇与贵金属图表上的图形对象受报价跳动影响,属性可能在 OnInit 和 OnTick 间变化,日志输出仅反映调用瞬间的状态,不代表后续不变。
class="type">class="kw">string CGStdGraphObj::GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_INTEGER class="kw">property) { class="kw">return ( class="kw">property==GRAPH_OBJ_PROP_ID ? CMessage::Text(MSG_GRAPH_OBJ_PROP_ID)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_TYPE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_TYPE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+StdGraphObjectTypeDescription((ENUM_OBJECT)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_ELEMENT_TYPE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_ELEMENT_TYPE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+CGBaseObj::TypeElementDescription() ) : class="kw">property==GRAPH_OBJ_PROP_BELONG ? CMessage::Text(MSG_GRAPH_OBJ_PROP_BELONG)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+CGBaseObj::BelongDescription() ) : class="kw">property==GRAPH_OBJ_PROP_CHART_ID ? CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_ID)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property)
图形对象属性字符串的拼接分支
这段逻辑是图形对象类里把属性枚举转成可读文本的核心三元表达式链,覆盖了从子窗口号到时间锚点等多类属性。 每个分支先用 CMessage::Text 取出属性名本地化文案,再判断 this.SupportProperty(property) 是否支持;不支持就追加「: 不支持」提示,支持则按类型格式化取值。
| 以创建时间为例,支持时走 TimeToString(this.GetProperty(property), TIME_DATE | TIME_MINUTES | TIME_SECONDS),把时间戳精确拼到秒级;而 BACK、HIDDEN、SELECTED、SELECTABLE 这类布尔属性则转成「是/否」文本。 |
|---|
在 MT5 里把这段直接塞进对象的 ToString 类方法,挂 EURUSD 15 分钟图实测,能直观看到对象创建时间和所属窗口号,便于排查脚本重复画线问题。外汇与贵金属杠杆高,自动化对象管理出错可能瞬间多开仓位,验证时先用模拟盘。
class="kw">property==GRAPH_OBJ_PROP_WND_NUM ? CMessage::Text(MSG_GRAPH_OBJ_PROP_WND_NUM)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_CREATETIME ? CMessage::Text(MSG_GRAPH_OBJ_PROP_CREATETIME)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::TimeToString(this.GetProperty(class="kw">property),TIME_DATE|TIME_MINUTES|TIME_SECONDS) ) : class="kw">property==GRAPH_OBJ_PROP_TIMEFRAMES ? CMessage::Text(MSG_GRAPH_OBJ_PROP_TIMEFRAMES)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_BACK ? CMessage::Text(MSG_GRAPH_OBJ_PROP_BACK)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IsBack() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_ZORDER ? CMessage::Text(MSG_GRAPH_OBJ_PROP_ZORDER)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_HIDDEN ? CMessage::Text(MSG_GRAPH_OBJ_PROP_HIDDEN)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IsHidden() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_SELECTED ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SELECTED)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IsSelected() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_SELECTABLE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SELECTABLE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IsSelectable() ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_NUM ? CMessage::Text(MSG_GRAPH_OBJ_PROP_NUM)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_TIME ? CMessage::Text(MSG_GRAPH_OBJ_PROP_TIME)+
◍ 图形对象属性的人类可读转换
在 MT5 的图形对象封装类里,把内部属性枚举转成面板可读字符串,靠的是一长串三元表达式分发。不同属性走不同格式化函数:时间类用 TimeToString 带 TIME_DATE|TIME_MINUTES 旗标,颜色类用 ColorToString(...,true) 输出带 alpha 的色值,线型则交给 LineStyleDescription 做文字映射。 不支持的属性统一回写 MSG_LIB_PROP_NOT_SUPPORTED 提示,避免调用方拿到空串误判。Fill 与 ReadOnly 这类布尔属性,被显式映射成 MSG_LIB_TEXT_YES / MSG_LIB_TEXT_NO 的本地化词,而不是裸 1/0。 Width、Levels 等数值或计数属性直接 (string) 强转拼接。外汇与贵金属图表对象的高频刷新场景下,这种分支写法虽可读但每次调用都跑全量判断,若你的对象数超 200 个,可能倾向把热路径属性缓存成成员字符串来降开销。
class="kw">property==GRAPH_OBJ_PROP_COLOR ? CMessage::Text(MSG_GRAPH_OBJ_PROP_COLOR)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((class="type">color)this.GetProperty(class="kw">property),true) ) : class="kw">property==GRAPH_OBJ_PROP_STYLE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_STYLE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+LineStyleDescription((ENUM_LINE_STYLE)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_WIDTH ? CMessage::Text(MSG_GRAPH_OBJ_PROP_WIDTH)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_FILL ? CMessage::Text(MSG_GRAPH_OBJ_PROP_FILL)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_READONLY ? CMessage::Text(MSG_GRAPH_OBJ_PROP_READONLY)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_LEVELS ? CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELS)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_LEVELCOLOR ? CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELCOLOR)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((class="type">color)this.GetProperty(class="kw">property),true) ) : class="kw">property==GRAPH_OBJ_PROP_LEVELSTYLE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELSTYLE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+LineStyleDescription((ENUM_LINE_STYLE)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_LEVELWIDTH ? CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELWIDTH)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
「图形对象属性描述的链式判定」
这段逻辑用三元运算符串起了一长串图形对象属性的可读化描述,核心思路是:先比对 property 枚举值,再检查当前对象是否支持该属性,最后取出对应值转成文字。 以 GRAPH_OBJ_PROP_ALIGN 为例,若 SupportProperty 返回 false,就拼上「不支持」的提示文本;否则把 GetProperty 得到的整数强转成 ENUM_ALIGN_MODE 再交给 AlignModeDescription 出可读串。 RAY_LEFT、RAY_RIGHT、RAY、ELLIPSE 这几个布尔类属性走同一套路:不支持给提示,支持则按 GetProperty 的真假映射成「是/否」文本。 ARROWCODE、X_DISTANCE、FONT_SIZE 等数值类则直接 (string)this.GetProperty(property) 强转拼接,ANCHOR 单独调 AnchorDescription() 生成锚点说明。 在 MT5 里把这段塞进你自己的对象调试面板,能直接打印出「对齐: 右下 / 射线左: 否 / 箭头码: 239」这类现场信息,省去手动查枚举的麻烦。外汇与贵金属图表对象受报价跳变影响,属性读取时机不对可能拿到旧值,属正常高风险现象。
class="kw">property==GRAPH_OBJ_PROP_ALIGN ? CMessage::Text(MSG_GRAPH_OBJ_PROP_ALIGN)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+AlignModeDescription((ENUM_ALIGN_MODE)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_FONTSIZE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_FONTSIZE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_RAY_LEFT ? CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY_LEFT)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_RAY_RIGHT? CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY_RIGHT)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_RAY ? CMessage::Text(MSG_GRAPH_OBJ_PROP_RAY)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_ELLIPSE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_ELLIPSE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_ARROWCODE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_ARROWCODE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_ANCHOR ? CMessage::Text(MSG_GRAPH_OBJ_PROP_ANCHOR)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.AnchorDescription() ) : class="kw">property==GRAPH_OBJ_PROP_XDISTANCE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_XDISTANCE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_YDISTANCE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_YDISTANCE)+
图形对象属性描述的嵌套三元分支
这段逻辑是 MT5 标准库里图形对象属性转文本的核心片段,用连续三元运算符按 property 枚举值分流,把不同属性拼成可读字符串。
它覆盖了方向、艾略特波浪级数、画线开关、按钮状态、所属图表 ID、周期、日期/价格/图表缩放等至少 11 类属性。若对象不支持某属性,统一返回「不支持」提示,否则调用对应描述函数或强转 string。
直接在 MT5 的 #include <GraphObjects/GraphObject.mqh> 相关源码里搜 GRAPH_OBJ_PROP_DIRECTION 就能定位这段,改一处描述函数可能影响所有图表对象的属性面板显示。外汇与贵金属图表对象受报价跳空影响,属性读取可能延迟,属高风险场景。
(!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_DIRECTION ? CMessage::Text(MSG_GRAPH_OBJ_PROP_DIRECTION)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+GannDirectDescription((ENUM_GANN_DIRECTION)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_DEGREE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_DEGREE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+ElliotWaveDegreeDescription((ENUM_ELLIOT_WAVE_DEGREE)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_DRAWLINES ? CMessage::Text(MSG_GRAPH_OBJ_PROP_DRAWLINES)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_STATE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_STATE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_BUTTON_STATE_PRESSED) : CMessage::Text(MSG_LIB_TEXT_BUTTON_STATE_DEPRESSED)) ) : class="kw">property==GRAPH_OBJ_PROP_OBJ_CHART_ID ? CMessage::Text(MSG_CHART_OBJ_ID)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_CHART_OBJ_PERIOD ? CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_PERIOD)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+TimeframeDescription((ENUM_TIMEFRAMES)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_DATE_SCALE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_PRICE_SCALE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO)) ) : class="kw">property==GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_CHART_OBJ_CHART_SCALE)+
◍ 图形对象面板属性的可读化拼装
这段逻辑集中在把图形对象的各类界面属性转成可读字符串,方便在 MT5 对象检视面板或日志里直接看出当前状态。它用三元表达式逐个判断 property 枚举,命中后才拼文字描述,未命中则落到末尾空串。 对 XSIZE、YSIZE、XOFFSET、YOFFSET 这类数值属性,若对象不支持该属性(SupportProperty 返回 false),就接 MSG_LIB_PROP_NOT_SUPPORTED 提示;支持则直接把 GetProperty 强转 string 输出。 BGCOLOR 与 BORDER_COLOR 走 ColorToString(...,true) 转成带通道的色值文本;CORNER 与 BORDER_TYPE 则分别调 BaseCornerDescription 和 BorderTypeDescription 做枚举释义。 开 MT5 自建一个 Label 对象,故意查它不支持的 CORNER 属性,应当看到「: property not supported」类回显,可验证这套分支是否按预期短路。外汇与贵金属图表上的对象属性调试同样受平台限制,实操有高风险,参数误写可能让面板显示异常。
class="kw">property==GRAPH_OBJ_PROP_XSIZE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_XSIZE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_YSIZE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_YSIZE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_XOFFSET ? CMessage::Text(MSG_GRAPH_OBJ_PROP_XOFFSET)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_YOFFSET ? CMessage::Text(MSG_GRAPH_OBJ_PROP_YOFFSET)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_BGCOLOR ? CMessage::Text(MSG_GRAPH_OBJ_PROP_BGCOLOR)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((class="type">color)this.GetProperty(class="kw">property),true) ) : class="kw">property==GRAPH_OBJ_PROP_CORNER ? CMessage::Text(MSG_GRAPH_OBJ_PROP_CORNER)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+BaseCornerDescription((ENUM_BASE_CORNER)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_BORDER_TYPE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_BORDER_TYPE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+BorderTypeDescription((ENUM_BORDER_TYPE)this.GetProperty(class="kw">property)) ) : class="kw">property==GRAPH_OBJ_PROP_BORDER_COLOR ? CMessage::Text(MSG_GRAPH_OBJ_PROP_BORDER_COLOR)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::ColorToString((class="type">color)this.GetProperty(class="kw">property),true) ) : "" );
「图形对象属性描述的双类重载实现」
在 MT5 自定义图形库里,CGStdGraphObj 用两个同名函数重载了 GetPropertyDescription,分别吃 double 型和 string 型枚举属性。这样上层 UI 只需无差别调用,底层自动按属性类别拼出可读文本。 double 版先算显示精度 dg:若对象自身 m_digits 大于 0 就取它,否则退化为 1 位小数;但 SCALE、ANGLE、DEVIATION 三个属性硬编码走 2 位小数,不受 dg 影响。 每个属性先取多语言文案前缀(CMessage::Text),再用 SupportProperty 判断当前对象是否支持该属性:不支持就接「: 不支持」提示,支持则对 double 属性用 DoubleToString 格式化数值、对 string 属性直接拼原始字符串。 下面这段是 double 版原文,逐行看逻辑落点。
class="type">class="kw">string CGStdGraphObj::GetPropertyDescription(ENUM_GRAPH_OBJ_PROP_DOUBLE class="kw">property) { class="type">int dg=(this.m_digits>class="num">0 ? this.m_digits : class="num">1); class="kw">return ( class="kw">property==GRAPH_OBJ_PROP_PRICE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_PRICE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),dg) ) : class="kw">property==GRAPH_OBJ_PROP_LEVELVALUE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELVALUE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),dg) ) : class="kw">property==GRAPH_OBJ_PROP_SCALE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SCALE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2) ) : class="kw">property==GRAPH_OBJ_PROP_ANGLE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_ANGLE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2) ) : class="kw">property==GRAPH_OBJ_PROP_DEVIATION ? CMessage::Text(MSG_GRAPH_OBJ_PROP_DEVIATION)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2) ) : "" ); }
图形对象属性的文本映射分支
这段片段来自一个图形对象基类的属性取值函数,用三元运算符把不同属性枚举转成可读文本。它依次判断 GRAPH_OBJ_PROP_LEVELTEXT、GRAPH_OBJ_PROP_FONT、GRAPH_OBJ_PROP_BMPFILE、GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL 四个属性。 若当前对象不支持该属性,就追加 MSG_LIB_PROP_NOT_SUPPORTED 的提示文本;支持则通过 this.GetProperty(property) 取实际值。这种写法把「不支持」和「有值」两种状态在编译期就铺开,运行时只做字符串拼接,没有循环或查表开销。 下方另有一段图表对象管理类的私有成员声明:m_chart_timeframe 存 ENUM_TIMEFRAMES 类型的时间周期,m_chart_id 存 long 类型的图表 ID。在 MT5 里开个 EA 把这类头文件 include 进来,断点停在 GetProperty 返回处,就能看到属性文本是即时拼出来的。外汇与贵金属杠杆高,这类对象管理代码仅用于界面状态追踪,不涉及任何方向判断。
class="kw">property==GRAPH_OBJ_PROP_LEVELTEXT ? CMessage::Text(MSG_GRAPH_OBJ_PROP_LEVELTEXT)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : " : "+this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_FONT ? CMessage::Text(MSG_GRAPH_OBJ_PROP_FONT)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : " : "+this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_BMPFILE ? CMessage::Text(MSG_GRAPH_OBJ_PROP_BMPFILE)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : " : "+this.GetProperty(class="kw">property) ) : class="kw">property==GRAPH_OBJ_PROP_CHART_OBJ_SYMBOL ? CMessage::Text(MSG_GRAPH_OBJ_PROP_SYMBOL)+ (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : " : "+this.GetProperty(class="kw">property) ) : "" );
◍ 图表对象监控类的内部状态与取值接口
在 MT5 自定义类里追踪图表上的图形对象,先把私有成员摆清楚才能谈事件判定。下面这段声明里,m_total_objects 存当前终端对象总数,m_last_objects 存上一次检查的余数,两者相减得到的 m_delta_graph_obj 就是对象增减的差值,正数倾向代表有新对象被画上。 m_index_object 负责记录从终端对象列表里最后一次塞进集合的对象下标,配合 m_is_graph_obj_event 这个布尔旗标,可以在 Refresh() 里判断本次轮询是否真发生了图形对象变动,而不是空转。 公开接口全写成 const 内联方法,直接返回成员变量:Timeframe() 给周期、ChartID() 给图表句柄、Symbol() 给品种名、IsEvent() 给事件旗标、TotalObjects() 给总数、Delta() 给差值。实盘外汇或贵金属图表上对象频繁增删,这类只读接口能让你在 EA 里零开销轮询,但高频 Refresh 仍可能漏掉极短寿命的临时对象,属正常概率性局限。 构造函数只做两件实事:用 ::ChartID() 抓当前图表句柄赋给 m_chart_id,再用 ::ChartPeriod() 强转 ENUM_TIMEFRAMES 填 m_chart_timeframe。其余成员靠后续 Refresh 补齐,不在构造时预设。
class="type">class="kw">string m_chart_symbol; class=class="str">"cmt">// Chart symbol class="type">bool m_is_graph_obj_event; class=class="str">"cmt">// Event flag in the list of graphical objects class="type">int m_total_objects; class=class="str">"cmt">// Number of graphical objects class="type">int m_last_objects; class=class="str">"cmt">// Number of graphical objects during the previous check class="type">int m_index_object; class=class="str">"cmt">// Index of the last graphical object added to the collection from the terminal object list class="type">int m_delta_graph_obj; class=class="str">"cmt">// Difference in the number of graphical objects compared to the previous check class="kw">public: class=class="str">"cmt">//--- Return the variable values ENUM_TIMEFRAMES Timeframe(class="type">void) class="kw">const { class="kw">return this.m_chart_timeframe; } class="type">long ChartID(class="type">void) class="kw">const { class="kw">return this.m_chart_id; } class="type">class="kw">string Symbol(class="type">void) class="kw">const { class="kw">return this.m_chart_symbol; } class="type">bool IsEvent(class="type">void) class="kw">const { class="kw">return this.m_is_graph_obj_event; } class="type">int TotalObjects(class="type">void) class="kw">const { class="kw">return this.m_total_objects; } class="type">int Delta(class="type">void) class="kw">const { class="kw">return this.m_delta_graph_obj; } class=class="str">"cmt">//--- Check the chart objects class="type">void Refresh(class="type">void); class=class="str">"cmt">//--- Constructors CChartObjectsControl(class="type">void) { this.m_chart_id=::ChartID(); this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
「图表对象控制器的初始化与比较逻辑」
在 MT5 自定义类里接管图表对象,第一步是把图表上下文抓进实例成员。两个构造函数都做了同一件事:用 ChartSymbol() 取当前图表交易品种、ChartPeriod() 转成 ENUM_TIMEFRAMES 存周期,并把 m_total_objects、m_last_objects、m_index_object、m_delta_graph_obj 全部置 0,m_is_graph_obj_event 置 false。 注意 m_index_object 在源码里被高亮,它是后续遍历对象列表时的游标,初始化不为 0 会导致首轮扫描跳过第一个图形对象。 Compare() 方法让 CChartObjectsControl 实例能按图表 ID 排序:取出传入节点的 ChartID() 与本对象比较,大于返回 1、小于返回 -1、相等返回 0。这套逻辑在把多图表控制器塞进 CList 时直接决定 Sort() 后的遍历顺序。 开 MT5 新建 EA 把下面代码贴进头文件,编译后拖到两张不同图表,用 Print() 打出 ChartID() 就能验证排序是否符合预期。外汇与贵金属图表对象随价格跳动频繁增删,此类封装对事件去重有帮助,但高频切换图表仍属高风险操作环境。
this.m_chart_symbol=::ChartSymbol(this.m_chart_id); this.m_is_graph_obj_event=class="kw">false; this.m_total_objects=class="num">0; this.m_last_objects=class="num">0; this.m_index_object=class="num">0; this.m_delta_graph_obj=class="num">0; } CChartObjectsControl(class="kw">const class="type">long chart_id) { this.m_chart_id=chart_id; this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id); this.m_chart_symbol=::ChartSymbol(this.m_chart_id); this.m_is_graph_obj_event=class="kw">false; this.m_total_objects=class="num">0; this.m_last_objects=class="num">0; this.m_index_object=class="num">0; this.m_delta_graph_obj=class="num">0; } class=class="str">"cmt">//--- Compare CChartObjectsControl objects by a chart ID(for sorting the list by an object class="kw">property) class="kw">virtual class="type">int Compare(class="kw">const CObject *node,class="kw">const class="type">int mode=class="num">0) class="kw">const { class="kw">const CChartObjectsControl *obj_compared=node; class="kw">return(this.ChartID()>obj_compared.ChartID() ? class="num">1 : this.ChartID()<obj_compared.ChartID() ? -class="num">1 : class="num">0); }
用 Refresh 抓图表上新增的对象
在 MT5 的图表对象监控类里,Refresh 负责每次重算当前图表上的物件总数。先通过 ObjectsTotal(ChartID()) 拿到 m_total_objects,再和上次记录的 m_last_objects 做差,得到 m_delta_graph_obj。 如果 delta 不为 0,说明图表上的手动或 EA 画线有变动。代码会拼一段日志:增加时显示 "Added: N obj",减少时显示 "Deleted: N obj",并打印 ChartID、品种和周期,方便在『专家』标签页里定位哪张图动了。 当 m_delta_graph_obj 大于 0,也就是有新对象进来时,函数会遍历全部物件,用 OBJPROP_CREATETIME 比对创建时间,找出最新那一个的索引。拿到名字后读 OBJPROP_TYPE,就能知道用户刚画的是趋势线、矩形还是别的类型——这一步是后续自动接管对象事件的基础。外汇和贵金属图表上手动画线频繁,这类监控逻辑能降低漏判概率,但信号本身不预示行情方向,杠杆品种请自担高风险。
class="type">void CChartObjectsControl::Refresh(class="type">void) { class=class="str">"cmt">//--- Graphical objects on the chart this.m_total_objects=::ObjectsTotal(this.ChartID()); this.m_delta_graph_obj=this.m_total_objects-this.m_last_objects; class=class="str">"cmt">//--- If the number of objects has changed if(this.m_delta_graph_obj!=class="num">0) { class=class="str">"cmt">//--- Create the class="type">class="kw">string and display it in the journal with the chart ID, its symbol and timeframe class="type">class="kw">string txt=", "+(m_delta_graph_obj>class="num">0 ? "Added: " : "Deleted: ")+(class="type">class="kw">string)fabs(m_delta_graph_obj)+" obj"; Print(DFUN,"ChartID=",this.ChartID(),", ",this.Symbol(),", ",TimeframeDescription(this.Timeframe()),txt); } class=class="str">"cmt">//--- If an object is added to the chart if(this.m_delta_graph_obj>class="num">0) { class="type">int index=class="num">0; class="type">class="kw">datetime time=class="num">0; class="type">class="kw">string name=""; class=class="str">"cmt">//--- find the last added graphical object and write its index for(class="type">int j=class="num">0;j<this.m_total_objects;j++) { name=::ObjectName(this.ChartID(),j); class="type">class="kw">datetime tm=(class="type">class="kw">datetime)::ObjectGetInteger(this.ChartID(),name,OBJPROP_CREATETIME); if(tm>time) { time=tm; index=j; } } class=class="str">"cmt">//--- Select the last graphical object by its index name=::ObjectName(this.ChartID(),index); if(name!="") { class=class="str">"cmt">//--- Create the object of the abstract graphical object class ENUM_OBJECT type=(ENUM_OBJECT)::ObjectGetInteger(this.ChartID(),name,OBJPROP_TYPE);
◍ 析构里清掉计时器与图表注释
引擎析构函数只做两件事:先调用 EventKillTimer() 杀掉之前用 EventSetTimer 起的定时器,避免 EA 卸载后还周期性触发 OnTimer;紧跟着用 Comment("") 把图表左上角可能残留的文字说明清空,防止退出后屏幕上还漂着旧提示。 这段逻辑看着简单,但在 MT5 实测里常被忽略——若 EA 在 OnTimer 里写了下单或刷屏代码,没 kill timer 就移除 EA,部分构建版本仍会空跑定时器直到终端重启。清 Comment 则是纯体验问题:不清的话,你切到别的品种还能看见上一套策略留下的参数残影。 外汇与贵金属交易自带高杠杆高风险,这类底层清理动作不影响盈亏,但决定你多图表作业时界面是否干净、资源是否泄漏。
CEngine::~CEngine() { ::EventKillTimer(); ::Comment(""); }
「借旧 EA 跑通图形对象挂载」
验证抽象图形对象体系是否可用,最省事的办法是直接复用前一篇的 EA,把它丢进 \MQL5\Experts\TestDoEasy\Part83\ 目录,文件名取 TestDoEasyPart83.mq5。EA 本身的交易与信号逻辑一行都不用动,只在 OnDeinit() 里把销毁毫秒计时器和清注释的动作,换成调用库的同名析构方法。 第一次编译会报 'CGStdGraphObj::CGStdGraphObj' - cannot access protected member function,1 errors, 0 warnings。原因很直白:图形对象基类的参数型构造函数在 private/protected 区段声明,外部实例化时被挡住了。 临时把该构造函数所在区的访问说明符改成 public,重新编译后启动 EA,各类图形对象正常挂到图表,日志同步打印出新对象的简述与类型。现象表明对象派生链在当前架构下可按预期工作。外汇与贵金属品种上做这类挂载测试时波动随机性强,任何对象渲染异常都可能伴随报价跳空,属高风险操作。 下面这段是 OnDeinit() 改动前后的对照,以及报错与访问符修正的原文片段:
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(class="kw">const class="type">int reason) { class=class="str">"cmt">//--- destroy timer EventKillTimer(); Comment(""); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert deinitialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnDeinit(class="kw">const class="type">int reason) { class=class="str">"cmt">//--- Deinitialize library engine.OnDeinit(); } class=class="str">"cmt">//+------------------------------------------------------------------+ &class="macro">#x27;CGStdGraphObj::CGStdGraphObj&class="macro">#x27; - cannot access class="kw">protected member function see declaration of &class="macro">#x27;CGStdGraphObj::CGStdGraphObj&class="macro">#x27; class="num">1 errors, class="num">0 warnings class=class="str">"cmt">//--- Default constructor CGStdGraphObj(){ this.m_type=OBJECT_DE_TYPE_GSTD_OBJ; } class="kw">protected: class="kw">public: class=class="str">"cmt">//--- Protected parametric constructor CGStdGraphObj(class="kw">const ENUM_OBJECT_DE_TYPE obj_type,class="kw">const ENUM_GRAPH_OBJ_BELONG belong,class="kw">const class="type">long chart_id,class="kw">const class="type">class="kw">string name);
图形对象类还要往下拆
这一篇把 DoEasy 函数库里图形相关的八十二个分篇收了口,当前库版本附带的全部源文件和一个 MQL5 测试 EA 打成了 4068.71 KB 的 ZIP,直接拖进 MT5 的 MQL5 目录就能编译跑通。 下一篇计划落地标准图形对象的抽象基类,再把图形对象集合的增删逻辑细化一层。也就是说,现在你在 ZIP 里看到的集合类还只是容器骨架,真实的标准对象(趋势线、矩形、文本标签)还没挂进去。 评论区里有个细节值得在 MT5 里自己验证:有开发者提到用「+」拼字符串在长时间运行的大合并场景下可能出不可预知行为,作者回说没做剖析但长对象名实测可用。你若自己扩这个库,建议把对象名生成改成哈希或 ResourceCreate 思路,别等真碰到命名长度上限再返工。 俄文原作者的系列还在继续,第八十四、八十五篇已经把抽象标准图形对象的衍生类和集合添加功能铺开了,想跟进就照着测试 EA 里的调用方式接进去,外汇和贵金属图表上跑一套自定义图形前先想清楚点差和滑点风险。