DoEasy 函数库中的图形(第九十六部分):窗体对象中的图形和鼠标事件的处理(基础篇)
在窗体对象里接住鼠标动作
DoEasy 函数库做到第九十六部分时,已经把图形画进了 MT5 的窗体对象,但光有静态图形不够,真正能用的面板得响应鼠标。这一节的核心是把鼠标事件挂到窗体对象上,让图表上的自定义控件能识别点击、移动和释放。 原文示例发布于 2022 年 4 月 14 日,在 MetaTrader 5 环境下运行,发布时显示浏览量 1258、评论 4 条,说明这类底层封装对普通交易者偏冷门,但做自动化面板的人绕不开。 改进方向集中在库类的窗体对象封装:把原先只负责绘制的类,扩展出鼠标事件处理接口。你在 MT5 里开个 EA 把这类窗体对象拖上去,如果不接鼠标事件,按钮点了没反应;接上之后才能拿到坐标和按键状态,进而驱动逻辑。 外汇和贵金属杠杆高、滑点随机,用窗体面板做手动辅助可以,但别把鼠标触发直接连到自动下单而不加风控,实盘可能因事件抖动画错单。
◍ 给画布窗体接上鼠标拖拽逻辑
上一阶段已经给复合图形对象的基准定位点加了控件,下一步要让这些控件能用鼠标直接拖。核心是在画布里的图形元素和窗体对象上挂鼠标事件处理:光标悬停到窗体要能识别并改属性,还得判断用户是不是按住了按钮不放。 具体交互上,如果在窗体内部按下鼠标并移动,就要禁掉图表滚动、十字光标和右键菜单,窗体本身跟着光标走;而鼠标压在其他窗体上时它们不响应。若在空白窗体外拖拽,整图随设置移动,途经的窗体不能误触发重定位,否则布局会乱。 品种对象类也要补新属性跟踪。新增枚举里 ENUM_SYMBOL_SECTOR 和 ENUM_SYMBOL_INDUSTRY 常量规模不小,消息类会加多个索引,取属性描述的方法体偏长但必要。 开 MT5 自建一个 CCanvas 窗体,挂上 OnMouseEvent 先打印悬停与按键状态,验证悬停识别与拖拽禁滚动是否生效,再决定复合对象怎么重排。
「给 DoEasy 库补上品种行业属性与画布交互位」
在 Data.mqh 里给消息索引追加了 MSG_SYM_PROP_SECTOR 与 MSG_SYM_PROP_INDUSTRY,分别对应经济部门和行业分支。这两个索引让品种对象能携带基本面分类信息,后续排序时可直接按板块筛符号,而不必在 EA 里自己维护映射表。 Defines.mqh 中把品种对象的整型、实数型、字符串型枚举都加了新常量,同时把画布图形元素的整型属性数量从 22 改到 23,并补了对应的排序条件枚举。少改一个宏替换,编译不会报错但运行时取属性会越界,这点动手前先全局搜一下 ENUM_PROP_INTEGER 的计数宏。 Symbol.mqh 的公开段声明了 Sector() 和 Industry() 两个方法,构造函数里给新属性赋初值,并在返回字符串型属性描述的方法末尾挂上文本映射。注意原文把 ButtonKeyState() 改名后,MouseState.mqh 私密段补了鼠标按钮状态的比特位十六进制表,这是窗体只响应单一悬停对象的前提。 GCnvElement.mqh 里把“交互”属性做成了可继承的虚拟事件处理:参数化构造函数会检查传入 name 是否已含程序名子串,有就不重复加前缀;同时修掉了 m_type 错赋值的 bug。下面这段是消息索引新增部分的原文片段,可对照抄进你本地库。 外汇与贵金属品种流动性分层明显,用板块属性做观察名单时仍属高风险操作,参数验证请在策略测试器离线跑。
class=class="str">"cmt">//--- CSymbol MSG_SYM_PROP_INDEX, class=class="str">"cmt">// Index in "Market Watch window" MSG_SYM_PROP_SECTOR, class=class="str">"cmt">// Economic sector MSG_SYM_PROP_INDUSTRY, class=class="str">"cmt">// Industry or economy branch MSG_SYM_PROP_CUSTOM, class=class="str">"cmt">// Custom symbol MSG_SYM_PROP_CHART_MODE, class=class="str">"cmt">// Price type used for generating symbols bars MSG_SYM_PROP_EXIST, class=class="str">"cmt">// Symbol with this name exists MSG_SYM_PROP_SELECT, class=class="str">"cmt">// Symbol selected in Market Watch MSG_SYM_PROP_VISIBLE, class=class="str">"cmt">// Symbol visible in Market Watch MSG_SYM_PROP_SESSION_DEALS, class=class="str">"cmt">// Number of deals in the current session MSG_SYM_PROP_SESSION_BUY_ORDERS, class=class="str">"cmt">// Number of Buy orders at the moment MSG_SYM_PROP_SESSION_SELL_ORDERS, class=class="str">"cmt">// Number of Sell orders at the moment MSG_SYM_PROP_VOLUME, class=class="str">"cmt">// Volume of the last deal MSG_SYM_PROP_VOLUMEHIGH, class=class="str">"cmt">// Maximal day volume MSG_SYM_PROP_VOLUMELOW, class=class="str">"cmt">// Minimal day volume
品种属性枚举里的报价与衍生品字段
在 MT5 的 ENUM_SYMBOL_INFO_STRING / 整数属性枚举体系中,有一批 MSG_SYM_PROP_* 常量专门描述交易品种的元信息,写 EA 或指标时若漏掉这几个,回测日志很容易对不上真实品种。 下面这段代码片段列出了其中关键的属性标识:最新报价时间、毫秒级时间戳、小数位数、合约手数小数位、会话最高限价、对冲单保证金,以及一批带背景高亮的衍生品与统计字段。 其中 MSG_SYM_PROP_TIME_MSC 给出毫秒级最后报价时间,比 MSG_SYM_PROP_TIME 的秒级精度更适合做高频tick对齐;MSG_SYM_PROP_PRICE_CHANGE 是当日相对前一交易日收盘的涨跌幅百分比,MSG_SYM_PROP_PRICE_VOLATILITY 直接给出现价波动率百分比,这两个值做贵金属日内风控阈值时很实用。 被高亮的 MSG_SYM_PROP_PRICE_DELTA / THETA / GAMMA / VEGA / RHO / OMEGA 等,是期权或权证的希腊字母参数,做外汇期权策略时要靠它们算对冲比;MSG_SYM_PROP_COUNTRY 和 MSG_SYM_PROP_SECTOR_NAME 则标明了标的所属国别与行业板块,对跨市场扫描有帮助。外汇与贵金属杠杆高,用这些字段做自动筛选仍须人工复核流动性风险。
MSG_SYM_PROP_TIME, class=class="str">"cmt">// Latest quote time MSG_SYM_PROP_TIME_MSC, class=class="str">"cmt">// Time of the last quote in milliseconds MSG_SYM_PROP_DIGITS, class=class="str">"cmt">// Number of decimal places MSG_SYM_PROP_DIGITS_LOTS, class=class="str">"cmt">// Digits after a decimal point in the value of the lot MSG_SYM_PROP_SESSION_PRICE_LIMIT_MAX, class=class="str">"cmt">// Maximum session price MSG_SYM_PROP_MARGIN_HEDGED, class=class="str">"cmt">// Size of a contract or margin for one lot of hedged positions MSG_SYM_PROP_PRICE_CHANGE, class=class="str">"cmt">// Change of the current price relative to the end of the previous trading day in % MSG_SYM_PROP_PRICE_VOLATILITY, class=class="str">"cmt">// Price volatility in % MSG_SYM_PROP_PRICE_THEORETICAL, class=class="str">"cmt">// Theoretical option price MSG_SYM_PROP_PRICE_DELTA, class=class="str">"cmt">// Option/warrant delta MSG_SYM_PROP_PRICE_THETA, class=class="str">"cmt">// Option/warrant theta MSG_SYM_PROP_PRICE_GAMMA, class=class="str">"cmt">// Option/warrant gamma MSG_SYM_PROP_PRICE_VEGA, class=class="str">"cmt">// Option/warrant vega MSG_SYM_PROP_PRICE_RHO, class=class="str">"cmt">// Option/warrant rho MSG_SYM_PROP_PRICE_OMEGA, class=class="str">"cmt">// Option/warrant omega MSG_SYM_PROP_PRICE_SENSITIVITY, class=class="str">"cmt">// Option/warrant sensitivity class=class="str">"cmt">//--- MSG_SYM_PROP_NAME, class=class="str">"cmt">// Symbol name MSG_SYM_PROP_BASIS, class=class="str">"cmt">// Underlying asset of derivative MSG_SYM_PROP_COUNTRY, class=class="str">"cmt">// Country MSG_SYM_PROP_SECTOR_NAME, class=class="str">"cmt">// Economic sector
◍ 枚举符号属性与事件的中文映射
在 MT5 的终端本地化机制里,SYMBOL 相关的中文文案并不是硬编码在 EA 里,而是由一组 MSG_SYM_* 枚举驱动。搞懂这些枚举名,你才能在自定义面板或日志里把英文语境替换成符合自己习惯的中文标注。 下面这段枚举覆盖了品种属性、行情来源、分类路径等静态描述,也包含 Market Watch 增删排序、DOM 订阅等动态事件。直接在 MT5 的 MetaEditor 里全局搜索 MSG_SYM_PROP_INDUSTRY_NAME,就能定位到系统自带的中文翻译资源文件。 注意 MSG_SYM_PROP_CAYEGORY 在原生枚举里拼写为 CAYEGORY(疑似笔误),调用时若自行声明需保持原拼写,否则编译会报未定义。外汇与贵金属品种同样走这套枚举,但报价受杠杆与流动性影响,高风险特征不会改变。
MSG_SYM_PROP_INDUSTRY_NAME, class=class="str">"cmt">// Economy or industry branch MSG_SYM_PROP_CURRENCY_BASE, class=class="str">"cmt">// Basic currency of symbol MSG_SYM_PROP_CURRENCY_PROFIT, class=class="str">"cmt">// Profit currency MSG_SYM_PROP_CURRENCY_MARGIN, class=class="str">"cmt">// Margin currency MSG_SYM_PROP_BANK, class=class="str">"cmt">// Feeder of the current quote MSG_SYM_PROP_DESCRIPTION, class=class="str">"cmt">// Symbol description MSG_SYM_PROP_FORMULA, class=class="str">"cmt">// Formula used for custom symbol pricing MSG_SYM_PROP_ISIN, class=class="str">"cmt">// Symbol name in ISIN system MSG_SYM_PROP_PAGE, class=class="str">"cmt">// Address of web page containing symbol information MSG_SYM_PROP_PATH, class=class="str">"cmt">// Location in symbol tree MSG_SYM_PROP_CAYEGORY, class=class="str">"cmt">// Symbol category MSG_SYM_PROP_EXCHANGE, class=class="str">"cmt">// Name of an exchange a symbol is traded on class=class="str">"cmt">//--- class=class="str">"cmt">//--- MSG_SYM_EVENT_SYMBOL_ADD, class=class="str">"cmt">// Added symbol to Market Watch window MSG_SYM_EVENT_SYMBOL_DEL, class=class="str">"cmt">// Symbol removed from Market Watch window MSG_SYM_EVENT_SYMBOL_SORT, class=class="str">"cmt">// Changed location of symbols in Market Watch window MSG_SYM_SYMBOLS_MODE_CURRENT, class=class="str">"cmt">// Work with current symbol only MSG_SYM_SYMBOLS_MODE_DEFINES, class=class="str">"cmt">// Work with predefined symbol list MSG_SYM_SYMBOLS_MODE_MARKET_WATCH, class=class="str">"cmt">// Work with Market Watch window symbols MSG_SYM_SYMBOLS_MODE_ALL, class=class="str">"cmt">// Work with full list of all available symbols MSG_SYM_SYMBOLS_BOOK_ADD, class=class="str">"cmt">// Subscribed to Depth of Market MSG_SYM_SYMBOLS_BOOK_DEL, class=class="str">"cmt">// Unsubscribed from Depth of Market MSG_SYM_SYMBOLS_MODE_BOOK class=class="str">"cmt">// Subscription to Depth of Market
「枚举里的板块与行业标签怎么读」
在 MT5 的枚举体系里,品种属性不止于报价和点值,系统还用一组 MSG_SYM_SECTOR_* 常量把标的归到宏观板块。从代码看,板块覆盖 Currencies、Cryptocurrencies、Commodities、Energy、Basic materials 等 15 类,外汇和贵金属交易者最常用的是 MSG_SYM_SECTOR_CURRENCY 与 MSG_SYM_SECTOR_COMMODITIES 两项。 比板块更细一层的是 ENUM_SYMBOL_INDUSTRY,它把基础材料进一步拆成铝、铜、化工、焦煤等子行业。比如 MSG_SYM_INDUSTRY_COPPER 和 MSG_SYM_INDUSTRY_ALUMINIUM 就是区分有色品种的钥匙,做跨品种相关性时比只看符号前缀靠谱。 开 MT5 按 F4 进 MetaEditor,全局搜 ENUM_SYMBOL_SECTOR 就能看到完整清单;写 EA 时可用 SymbolInfoInteger(_Symbol, SYMBOL_SECTOR) 取当前品种板块号,再比对枚举做分池处理。外汇与贵金属杠杆高、波动剧烈,依板块分类做风控只是辅助,不能替代仓位管理。
MSG_SYM_SYMBOLS_ERR_BOOK_ADD, class=class="str">"cmt">// Error subscribing to DOM MSG_SYM_SYMBOLS_ERR_BOOK_DEL, class=class="str">"cmt">// Error unsubscribing from DOM class=class="str">"cmt">//--- ENUM_SYMBOL_SECTOR MSG_SYM_SECTOR_UNDEFINED, class=class="str">"cmt">// Undefined MSG_SYM_SECTOR_BASIC_MATERIALS, class=class="str">"cmt">// Basic materials MSG_SYM_SECTOR_COMMUNICATION_SERVICES, class=class="str">"cmt">// Communication services MSG_SYM_SECTOR_CONSUMER_CYCLICAL, class=class="str">"cmt">// Consumer cyclical MSG_SYM_SECTOR_CONSUMER_DEFENSIVE, class=class="str">"cmt">// Consumer defensive MSG_SYM_SECTOR_CURRENCY, class=class="str">"cmt">// Currencies MSG_SYM_SECTOR_CURRENCY_CRYPTO, class=class="str">"cmt">// Cryptocurrencies MSG_SYM_SECTOR_ENERGY, class=class="str">"cmt">// Energy MSG_SYM_SECTOR_FINANCIAL, class=class="str">"cmt">// Finance MSG_SYM_SECTOR_HEALTHCARE, class=class="str">"cmt">// Healthcare MSG_SYM_SECTOR_INDUSTRIALS, class=class="str">"cmt">// Industrials MSG_SYM_SECTOR_REAL_ESTATE, class=class="str">"cmt">// Real estate MSG_SYM_SECTOR_TECHNOLOGY, class=class="str">"cmt">// Technology MSG_SYM_SECTOR_UTILITIES, class=class="str">"cmt">// Utilities MSG_SYM_SECTOR_INDEXES, class=class="str">"cmt">// Indices MSG_SYM_SECTOR_COMMODITIES, class=class="str">"cmt">// Commodities class=class="str">"cmt">//--- ENUM_SYMBOL_INDUSTRY MSG_SYM_INDUSTRY_UNDEFINED, class=class="str">"cmt">// Undefined class=class="str">"cmt">//--- Basic materials MSG_SYM_INDUSTRY_AGRICULTURAL_INPUTS, class=class="str">"cmt">// Agricultural inputs MSG_SYM_INDUSTRY_ALUMINIUM, class=class="str">"cmt">// Aluminium MSG_SYM_INDUSTRY_BUILDING_MATERIALS, class=class="str">"cmt">// Building materials MSG_SYM_INDUSTRY_CHEMICALS, class=class="str">"cmt">// Chemicals MSG_SYM_INDUSTRY_COKING_COAL, class=class="str">"cmt">// Coking coal MSG_SYM_INDUSTRY_COPPER, class=class="str">"cmt">// Copper