DoEasy. 控件 (第 1 部分): 第一步(基础篇)
从零搭一个 WinForms 风格面板对象类
在 MT5 里做自定义控件,第一步不是画按钮,而是把面板对象抽象成一个可复用的 C++ 类。DoEasy 库的思路是先用一个 WinForms 风格的面板对象类打底,后续所有控件都挂在这个面板上,避免每次重画坐标轴与边框。 原文示例发布于 2022 年 6 月 14 日,截至归档页显示阅读量 1925、评论 2 条,说明这类底层封装需求在量化社群里并不冷门,但真正动手改库的人少。 改进库类的核心动作是重写对象生命周期:创建时注册到图表、销毁时清理资源、重绘时只动脏区。测试阶段建议直接拖一个空面板到 EURUSD 的 M5 图,观察是否随窗口缩放稳定贴边——外汇与贵金属杠杆高,面板遮挡报价区会直接影响下单判断,验证时务必留好右侧空间。
「为什么先搭控件再补旧坑」
这一篇开启一个新系列,目标是用 MQL5 仿照 Windows 窗体样式写一套控件。MS Visual Studio 元素面板里列了八大组:全部 Windows 窗体、标准控件、容器、菜单和工具条、数据、组件、打印、对话框,但函数库并不需要全复刻,只挑最关键的做。 切换主题不是旧坑不填,而是之前的图形对象函数库越写越难管,没有控件根本玩不转。先把控件按 Windows 窗体风格补齐,回头再续上前面的主题,那时工具链才完整。 底层已经有一个基于 CCanvas 的图形元素对象类,所有图形对象都从它派生;窗体类站在图形元素之上,自带移动和操纵方法。面板类将继承窗体类,并往里加新属性,它既是窗口基础,也是装其它控件的容器,支持父面板套子面板多层嵌套。 动手写面板前,得先回头修一遍现有函数库对象类——前面主题里没改完的缺陷和遗漏,要边补边修正,否则控件挂上去也是歪的。
◍ 给 DoEasy 库补上延迟与对冲两个新属性
终端 3260 更新后,MQL5 在 ENUM_SYMBOL_INFO_INTEGER 里加了 SYMBOL_SUBSCRIPTION_DELAY,只针对订阅类品种,且必须在市场观察中选中才能取到,否则返回 ERR_MARKET_NOT_SELECTED (4302)。延迟通常出现在试用数据源上,做订阅行情解析时若漏掉这个前置条件,读取会直接报错。 账户端新增 ACCOUNT_HEDGE_ALLOWED,仅在对冲账户有意义:禁用时同一品种不允许互逆持仓或挂单,强开卖单会返回 TRADE_RETCODE_HEDGE_PROHIBITED。这条对跨品种对冲策略的订单路由判断很关键,尤其在监管限制账户上。 我们把这两个属性接进 DoEasy 的对象层。账户类 Account.mqh 里,整数型属性数量从 11 扩到 12,构造函数对 MQL5 版本低于 3245 的情况强制写 false,>=3245 才从终端属性取真值,MQL4 永远 false。品种类 Symbol.mqh 同理,整数型属性从 40 到 41,GetSubscriptionDelay() 在老终端返回 0。 图形基类也动了:GBaseObj 的 SetChartID() 现在接受 0 或 NULL 并自动回退到 ChartID(),GCnvElement 加了元素可用性标志和默认字体值。Form 类把 GetList() 改名 GetListElements(),新增逆初始化方法,能从继承类里干净卸掉父类动态对象。 下面这段是消息索引枚举的改动片段,高亮行就是为 <3245 终端补的提示索引,复制进 Data.mqh 即可用。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| List of the library&class="macro">#x27;s text message indices | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_MESSAGES_LIB { MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST, class=class="str">"cmt">// Beginning of the parameter list MSG_LIB_PARAMS_LIST_END, class=class="str">"cmt">// End of the parameter list MSG_LIB_PROP_NOT_SUPPORTED, class=class="str">"cmt">// Property not supported MSG_LIB_PROP_NOT_SUPPORTED_MQL4, class=class="str">"cmt">// Property not supported in MQL4 MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_2155, class=class="str">"cmt">// Property not supported in MetaTrader class="num">5 versions lower than class="num">2155 MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_3245, class=class="str">"cmt">// Property not supported in MetaTrader class="num">5 versions lower than class="num">3245 MSG_LIB_PROP_NOT_SUPPORTED_POSITION, class=class="str">"cmt">// Property not supported for position
枚举常量与本地化字符串的版本断点
在 MQL5 标准库的属性枚举里,MSG_SYM_PROP_BACKGROUND_COLOR 指向 Market Watch 中品种的背景色,MSG_SYM_PROP_SUBSCRIPTION_DELAY 则描述订阅制品种推送报价的延迟字段。账户侧有 MSG_ACC_PROP_FIFO_CLOSE 强制 FIFO 平仓标志,以及 MSG_ACC_PROP_HEDGE_ALLOWED 决定可否开反向仓和挂单。 本地化字符串表暴露了明确的构建号断点:部分属性在 MetaTrader5 build 低于 2155 时不支持,另一批(如订阅延迟、对冲允许、Panel 控件)则要求 build 不低于 3245。如果你的终端还停在老版本,直接调用这些枚举会返回不支持错误。 画布参数也写死在宏里:PAUSE_FOR_CANV_UPDATE 为 16 毫秒的画布刷新间隔,CLR_CANV_NULL 定义为 0x00FFFFFF,即带 alpha 通道画布的透明零值。开 MT5 按 F4 搜这些宏,比对自家终端 build 号就能验证兼容性。
class="macro">#define PAUSE_FOR_CANV_UPDATE(class="num">16) class=class="str">"cmt">// Canvas update frequency class="macro">#define CLR_CANV_NULL(0x00FFFFFF) class=class="str">"cmt">// Zero for the canvas with the alpha channel
「画布对象的底层常量与类型枚举」
在 MT5 自定义界面库里,画布上所有文字与图形的默认外观都由一组宏定死。前景色 CLR_FORE_COLOR 取 C'0x2D,0x43,0x48'(深蓝灰),默认字体 Calibri、字号 8,表单工作区外留白 OUTER_AREA_SIZE = 16 像素,这些数值直接决定你后来拖控件时的边距观感。 对象类型用 ENUM_OBJECT_DE_TYPE 枚举管理,起点 OBJECT_DE_TYPE_GBASE = COLLECTION_ID_LIST_END+1,说明图形对象ID接续了集合ID之后分配,不会撞号。往下分了图形基类、图元、表单、阴影,以及 WinForms 风格的面板 GWF_PANEL。 动画子系统也挂在同一枚举下:单帧 GFRAME、文本帧 GFRAME_TEXT、矩形帧 GFRAME_QUAD、几何帧 GFRAME_GEOMETRY,最后由 GANIMATIONS 统管。开 MT5 新建 EA 时,把这段枚举原样贴进头文件,就能用 Switch 按类型分流鼠标事件。
class="macro">#define CLR_FORE_COLOR(C&class="macro">#x27;0x2D,0x43,0x48&class="macro">#x27;) class=class="str">"cmt">// Default class="type">color for texts of objects on canvas class="macro">#define DEF_FONT("Calibri") class=class="str">"cmt">// Default font class="macro">#define DEF_FONT_SIZE(class="num">8) class=class="str">"cmt">// Default font size class="macro">#define OUTER_AREA_SIZE(class="num">16) class=class="str">"cmt">// Size of one side of the outer area around the form workspace class=class="str">"cmt">//--- Graphical object parameters class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| List of library object types | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_OBJECT_DE_TYPE { class=class="str">"cmt">//--- Graphics OBJECT_DE_TYPE_GBASE = COLLECTION_ID_LIST_END+class="num">1, class=class="str">"cmt">// "Base object of all library graphical objects" object type OBJECT_DE_TYPE_GELEMENT, class=class="str">"cmt">// "Graphical element" object type OBJECT_DE_TYPE_GFORM, class=class="str">"cmt">// Form object type OBJECT_DE_TYPE_GFORM_CONTROL, class=class="str">"cmt">// "Form for managing pivot points of graphical object" object type OBJECT_DE_TYPE_GSHADOW, class=class="str">"cmt">// Shadow object type class=class="str">"cmt">//--- WinForms OBJECT_DE_TYPE_GWF_PANEL, class=class="str">"cmt">// WinForms Panel object type class=class="str">"cmt">//--- Animation OBJECT_DE_TYPE_GFRAME, class=class="str">"cmt">// "Single animation frame" object type OBJECT_DE_TYPE_GFRAME_TEXT, class=class="str">"cmt">// "Single text animation frame" object type OBJECT_DE_TYPE_GFRAME_QUAD, class=class="str">"cmt">// "Single rectangular animation frame" object type OBJECT_DE_TYPE_GFRAME_GEOMETRY, class=class="str">"cmt">// "Single geometric animation frame" object type OBJECT_DE_TYPE_GANIMATIONS class=class="str">"cmt">// "Animations" object type };
◍ 账户与品种属性枚举的排序边界
在 MT5 的底层枚举设计里,账户整数属性被收进 ENUM_ACCOUNT_PROP_INTEGER,其中 ACCOUNT_PROP_HEDGE_ALLOWED 决定该账户能否开反向仓和挂单,ACCOUNT_PROP_FIFO_CLOSE 则标记是否强制 FIFO 平仓。这两个字段直接决定你的套利和对冲策略在某账户上是否跑得通。 宏定义给出硬数字:ACCOUNT_PROP_INTEGER_TOTAL 为 12,ACCOUNT_PROP_INTEGER_SKIP 为 0,意味着参与排序的整数属性共 12 个、无跳过项。FIRST_ACC_DBL_PROP 与 FIRST_ACC_STR_PROP 据此偏移,把双精度与字符串属性接续排在后段。 排序枚举 ENUM_SORT_ACCOUNT_MODE 里,SORT_BY_ACCOUNT_HEDGE_ALLOWED 和 SORT_BY_ACCOUNT_FIFO_CLOSE 对应前面两个整数属性;从 SORT_BY_ACCOUNT_BALANCE = FIRST_ACC_DBL_PROP 起,排序切入余额、信用等真实数值属性。写多账户扫描器时,按这个偏移量取属性才不会越界。 品种侧另有 ENUM_SYMBOL_PROP_INTEGER,例如 SYMBOL_PROP_OPTION_MODE 标出期权类型(取自 ENUM_SYMBOL_OPTION_MODE)。黄金期权或外汇奇异期权品种,先读这个字段再决定报价解析逻辑,能避开用错合约结构的坑。外汇与贵金属杠杆高,属性误读可能导致下单被拒或保证金计算偏差。
class=class="str">"cmt">//--- Managing graphical objects class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Account integer properties | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_ACCOUNT_PROP_INTEGER { ... ACCOUNT_PROP_FIFO_CLOSE, class=class="str">"cmt">// Flag of a position closure by FIFO rule only ACCOUNT_PROP_HEDGE_ALLOWED class=class="str">"cmt">// Permission to open opposite positions and set pending orders }; class="macro">#define ACCOUNT_PROP_INTEGER_TOTAL(class="num">12) class=class="str">"cmt">// Total number of integer properties class="macro">#define ACCOUNT_PROP_INTEGER_SKIP(class="num">0) class=class="str">"cmt">// Number of integer account properties not used in sorting class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Possible account sorting criteria | class=class="str">"cmt">//+------------------------------------------------------------------+ class="macro">#define FIRST_ACC_DBL_PROP(ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP) class="macro">#define FIRST_ACC_STR_PROP(ACCOUNT_PROP_INTEGER_TOTAL-ACCOUNT_PROP_INTEGER_SKIP+ACCOUNT_PROP_DOUBLE_TOTAL-ACCOUNT_PROP_DOUBLE_SKIP) enum ENUM_SORT_ACCOUNT_MODE { ... SORT_BY_ACCOUNT_FIFO_CLOSE, class=class="str">"cmt">// Sort by the flag of a position closure by FIFO rule only SORT_BY_ACCOUNT_HEDGE_ALLOWED, class=class="str">"cmt">// Sort by permission to open opposite positions and set pending orders class=class="str">"cmt">//--- Sort by real properties SORT_BY_ACCOUNT_BALANCE = FIRST_ACC_DBL_PROP, class=class="str">"cmt">// Sort by an account balance in the deposit currency SORT_BY_ACCOUNT_CREDIT, class=class="str">"cmt">// Sort by credit in a deposit currency ... SORT_BY_ACCOUNT_COMPANY class=class="str">"cmt">// Sort by a name of a company serving an account }; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Symbol integer properties | class=class="str">"cmt">//+------------------------------------------------------------------+ enum ENUM_SYMBOL_PROP_INTEGER { class=class="str">"cmt">//--- ... SYMBOL_PROP_OPTION_MODE, class=class="str">"cmt">// Option type(from the ENUM_SYMBOL_OPTION_MODE enumeration) };