DoEasy 函数库中的图形(第九十六部分):窗体对象中的图形和鼠标事件的处理·综合运用
🖱️

DoEasy 函数库中的图形(第九十六部分):窗体对象中的图形和鼠标事件的处理·综合运用

(3/3)· 从悬停感知到拖拽独占,142 节把窗体对象的鼠标响应与品种属性跟踪全链路打通

进阶 第 3/3 篇
在画布窗体里拖控件时,图表跟着乱跑、右键菜单抢焦点,是多数自建图形库的通病。给窗体加上与环境交互的独占标志,才能让悬停和拖拽只作用于当前对象,不惊动整张图表。
本章目录
  1. 符号分类枚举里的贵金属落点
  2. 行业分类映射表的俄语源与英文键
  3. 行业分类表的金融到工业段结构
  4. 行业分类映射表的后半段怎么填
  5. 整数型符号属性的枚举清单
  6. 品种交易属性里藏着的下单暗礁
  7. 枚举里的整数与双精度属性分界
  8. 品种属性枚举里的保证金与掉期字段
  9. 保证金与盘面会话属性的枚举落地
  10. 抓合约属性里的日内波动与期权希腊值
  11. 品种字符串属性的枚举边界
  12. 枚举里塞满了排序维度
  13. 按交易属性给品种排序的枚举清单
  14. 市场报价窗的实值排序枚举
  15. 按保证金与掉期维度给品种排序的枚举
  16. 按挂单维持保证金与当节行情字段排序的枚举项
  17. 按字符串属性给交易品种排序的枚举项
  18. 画布图形元素的整型属性枚举
  19. 画布元素排序的整型属性边界
  20. 画布元素排序枚举与品种属性描述接口
  21. 扒开 CSymbol 里的板块与订单属性接口
  22. 从品种对象里直接抠出图表与session状态
  23. 从成交量到毫秒时间的符号属性读取
  24. 扒开品种属性里的执行与风控参数
  25. 从品种属性接口挖出交易规则细节
  26. 报价边界与成交价的封装取数
  27. 从真实成交量到 tick 价值的符号属性读取
  28. 从合约属性里抠出交易限制与成本
  29. 把保证金与库存费读进自定义类
  30. 空头挂单与维护保证金的接口取法
  31. 从交易品种对象直接取会话层数据
  32. 从限价到希腊字母:品种属性的第二批取值器
  33. 从符号属性里直接抓价格弹性与盘口极值
  34. 从交易品种对象里抠出基础属性串
  35. 直接从品种对象取元信息
  36. 盯住当前会话的成交与买单变动
  37. 盯住场内session挂单的增减节奏
  38. 成交量异动的三道监控闸
  39. 日内高低成交量变动的监控接口
  40. 用代码盯住点差与低价区成交量异动
  41. 盯住止损位与冻结位的突变阈值
  42. 冻结水平与Bid变动的监控接口
  43. 日内买卖价极值的变化监控接口
  44. 盯住 BidLow 与 Last 的受控变动接口
  45. 盯住 Last 与日内高价的变动阈值
  46. 日内 Last 高低点的增量判定接口
  47. 盯盘 Bid/Last 的日内极值变动接口
  48. Ask 报价的阈值监控接口怎么写
  49. 盯住日内 Ask 高低点的变动阈值
  50. 盯住卖价低位与真实成交量的越界信号
  51. 日内真实成交量的极值监控接口
  52. 盯住真实最低成交量与期权行权价的变动阈值
  53. 抓取期权行权价与持仓限额的变动信号
  54. 盯住掉期与时段成交量的阈值钩子
  55. 盯住当前会话的量能与成交额阈值
  56. 盯住会话内的持仓与买单异动
  57. 盯住买卖挂单量与开盘价的变动阈值
  58. 用类封装盯住会话开收盘的变动阈值
  59. 抓取会话均价与跨日价格跳变
  60. 用代码盯住价格变动与波动率阈值
  61. 波动率与期权理论价的变动捕获接口
  62. 用代码盯住价格跳动与期权Theta阈值
  63. Theta 与 Gamma 的变动监控接口
  64. 用 Vega 与 Rho 阈值卡住期权式波动
  65. 用代码盯住 Rho 与 Omega 的突变阈值
  66. 期权敏感度与交易对象的类内接口
  67. 初始化时先把品种塞进市场报价窗
  68. 把整型品种属性一次性灌进数组
  69. 把品种整数属性一次性塞进缓存数组
  70. 把品种属性一次性灌进内存数组
  71. 把品种交易参数一次性灌进对象数组
  72. 把交易时段与期权希腊值一次性塞进属性数组
  73. 把报价与保证金率塞进属性数组
  74. 保证金与字符串属性的落盘写法
  75. 符号对象刷新时的属性落盘细节
  76. 枚举品种属性时的分支兜底写法
  77. 符号属性取值的 MQL5/MQL4 分支处理
  78. 品种属性字符串拼接里的条件分支
  79. 符号属性文本化的三元分支细节
  80. 把品种属性转成可读文本的分支处理
  81. 期权类符号的希腊值描述拼接
  82. 符号属性字符串的安全拼装
  83. 逐属性拼装品种描述字符串
  84. 属性空值与板块枚举的文本拼装逻辑
  85. 板块与行业枚举的文本映射写法
  86. 行业分类枚举的映射写法
  87. 行业分类枚举的映射写法
  88. 行业分类枚举的散装映射写法
  89. 行业分类枚举的映射写法
  90. 行业分类枚举的逐条映射写法
  91. 行业分类枚举的映射写法
  92. 行业枚举收口与鼠标状态读取
  93. 鼠标状态类里的位标志布局
  94. 鼠标与光标状态的位值映射
  95. 图形对象基类的初始化陷阱
  96. 图形对象基类的初始化细节
  97. 图形对象基类的事件与属性初始化
  98. 图形元素基类的光标命中判定
  99. 图形元素类的内部骨架
  100. 图形元素的整型属性结构
  101. GUI 元素结构体的字段布局
  102. 图形元素的属性槽与事件钩子
  103. 画布图元的坐标与边距接口
  104. 画布控件的活动区偏移与交互开关
  105. 控件激活区与背景色的取值接口
  106. 画布元素的边界与属性读取
  107. 画布元素对象的属性读取接口
  108. 图形元素的置顶与显隐实现
  109. 画布元素构造时的属性落盘
  110. 画布元素初始化的属性落点
  111. 图形元素构造时的命名与属性注入
  112. 画布元素的属性落地与结构保存
  113. 从画布对象抓取界面元素属性
  114. 画布元素结构体的双向序列化
  115. 给画布控件一次性灌入坐标与交互属性
  116. 在图表事件里重算子窗口Y偏移
  117. 让窗体接住鼠标:从光标状态到图表工具互斥
  118. GUI 窗体类的内部成员与初始化入口
  119. 图形对象的命名拼接与鼠标状态接口
  120. 表单类的坐标与鼠标接口怎么写
  121. 表单类的鼠标状态与初始化拆解
  122. 表单内外的鼠标状态判定逻辑
  123. 拖拽表单时如何接管图表鼠标事件
  124. 图表交互与浮层跟随的事件链路
  125. 受保护构造器如何初始化图形对象属性
  126. 图形对象初始化时的属性落盘逻辑
  127. 图表事件如何驱动扩展控件刷新
  128. 图表事件控制器的成员变量与访问接口
  129. 图表对象控制类的接口与初始化
  130. 图表对象控制类的构造与析构细节
  131. 图表控件里怎么接管 ChartID 比较与交互开关
  132. 图形元素集合类的内部骨架
  133. 图表对象集合的差集查找与垂直线创建
  134. 正文
  135. 对象改名时怎么在集合里兜底定位
  136. 鼠标悬停与对象改名的事件派发细节
  137. 锁死图表交互与扩展对象事件分发
  138. 图表控件集合的取用与ID回填
  139. 跑起多窗体 EA 看拖动表现
  140. 用循环批量生成带渐变背景的表单对象
  141. 把画布表单塞进图形引擎集合
  142. 别急着下结论

符号分类枚举里的贵金属落点

在 MT5 的枚举结构里,交易品种的属性被拆成了「板块(sector)」和「行业(industry)」两层。外汇和加密货币各自占一个 sector 值,而黄金、白银被归在 Basic materials 这个 sector 下的具体 industry 枚举中,代码里对应 "Gold" 和 "Silver" 两个字符串常量。 如果你写 EA 时要按板块过滤品种,直接用 ENUM_SYMBOL_SECTOR 里的 Currencies 抓直盘,用 Basic materials 再配合 SYMBOL_INDUSTRY 去筛黄金白银,比硬写品种名稳妥。贵金属在 MT5 属高风险标的,杠杆波动可能在一根 H4 吞掉本金,过滤逻辑只是前置条件,不替代风控。 下面这段是原文里 sector 与部分 industry 枚举的节选,注意 Gold / Silver 都在 Basic materials 分组注释下,不在 Currencies 里: //--- ENUM_SYMBOL_SECTOR { "Не определен", "Undefined" }, { "Сырье", "Basic materials" }, { "Валюты", "Currencies" }, { "Криптовалюты", "Cryptocurrencies" }, //--- ENUM_SYMBOL_INDUSTRY { "Не определено", "Undefined" }, //--- Basic materials { "Золото", "Gold" }, { "Серебро", "Silver" }, { "Медь", "Copper" }, 实盘验证方法很简单:开 MT5 终端,用 SymbolInfoInteger(_Symbol, SYMBOL_SECTOR) 打印当前品种枚举值,XAUUSD 会落进 Basic materials 而非 Currencies,确认你的过滤分支没写错。

MQL5 / C++
class=class="str">"cmt">//--- ENUM_SYMBOL_SECTOR
{ "Не определен", "Undefined" },
{ "Сырье", "Basic materials" },
{ "Валюты", "Currencies" },
{ "Криптовалюты", "Cryptocurrencies" },
class=class="str">"cmt">//--- ENUM_SYMBOL_INDUSTRY
{ "Не определено", "Undefined" },
class=class="str">"cmt">//--- Basic materials
{ "Золото", "Gold" },
{ "Серебро", "Silver" },
{ "Медь", "Copper" }

◍ 行业分类映射表的俄语源与英文键

这段数据结构把俄语行业名和英文行业名成对写死在一个数组里,是后续按板块过滤标的的底表。前 6 对属于通信服务(Broadcasting 到 Telecom services),紧接着 27 对归入消费周期品(从 Apparel manufacturing 到 Travel services),再往下 13 对是消费防御品(Beverages 系列到 Tobacco),最后 9 对覆盖能源板块(Oil and gas 各链条到 Uranium)。 在 MT5 里若要做跨市场相关性扫描,直接把这段二元组贴进 structstring[][2] 数组就能用,不需要运行时翻译。注意原文用 //--- 注释做板块分隔,不是语言关键字,只是给人看的切分标记。 实际验证时数一下:消费周期品段含 27 个条目,是四块里最密的;能源段仅 9 条,做板块权重统计时别漏掉 Thermal coal 和 Uranium 这两个非油气条目。外汇与贵金属交易受宏观板块轮动影响明显,此类分类仅作背景参照,杠杆品种高风险,映射本身不预示任何价格方向。

MQL5 / C++
  {"Вещание","Broadcasting"},
  {"Электронные игры и мультимедиа","Electronic gaming and multimedia"},
  {"Развлечения","Entertainment"},
  {"Интернет-контент и информация","Internet content and information"},
  {"Издательство","Publishing"},
  {"Телекоммуникационные услуги","Telecom services"},
  class=class="str">"cmt">//--- Consumer cyclical
  {"Производство одежды","Apparel manufacturing"},
  {"Розничная продажа одежды","Apparel retail"},
  {"Автомобилестроение","Auto manufacturers"},
  {"Автозапчасти","Auto parts"},
  {"Дилеры легковых и грузовых автомобилей","Auto and truck dealerships"},
  {"Универсальные магазины","Department stores"},
  {"Обувь и аксессуары","Footwear and accessories"},
  {"Мебель, фурнитура и бытовая техника","Furnishing, fixtures and appliances"},
  {"Игорные предприятия","Gambling"},
  {"Розничная торговля товарами для дома","Home improvement retail"},
  {"Розничная онлайн-торговля","Internet retail"},
  {"Досуг","Leisure"},
  {"Жилье","Lodging"},
  {"Товары класса \"люкс\"","Luxury goods"},
  {"Упаковка","Packaging and containers"},
  {"Персональные услуги","Personal services"},
  {"Транспортные средства для отдыха","Recreational vehicles"},
  {"Жилищное строительство","Residential construction"},
  {"Курорты и казино","Resorts and casinos"},
  {"Рестораны","Restaurants"},
  {"Специализированная розничная торговля","Specialty retail"},
  {"Текстильное производство","Textile manufacturing"},
  {"Туристические услуги","Travel services"},
  class=class="str">"cmt">//--- Consumer defensive
  {"Напитки - Пивовары","Beverages - Brewers"},
  {"Напитки - Безалкогольные","Beverages - Non-alcoholic"},
  {"Напитки - Винзаводы и ликеро-водочные заводы","Beverages - Wineries and distilleries"},
  {"Кондитеры","Confectioners"},
  {"Дисконтные магазины","Discount stores"},
  {"Образование и обучение","Education and training services"},
  {"Сельскохозяйственные продукты","Farm products"},
  {"Дистрибуция продуктов питания","Food distribution"},
  {"Продуктовые магазины","Grocery stores"},
  {"Товары для дома и быта","Household and personal products"},
  {"Упакованные продукты","Packaged foods"},
  {"Табак","Tobacco"},
  class=class="str">"cmt">//--- Energy
  {"Бурение нефтяных и газовых скважин","Oil and gas drilling"},
  {"Добыча и переработка нефти и газа","Oil and gas extraction and processing"},
  {"Нефтегазовое оборудование и услуги","Oil and gas equipment and services"},
  {"Интегрированные нефтегазовые компании","Oil and gas integrated"},
  {"Транспортировка нефти и газа","Oil and gas midstream"},
  {"Переработка и сбыт нефти и газа","Oil and gas refining and marketing"},
  {"Энергетический уголь","Thermal coal"},
  {"Уран","Uranium"}

「行业分类表的金融到工业段结构」

在 MT5 的标的元数据系统里,行业分类常以俄英双字符串的二维数组落地,每段用注释分隔大类。上面这段覆盖了 Finance、Healthcare、Industrials 三个板块,合计 44 个细分行业条目,可直接用于跨市场板块轮动扫描。 代码里每个花括号就是一条记录:前项是俄文原始名(数据源本地语言),后项是英文规范名(方便 EA 内部统一比对)。比如 "Банки - Региональные" 对应 "Banks - Regional",回测时若只认英文名,俄文段就是冗余但不可删——它是券商数据源的键。 实际用法:把这段贴进 string sectors[][2] 初始化,再用 StringFind() 做板块过滤。外汇和贵金属虽不在此分类内,但股指 CFD 与板块 ETF 联动时,这类映射能帮你快速隔离风险敞口,注意跨市场操作杠杆品种属高风险行为,仓位倾向轻配。

MQL5 / C++
  class=class="str">"cmt">//--- Finance
  {"Биржевой фонд","Exchange traded fund"},
  {"Управление активами","Assets management"},
  {"Банки - Диверсифицированные","Banks - Diversified"},
  {"Банки - Региональные","Banks - Regional"},
  {"Финансовые рынки","Capital markets"},
  {"Закрытый фонд - Долговые инструменты","Closed-End fund - Debt"},
  {"Закрытый фонд - Акции","Closed-end fund - Equity"},
  {"Закрытый фонд - Иностранные","Closed-end fund - Foreign"},
  {"Кредитные услуги","Credit services"},
  {"Финансовые конгломераты","Financial conglomerates"},
  {"Финансовые данные и биржи","Financial data and stock exchange"},
  {"Страховые брокеры","Insurance brokers"},
  {"Страхование - Диверсифицированные","Insurance - Diversified"},
  {"Страхование - Жизнь","Insurance - Life"},
  {"Страхование - Недвижимость и несчастные случаи","Insurance - Property and casualty"},
  {"Страхование - Перестрахование","Insurance - Reinsurance"},
  {"Страхование - Специальное","Insurance - Specialty"},
  {"Ипотечное финансирование","Mortgage finance"},
  {"Шелл-компании","Shell companies"},
  class=class="str">"cmt">//--- Healthcare
  {"Биотехнологии","Biotechnology"},
  {"Диагностика и исследования","Diagnostics and research"},
  {"Фармацевтическое производство - Общее","Drugs manufacturers - general"},
  {"Фармацевтическое производство - Специальное и дженерики","Drugs manufacturers - Specialty and generic"},
  {"Планы здравоохранения","Healthcare plans"},
  {"Информационные службы здравоохранения","Health information services"},
  {"Медицинские учреждения","Medical care facilities"},
  {"Медицинское оборудование","Medical devices"},
  {"Медицинские дистрибьюторы","Medical distribution"},
  {"Медицинские инструменты и расходные материалы","Medical instruments and supplies"},
  {"Фармацевтические ритейлеры","Pharmaceutical retailers"},
  class=class="str">"cmt">//--- Industrials
  {"Аэрокосмическая и оборонная промышленность","Aerospace and defense"},
  {"Авиакомпании","Airlines"},
  {"Аэропорты и воздушные перевозки","Airports and air services"},
  {"Строительные материалы и оборудование","Building products and equipment"},
  {"Деловое оборудование и материалы","Business equipment and supplies"},
  {"Конгломераты","Conglomerates"},
  {"Консалтинговые услуги","Consulting services"},
  {"Электрооборудование и запчасти","Electrical equipment and parts"},
  {"Инженерное дело и строительство","Engineering and construction"},
  {"Сельскохозяйственное и тяжелое строительное оборудование","Farm and heavy construction machinery"},
  {"Промышленные дистрибьюторы","Industrial distribution"},
  {"Инфраструктурные операции","Infrastructure operations"},
  {"Интегрированные перевозки и логистика","Integrated freight and logistics"},
  {"Морские перевозки","Marine shipping"},
  {"Металлопроизводство","Metal fabrication"},
  {"Контроль загрязнения и очистка","Pollution and treatment controls"}

行业分类映射表的后半段怎么填

在构建跨市场扫描器时,行业标签的中俄双语文段往往直接以结构体数组形式硬编码进 EA。上面这段覆盖了从铁路、租赁、安防,到房地产 REIT 细分(含医疗、酒店、工业、抵押等 11 个子项),再到科技与公用事业三大类的映射,总计约 47 个行业键值对。 注意 //--- Real estate 这类注释不是摆设,它们是后续用 StringFind() 切分板块的锚点;若你要在 MT5 里复刻,删掉注释会导致分类遍历逻辑断点。 科技段里把「Солнечная энергетика」译成 Solar 而非归到公用事业,说明原表作者把光伏单独拎出来跟踪——这对贵金属交易者也有用,因为太阳能需求会牵引银价波动,属于间接相关板块。 公用事业末段的「Регулируемые водоканалы」对应 Utilities - Regulated water,是整张表最后一个叶子节点;复制时漏掉结尾分号会在编译期报 'expected ;' 错误,开 MT5 直接测一遍最稳妥。

MQL5 / C++
  {"Железные дороги","Railroads"},
  {"Аренда и лизинг","Rental and leasing services"},
  {"Безопасность и защита","Security and protection services"},
  {"Специализированные бизнес-услуги","Specialty business services"},
  {"Специализированные промышленные машины","Specialty industrial machinery"},
  {"Услуги по трудоустройству","Stuffing and employment services"},
  {"Инструменты и инвентарь","Tools and accessories"},
  {"Грузоперевозки","Trucking"},
  {"Управление отходами","Waste management"},
  class=class="str">"cmt">//--- Real estate
  {"Недвижимость - Строительство","Real estate - Development"},
  {"Недвижимость - Диверсифицированные","Real estate - Diversified"},
  {"Услуги в сфере недвижимости","Real estate services"},
  {"Инвестиционный фонд недвижимости - Диверсифицированные","REIT - Diversified"},
  {"Инвестиционный фонд недвижимости - Медицинские учреждения","REIT - Healthcase facilities"},
  {"Инвестиционный фонд недвижимости - Отели","REIT - Hotel and motel"},
  {"Инвестиционный фонд недвижимости - Промышленность","REIT - Industrial"},
  {"Инвестиционный фонд недвижимости - Ипотека","REIT - Mortgage"},
  {"Инвестиционный фонд недвижимости - Офисы","REIT - Office"},
  {"Инвестиционный фонд недвижимости - Жилые помещения","REIT - Residential"},
  {"Инвестиционный фонд недвижимости - Розница","REIT - Retail"},
  {"Инвестиционный фонд недвижимости - Специальные помещения","REIT - Specialty"},
  class=class="str">"cmt">//--- Technology
  {"Коммуникационное оборудование","Communication equipment"},
  {"Компьютерное оборудование","Computer hardware"},
  {"Бытовая электроника","Consumer electronics"},
  {"Электронные компоненты","Electronic components"},
  {"Дистрибуция электроники и компьютеров","Electronics and computer distribution"},
  {"Услуги информационных технологий","Information technology services"},
  {"Научно-технические инструменты","Scientific and technical instruments"},
  {"Полупроводниковое оборудование и материалы","Semiconductor equipment and materials"},
  {"Полупроводники","Semiconductors"},
  {"Программное обеспечение - Приложения","Software - Application"},
  {"Программное обеспечение - Инфраструктура","Software - Infrastructure"},
  {"Солнечная энергетика","Solar"},
  class=class="str">"cmt">//--- Utilities
  {"Коммунальные предприятия - Диверсифицированные","Utilities - Diversified"},
  {"Коммунальные предприятия - Независимые производители энергии","Utilities - Independent power producers"},
  {"Коммунальные предприятия - Возобновляемая энергия","Utilities - Renewable"},
  {"Коммунальные предприятия - Регулируемые электрические компании","Utilities - Regulated electric"},
  {"Коммунальные предприятия - Регулируемые газовые компании","Utilities - Regulated gas"},
  {"Коммунальные предприятия - Регулируемые водоканалы","Utilities - Regulated water"},

class=class="str">"cmt">//--- CAccount
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Symbol integer properties                                        |
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 整数型符号属性的枚举清单

在 MT5 的底层接口里,整数类符号属性由 ENUM_SYMBOL_PROP_INTEGER 枚举统一定义,写 EA 或指标时直接引用这些常量比硬编码数字稳妥得多。 下面这段枚举列出了从状态到报价精度的一系列关键字段:SYMBOL_PROP_STATUS 取 0 表示符号状态;SYMBOL_PROP_INDEX_MW 是品种在行情窗口中的序号;SYMBOL_PROP_SECTOR 与 SYMBOL_PROP_INDUSTRY 标注资产所属经济板块与行业,对跨品种相关性过滤有用;SYMBOL_PROP_TIME_MSC 给出最近报价的毫秒级时间戳,比 SYMBOL_PROP_TIME 的秒级粒度更适合做高频去重。 实际调试时,SYMBOL_PROP_DIGITS 决定小数位(如 XAUUSD 常见为 2,部分券商报价达 3),直接读取可避免手填点值出错。外汇与贵金属杠杆高、滑点突变频繁,用这些属性做运行时校验,可能降低因品种参数误配导致的异常平仓概率。

MQL5 / C++
enum ENUM_SYMBOL_PROP_INTEGER
  {
   SYMBOL_PROP_STATUS = class="num">0,                                                              class=class="str">"cmt">// Symbol status
   SYMBOL_PROP_INDEX_MW,                                                                class=class="str">"cmt">// Symbol index in the Market Watch window
   SYMBOL_PROP_SECTOR,                                                                  class=class="str">"cmt">// Sector of the economy the asset belongs to
   SYMBOL_PROP_INDUSTRY,                                                                class=class="str">"cmt">// Industry or the economy branch the symbol belongs to
   SYMBOL_PROP_CUSTOM,                                                                  class=class="str">"cmt">// Custom symbol flag
   SYMBOL_PROP_CHART_MODE,                                                              class=class="str">"cmt">// The price type used for generating bars – Bid or Last(from the ENUM_SYMBOL_CHART_MODE enumeration)
   SYMBOL_PROP_EXIST,                                                                   class=class="str">"cmt">// Flag indicating that the symbol under this name exists
   SYMBOL_PROP_SELECT,                                                                  class=class="str">"cmt">// The indication that the symbol is selected in Market Watch
   SYMBOL_PROP_VISIBLE,                                                                 class=class="str">"cmt">// The indication that the symbol is displayed in Market Watch
   SYMBOL_PROP_SESSION_DEALS,                                                           class=class="str">"cmt">// The number of deals in the current session 
   SYMBOL_PROP_SESSION_BUY_ORDERS,                                                      class=class="str">"cmt">// The total number of Buy orders at the moment
   SYMBOL_PROP_SESSION_SELL_ORDERS,                                                     class=class="str">"cmt">// The total number of Sell orders at the moment
   SYMBOL_PROP_VOLUME,                                                                  class=class="str">"cmt">// Last deal volume
   SYMBOL_PROP_VOLUMEHIGH,                                                              class=class="str">"cmt">// Maximum volume within a day
   SYMBOL_PROP_VOLUMELOW,                                                               class=class="str">"cmt">// Minimum volume within a day
   SYMBOL_PROP_TIME,                                                                    class=class="str">"cmt">// Latest quote time
   SYMBOL_PROP_TIME_MSC,                                                                class=class="str">"cmt">// Time of the last quote in milliseconds
   SYMBOL_PROP_DIGITS                                                                   class=class="str">"cmt">// Number of decimal places
  };

「品种交易属性里藏着的下单暗礁」

在 MT5 里用 SymbolInfoInteger 取品种属性时,有一批枚举值直接决定你能不能下单、挂单距离够不够、隔夜费怎么收。很多 EA 在跨品种跑崩,不是逻辑错,而是没读这些字段就硬编码了参数。 SYMBOL_PROP_TRADE_STOPS_LEVEL 返回的是当前价到止损单的最小点数距离,欧美可能只有 0 点,某些黄金经纪商能到 150 点以上;SYMBOL_PROP_TRADE_FREEZE_LEVEL 则是价格触碰后禁止修改平仓的冻结距离,不读它去改单会直接报 10014 错误。 SYMBOL_PROP_TRADE_EXEMODE 给出成交执行模式(市价、交易所、请求等),SYMBOL_PROP_FILLING_MODE 和 SYMBOL_PROP_ORDER_MODE 分别限定了允许的填单与挂单类型。外汇贵金属高杠杆高风险,忽略这些属性在新闻行情里吃滑点是概率极高的。 开 MT5 按 F4 把下面这段枚举片段塞进观察窗口,对着 XAUUSD 和 EURUSD 各读一次,差距会立刻显现。

MQL5 / C++
  SYMBOL_PROP_DIGITS_LOTS,                                                                     class=class="str">"cmt">// Number of decimal places for a lot
  SYMBOL_PROP_SPREAD,                                                                           class=class="str">"cmt">// Spread in points
  SYMBOL_PROP_SPREAD_FLOAT,                                                                     class=class="str">"cmt">// Floating spread flag
  SYMBOL_PROP_TICKS_BOOKDEPTH,                                                                  class=class="str">"cmt">// Maximum number of orders displayed in the Depth of Market
  SYMBOL_PROP_BOOKDEPTH_STATE,                                                                  class=class="str">"cmt">// Flag of subscription to DOM
  SYMBOL_PROP_TRADE_CALC_MODE,                                                                  class=class="str">"cmt">// Contract price calculation method(from the ENUM_SYMBOL_CALC_MODE enumeration)
  SYMBOL_PROP_TRADE_MODE,                                                                       class=class="str">"cmt">// Order execution type(from the ENUM_SYMBOL_TRADE_MODE enumeration)
  SYMBOL_PROP_START_TIME,                                                                       class=class="str">"cmt">// Symbol trading start date(usually used for futures)
  SYMBOL_PROP_EXPIRATION_TIME,                                                                  class=class="str">"cmt">// Symbol trading end date(usually used for futures)
  SYMBOL_PROP_TRADE_STOPS_LEVEL,                                                                class=class="str">"cmt">// Minimum distance in points from the current close price for setting Stop orders
  SYMBOL_PROP_TRADE_FREEZE_LEVEL,                                                               class=class="str">"cmt">// Freeze distance for trading operations(in points)
  SYMBOL_PROP_TRADE_EXEMODE,                                                                    class=class="str">"cmt">// Deal execution mode(from the ENUM_SYMBOL_TRADE_EXECUTION enumeration)
  SYMBOL_PROP_SWAP_MODE,                                                                        class=class="str">"cmt">// Swap calculation model(from the ENUM_SYMBOL_SWAP_MODE enumeration)
  SYMBOL_PROP_SWAP_ROLLOVER3DAYS,                                                               class=class="str">"cmt">// Triple-day swap(from the ENUM_DAY_OF_WEEK enumeration)
  SYMBOL_PROP_MARGIN_HEDGED_USE_LEG,                                                            class=class="str">"cmt">// Calculating hedging margin using the larger leg(Buy or Sell)
  SYMBOL_PROP_EXPIRATION_MODE,                                                                  class=class="str">"cmt">// Flags of allowed order expiration modes
  SYMBOL_PROP_FILLING_MODE,                                                                     class=class="str">"cmt">// Flags of allowed order filling modes
  SYMBOL_PROP_ORDER_MODE,                                                                       class=class="str">"cmt">// Flags of allowed order types
  SYMBOL_PROP_ORDER_GTC_MODE                                                                    class=class="str">"cmt">// Expiration of Stop Loss and Take Profit orders if SYMBOL_EXPIRATION_MODE=SYMBOL_EXPIRATION_GTC(from the ENUM_SYMBOL_ORDER_GTC_MODE enumeration)

枚举里的整数与双精度属性分界

MT5 在符号属性枚举上把整数型和双精度型拆成两套 enum,中间用宏做硬衔接。整数属性总数被定为 40(SYMBOL_PROP_INTEGER_TOTAL),其中 1 个不参与市场报价窗排序(SYMBOL_PROP_INTEGER_SKIP),这意味着实际可用于排序的整数字段是 39 个。 双精度属性枚举 ENUM_SYMBOL_PROP_DOUBLE 的起始值直接接在整数总数后面:SYMBOL_PROP_BID = SYMBOL_PROP_INTEGER_TOTAL,也就是从 40 开始排。这样设计让 SymbolInfoInteger 和 SymbolInfoDouble 两套函数能共用同一属性索引空间而不撞车。 报价类双精度字段覆盖了 Bid/Ask/Last 三组及各自当日高低:SYMBOL_PROP_BIDHIGH、SYMBOL_PROP_ASKLOW 等。做日内振幅统计时,直接读这些属性比自己维护高低点数组更省事,且数据来自终端内核、延迟更低。外汇与贵金属杠杆品种波动剧烈,用这些属性做阈值触发时须自行评估滑点与断崖风险。

MQL5 / C++
enum ENUM_SYMBOL_PROP_DOUBLE
  {
  SYMBOL_PROP_BID = SYMBOL_PROP_INTEGER_TOTAL,  class=class="str">"cmt">// Bid - the best price at which a symbol can be sold
  SYMBOL_PROP_BIDHIGH,                          class=class="str">"cmt">// The highest Bid price of the day
  SYMBOL_PROP_BIDLOW,                           class=class="str">"cmt">// The lowest Bid price of the day
  SYMBOL_PROP_ASK,                              class=class="str">"cmt">// Ask - best price, at which an instrument can be bought
  SYMBOL_PROP_ASKHIGH,                          class=class="str">"cmt">// The highest Ask price of the day
  SYMBOL_PROP_ASKLOW,                           class=class="str">"cmt">// The lowest Ask price of the day
  SYMBOL_PROP_LAST,                             class=class="str">"cmt">// The price at which the last deal was executed
  SYMBOL_PROP_LASTHIGH,                         class=class="str">"cmt">// The highest Last price of the day
  SYMBOL_PROP_LASTLOW,                          class=class="str">"cmt">// The lowest Last price of the day
  SYMBOL_PROP_VOLUME_REAL,                      class=class="str">"cmt">// Volume of the day
  SYMBOL_PROP_VOLUMEHIGH_REAL,                  class=class="str">"cmt">// Maximum Volume within a day
  SYMBOL_PROP_VOLUMELOW_REAL,                   class=class="str">"cmt">// Minimum Volume within a day
  };

◍ 品种属性枚举里的保证金与掉期字段

在 MT5 的 ENUM_SYMBOL_INFO_DOUBLE / INTEGER 属性体系里,有一批以 SYMBOL_PROP_ 开头的枚举值专门暴露合约的微观参数。上面列出的片段覆盖了期权执行价、点值、Tick 价值、合约大小、应计利息、债券面值、流动性率,以及交易量限制与掉期、保证金系列。 其中 SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT 与 SYMBOL_PROP_TRADE_TICK_VALUE_LOSS 是分开计算的:盈利单和亏损单每个 Tick 的账户币种价值可能不同,这在交叉盘或贵金属上尤其要手动核对。 SYMBOL_PROP_VOLUME_LIMIT 限制的是同一方向(只买或只卖)已开仓加挂单的总量,不是多空合计;写 EA 风控时若忽略这点,可能在扩容加仓逻辑里误触交易拒绝。 保证金相关字段分了总表和定向表:SYMBOL_PROP_MARGIN_INITIAL 是通用初始保证金,而 SYMBOL_PROP_MARGIN_LONG_INITIAL、SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL、SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL 则把多单和两类买入挂单拆开算。外汇与贵金属杠杆波动大、高风险,实盘前应在 MT5 品种规格窗口逐项比对这几个返回值。

MQL5 / C++
  SYMBOL_PROP_OPTION_STRIKE,                                                                     class=class="str">"cmt">// Option execution price
  SYMBOL_PROP_POINT,                                                                               class=class="str">"cmt">// One point value
  SYMBOL_PROP_TRADE_TICK_VALUE,                                                                    class=class="str">"cmt">// SYMBOL_TRADE_TICK_VALUE_PROFIT value
  SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT,                                                             class=class="str">"cmt">// Calculated tick value for a winning position
  SYMBOL_PROP_TRADE_TICK_VALUE_LOSS,                                                               class=class="str">"cmt">// Calculated tick value for a losing position
  SYMBOL_PROP_TRADE_TICK_SIZE,                                                                     class=class="str">"cmt">// Minimum price change
  SYMBOL_PROP_TRADE_CONTRACT_SIZE,                                                                 class=class="str">"cmt">// Trade contract size
  SYMBOL_PROP_TRADE_ACCRUED_INTEREST,                                                              class=class="str">"cmt">// Accrued interest
  SYMBOL_PROP_TRADE_FACE_VALUE,                                                                    class=class="str">"cmt">// Face value – initial bond value set by an issuer
  SYMBOL_PROP_TRADE_LIQUIDITY_RATE,                                                                class=class="str">"cmt">// Liquidity rate – the share of an asset that can be used for a margin
  SYMBOL_PROP_VOLUME_MIN,                                                                          class=class="str">"cmt">// Minimum volume for a deal
  SYMBOL_PROP_VOLUME_MAX,                                                                          class=class="str">"cmt">// Maximum volume for a deal
  SYMBOL_PROP_VOLUME_STEP,                                                                         class=class="str">"cmt">// Minimum volume change step for deal execution
  SYMBOL_PROP_VOLUME_LIMIT,                                                                        class=class="str">"cmt">// The maximum allowed total volume of an open position and pending orders in one direction(either buy or sell)
  SYMBOL_PROP_SWAP_LONG,                                                                           class=class="str">"cmt">// Long swap value
  SYMBOL_PROP_SWAP_SHORT,                                                                          class=class="str">"cmt">// Short swap value
  SYMBOL_PROP_MARGIN_INITIAL,                                                                      class=class="str">"cmt">// Initial margin
  SYMBOL_PROP_MARGIN_MAINTENANCE,                                                                  class=class="str">"cmt">// Maintenance margin for an instrument
  SYMBOL_PROP_MARGIN_LONG_INITIAL,                                                                 class=class="str">"cmt">// Initial margin requirement applicable to class="type">long positions
  SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL,                                                             class=class="str">"cmt">// Initial margin requirement applicable to BuyStop orders
  SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL                                                            class=class="str">"cmt">// Initial margin requirement applicable to BuyLimit orders

「保证金与盘面会话属性的枚举落地」

在 MT5 的 SymbolInfoInteger / SymbolInfoDouble 体系里,保证金和会话类属性被拆得很细。光是初始保证金就区分了多头、空头、以及 BuyStop / BuyLimit / BuyStopLimit / SellStop / SellLimit / SellStopLimit 七种挂单类型,维护保证金同理分了多空与四类挂单。 下面这段枚举节选直接对应了这些属性常量,复制进 MT5 头文件就能用 SymbolInfoDouble(symbol, 常量) 取到实时值。外汇与贵金属杠杆波动大,同一品种在不同经纪商下的 INITIAL 与 MAINTENANCE 可能差出数倍,属于高风险参数,调仓前务必本地核实。 会话类属性则给了盘口微观结构视角:SESSION_VOLUME 是当节成交总量,SESSION_INTEREST 是未平敞口,SESSION_BUY_ORDERS_VOLUME 与 SELL_ORDERS_VOLUME 是瞬时买卖挂单累计。结合 SESSION_OPEN / CLOSE / AW(加权平均价),可以算出当前会话的被动成交占比,倾向用来识别流动性真空时段。

MQL5 / C++
  SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL,              class=class="str">"cmt">// Initial margin requirement applicable to BuyStopLimit orders
  SYMBOL_PROP_MARGIN_LONG_MAINTENANCE,                    class=class="str">"cmt">// Maintenance margin requirement applicable to class="type">long positions
  SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE,                class=class="str">"cmt">// Maintenance margin requirement applicable to BuyStop orders
  SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE,               class=class="str">"cmt">// Maintenance margin requirement applicable to BuyLimit orders
  SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE,           class=class="str">"cmt">// Maintenance margin requirement applicable to BuyStopLimit orders
  SYMBOL_PROP_MARGIN_SHORT_INITIAL,                       class=class="str">"cmt">// Initial margin requirement applicable to class="type">class="kw">short positions
  SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL,                   class=class="str">"cmt">// Initial margin requirement applicable to SellStop orders
  SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL,                  class=class="str">"cmt">// Initial margin requirement applicable to SellLimit orders
  SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL,              class=class="str">"cmt">// Initial margin requirement applicable to SellStopLimit orders
  SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE,                   class=class="str">"cmt">// Maintenance margin requirement applicable to class="type">class="kw">short positions
  SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE,               class=class="str">"cmt">// Maintenance margin requirement applicable to SellStop orders
  SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE,              class=class="str">"cmt">// Maintenance margin requirement applicable to SellLimit orders
  SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE,          class=class="str">"cmt">// Maintenance margin requirement applicable to SellStopLimit orders
  SYMBOL_PROP_SESSION_VOLUME,                             class=class="str">"cmt">// The total volume of deals in the current session
  SYMBOL_PROP_SESSION_TURNOVER,                           class=class="str">"cmt">// The total turnover in the current session
  SYMBOL_PROP_SESSION_INTEREST,                           class=class="str">"cmt">// The total volume of open positions
  SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,                  class=class="str">"cmt">// The total volume of Buy orders at the moment
  SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,                 class=class="str">"cmt">// The total volume of Sell orders at the moment
  SYMBOL_PROP_SESSION_OPEN,                               class=class="str">"cmt">// Open price of the session
  SYMBOL_PROP_SESSION_CLOSE,                              class=class="str">"cmt">// Close price of the session
  SYMBOL_PROP_SESSION_AW,                                 class=class="str">"cmt">// The average weighted price of the session
  SYMBOL_PROP_SESSION_PRICE_SETTLEMENT,                   class=class="str">"cmt">// The settlement price of the current session
  SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN                     class=class="str">"cmt">// Minimum allowable price value for the session

抓合约属性里的日内波动与期权希腊值

MT5 的 SYMBOL_PROP_DOUBLE 枚举里有一组容易被忽略的实时字段,直接反映品种当日的统计与衍生品风险状态。比如 SYMBOL_PROP_PRICE_CHANGE 给出当前价相对前一交易日收盘的涨跌百分比,SYMBOL_PROP_PRICE_VOLATILITY 则是该品种当时的波动率百分比,两者都是盯盘时判断异动的可验证数据点。 对于带期权属性的品种,枚举还暴露了全套希腊值:DELTA 衡量标的价变动 1 单位时期权价的变动,THETA 是到期临近每天流失的点数,GAMMA 描述 DELTA 的变化速率,VEGA 对应波动率每变 1% 的期权价变动,RHO 是利率变 1% 的敏感度,OMEGA 为弹性,SENSITIVITY 则反推标的需动几点才令期权动 1 点。 宏定义给出硬数字:SYMBOL_PROP_DOUBLE_TOTAL 为 68,即双精度属性共 68 个;SYMBOL_PROP_DOUBLE_SKIP 为 0,代表没有跳过不参与排序的双精度属性。开 MT5 在 MetaEditor 搜 ENUM_SYMBOL_PROP_DOUBLE 就能逐行核对,外汇与贵金属杠杆高,这些数值仅作风险参考,实际敞口可能迅速放大。

MQL5 / C++
  SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX,                 class=class="str">"cmt">// Maximum allowable price value for the session
  SYMBOL_PROP_MARGIN_HEDGED,                             class=class="str">"cmt">// Size of a contract or margin for one lot of hedged positions(oppositely directed positions at one symbol).
  SYMBOL_PROP_PRICE_CHANGE,                              class=class="str">"cmt">// Change of the current price relative to the end of the previous trading day in %
  SYMBOL_PROP_PRICE_VOLATILITY,                          class=class="str">"cmt">// Price volatility in %
  SYMBOL_PROP_PRICE_THEORETICAL,                         class=class="str">"cmt">// Theoretical option price
  SYMBOL_PROP_PRICE_DELTA,                               class=class="str">"cmt">// Option/warrant delta. Shows the value the option price changes by, when the underlying asset price changes by class="num">1
  SYMBOL_PROP_PRICE_THETA,                               class=class="str">"cmt">// Option/warrant theta. Number of points the option price is to lose every day due to a temporary breakup, i.e. when the expiration date approaches
  SYMBOL_PROP_PRICE_GAMMA,                               class=class="str">"cmt">// Option/warrant gamma. Shows the change rate of delta – how quickly or slowly the option premium changes
  SYMBOL_PROP_PRICE_VEGA,                                class=class="str">"cmt">// Option/warrant vega. Shows the number of points the option price changes by when the volatility changes by class="num">1%
  SYMBOL_PROP_PRICE_RHO,                                 class=class="str">"cmt">// Option/warrant rho. Reflects the sensitivity of the theoretical option price to the interest rate changing by class="num">1%
  SYMBOL_PROP_PRICE_OMEGA,                               class=class="str">"cmt">// Option/warrant omega. Option elasticity – a relative percentage change of the option price by the percentage change of the underlying asset price
  SYMBOL_PROP_PRICE_SENSITIVITY,                         class=class="str">"cmt">// Option/warrant sensitivity. Shows by how many points the price of the option&class="macro">#x27;s underlying asset should change so that the price of the option changes by one point
  };

class="macro">#define SYMBOL_PROP_DOUBLE_TOTAL(class="num">68)                 class=class="str">"cmt">// Total number of real properties
class="macro">#define SYMBOL_PROP_DOUBLE_SKIP(class="num">0)                  class=class="str">"cmt">// Number of real symbol properties not used in sorting

enum ENUM_SYMBOL_PROP_STRING
  {
  SYMBOL_PROP_NAME = (SYMBOL_PROP_INTEGER_TOTAL+SYMBOL_PROP_DOUBLE_TOTAL),   class=class="str">"cmt">// Symbol name

◍ 品种字符串属性的枚举边界

在 MT5 的 ENUM_SYMBOL_INFO_STRING 枚举里,除常见的基点资产、基础货币、盈利货币、保证金货币外,还包含报价源、描述、自定义品种公式、ISIN 码、信息页、符号树路径、分类、交易所等字段。 其中 SYMBOL_PROP_COUNTRY、SYMBOL_PROP_SECTOR_NAME、SYMBOL_PROP_INDUSTRY_NAME 用于标识衍生品所属国家与经济板块,对跨市场扫描外汇或贵金属关联标的时做过滤有用。 宏定义 SYMBOL_PROP_STRING_TOTAL 被显式设为 16,说明字符串类属性共 16 项;写 SymbolInfoString 循环遍历时,上限直接套这个宏可避免硬编数字。外汇与贵金属杠杆高,用这些属性做自动化分类时仍需人工核对品种风险。 另一个宏 FIRST_SYM_DBL_PROP 以 (SYMBOL_PROP_INTEGER_TOTAL - SYMBOL_PROP_INTEGER_SKIP) 计算,暗示双精度属性枚举起点由整型属性总数和跳过数推导,改头文件常量后编译期即重算偏移。

MQL5 / C++
  SYMBOL_PROP_BASIS,                                                                 class=class="str">"cmt">// Name of the underlaying asset for a derivative symbol
  SYMBOL_PROP_CURRENCY_BASE,                                                          class=class="str">"cmt">// Instrument base currency
  SYMBOL_PROP_CURRENCY_PROFIT,                                                        class=class="str">"cmt">// Profit currency
  SYMBOL_PROP_CURRENCY_MARGIN,                                                        class=class="str">"cmt">// Margin currency
  SYMBOL_PROP_BANK,                                                                   class=class="str">"cmt">// Source of the current quote
  SYMBOL_PROP_DESCRIPTION,                                                            class=class="str">"cmt">// String description of a symbol
  SYMBOL_PROP_FORMULA,                                                                class=class="str">"cmt">// The formula used for custom symbol pricing
  SYMBOL_PROP_ISIN,                                                                   class=class="str">"cmt">// The name of a trading symbol in the international system of securities identification numbers(ISIN)
  SYMBOL_PROP_PAGE,                                                                   class=class="str">"cmt">// The web page containing symbol information
  SYMBOL_PROP_PATH,                                                                   class=class="str">"cmt">// Location in the symbol tree
  SYMBOL_PROP_CATEGORY,                                                               class=class="str">"cmt">// Symbol category
  SYMBOL_PROP_EXCHANGE,                                                               class=class="str">"cmt">// Name of an exchange a symbol is traded on
  SYMBOL_PROP_COUNTRY,                                                                class=class="str">"cmt">// Country the trading instrument belongs to
  SYMBOL_PROP_SECTOR_NAME,                                                            class=class="str">"cmt">// Economy sector the trading instrument belongs to
  SYMBOL_PROP_INDUSTRY_NAME,                                                          class=class="str">"cmt">// Economy or industry branch the trading instrument belongs to
  };
class="macro">#define SYMBOL_PROP_STRING_TOTAL(class="num">16)                                               class=class="str">"cmt">// Total number of class="type">class="kw">string properties
class="macro">#define FIRST_SYM_DBL_PROP(SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP)

「枚举里塞满了排序维度」

在写多品种扫描器时,先别急着调策略,得想清楚按什么维度给品种排队。下面这段枚举把 MT5 里能拿来排序的符号属性几乎列全了:从市场状态、自选窗口序号,到板块行业分类,再到当前会话的买卖挂单量,粒度细到毫秒级报价时间都有。 实际盯盘里,如果你只关心外汇高波动时段,用 SORT_BY_SYMBOL_SESSION_DEALS 按当日成交笔数排,往往比按名称排更容易抓到异动品种;贵金属 XAUUSD 在风险事件下该值可能瞬间放大数倍。 注意 SORT_BY_SYMBOL_SECTOR 和 SORT_BY_SYMBOL_INDUSTRY 在部分券商品种上返回空值,排序前最好用 SymbolInfoInteger 探一下,避免整列塌掉。外汇与贵金属杠杆高、滑点大,任何排序结果都只是概率偏好,不是方向保证。

MQL5 / C++
class="macro">#define FIRST_SYM_STR_PROP(SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_INTEGER_SKIP+SYMBOL_PROP_DOUBLE_TOTAL-SYMBOL_PROP_DOUBLE_SKIP)
enum ENUM_SORT_SYMBOLS_MODE
  {
class=class="str">"cmt">//--- Sort by integer properties
   SORT_BY_SYMBOL_STATUS = class="num">0,                                                        class=class="str">"cmt">// Sort by symbol status
   SORT_BY_SYMBOL_INDEX_MW,                                                          class=class="str">"cmt">// Sort by index in the Market Watch window
   SORT_BY_SYMBOL_SECTOR,                                                            class=class="str">"cmt">// Sort by economic sector
   SORT_BY_SYMBOL_INDUSTRY,                                                          class=class="str">"cmt">// Sort by industry or economy sector
   SORT_BY_SYMBOL_CUSTOM,                                                            class=class="str">"cmt">// Sort by custom symbol class="kw">property
   SORT_BY_SYMBOL_CHART_MODE,                                                        class=class="str">"cmt">// Sort by price type for constructing bars – Bid or Last(from the ENUM_SYMBOL_CHART_MODE enumeration)
   SORT_BY_SYMBOL_EXIST,                                                             class=class="str">"cmt">// Sort by the flag that a symbol with such a name exists
   SORT_BY_SYMBOL_SELECT,                                                            class=class="str">"cmt">// Sort by the flag indicating that a symbol is selected in Market Watch
   SORT_BY_SYMBOL_VISIBLE,                                                           class=class="str">"cmt">// Sort by the flag indicating that a selected symbol is displayed in Market Watch
   SORT_BY_SYMBOL_SESSION_DEALS,                                                     class=class="str">"cmt">// Sort by the number of deals in the current session 
   SORT_BY_SYMBOL_SESSION_BUY_ORDERS,                                                class=class="str">"cmt">// Sort by the total number of current buy orders
   SORT_BY_SYMBOL_SESSION_SELL_ORDERS,                                               class=class="str">"cmt">// Sort by the total number of current sell orders
   SORT_BY_SYMBOL_VOLUME,                                                            class=class="str">"cmt">// Sort by last deal volume
   SORT_BY_SYMBOL_VOLUMEHIGH,                                                        class=class="str">"cmt">// Sort by maximum volume for a day
   SORT_BY_SYMBOL_VOLUMELOW,                                                         class=class="str">"cmt">// Sort by minimum volume for a day
   SORT_BY_SYMBOL_TIME,                                                              class=class="str">"cmt">// Sort by the last quote time
   SORT_BY_SYMBOL_TIME_MSC,                                                          class=class="str">"cmt">// Sort by the last quote time in milliseconds
   SORT_BY_SYMBOL_DIGITS,                                                            class=class="str">"cmt">// Sort by a number of decimal places

按交易属性给品种排序的枚举清单

在 MT5 的 Symbols 集合筛选与排序接口里,有一组以 SORT_BY_SYMBOL_ 开头的枚举常量,专门用来对交易品种按合约属性做排序,而不是按价格或时间。 下面这段枚举直接列出了可排序的维度:手数小数位、点差与点差浮动标记、市场深度最大展示笔数与 DOM 订阅状态、合约计价方式、订单执行类型、期货起止交易日、止损最小距离与冻结距离、执行模式、掉期计算模型与三倍掉期日、对冲保证金按大边计算标记、过期/成交/挂单类型与 GTC 模式、期权类型等。 实盘写 EA 时,如果你要批量扫描跨品种机会,用这些排序枚举比自己写比较函数更省事;比如优先挑点差小、冻结水平低的品种,可能降低滑点与被拒单概率。外汇与贵金属波动剧烈,高杠杆下风险显著,排序只是预处理,不等于信号。

MQL5 / C++
  SORT_BY_SYMBOL_DIGITS_LOT,                                                                       class=class="str">"cmt">// Sort by a number of decimal places in a lot
  SORT_BY_SYMBOL_SPREAD,                                                                             class=class="str">"cmt">// Sort by spread in points
  SORT_BY_SYMBOL_SPREAD_FLOAT,                                                                       class=class="str">"cmt">// Sort by floating spread
  SORT_BY_SYMBOL_TICKS_BOOKDEPTH,                                                                    class=class="str">"cmt">// Sort by a maximum number of requests displayed in the market depth
  SORT_BY_SYMBOL_BOOKDEPTH_STATE,                                                                    class=class="str">"cmt">// Sort by the DOM subscription flag
  SORT_BY_SYMBOL_TRADE_CALC_MODE,                                                                    class=class="str">"cmt">// Sort by contract price calculation method(from the ENUM_SYMBOL_CALC_MODE enumeration)
  SORT_BY_SYMBOL_TRADE_MODE,                                                                         class=class="str">"cmt">// Sort by order execution type(from the ENUM_SYMBOL_TRADE_MODE enumeration)
  SORT_BY_SYMBOL_START_TIME,                                                                         class=class="str">"cmt">// Sort by an instrument trading start date(usually used for futures)
  SORT_BY_SYMBOL_EXPIRATION_TIME,                                                                    class=class="str">"cmt">// Sort by an instrument trading end date(usually used for futures)
  SORT_BY_SYMBOL_TRADE_STOPS_LEVEL,                                                                  class=class="str">"cmt">// Sort by the minimum indent from the current close price(in points) for setting Stop orders
  SORT_BY_SYMBOL_TRADE_FREEZE_LEVEL,                                                                 class=class="str">"cmt">// Sort by trade operation freeze distance(in points)
  SORT_BY_SYMBOL_TRADE_EXEMODE,                                                                      class=class="str">"cmt">// Sort by trade execution mode(from the ENUM_SYMBOL_TRADE_EXECUTION enumeration)
  SORT_BY_SYMBOL_SWAP_MODE,                                                                          class=class="str">"cmt">// Sort by swap calculation model(from the ENUM_SYMBOL_SWAP_MODE enumeration)
  SORT_BY_SYMBOL_SWAP_ROLLOVER3DAYS,                                                                 class=class="str">"cmt">// Sort by week day for accruing a triple swap(from the ENUM_DAY_OF_WEEK enumeration)
  SORT_BY_SYMBOL_MARGIN_HEDGED_USE_LEG,                                                              class=class="str">"cmt">// Sort by the calculation mode of a hedged margin using the larger leg(Buy or Sell)
  SORT_BY_SYMBOL_EXPIRATION_MODE,                                                                    class=class="str">"cmt">// Sort by flags of allowed order expiration modes
  SORT_BY_SYMBOL_FILLING_MODE,                                                                       class=class="str">"cmt">// Sort by flags of allowed order filling modes
  SORT_BY_SYMBOL_ORDER_MODE,                                                                         class=class="str">"cmt">// Sort by flags of allowed order types
  SORT_BY_SYMBOL_ORDER_GTC_MODE,                                                                     class=class="str">"cmt">// Sort by StopLoss and TakeProfit orders lifetime
  SORT_BY_SYMBOL_OPTION_MODE                                                                         class=class="str">"cmt">// Sort by option type(from the ENUM_SYMBOL_OPTION_MODE enumeration)

◍ 市场报价窗的实值排序枚举

在 MT5 的行情观察窗口里,程序化排序依赖一组枚举常量,其中从 SORT_BY_SYMBOL_BID 开始的区段专门对应真实行情属性,而非字符串类的名称或期权方向。 这些枚举覆盖了买卖价、成交价、当日高低、成交量以及合约相关的tick价值。比如 SORT_BY_SYMBOL_BIDLOW 指向当日最低买价,SORT_BY_SYMBOL_VOLUMEHIGH_REAL 指向当日最大成交量,写排序脚本时直接传这些常量就能按实值重排品种列表。 需要留意的是,SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT 与 _LOSS 分别对应盈利持仓和亏损持仓下计算出的单tick价格,这在多品种批量测算保证金占用时比单纯看点值更贴近实际。外汇与贵金属杠杆高,排序只是观察手段,不构成方向判断。

MQL5 / C++
  SORT_BY_SYMBOL_OPTION_RIGHT,                                                                     class=class="str">"cmt">// Sort by option right(Call/Put) (from the ENUM_SYMBOL_OPTION_RIGHT enumeration)
class=class="str">"cmt">//--- Sort by real properties
  SORT_BY_SYMBOL_BID = FIRST_SYM_DBL_PROP,                                                          class=class="str">"cmt">// Sort by Bid
  SORT_BY_SYMBOL_BIDHIGH,                                                                            class=class="str">"cmt">// Sort by maximum Bid for a day
  SORT_BY_SYMBOL_BIDLOW,                                                                             class=class="str">"cmt">// Sort by minimum Bid for a day
  SORT_BY_SYMBOL_ASK,                                                                                class=class="str">"cmt">// Sort by Ask
  SORT_BY_SYMBOL_ASKHIGH,                                                                            class=class="str">"cmt">// Sort by maximum Ask for a day
  SORT_BY_SYMBOL_ASKLOW,                                                                             class=class="str">"cmt">// Sort by minimum Ask for a day
  SORT_BY_SYMBOL_LAST,                                                                               class=class="str">"cmt">// Sort by the last deal price
  SORT_BY_SYMBOL_LASTHIGH,                                                                           class=class="str">"cmt">// Sort by maximum Last for a day
  SORT_BY_SYMBOL_LASTLOW,                                                                            class=class="str">"cmt">// Sort by minimum Last for a day
  SORT_BY_SYMBOL_VOLUME_REAL,                                                                        class=class="str">"cmt">// Sort by Volume for a day
  SORT_BY_SYMBOL_VOLUMEHIGH_REAL,                                                                    class=class="str">"cmt">// Sort by maximum Volume for a day
  SORT_BY_SYMBOL_VOLUMELOW_REAL,                                                                     class=class="str">"cmt">// Sort by minimum Volume for a day
  SORT_BY_SYMBOL_OPTION_STRIKE,                                                                      class=class="str">"cmt">// Sort by an option execution price
  SORT_BY_SYMBOL_POINT,                                                                              class=class="str">"cmt">// Sort by a single point value
  SORT_BY_SYMBOL_TRADE_TICK_VALUE,                                                                   class=class="str">"cmt">// Sort by SYMBOL_TRADE_TICK_VALUE_PROFIT value
  SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT,                                                            class=class="str">"cmt">// Sort by a calculated tick price for a profitable position
  SORT_BY_SYMBOL_TRADE_TICK_VALUE_LOSS,                                                              class=class="str">"cmt">// Sort by a calculated tick price for a loss-making position
  SORT_BY_SYMBOL_TRADE_TICK_SIZE,                                                                    class=class="str">"cmt">// Sort by a minimum price change
  SORT_BY_SYMBOL_TRADE_CONTRACT_SIZE,                                                                class=class="str">"cmt">// Sort by a trading contract size
  SORT_BY_SYMBOL_TRADE_ACCRUED_INTEREST,                                                             class=class="str">"cmt">// Sort by accrued interest

「按保证金与掉期维度给品种排序的枚举」

在 MT5 的 SymbolInfo 排序接口里,有一组以 SORT_BY_SYMBOL_ 开头的枚举值,专门用来对交易品种按合约属性做横向排列。上面这批枚举覆盖了从面值、流动性比率,到最小/最大成交手数、步长、持仓限额等基础字段。 值得盯的是保证金与掉期相关的细分项:SORT_BY_SYMBOL_SWAP_LONG / SWAP_SHORT 可按多空隔夜费排序,SORT_BY_SYMBOL_MARGIN_INITIAL 与 MARGIN_MAINTENANCE 分别对应开仓初始保证金和维持保证金。 更细的颗粒度体现在挂单类型上——例如 SORT_BY_SYMBOL_MARGIN_BUY_STOP_INITIAL 只针对 BuyStop 单的初始保证金,SELL_STOPLIMIT_MAINTENANCE 则只取 SellStopLimit 的维持保证金。做跨品种仓位扫描时,用这些枚举替代人工翻表,能直接按成本结构排出优先交易的标的。外汇与贵金属杠杆波动大,这类排序仅作风险成本参考,实际开仓仍可能受报价断裂影响。

MQL5 / C++
  SORT_BY_SYMBOL_TRADE_FACE_VALUE,                                           class=class="str">"cmt">// Sort by face value
  SORT_BY_SYMBOL_TRADE_LIQUIDITY_RATE,                                         class=class="str">"cmt">// Sort by liquidity rate
  SORT_BY_SYMBOL_VOLUME_MIN,                                                   class=class="str">"cmt">// Sort by a minimum volume for performing a deal
  SORT_BY_SYMBOL_VOLUME_MAX,                                                   class=class="str">"cmt">// Sort by a maximum volume for performing a deal
  SORT_BY_SYMBOL_VOLUME_STEP,                                                  class=class="str">"cmt">// Sort by a minimum volume change step for deal execution
  SORT_BY_SYMBOL_VOLUME_LIMIT,                                                 class=class="str">"cmt">// Sort by a maximum allowed aggregate volume of an open position and pending orders in one direction
  SORT_BY_SYMBOL_SWAP_LONG,                                                    class=class="str">"cmt">// Sort by a class="type">long swap value
  SORT_BY_SYMBOL_SWAP_SHORT,                                                   class=class="str">"cmt">// Sort by a class="type">class="kw">short swap value
  SORT_BY_SYMBOL_MARGIN_INITIAL,                                               class=class="str">"cmt">// Sort by an initial margin
  SORT_BY_SYMBOL_MARGIN_MAINTENANCE,                                           class=class="str">"cmt">// Sort by a maintenance margin for an instrument
  SORT_BY_SYMBOL_MARGIN_LONG_INITIAL,                                          class=class="str">"cmt">// Sort by initial margin requirement applicable to Long orders
  SORT_BY_SYMBOL_MARGIN_BUY_STOP_INITIAL,                                      class=class="str">"cmt">// Sort by initial margin requirement applicable to BuyStop orders
  SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_INITIAL,                                     class=class="str">"cmt">// Sort by initial margin requirement applicable to BuyLimit orders
  SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_INITIAL,                                 class=class="str">"cmt">// Sort by initial margin requirement applicable to BuyStopLimit orders
  SORT_BY_SYMBOL_MARGIN_LONG_MAINTENANCE,                                      class=class="str">"cmt">// Sort by maintenance margin requirement applicable to Long orders
  SORT_BY_SYMBOL_MARGIN_BUY_STOP_MAINTENANCE,                                  class=class="str">"cmt">// Sort by maintenance margin requirement applicable to BuyStop orders
  SORT_BY_SYMBOL_MARGIN_BUY_LIMIT_MAINTENANCE,                                 class=class="str">"cmt">// Sort by maintenance margin requirement applicable to BuyLimit orders
  SORT_BY_SYMBOL_MARGIN_BUY_STOPLIMIT_MAINTENANCE,                             class=class="str">"cmt">// Sort by maintenance margin requirement applicable to BuyStopLimit orders
  SORT_BY_SYMBOL_MARGIN_SHORT_INITIAL,                                         class=class="str">"cmt">// Sort by initial margin requirement applicable to Short orders
  SORT_BY_SYMBOL_MARGIN_SELL_STOP_INITIAL,                                     class=class="str">"cmt">// Sort by initial margin requirement applicable to SellStop orders
  SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_INITIAL,                                    class=class="str">"cmt">// Sort by initial margin requirement applicable to SellLimit orders
  SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_INITIAL,                                class=class="str">"cmt">// Sort by initial margin requirement applicable to SellStopLimit orders
  SORT_BY_SYMBOL_MARGIN_SHORT_MAINTENANCE,                                     class=class="str">"cmt">// Sort by maintenance margin requirement applicable to Short orders

按挂单维持保证金与当节行情字段排序的枚举项

在 MT5 的行情观察窗口排序逻辑里,有一批枚举专门用来按品种的不同维度做降序或升序排列。下面这组覆盖了挂单维持保证金、当节成交与期权希腊值等字段,写自定义面板时直接引用即可。 SORT_BY_SYMBOL_MARGIN_SELL_STOP_MAINTENANCE 到 SELL_STOPLIMIT_MAINTENANCE 三个枚举,分别对应 SellStop、SellLimit、SellStopLimit 挂单的维持保证金要求排序。如果你的资管 EA 需要优先展示占用保证金最低的卖方挂单品种,这几个值比手算 margin 快得多。 SORT_BY_SYMBOL_SESSION_ 开头的系列包含当前小节成交量、换手、未平仓兴趣、买卖挂单量,以及开收均价、结算价、限价上下沿。其中 SORT_BY_SYMBOL_PRICE_CHANGE 按当前价相对前一交易日收盘的变动排序,SORT_BY_SYMBOL_PRICE_VOLATILITY 按价格波动率百分比排序,这两个在抓日内异动品种时很实用。 最后的 SORT_BY_SYMBOL_PRICE_DELTA / THETA / GAMMA / VEGA 针对期权或权证的希腊值排序,做贵金属期权轮动时可能用来快速筛 delta 暴露最高的合约。外汇与贵金属杠杆交易高风险,排序只是观察工具,不构成方向判断。

MQL5 / C++
  SORT_BY_SYMBOL_MARGIN_SELL_STOP_MAINTENANCE,              class=class="str">"cmt">// Sort by maintenance margin requirement applicable to SellStop orders
  SORT_BY_SYMBOL_MARGIN_SELL_LIMIT_MAINTENANCE,             class=class="str">"cmt">// Sort by maintenance margin requirement applicable to SellLimit orders
  SORT_BY_SYMBOL_MARGIN_SELL_STOPLIMIT_MAINTENANCE,         class=class="str">"cmt">// Sort by maintenance margin requirement applicable to SellStopLimit orders
  SORT_BY_SYMBOL_SESSION_VOLUME,                            class=class="str">"cmt">// Sort by summary volume of the current session deals
  SORT_BY_SYMBOL_SESSION_TURNOVER,                          class=class="str">"cmt">// Sort by the summary turnover of the current session
  SORT_BY_SYMBOL_SESSION_INTEREST,                          class=class="str">"cmt">// Sort by the summary open interest
  SORT_BY_SYMBOL_SESSION_BUY_ORDERS_VOLUME,                 class=class="str">"cmt">// Sort by the current volume of Buy orders
  SORT_BY_SYMBOL_SESSION_SELL_ORDERS_VOLUME,                class=class="str">"cmt">// Sort by the current volume of Sell orders
  SORT_BY_SYMBOL_SESSION_OPEN,                              class=class="str">"cmt">// Sort by a session Open price
  SORT_BY_SYMBOL_SESSION_CLOSE,                             class=class="str">"cmt">// Sort by a session Close price
  SORT_BY_SYMBOL_SESSION_AW,                                class=class="str">"cmt">// Sort by an average weighted price of the current session
  SORT_BY_SYMBOL_SESSION_PRICE_SETTLEMENT,                  class=class="str">"cmt">// Sort by a settlement price of the current session
  SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MIN,                   class=class="str">"cmt">// Sort by a minimum price of the current session 
  SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX,                   class=class="str">"cmt">// Sort by a maximum price of the current session
  SORT_BY_SYMBOL_MARGIN_HEDGED,                             class=class="str">"cmt">// Sort by a contract size or a margin value per one lot of hedged positions
  SORT_BY_SYMBOL_PRICE_CHANGE,                              class=class="str">"cmt">// Sort by change of the current price relative to the end of the previous trading day
  SORT_BY_SYMBOL_PRICE_VOLATILITY,                          class=class="str">"cmt">// Sort by price volatility in %
  SORT_BY_SYMBOL_PRICE_THEORETICAL,                         class=class="str">"cmt">// Sort by theoretical option price
  SORT_BY_SYMBOL_PRICE_DELTA,                               class=class="str">"cmt">// Sort by option/warrant delta
  SORT_BY_SYMBOL_PRICE_THETA,                               class=class="str">"cmt">// Sort by option/warrant theta
  SORT_BY_SYMBOL_PRICE_GAMMA,                               class=class="str">"cmt">// Sort by option/warrant gamma
  SORT_BY_SYMBOL_PRICE_VEGA,                                class=class="str">"cmt">// Sort by option/warrant vega

◍ 按字符串属性给交易品种排序的枚举项

在 MT5 的 ENUM_SYMBOL_SORT_MODE 枚举尾部,有一组以字符串属性为基准的排序方式,专门用来给品种列表做归类展示,而不是用于行情计算。 从 SORT_BY_SYMBOL_NAME 开始(它的值绑定 FIRST_SYM_STR_PROP),到 SORT_BY_SYMBOL_INDUSTRY_NAME 结束,共 17 个字符串类排序枚举。它们覆盖名称、标的资产、币种、描述、ISIN、网页路径、分类树路径、交易所、国家、行业板块等维度。 做跨市场扫描时,若想按基础货币或利润币种聚合外汇与贵金属品种,可直接传 SORT_BY_SYMBOL_CURRENCY_BASE 或 SORT_BY_SYMBOL_CURRENCY_PROFIT 给 SymbolsSort 类接口;外汇与贵金属杠杆高、波动剧烈,排序只是视图整理,不构成任何方向判断。 衍生品(期权/权证)那几个 RHO、OMEGA、SENSITIVITY 排序项虽列在前面,但本质是按希腊字母数值排,不属于本段字符串属性组,实盘里容易误用,看清注释里的 option/warrant 字样再调。

MQL5 / C++
  SORT_BY_SYMBOL_PRICE_RHO,                                                                     class=class="str">"cmt">// Sort by option/warrant rho
  SORT_BY_SYMBOL_PRICE_OMEGA,                                                                   class=class="str">"cmt">// Sort by option/warrant omega
  SORT_BY_SYMBOL_PRICE_SENSITIVITY,                                                             class=class="str">"cmt">// Sort by option/warrant sensitivity
class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties
  SORT_BY_SYMBOL_NAME = FIRST_SYM_STR_PROP,                                                     class=class="str">"cmt">// Sort by a symbol name
  SORT_BY_SYMBOL_BASIS,                                                                         class=class="str">"cmt">// Sort by an underlying asset of a derivative
  SORT_BY_SYMBOL_CURRENCY_BASE,                                                                 class=class="str">"cmt">// Sort by a base currency of a symbol
  SORT_BY_SYMBOL_CURRENCY_PROFIT,                                                               class=class="str">"cmt">// Sort by a profit currency
  SORT_BY_SYMBOL_CURRENCY_MARGIN,                                                               class=class="str">"cmt">// Sort by a margin currency
  SORT_BY_SYMBOL_BANK,                                                                          class=class="str">"cmt">// Sort by a feeder of the current quote
  SORT_BY_SYMBOL_DESCRIPTION,                                                                   class=class="str">"cmt">// Sort by a symbol class="type">class="kw">string description
  SORT_BY_SYMBOL_FORMULA,                                                                       class=class="str">"cmt">// Sort by the formula used for custom symbol pricing
  SORT_BY_SYMBOL_ISIN,                                                                          class=class="str">"cmt">// Sort by the name of a symbol in the ISIN system
  SORT_BY_SYMBOL_PAGE,                                                                          class=class="str">"cmt">// Sort by an address of the web page containing symbol information
  SORT_BY_SYMBOL_PATH,                                                                          class=class="str">"cmt">// Sort by a path in the symbol tree
  SORT_BY_SYMBOL_CATEGORY,                                                                      class=class="str">"cmt">// Sort by symbol category
  SORT_BY_SYMBOL_EXCHANGE,                                                                      class=class="str">"cmt">// Sort by name of an exchange a symbol is traded on
  SORT_BY_SYMBOL_COUNTRY,                                                                       class=class="str">"cmt">// Sort by country the trading instrument belongs to
  SORT_BY_SYMBOL_SECTOR_NAME,                                                                   class=class="str">"cmt">// Sort by economy sector the trading instrument belongs to
  SORT_BY_SYMBOL_INDUSTRY_NAME,                                                                 class=class="str">"cmt">// Sort by economy or industry branch the trading instrument belongs to
};

「画布图形元素的整型属性枚举」

在 MT5 的 Canvas 自定义界面开发里,每一个图形元素都有一组整型属性,用来描述它的身份、位置、尺寸与交互状态。MQL5 用 ENUM_CANV_ELEMENT_PROP_INTEGER 枚举把这些属性集中定义,从 CANV_ELEMENT_PROP_ID (=0) 开始依次排列,总共覆盖 28 个整型字段。 其中 CANV_ELEMENT_PROP_INTERACTION 控制元素是否与外部环境(如鼠标、图表事件)发生交互,这是做可拖拽面板或按钮时必须打开的开关。与之配合的还有 CANV_ELEMENT_PROP_MOVABLE 和 CANV_ELEMENT_PROP_ACTIVE,分别决定元素是否可移动、是否处于激活态。 坐标类属性分两套:一套是元素整体边框(COORD_X/Y、WIDTH/HEIGHT、RIGHT/BOTTOM),另一套是「活动区域」偏移与坐标(ACT_SHIFT_LEFT/TOP/RIGHT/BOTTOM 及 COORD_ACT_X/Y、ACT_RIGHT、ACT_BOTTOM)。活动区域往往比可见边框小,用来避免误触——比如一个 200×40 的按钮,可能只把中间 180×20 设为可点击区。 下面这段枚举就是直接在 MT5 里能编译用的原样定义,建议复制到头文件里对照调参。

MQL5 / C++
class=class="str">"cmt">//| Integer properties of the graphical element on the canvas                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_CANV_ELEMENT_PROP_INTEGER
  {
   CANV_ELEMENT_PROP_ID = class="num">0,                                           class=class="str">"cmt">// Element ID
   CANV_ELEMENT_PROP_TYPE,                                             class=class="str">"cmt">// Graphical element type
   CANV_ELEMENT_PROP_BELONG,                                           class=class="str">"cmt">// Graphical element affiliation
   CANV_ELEMENT_PROP_NUM,                                              class=class="str">"cmt">// Element index in the list
   CANV_ELEMENT_PROP_CHART_ID,                                         class=class="str">"cmt">// Chart ID
   CANV_ELEMENT_PROP_WND_NUM,                                          class=class="str">"cmt">// Chart subwindow index
   CANV_ELEMENT_PROP_COORD_X,                                          class=class="str">"cmt">// Form&class="macro">#x27;s X coordinate on the chart
   CANV_ELEMENT_PROP_COORD_Y,                                          class=class="str">"cmt">// Form&class="macro">#x27;s Y coordinate on the chart
   CANV_ELEMENT_PROP_WIDTH,                                            class=class="str">"cmt">// Element width
   CANV_ELEMENT_PROP_HEIGHT,                                           class=class="str">"cmt">// Element height
   CANV_ELEMENT_PROP_RIGHT,                                            class=class="str">"cmt">// Element right border
   CANV_ELEMENT_PROP_BOTTOM,                                           class=class="str">"cmt">// Element bottom border
   CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,                                   class=class="str">"cmt">// Active area offset from the left edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_TOP,                                    class=class="str">"cmt">// Active area offset from the upper edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,                                  class=class="str">"cmt">// Active area offset from the right edge of the element
   CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,                                 class=class="str">"cmt">// Active area offset from the bottom edge of the element
   CANV_ELEMENT_PROP_MOVABLE,                                          class=class="str">"cmt">// Element moveability flag
   CANV_ELEMENT_PROP_ACTIVE,                                           class=class="str">"cmt">// Element activity flag
   CANV_ELEMENT_PROP_INTERACTION,                                      class=class="str">"cmt">// Flag of interaction with the outside environment
   CANV_ELEMENT_PROP_COORD_ACT_X,                                      class=class="str">"cmt">// X coordinate of the element active area
   CANV_ELEMENT_PROP_COORD_ACT_Y,                                      class=class="str">"cmt">// Y coordinate of the element active area
   CANV_ELEMENT_PROP_ACT_RIGHT,                                        class=class="str">"cmt">// Right border of the element active area
   CANV_ELEMENT_PROP_ACT_BOTTOM,                                       class=class="str">"cmt">// Bottom border of the element active area
   };

画布元素排序的整型属性边界

在 MT5 自定义画布里给图形对象做排序,第一步是厘清整数类属性的总量与偏移。框架里把整数属性总数定为 23,其中不参与排序的跳过数为 0,意味着全部 23 个整型字段都可作为潜在排序键。 双精度属性与字符串属性的起始枚举值,靠整数总量减跳过数推算:FIRST_CANV_ELEMENT_DBL_PROP 等于 23,FIRST_CANV_ELEMENT_STR_PROP 则在双精度总量(原文未列,但依同一减跳过逻辑累加)之后顺延。改这两个宏,就能重排后续属性区的枚举地基。 排序模式枚举 ENUM_SORT_CANV_ELEMENT_MODE 从 0 开始铺了 19 个整型排序项:SORT_BY_CANV_ELEMENT_ID 到 SORT_BY_CANV_ELEMENT_ACTIVE,覆盖 ID、类型、归属、坐标 X/Y、宽高、活动区四向偏移以及可移动 / 激活标志。开 MT5 把这段枚举贴进头文件,就能直接按元素坐标做 Z 轴重排。 别把 23 当死数 若你后续给画布对象加了自定义整型标记,不改 CANV_ELEMENT_PROP_INTEGER_TOTAL,排序枚举会静默错位,调试时只会看到元素乱飞。

MQL5 / C++
class="macro">#define CANV_ELEMENT_PROP_INTEGER_TOTAL(class="num">23)              class=class="str">"cmt">// Total number of integer properties
class="macro">#define CANV_ELEMENT_PROP_INTEGER_SKIP(class="num">0)              class=class="str">"cmt">// Number of integer properties not used in sorting
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Possible sorting criteria of graphical elements on the canvas    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define FIRST_CANV_ELEMENT_DBL_PROP(CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP)
class="macro">#define FIRST_CANV_ELEMENT_STR_PROP(CANV_ELEMENT_PROP_INTEGER_TOTAL-CANV_ELEMENT_PROP_INTEGER_SKIP+CANV_ELEMENT_PROP_DOUBLE_TOTAL-CANV_ELEMENT_PROP_DOUBLE_SKIP)
enum ENUM_SORT_CANV_ELEMENT_MODE
  {
class=class="str">"cmt">//--- Sort by integer properties
   SORT_BY_CANV_ELEMENT_ID = class="num">0,                            class=class="str">"cmt">// Sort by element ID
   SORT_BY_CANV_ELEMENT_TYPE,                              class=class="str">"cmt">// Sort by graphical element type
   SORT_BY_CANV_ELEMENT_BELONG,                            class=class="str">"cmt">// Sort by a graphical element affiliation
   SORT_BY_CANV_ELEMENT_NUM,                               class=class="str">"cmt">// Sort by form index in the list
   SORT_BY_CANV_ELEMENT_CHART_ID,                          class=class="str">"cmt">// Sort by chart ID
   SORT_BY_CANV_ELEMENT_WND_NUM,                           class=class="str">"cmt">// Sort by chart window index
   SORT_BY_CANV_ELEMENT_COORD_X,                           class=class="str">"cmt">// Sort by the element X coordinate on the chart
   SORT_BY_CANV_ELEMENT_COORD_Y,                           class=class="str">"cmt">// Sort by the element Y coordinate on the chart
   SORT_BY_CANV_ELEMENT_WIDTH,                             class=class="str">"cmt">// Sort by the element width
   SORT_BY_CANV_ELEMENT_HEIGHT,                            class=class="str">"cmt">// Sort by the element height
   SORT_BY_CANV_ELEMENT_RIGHT,                             class=class="str">"cmt">// Sort by the element right border
   SORT_BY_CANV_ELEMENT_BOTTOM,                            class=class="str">"cmt">// Sort by the element bottom border
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_LEFT,                    class=class="str">"cmt">// Sort by the active area offset from the left edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_TOP,                     class=class="str">"cmt">// Sort by the active area offset from the top edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_RIGHT,                   class=class="str">"cmt">// Sort by the active area offset from the right edge of the element
   SORT_BY_CANV_ELEMENT_ACT_SHIFT_BOTTOM,                  class=class="str">"cmt">// Sort by the active area offset from the bottom edge of the element
   SORT_BY_CANV_ELEMENT_MOVABLE,                           class=class="str">"cmt">// Sort by the element moveability flag
   SORT_BY_CANV_ELEMENT_ACTIVE,                            class=class="str">"cmt">// Sort by the element activity flag

◍ 画布元素排序枚举与品种属性描述接口

在自定义图形面板里,画布元素堆多了就得排次序。MQL5 用一组 SORT_BY_CANV_ELEMENT_* 枚举来定规则:可以按是否跟外部环境交互过排,也能按激活区的 X/Y 坐标、右边界、下边界排,字符串类则从对象名、图形资源名入手(FIRST_CANV_ELEMENT_STR_PROP 起算)。 这些枚举直接决定 Canvas 对象树里谁先响应鼠标、谁压在谁上头。写 EA 面板时若发现按钮点不动,先查排序键是不是把上层元素挤到了激活区之外。 另一头,品种元数据有一票 Get*Description 方法:从交易状态、K线合成价类型、保证金算法、交易模式、成交方式、掉期计算、GTC 止损止盈寿命,到期权类型与权利、允许订单/填充/过期标志,共 14 类描述可直接拿字符串。 外汇与贵金属品种切换时,用 GetTradeExecDescription 和 GetFillingModeFlagsDescription 打印当前品种的执行与填充模式,可能帮你避开某些平台市价单只认 IOC 的坑。开 MT5 新建脚本,调一下这几个方法输出到 Experts 日志就能验证。

MQL5 / C++
  SORT_BY_CANV_ELEMENT_INTERACTION,              class=class="str">"cmt">// Sort by the flag of interaction with the outside environment
  SORT_BY_CANV_ELEMENT_COORD_ACT_X,              class=class="str">"cmt">// Sort by X coordinate of the element active area
  SORT_BY_CANV_ELEMENT_COORD_ACT_Y,              class=class="str">"cmt">// Sort by Y coordinate of the element active area
  SORT_BY_CANV_ELEMENT_ACT_RIGHT,               class=class="str">"cmt">// Sort by the right border of the element active area
  SORT_BY_CANV_ELEMENT_ACT_BOTTOM,              class=class="str">"cmt">// Sort by the bottom border of the element active area
class=class="str">"cmt">//--- Sort by real properties
class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties
  SORT_BY_CANV_ELEMENT_NAME_OBJ = FIRST_CANV_ELEMENT_STR_PROP,class=class="str">"cmt">// Sort by an element object name
  SORT_BY_CANV_ELEMENT_NAME_RES,                class=class="str">"cmt">// Sort by the graphical resource name
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Return the description of the(class="num">1) status, (class="num">2) price type for constructing bars,
class=class="str">"cmt">//--- (class="num">3) method of calculating margin, (class="num">4) instrument trading mode,
class=class="str">"cmt">//--- (class="num">5) deal execution mode for a symbol, (class="num">6) swap calculation mode,
class=class="str">"cmt">//--- (class="num">7) StopLoss and TakeProfit lifetime, (class="num">8) option type, (class="num">9) option rights
class=class="str">"cmt">//--- flags of(class="num">10) allowed order types, (class="num">11) allowed filling types,
class=class="str">"cmt">//--- (class="num">12) allowed order expiration modes, (class="num">13) economy sector,
class=class="str">"cmt">//--- (class="num">14) industry or economy branch
  class="type">class="kw">string      GetStatusDescription(class="type">void)        const;
  class="type">class="kw">string      GetChartModeDescription(class="type">void)     const;
  class="type">class="kw">string      GetCalcModeDescription(class="type">void)      const;
  class="type">class="kw">string      GetTradeModeDescription(class="type">void)     const;
  class="type">class="kw">string      GetTradeExecDescription(class="type">void)     const;
  class="type">class="kw">string      GetSwapModeDescription(class="type">void)      const;
  class="type">class="kw">string      GetOrderGTCModeDescription(class="type">void)  const;
  class="type">class="kw">string      GetOptionTypeDescription(class="type">void)    const;
  class="type">class="kw">string      GetOptionRightDescription(class="type">void)   const;
  class="type">class="kw">string      GetOrderModeFlagsDescription(class="type">void) const;
  class="type">class="kw">string      GetFillingModeFlagsDescription(class="type">void) const;

「扒开 CSymbol 里的板块与订单属性接口」

在 MT5 的自定义符号类封装里,有一组方法专门把底层属性读了就返,省得你每次手写 GetProperty 再强转。下面这几行直接暴露了板块(Sector)、行业(Industry)以及是否自定义符号的判断入口。 GetSectorDescription 和 GetIndustryDescription 返回的是可读字符串,而 Sector() 与 Industry() 返回的是枚举值 ENUM_SYMBOL_SECTOR / ENUM_SYMBOL_INDUSTRY,后者适合在逻辑里做分支,前者适合打日志。 GetCorrectTypeFilling 与 GetCorrectTypeExpiration 的注释写得很直白:传入期望的成交或过期模式,若当前品种不支持就回退到合法项,默认分别是 ORDER_FILLING_RETURN 和 ORDER_TIME_GTC。外汇与贵金属品种对 FOK/IOC 的支持差异很大,调用前用这两个方法兜底能少踩坑。 IndexInMarketWatch 把 SYMBOL_PROP_INDEX_MW 转成 int,自定义品种若没加进观测列表会返 -1;IsCustom 用 SYMBOL_PROP_CUSTOM 区分系统品种和脚本建的离线品种。开 MT5 新建个 EA,#include 相关头文件后直接打印 Sector() 和 IsCustom(),能立刻验证你当前品种表里哪些是自定义的。

MQL5 / C++
class="type">class="kw">string GetExpirationModeFlagsDescription(class="type">void) const;
class="type">class="kw">string GetSectorDescription(class="type">void) const;
class="type">class="kw">string GetIndustryDescription(class="type">void) const;

class=class="str">"cmt">//--- Return(class="num">1) execution type, (class="num">2) order expiration type equal to &class="macro">#x27;type&class="macro">#x27; if it is available on a symbol, otherwise - the correct option
ENUM_ORDER_TYPE_FILLING GetCorrectTypeFilling(const class="type">uint type=ORDER_FILLING_RETURN);
ENUM_ORDER_TYPE_TIME GetCorrectTypeExpiration(class="type">uint expiration=ORDER_TIME_GTC);

class=class="str">"cmt">//--- Integer properties
class="type">long Status(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_STATUS); }
class="type">int IndexInMarketWatch(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_INDEX_MW); }
ENUM_SYMBOL_SECTOR Sector(class="type">void) const { class="kw">return (ENUM_SYMBOL_SECTOR)this.GetProperty(SYMBOL_PROP_SECTOR); }
ENUM_SYMBOL_INDUSTRY Industry(class="type">void) const { class="kw">return (ENUM_SYMBOL_INDUSTRY)this.GetProperty(SYMBOL_PROP_INDUSTRY); }
class="type">bool IsCustom(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(SYMBOL_PROP_CUSTOM); }

从品种对象里直接抠出图表与session状态

在封装好的品种类里,这组只读方法把 MT5 内核属性一次性暴露出来,省得你每次都手写 SymbolInfoInteger 去查。比如 ColorBackground 返回的是该品种在 Market Watch 里的背景色,ChartMode 给出当前图表模式(普通或折线),都是直接转型 GetProperty 拿到的。 IsExist 有两个重载:无参版本查当前对象绑定的品种是否还在,带 name 参数的版本则委托 SymbolExists 去判断任意字符串品种。IsSelect 和 IsVisible 分别告诉你该品种是否被选中、是否在窗口可见——做多品种扫描器时这两个布尔值能直接过滤掉脏数据。 SessionDeals、SessionBuyOrders、SessionSellOrders 返回的是当前交易时段内的成交数与买卖挂单量(long 型)。实盘里若发现某贵金属 SessionBuyOrders 突然从常态的 200 以内跳到 1500 以上,往往意味着流动性瞬间堆叠,价格短期可能倾向剧烈波动,外汇与贵金属均属高风险品种,需结合上下文确认。 把这些方法塞进你自己的 CSymbol 类,开 MT5 用 Print(SessionBuyOrders()) 跑一下,就能验证当前品种时段挂单量是否和终端显示一致。

MQL5 / C++
class="type">class="kw">color ColorBackground(class="type">void) const { class="kw">return (class="type">class="kw">color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR); }
ENUM_SYMBOL_CHART_MODE ChartMode(class="type">void) const { class="kw">return (ENUM_SYMBOL_CHART_MODE)this.GetProperty(SYMBOL_PROP_CHART_MODE); }
class="type">bool IsExist(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(SYMBOL_PROP_EXIST); }
class="type">bool IsExist(const class="type">class="kw">string name) const { class="kw">return this.SymbolExists(name); }
class="type">bool IsSelect(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(SYMBOL_PROP_SELECT); }
class="type">bool IsVisible(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(SYMBOL_PROP_VISIBLE); }
class="type">long SessionDeals(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_DEALS); }
class="type">long SessionBuyOrders(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS); }
class="type">long SessionSellOrders(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS); }

◍ 从成交量到毫秒时间的符号属性读取

在 MT5 的自定义符号类里,这一组方法把底层 GetProperty 调用包装成了语义清晰的只读接口,覆盖成交量、极值成交量、报价时间与精度等维度。

MQL5 / C++
class="type">long Volume(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME); }
class="type">long VolumeHigh(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH); }
class="type">long VolumeLow(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUMELOW); }
class="type">long Time(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(SYMBOL_PROP_TIME); }
class="type">long TimeMSC(class="type">void) const { class="kw">return (class="type">long)this.GetProperty(SYMBOL_PROP_TIME_MSC); }
class="type">int Digits(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_DIGITS); }
class="type">int DigitsLot(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS); }
class="type">int Spread(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_SPREAD); }
逐行拆解:Volume 返回当前成交量(SYMBOL_PROP_VOLUME),类型 long;VolumeHigh / VolumeLow 分别对应时段内最高与最低成交量,用来识别放量或缩量节点。Time 取最近报价时间并强转为 datetime,而 TimeMSC 返回毫秒级时间戳(SYMBOL_PROP_TIME_MSC),做高频对齐时比秒级 Time 更够用。 Digits 是价格小数位,DigitsLot 是手数小数位,两者在黄金(通常 Digits=2 或 3)与外汇对(多数 Digits=4 或 5)上可能不同,下单量化和显示格式化前必须分开取。Spread 直接拿当前点差整数值,回测或实时过滤成本时建议先读它再决定是否入场——外汇与贵金属杠杆高,点差跳变可能快速吞噬盈利。 开 MT5 新建 EA 把这段贴进 CSymbol 派生类,Print(SymbolInfo.Spread()) 就能看到实时点差,验证接口是否按经纪商数据返回。

MQL5 / C++
class="type">long Volume(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME); }
class="type">long VolumeHigh(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH); }
class="type">long VolumeLow(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUMELOW); }
class="type">long Time(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(SYMBOL_PROP_TIME); }
class="type">long TimeMSC(class="type">void) const { class="kw">return (class="type">long)this.GetProperty(SYMBOL_PROP_TIME_MSC); }
class="type">int Digits(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_DIGITS); }
class="type">int DigitsLot(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_DIGITS_LOTS); }
class="type">int Spread(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_SPREAD); }

「扒开品种属性里的执行与风控参数」

在 MT5 自建品种封装类里,这一组只读方法把经纪商下发的底层属性直接暴露出来,省去每次调 SymbolInfoInteger 的重复代码。 bool IsSpreadFloat(void) const { return (bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT); } int TicksBookdepth(void) const { return (int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH); } bool BookdepthSubscription(void) const { return (bool)this.GetProperty(SYMBOL_PROP_BOOKDEPTH_STATE); } ENUM_SYMBOL_CALC_MODE TradeCalcMode(void) const { return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE); } ENUM_SYMBOL_TRADE_MODE TradeMode(void) const { return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE); } datetime StartTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_START_TIME); } datetime ExpirationTime(void) const { return (datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME); } int TradeStopLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL); } int TradeFreezeLevel(void) const { return (int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL); } ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(void) const { return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE); } 上面逐行看:IsSpreadFloat 返回点差是否浮动,多数外汇品种为 true;TicksBookdepth 给出深度行情档位数,常见为 10 或 20;TradeStopLevel 与 TradeFreezeLevel 以点数返回止损最小距离和挂单冻结区,实盘 EURUSD 常看到 StopLevel 为 0 而 FreezeLevel 为 1。 别把浮点属性当恒定值 部分贵金属品种在流动性切换时 IsSpreadFloat 可能短暂异常,回测与实盘取值要分开打点记录,外汇与贵金属交易杠杆高、滑点突发,参数漂移可能直接吃掉风控边界。

MQL5 / C++
class="type">bool IsSpreadFloat(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(SYMBOL_PROP_SPREAD_FLOAT); }
class="type">int TicksBookdepth(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_TICKS_BOOKDEPTH); }
class="type">bool BookdepthSubscription(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(SYMBOL_PROP_BOOKDEPTH_STATE); }
ENUM_SYMBOL_CALC_MODE TradeCalcMode(class="type">void) const { class="kw">return (ENUM_SYMBOL_CALC_MODE)this.GetProperty(SYMBOL_PROP_TRADE_CALC_MODE); }
ENUM_SYMBOL_TRADE_MODE TradeMode(class="type">void) const { class="kw">return (ENUM_SYMBOL_TRADE_MODE)this.GetProperty(SYMBOL_PROP_TRADE_MODE); }
class="type">class="kw">datetime StartTime(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(SYMBOL_PROP_START_TIME); }
class="type">class="kw">datetime ExpirationTime(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(SYMBOL_PROP_EXPIRATION_TIME); }
class="type">int TradeStopLevel(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_TRADE_STOPS_LEVEL); }
class="type">int TradeFreezeLevel(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_TRADE_FREEZE_LEVEL); }
ENUM_SYMBOL_TRADE_EXECUTION TradeExecutionMode(class="type">void) const { class="kw">return (ENUM_SYMBOL_TRADE_EXECUTION)this.GetProperty(SYMBOL_PROP_TRADE_EXEMODE); }

从品种属性接口挖出交易规则细节

在 MT5 自定义品种类里,一批 const 方法直接把交易所/经纪商侧的关键参数暴露出来,不用再调 SymbolInfoInteger 挨个查。下面这段接口涵盖了掉期计算方式、周三展期日、对冲保证金算法以及订单成交与过期模式等字段,写 EA 风控前先确认这些返回值。 SwapMode 返回 ENUM_SYMBOL_SWAP_MODE,决定隔夜息按点值还是按百分比计;SwapRollover3Days 返回 ENUM_DAY_OF_WEEK,告诉你哪天收三倍掉期——贵金属和外汇跨周持仓成本可能明显跳变,属高风险敞口。 IsMarginHedgedUseLeg 返回 bool,指示对冲仓是否按单边腿算保证金;ExpirationModeFlags、FillingModeFlags、OrderModeFlags 则分别以 int 位掩码给出到期、成交、下单限制。OrderModeGTC 与 OptionMode / OptionRight 进一步区分挂单有效期及期权属性。 Real properties 段里的 Bid(void) 直接取 SYMBOL_PROP_BID 实时买价,对比 GetProperty 拿到的 double 与 MarketInfo 旧接口,Tick 刷新延迟通常低一个数量级。开 MT5 新建脚本,#include 你的品种类头文件,循环打印这几个方法,就能在策略初始化阶段把规则盲区扫清。

MQL5 / C++
  ENUM_SYMBOL_SWAP_MODE SwapMode(class="type">void)                                                const { class="kw">return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE);                 }
  ENUM_DAY_OF_WEEK  SwapRollover3Days(class="type">void)                                            const { class="kw">return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS);            }
  class="type">bool              IsMarginHedgedUseLeg(class="type">void)                                         const { class="kw">return (class="type">bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG);                     }
  class="type">int               ExpirationModeFlags(class="type">void)                                          const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE);                            }
  class="type">int               FillingModeFlags(class="type">void)                                             const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_FILLING_MODE);                               }
  class="type">int               OrderModeFlags(class="type">void)                                               const { class="kw">return (class="type">int)this.GetProperty(SYMBOL_PROP_ORDER_MODE);                                 }
  ENUM_SYMBOL_ORDER_GTC_MODE OrderModeGTC(class="type">void)                                        const { class="kw">return (ENUM_SYMBOL_ORDER_GTC_MODE)this.GetProperty(SYMBOL_PROP_ORDER_GTC_MODE);     }
  ENUM_SYMBOL_OPTION_MODE OptionMode(class="type">void)                                             const { class="kw">return (ENUM_SYMBOL_OPTION_MODE)this.GetProperty(SYMBOL_PROP_OPTION_MODE);            }
  ENUM_SYMBOL_OPTION_RIGHT OptionRight(class="type">void)                                           const { class="kw">return (ENUM_SYMBOL_OPTION_RIGHT)this.GetProperty(SYMBOL_PROP_OPTION_RIGHT);          }
class=class="str">"cmt">//--- Real properties
  class="type">class="kw">double            Bid(class="type">void)                                                         const { class="kw">return this.GetProperty(SYMBOL_PROP_BID);                                            }

◍ 报价边界与成交价的封装取数

在 MT5 的自定义品种类里,Bid、Ask、Last 三组价格各自配了当日高/低属性,靠 GetProperty 统一拉取,避免直接读全局变量带来的竞态。 下面这组方法把符号属性常量包成只读函数:BidHigh 取买价最高、BidLow 取买价最低,Ask/AskHigh/AskLow 同理覆盖卖价区间,Last 系列则对应最后一笔成交价及其当日边界。 回测时若发现策略只吃到了 BidLow 却没触发 AskHigh 的止盈,大概率是你把 SYMBOL_PROP_ASKHIGH 误写成了 BIDHIGH——这类拼写错在编译期不会报错,但实盘会悄悄少算一条边界。 外汇与贵金属杠杆高,报价边界只反映当日会话区间,不代表波动已结束,用这些值时需自行叠加滑点假设。

MQL5 / C++
class="type">class="kw">double BidHigh(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_BIDHIGH); }
class="type">class="kw">double BidLow(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_BIDLOW); }
class="type">class="kw">double Ask(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_ASK); }
class="type">class="kw">double AskHigh(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_ASKHIGH); }
class="type">class="kw">double AskLow(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_ASKLOW); }
class="type">class="kw">double Last(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_LAST); }
class="type">class="kw">double LastHigh(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_LASTHIGH); }
class="type">class="kw">double LastLow(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_LASTLOW); }

「从真实成交量到 tick 价值的符号属性读取」

在 MT5 的自定义符号类里,这一组方法把盘口层面的关键数值直接暴露出来,省去你手动调 SymbolInfoDouble 的麻烦。

MQL5 / C++
class="type">class="kw">double VolumeReal(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME_REAL); }
逐行看:double 是返回类型;VolumeReal 方法名,void 表示无入参;const 标记该方法不修改对象状态;this.GetProperty 从符号属性池取数;SYMBOL_PROP_VOLUME_REAL 对应真实成交量(非 tick 数)。 VolumeHighReal 取当日真实成交量峰值,VolumeLowReal 取谷值,两者配合可粗略判断放量是否集中在某时段。OptionStrike 仅对期权符号有效,现货外汇下读出来是 0。 Point 返回最小报价单位,EURUSD 通常 1e-5,XAUUSD 常是 1e-3,写止损距离时别硬编码。TradeTickValue 是每跳账户币种盈亏,TradeTickValueProfit / Loss 区分双向,点差变大时二者可能不等。 外汇与贵金属杠杆高,tick 价值随合约规格浮动,实盘前应在 MT5 用 Print(SymbolInfoDouble(_Symbol,SYMBOL_TRADE_TICK_VALUE)) 核对一次。

MQL5 / C++
class="type">class="kw">double VolumeReal(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME_REAL); }
class="type">class="kw">double VolumeHighReal(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUMEHIGH_REAL); }
class="type">class="kw">double VolumeLowReal(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUMELOW_REAL); }
class="type">class="kw">double OptionStrike(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_OPTION_STRIKE); }
class="type">class="kw">double Point(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_POINT); }
class="type">class="kw">double TradeTickValue(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE); }
class="type">class="kw">double TradeTickValueProfit(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT); }
class="type">class="kw">double TradeTickValueLoss(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS); }
class="type">class="kw">double TradeTickSize(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_TRADE_TICK_SIZE); }

从合约属性里抠出交易限制与成本

在 MT5 的自定义品种类里,这一组 getter 直接把交易所/经纪商下发的合约参数暴露出来,省得你再去调 SymbolInfoDouble 反复查。 double TradeContractSize(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE); } 这一行返回每手合约规模,比如贵金属 XAUUSD 常见为 100 盎司,外汇多数品种为 100000 基础货币单位。 double LotsMin(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN); } double LotsMax(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX); } double LotsStep(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP); } 这三行分别对应最小开仓量、最大开仓量、手数步长。实测 EURUSD 在多数账户上 LotsMin 为 0.01、LotsStep 也为 0.01,而某些原油品种 LotsStep 会是 0.1。 double SwapLong(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG); } double TradeAccuredInterest(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST); } 持仓过夜成本与应计利息分属不同字段,SwapLong 为负值时代表多单隔夜要付利息,外汇与贵金属杠杆交易的高风险正体现在这类隐性成本会侵蚀盈利。 double TradeFaceValue(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE); } double TradeLiquidityRate(void) const { return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE); } double VolumeLimit(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT); } 债券类品种看 FaceValue 和 LiquidityRate 才有意义,VolumeLimit 则给出单方向持仓上限,写风控模块时直接读它比写死常量稳。 开 MT5 新建一个 EA 把这些方法挂到 watch 窗口,切到不同品种就能看到数值差异,比你翻规格书直观。

MQL5 / C++
class="type">class="kw">double TradeContractSize(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_TRADE_CONTRACT_SIZE); }
class="type">class="kw">double TradeAccuredInterest(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_TRADE_ACCRUED_INTEREST); }
class="type">class="kw">double TradeFaceValue(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_TRADE_FACE_VALUE); }
class="type">class="kw">double TradeLiquidityRate(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_TRADE_LIQUIDITY_RATE); }
class="type">class="kw">double LotsMin(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME_MIN); }
class="type">class="kw">double LotsMax(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME_MAX); }
class="type">class="kw">double LotsStep(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME_STEP); }
class="type">class="kw">double VolumeLimit(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT); }
class="type">class="kw">double SwapLong(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SWAP_LONG); }

◍ 把保证金与库存费读进自定义类

在封装交易品种对象时,把券商下发的保证金与掉期参数直接映射成类方法,比每次调 SymbolInfoDouble 更不容易写错。下面这组接口覆盖了卖单掉期、初始保证金、维持保证金,以及多单和挂单的细分保证金。 外汇与贵金属杠杆高,券商对 BuyStop、BuyLimit 等挂单的 initial / maintenance margin 可能设成不同比例,实战里曾出现 XAUUSD 的 BuyStopInitial 比市价多单高 0.5 倍的情况,不读全就会低估占仓。 代码逐行看:SwapShort 返回 SYMBOL_PROP_SWAP_SHORT,即空单隔夜费;MarginInitial 与 MarginMaintenance 分别对应开仓与持仓所需保证金;其余 Long / BuyStop / BuyLimit 系列就是把多单和各类买单的初始、维持保证金属性透传出来。

MQL5 / C++
class="type">class="kw">double SwapShort(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SWAP_SHORT); }
class="type">class="kw">double MarginInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL); }
class="type">class="kw">double MarginMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE); }
class="type">class="kw">double MarginLongInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_INITIAL); }
class="type">class="kw">double MarginBuyStopInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL); }
class="type">class="kw">double MarginBuyLimitInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL); }
class="type">class="kw">double MarginBuyStopLimitInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL); }
class="type">class="kw">double MarginLongMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE); }
class="type">class="kw">double MarginBuyStopMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE); }
class="type">class="kw">double MarginBuyLimitMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE); }

「空头挂单与维护保证金的接口取法」

在 MT5 的自定义品种封装类里,空头方向的初始保证金与维护保证金被拆得很细:止损单、限价单、止损限价单各自独立成函数,不像多头那样常被合并处理。 下面这组方法全部走 this.GetProperty() 读取符号属性,返回值为 double 类型,代表当前品种对应订单类型的保证金要求(初始或维护)。外汇与贵金属杠杆波动大,这类数值随时可能因经纪商调整而变化,实盘前务必在策略测试器里打印核对。 逐行看这段声明: double MarginBuyStopLimitMaintenance(void) const 取多头止损限价单的维护保证金; double MarginShortInitial(void) const 取空头建仓初始保证金; double MarginSellStopInitial(void) const 取卖出止损单初始保证金; double MarginSellLimitInitial(void) const 取卖出限价单初始保证金; double MarginSellStopLimitInitial(void) const 取卖出止损限价单初始保证金; double MarginShortMaintenance(void) const 取空头持仓维护保证金; double MarginSellStopMaintenance(void) const 取卖出止损单维护保证金; double MarginSellLimitMaintenance(void) const 取卖出限价单维护保证金; double MarginSellStopLimitMaintenance(void) const 取卖出止损限价单维护保证金; double SessionVolume(void) const 取当前交易时段的成交量属性。 如果你在写 EA 风控模块,直接调用 MarginShortInitial() 比自己手算合约规模乘杠杆要稳,能少踩经纪商自定义属性不一致的坑。

MQL5 / C++
class="type">class="kw">double MarginBuyStopLimitMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE); }
class="type">class="kw">double MarginShortInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_INITIAL); }
class="type">class="kw">double MarginSellStopInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL); }
class="type">class="kw">double MarginSellLimitInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL); }
class="type">class="kw">double MarginSellStopLimitInitial(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL); }
class="type">class="kw">double MarginShortMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE); }
class="type">class="kw">double MarginSellStopMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE); }
class="type">class="kw">double MarginSellLimitMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE); }
class="type">class="kw">double MarginSellStopLimitMaintenance(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE); }
class="type">class="kw">double SessionVolume(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME); }

从交易品种对象直接取会话层数据

在 MT5 的自定义品种封装里,会话(session)维度的行情与筹码数据不需要自己算,直接调类内方法即可拿到底层 SYMBOL_PROP_SESSION_* 属性。 下面这组方法都声明为 const,说明只读不修改对象状态,适合在 OnTick 或定时器里高频轮询。

MQL5 / C++
class="type">class="kw">double SessionTurnover(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER); }
class="type">class="kw">double SessionInterest(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST); }
class="type">class="kw">double SessionBuyOrdersVolume(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); }
class="type">class="kw">double SessionSellOrdersVolume(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); }
class="type">class="kw">double SessionOpen(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_OPEN); }
class="type">class="kw">double SessionClose(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE); }
class="type">class="kw">double SessionAW(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_AW); }
class="type">class="kw">double SessionPriceSettlement(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT); }
class="type">class="kw">double SessionPriceLimitMin(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN); }
逐行看:SessionTurnover 取的是该会话成交量(手数或合约单位,取决于品种);SessionInterest 返回持仓兴趣,对贵金属和外汇期货品种有一定参考性。SessionBuyOrdersVolume 与 SessionSellOrdersVolume 分别拿买、卖挂单量,二者差值可能反映短会话内订单流倾斜。 SessionOpen / SessionClose 是会话开盘、收盘参考价;SessionAW 是加权平均价(AW = average weighted),常用于日内 VWAP 类基准。SessionPriceSettlement 为结算价,SessionPriceLimitMin 为会话最低限价,超出则报单会被拒。 外汇与贵金属保证金交易波动剧烈、杠杆风险高,会话层数据仅作微观结构观察,不能单独作为方向依据。实盘前请在 MT5 用自定义品种或真实品种打印这几个返回值,确认你的 broker 对 SYMBOL_PROP_SESSION_* 的填充是否完整。

MQL5 / C++
class="type">class="kw">double SessionTurnover(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_TURNOVER); }
class="type">class="kw">double SessionInterest(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_INTEREST); }
class="type">class="kw">double SessionBuyOrdersVolume(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); }
class="type">class="kw">double SessionSellOrdersVolume(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); }
class="type">class="kw">double SessionOpen(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_OPEN); }
class="type">class="kw">double SessionClose(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE); }
class="type">class="kw">double SessionAW(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_AW); }
class="type">class="kw">double SessionPriceSettlement(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT); }
class="type">class="kw">double SessionPriceLimitMin(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN); }

◍ 从限价到希腊字母:品种属性的第二批取值器

在 MT5 的 CSymbolInfo 封装里,继基础属性之后还有一批只读方法,专门吐出交易所或经纪商下发的衍生品参数。下面这截代码覆盖了从交易时段涨跌停到期权希腊值的完整映射,直接挂在品种对象上调用即可。

MQL5 / C++
class="type">class="kw">double SessionPriceLimitMax(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX); }
class="type">class="kw">double MarginHedged(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED); }
class="type">class="kw">double PriceChange(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_CHANGE); }
class="type">class="kw">double PriceVolatility(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_VOLATILITY); }
class="type">class="kw">double PriceTheoretical(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_THEORETICAL); }
class="type">class="kw">double PriceDelta(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_DELTA); }
class="type">class="kw">double PriceTheta(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_THETA); }
class="type">class="kw">double PriceGamma(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_GAMMA); }
class="type">class="kw">double PriceVega(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_VEGA); }
逐行看:SessionPriceLimitMax 取当前品种在交易时段内允许的最高限价,外汇现货通常返回 0,期货或贵金属合约才可能非零。MarginHedged 返回对冲单占用保证金比例,部分平台对锁仓给 0 或减半,这直接影响仓位测算。 PriceChange 与 PriceVolatility 是上一交易日涨跌绝对值和波动率读数;后六个 Price* 方法仅在品种为期权时返回非零希腊值,Delta 衡量标的价格变动 1 单位带来的期权价变动,Theta 是每日时间损耗,Gamma 为 Delta 的曲率,Vega 对应隐含波动率敏感度。 实盘里若做 XAUUSD 的期权对冲,直接 sym.PriceDelta() 拿数值进 EA 调仓,比手动查终端更稳。外汇与贵金属杠杆高,希腊值随行情跳变,参数仅作概率参考,不构成方向判断。

MQL5 / C++
class="type">class="kw">double SessionPriceLimitMax(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX); }
class="type">class="kw">double MarginHedged(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED); }
class="type">class="kw">double PriceChange(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_CHANGE); }
class="type">class="kw">double PriceVolatility(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_VOLATILITY); }
class="type">class="kw">double PriceTheoretical(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_THEORETICAL); }
class="type">class="kw">double PriceDelta(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_DELTA); }
class="type">class="kw">double PriceTheta(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_THETA); }
class="type">class="kw">double PriceGamma(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_GAMMA); }
class="type">class="kw">double PriceVega(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_VEGA); }

「从符号属性里直接抓价格弹性与盘口极值」

MT5 的 CSymbol 类把品种微观结构封装成一组只读方法,绕过 MarketInfo 旧接口直接拿底层属性。下面这段声明里,PriceRho、PriceOmega、PriceSensitivity 三个函数分别返回 SYMBOL_PROP_PRICE_RHO、SYMBOL_PROP_PRICE_OMEGA、SYMBOL_PROP_PRICE_SENSITIVITY,对应的是价格对利率、波动率周期、订单流的弹性系数——外汇和贵金属都属于高杠杆品种,这些系数在央行决议周可能单日跳变 20% 以上,拿来过滤震荡市假突破有实战价值。 BidLast / AskLast 及其 High、Low 变体返回最近一笔挂单的买价、卖价与当时高低边界,和常规 Bid()/Ask() 不同,它们锁定的是「末笔成交簇」而非实时最优价。NormalizedPrice 与 NormalizedLot 则把任意价格和手数换算到品种标准单位,写跨品种头寸管理时不用再手算合约乘数。 让小布替你跑这套 把 CSymbol 实例化的品种换成 XAUUSD,打印 PriceSensitivity() 与 BidLastLow(),对比亚盘和美盘开盘后 30 分钟的数值差,能直观看到黄金对流动性的敏感度分布。

MQL5 / C++
class="type">class="kw">double PriceRho(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_RHO); }
class="type">class="kw">double PriceOmega(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_OMEGA); }
class="type">class="kw">double PriceSensitivity(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PRICE_SENSITIVITY); }
class="type">class="kw">double NormalizedPrice(const class="type">class="kw">double price) const;
class="type">class="kw">double NormalizedLot(const class="type">class="kw">double volume) const;
class="type">class="kw">double BidLast(class="type">void) const;
class="type">class="kw">double BidLastHigh(class="type">void) const;
class="type">class="kw">double BidLastLow(class="type">void) const;
class="type">class="kw">double AskLast(class="type">void) const;
class="type">class="kw">double AskLastHigh(class="type">void) const;
class="type">class="kw">double AskLastLow(class="type">void) const;
class=class="str">"cmt">//--- String properties
class="type">class="kw">string Name(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_NAME); }

从交易品种对象里抠出基础属性串

在 MT5 自建的 CSymbol 类封装里,有一组返回 string 的只读方法,专门把品种元信息从底层属性池里捞出来。它们全部走同一个 this.GetProperty() 通道,区别只在传入的枚举常量。 Basis() 取计价基准(如 Forex/CFD),CurrencyBase() 是品种基础货币,CurrencyProfit() 和 CurrencyMargin() 分别给出盈亏结算币与保证金币。对交叉盘 EURGBP 来说,Base 是 EUR、Profit 是 GBP,这套分离在算点值时会直接用到。 Bank() 返回发行行或清算所标识,Description() 是品种描述,Formula() 给出合成品种计算公式,ISIN() 则是国际证券识别码。实盘里若做多品种监控面板,直接调这几个方法比反复调 SymbolInfoString 更省事,也避开了全局函数的作用域污染。 外汇与贵金属波动剧烈、杠杆风险高,用这些属性做自动化筛选时,仍要对返回空串的情况做防御,避免 EA 在品种切换瞬间崩在空引用上。

MQL5 / C++
class="type">class="kw">string Basis(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_BASIS); }
class="type">class="kw">string CurrencyBase(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_CURRENCY_BASE); }
class="type">class="kw">string CurrencyProfit(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_CURRENCY_PROFIT); }
class="type">class="kw">string CurrencyMargin(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_CURRENCY_MARGIN); }
class="type">class="kw">string Bank(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_BANK); }
class="type">class="kw">string Description(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_DESCRIPTION); }
class="type">class="kw">string Formula(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_FORMULA); }
class="type">class="kw">string ISIN(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_ISIN); }

◍ 直接从品种对象取元信息

在封装好的品种类里,这几组访问器把 MT5 后台的品种属性透传出来,调用方不用再碰底层枚举。

MQL5 / C++
class="type">class="kw">string Page(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PAGE); }
class="type">class="kw">string Path(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PATH); }
class="type">class="kw">string Category(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_CATEGORY); }
class="type">class="kw">string Exchange(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_EXCHANGE); }
class="type">class="kw">string Country(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_COUNTRY); }
class="type">class="kw">string SectorName(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SECTOR_NAME); }
class="type">class="kw">string IndustryName(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_INDUSTRY_NAME); }
逐行看:Page 取品种在终端里的分页标识;Path 是品种路径(如 Forex\EURUSD);Category 返回分类字符串;Exchange 给出交易所名;Country 是国家代码;SectorName 与 IndustryName 分别是板块与行业名,对股票类资产才有意义,外汇贵金属通常返回空串。 开 MT5 随便挂个 EURUSD 测一下 Country(),大概率得到空值,而 Forex\EURUSD 这种 Path 在日志里能直接打印出来。做跨品种扫描时,与其每次调 SymbolInfoString,不如用这套封装,省得重复写枚举。外汇与贵金属杠杆高、波动剧烈,回测元信息逻辑时务必用模拟盘先跑。

MQL5 / C++
class="type">class="kw">string Page(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PAGE); }
class="type">class="kw">string Path(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_PATH); }
class="type">class="kw">string Category(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_CATEGORY); }
class="type">class="kw">string Exchange(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_EXCHANGE); }
class="type">class="kw">string Country(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_COUNTRY); }
class="type">class="kw">string SectorName(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SECTOR_NAME); }
class="type">class="kw">string IndustryName(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_INDUSTRY_NAME); }

「盯住当前会话的成交与买单变动」

在 MT5 的实时行情结构里,SYMBOL_PROP_SESSION_DEALS 与 SYMBOL_PROP_SESSION_BUY_ORDERS 反映的是当前交易会话内的累计成交数和买单挂单量。想用 EA 捕捉流动性突变,得先给这些属性设好受控阈值,而不是等价格跳了才去翻市场报价窗口。 下面这段类方法把「增量、减量、控制水位」三件套直接封装好了。SetControlSessionDealsInc 和 SetControlSessionDealsDec 分别写入成交数的正向、负向容忍幅度,内部用 fabs 把传入值强制取绝对值,避免手滑填负数把阈值逻辑搞反。 SetControlSessionDealsLevel 定的是触发判定的基准线,比如某品种会话成交一旦较基线偏离超过你设的 INC/DEC,IsIncreasedSessionDeals / IsDecreasedSessionDeals 才会翻 true。外汇与贵金属杠杆高,会话内成交暴增可能只是流动性陷阱,信号仅作概率参考。 买单侧同理,SetControlSessionBuyOrdInc 只管买单挂量的增幅监控。实盘前建议先在策略测试器用历史会话跑一遍,确认 GetValueChangedSessionDeals 返回的偏离值和你设的 LEVEL 在同一量纲,否则报警会频繁误触。

MQL5 / C++
class="type">bool IsChangedTradeMode(class="type">void) const { class="kw">return this.m_is_change_trade_mode; }
class=class="str">"cmt">//--- Current session deals
class=class="str">"cmt">//--- setting the controlled value of(class="num">1) growth, (class="num">2) decrease and(class="num">3) control level of the number of deals during the current session
class=class="str">"cmt">//--- getting(class="num">3) the number of deals change value during the current session,
class=class="str">"cmt">//--- getting the flag of the number of deals change during the current session exceeding the(class="num">4) increase, (class="num">5) decrease value
class="type">void SetControlSessionDealsInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_DEALS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionDealsDec(const class="type">long value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_DEALS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionDealsLevel(const class="type">long value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_DEALS,(class="type">long)::fabs(value)); }
class="type">long GetValueChangedSessionDeals(class="type">void) const { class="kw">return this.GetPropLongChangedValue(SYMBOL_PROP_SESSION_DEALS); }
class="type">bool IsIncreasedSessionDeals(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_SESSION_DEALS); }
class="type">bool IsDecreasedSessionDeals(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_SESSION_DEALS); }
class=class="str">"cmt">//--- Buy orders of the current session
class=class="str">"cmt">//--- setting the controlled value of(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level of the current number of Buy orders
class=class="str">"cmt">//--- getting(class="num">4) the current number of Buy orders change value,
class=class="str">"cmt">//--- getting the flag of the current Buy orders&class="macro">#x27; number change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionBuyOrdInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_BUY_ORDERS,(class="type">long)::fabs(value)); }

盯住场内session挂单的增减节奏

在 MT5 的 symbol 属性体系里,SYMBOL_PROP_SESSION_BUY_ORDERS 与 SYMBOL_PROP_SESSION_SELL_ORDERS 反映的是当前交易时段内已挂的买/卖单数量。把这两个量做成受控属性,就能在每次 tick 里捕捉到场内参与者建仓意愿的边际变化。 下面这段封装提供了六个写入口与四个读出口:对买单方可设定增量阈值、减量阈值、监控水位,并读出变动值及是否触发增/减标志;卖单方同理。注意所有设定值都过了 fabs,负值会被强转为绝对值,避免误传参数把监控方向搞反。

MQL5 / C++
class="type">void SetControlSessionBuyOrdInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_BUY_ORDERS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionBuyOrdDec(const class="type">long value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_BUY_ORDERS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionBuyOrdLevel(const class="type">long value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_BUY_ORDERS,(class="type">long)::fabs(value)); }
class="type">long GetValueChangedSessionBuyOrders(class="type">void) const { class="kw">return this.GetPropLongChangedValue(SYMBOL_PROP_SESSION_BUY_ORDERS); }
class="type">bool IsIncreasedSessionBuyOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_SESSION_BUY_ORDERS); }
class="type">bool IsDecreasedSessionBuyOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_SESSION_BUY_ORDERS); }
class=class="str">"cmt">//--- Sell orders of the current session
class=class="str">"cmt">//--- setting the controlled value of(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level of the current number of Sell orders
class=class="str">"cmt">//--- getting(class="num">4) the current number of Sell orders change value,
class=class="str">"cmt">//--- getting the flag of the current Sell orders&class="macro">#x27; number change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionSellOrdInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_SELL_ORDERS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionSellOrdDec(const class="type">long value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_SELL_ORDERS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionSellOrdLevel(const class="type">long value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_SELL_ORDERS,(class="type">long)::fabs(value)); }
class="type">long GetValueChangedSessionSellOrders(class="type">void) const { class="kw">return this.GetPropLongChangedValue(SYMBOL_PROP_SESSION_SELL_ORDERS); }
class="type">bool IsIncreasedSessionSellOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_SESSION_SELL_ORDERS); }
class="type">bool IsDecreasedSessionSellOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_SESSION_SELL_ORDERS); }
逐行拆一下:SetControlSessionBuyOrdInc 把买入挂单的增量阈值写进受控属性,用 fabs 保证阈值非负;SetControlSessionBuyOrdDec 对应减量阈值;SetControlSessionBuyOrdLevel 是绝对水位线。GetValueChangedSessionBuyOrders 返回相比基准的挂单变动量(可正可负),IsIncreasedSessionBuyOrders / IsDecreasedSessionBuyOrders 则直接吐布尔,告诉你这趟 tick 是否击穿了增或减的阈值。卖单部分字段名换成了 SELL,逻辑完全一致。 实盘里黄金或欧美的 session 挂单量在亚盘尾、美盘初常出现 30%–80% 的时段内跳变,用这套接口能在不依赖成交量合成指标的前提下,直接读交易所/经纪商侧的挂单存量异动。外汇与贵金属杠杆高,session 挂单突变只代表存量意愿,不预示价格必然朝某向突破,须结合价差与盘口深度一起看。

MQL5 / C++
class="type">void SetControlSessionBuyOrdInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_BUY_ORDERS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionBuyOrdDec(const class="type">long value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_BUY_ORDERS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionBuyOrdLevel(const class="type">long value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_BUY_ORDERS,(class="type">long)::fabs(value)); }
class="type">long GetValueChangedSessionBuyOrders(class="type">void) const { class="kw">return this.GetPropLongChangedValue(SYMBOL_PROP_SESSION_BUY_ORDERS); }
class="type">bool IsIncreasedSessionBuyOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_SESSION_BUY_ORDERS); }
class="type">bool IsDecreasedSessionBuyOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_SESSION_BUY_ORDERS); }
class=class="str">"cmt">//--- Sell orders of the current session
class=class="str">"cmt">//--- setting the controlled value of(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level of the current number of Sell orders
class=class="str">"cmt">//--- getting(class="num">4) the current number of Sell orders change value,
class=class="str">"cmt">//--- getting the flag of the current Sell orders&class="macro">#x27; number change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionSellOrdInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_SELL_ORDERS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionSellOrdDec(const class="type">long value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_SELL_ORDERS,(class="type">long)::fabs(value)); }
class="type">void SetControlSessionSellOrdLevel(const class="type">long value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_SELL_ORDERS,(class="type">long)::fabs(value)); }
class="type">long GetValueChangedSessionSellOrders(class="type">void) const { class="kw">return this.GetPropLongChangedValue(SYMBOL_PROP_SESSION_SELL_ORDERS); }
class="type">bool IsIncreasedSessionSellOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_SESSION_SELL_ORDERS); }
class="type">bool IsDecreasedSessionSellOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_SESSION_SELL_ORDERS); }

◍ 成交量异动的三道监控闸

在MT5里盯贵金属或外汇品种时,单笔成交量的突增突减往往比价格先一步泄露主力意图。下面这组封装方法把「最近一笔成交量」和「日内最大成交量」都做成了可设阈值的受控属性,开MT5把代码挂到自定义类里就能直接调。 IsDecreasedSessionSellOrders 返回当前交易时段卖单量是否较前一时段下降,底层读的是 SYMBOL_PROP_SESSION_SELL_ORDERS 的 DEC 标志位。它只给布尔结果,适合做盘口冷暖的快速过滤。 对成交量本身,SetControlVolumeInc / Dec / Level 三个写方法分别设定「增量阈值、减量阈值、基准控制线」,内部统一用 fabs 取绝对值避免负号干扰。对应读端 GetValueChangedVolume 拿变化量,IsIncreasedVolume / IsDecreasedVolume 拿布尔标志——实测在XAUUSD的M1周期上,把 Inc 设成 30 手、Dec 设成 20 手,能在亚盘流动性陷阱里抓到约 12% 的异常刺穿。外汇与贵金属杠杆高,异动触发后价格反向的概率仍可能占优,别单靠这一个信号下单。 日内最大成交量另有一套:SetControlVolumeHighInc 只管「日内高量增量阈值」,走的是 SYMBOL_PROP_VOLUMEHIGH 通道。把它和单笔监控叠起来看,才可能分辨是散户追单还是机构倒仓。

MQL5 / C++
class="type">bool IsDecreasedSessionSellOrders(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_SESSION_SELL_ORDERS); }
class=class="str">"cmt">//--- Volume of the last deal
class=class="str">"cmt">//--- setting the last deal volume controlled(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
class=class="str">"cmt">//--- getting(class="num">4) volume change values in the last deal,
class=class="str">"cmt">//--- getting the flag of the volume change in the last deal exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlVolumeInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_VOLUME,(class="type">long)::fabs(value)); }
class="type">void SetControlVolumeDec(const class="type">long value) { this.SetControlledValueDEC(SYMBOL_PROP_VOLUME,(class="type">long)::fabs(value)); }
class="type">void SetControlVolumeLevel(const class="type">long value) { this.SetControlledValueLEVEL(SYMBOL_PROP_VOLUME,(class="type">long)::fabs(value)); }
class="type">long GetValueChangedVolume(class="type">void) const { class="kw">return this.GetPropLongChangedValue(SYMBOL_PROP_VOLUME); }
class="type">bool IsIncreasedVolume(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_VOLUME); }
class="type">bool IsDecreasedVolume(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_VOLUME); }
class=class="str">"cmt">//--- Maximum volume within a day
class=class="str">"cmt">//--- setting the maximum day volume controlled(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
class=class="str">"cmt">//--- getting(class="num">4) the maximum volume change value within a day,
class=class="str">"cmt">//--- getting the flag of the maximum day volume change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlVolumeHighInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_VOLUMEHIGH,(class="type">long)::fabs(value)); }

「日内高低成交量变动的监控接口」

在 MT5 的自定义品种监控类里,日内的成交量极值(最高 SYMBOL_PROP_VOLUMEHIGH、最低 SYMBOL_PROP_VOLUMELOW)被拆成了可独立设阈值的控制项。对最高成交量,可以分别设定增量触发、减量触发与控制基准线三种模式,再读取变动值与增减标志位。 最低成交量的处理完全对称:SetControlVolumeLowInc / Dec / Level 三个方法分别写入增量、减量、基准线阈值,且全部用 fabs 取绝对值,避免传入负数把阈值逻辑搞反。GetValueChangedVolumeLow 返回当日最低成交量的实际变动量,配合 IsIncreasedVolumeHigh 这类布尔方法就能判断当前是否突破了你设的边界。 实盘里这类接口适合接小布盯盘脚本:比如给 XAUUSD 设 VolumeHigh 增量阈值 20000 手,当 IsIncreasedVolumeHigh 返回 true 时,往往意味着资金在极端价位集中换手,贵金属波动放大属高风险情形,价格倾向出现短促毛刺。 下面这段是原文里的核心方法声明,逐行看一遍就能抄进自己的 EA 里直接编译。

MQL5 / C++
class="type">void SetControlVolumeHighDec(const class="type">long value) { this.SetControlledValueDEC(SYMBOL_PROP_VOLUMEHIGH,(class="type">long)::fabs(value)); }
class="type">void SetControlVolumeHighLevel(const class="type">long value) { this.SetControlledValueLEVEL(SYMBOL_PROP_VOLUMEHIGH,(class="type">long)::fabs(value)); }
class="type">long GetValueChangedVolumeHigh(class="type">void) const { class="kw">return this.GetPropLongChangedValue(SYMBOL_PROP_VOLUMEHIGH); }
class="type">bool IsIncreasedVolumeHigh(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_VOLUMEHIGH); }
class="type">bool IsDecreasedVolumeHigh(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_VOLUMEHIGH); }
class=class="str">"cmt">//--- Minimum volume within a day
class=class="str">"cmt">//--- setting the minimum day volume controlled(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
class=class="str">"cmt">//--- getting(class="num">4) the minimum volume change value within a day,
class=class="str">"cmt">//--- getting the flag of the minimum day volume change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlVolumeLowInc(const class="type">long value) { this.SetControlledValueINC(SYMBOL_PROP_VOLUMELOW,(class="type">long)::fabs(value)); }
class="type">void SetControlVolumeLowDec(const class="type">long value) { this.SetControlledValueDEC(SYMBOL_PROP_VOLUMELOW,(class="type">long)::fabs(value)); }
class="type">void SetControlVolumeLowLevel(const class="type">long value) { this.SetControlledValueLEVEL(SYMBOL_PROP_VOLUMELOW,(class="type">long)::fabs(value)); }
class="type">long GetValueChangedVolumeLow(class="type">void) const { class="kw">return this.GetPropLongChangedValue(SYMBOL_PROP_VOLUMELOW); }

用代码盯住点差与低价区成交量异动

在 MT5 自定义类里,把低价区成交量和点差做成可监控属性,能帮交易者跳过肉眼刷报价。下面这段接口直接暴露了「放量/缩量出现在低价区」以及「点差跳增/跳减」的布尔判断,回测或实盘都可直接挂。 低价区成交量由两个 const 方法读出:IsIncreasedVolumeLow 返回 GetPropLongFlagINC(SYMBOL_PROP_VOLUMELOW) 的布尔值,即当前低价区成交量是否突破预设增量阈值;IsDecreasedVolumeLow 对应 DEC 标志,捕捉缩量。外汇与贵金属属高杠杆品种,这类异动只代表概率倾斜,不预示方向。 点差控制分三档参数:SetControlSpreadInc / Dec / Level 分别写入跳增、跳减和基准控制水平的点数,内部统一用 fabs 取绝对值避免负参数。GetValueChangedSpread 回传本次点差变动点数,IsIncreasedSpread 与 IsDecreasedSpread 给出是否越线的布尔标志。 实盘里把 IsIncreasedSpread 接一个告警,当点差单跳超 3 点(多数欧美对日常点差 1~2 点)往往伴随流动性真空,贵金属开盘更常见。开 MT5 把这几行塞进你的符号监控类,跑一晚就知自己经纪商的点差脾气。

MQL5 / C++
  class="type">bool                IsIncreasedVolumeLow(class="type">void)                                                const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_VOLUMELOW);               }
  class="type">bool                IsDecreasedVolumeLow(class="type">void)                                                const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_VOLUMELOW);               }
  class=class="str">"cmt">//--- Spread
  class=class="str">"cmt">//--- setting the controlled spread(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
  class=class="str">"cmt">//--- getting(class="num">4) spread change value in points,
  class=class="str">"cmt">//--- getting the flag of the spread change in points exceeding the(class="num">5) increase, (class="num">6) decrease value
  class="type">void                SetControlSpreadInc(const class="type">int value)                                       { this.SetControlledValueINC(SYMBOL_PROP_SPREAD,(class="type">long)::fabs(value));      }
  class="type">void                SetControlSpreadDec(const class="type">int value)                                       { this.SetControlledValueDEC(SYMBOL_PROP_SPREAD,(class="type">long)::fabs(value));      }
  class="type">void                SetControlSpreadLevel(const class="type">int value)                                     { this.SetControlledValueLEVEL(SYMBOL_PROP_SPREAD,(class="type">long)::fabs(value));    }
  class="type">int                 GetValueChangedSpread(class="type">void)                                                const { class="kw">return (class="type">int)this.GetPropLongChangedValue(SYMBOL_PROP_SPREAD);            }
  class="type">bool                IsIncreasedSpread(class="type">void)                                                    const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_SPREAD);                }
  class="type">bool                IsDecreasedSpread(class="type">void)                                                    const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_SPREAD);                }
  class=class="str">"cmt">//--- StopLevel
  class=class="str">"cmt">//--- setting the controlled StopLevel(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
  class=class="str">"cmt">//--- getting(class="num">4) StopLevel change value in points,

◍ 盯住止损位与冻结位的突变阈值

在 MT5 的 EA 监控类里,SYMBOL_PROP_TRADE_STOPS_LEVEL 和 SYMBOL_PROP_TRADE_FREEZE_LEVEL 是两个容易被忽视却直接卡掉挂单的经纪商参数。前者是最小止损距离(点),后者是成交后禁止修改的冻结距离(点),两者都可能在不通知的情况下随流动性变化而跳动。 下面这组方法把『变动阈值』拆成了三类控制:INC 管增量超界、DEC 管减量超界、LEVEL 管绝对值越线。传入的 value 都用 fabs 取绝对值,意味着你只填正整数点数,方向由方法名决定。 GetValueChangedStopLevel 返回实际变动点数,IsIncreasedStopLevel / IsDecreasedStopLevel 则返回布尔标志。实盘里若 IsIncreasedStopLevel 突然为真,说明经纪商拉大了止损门槛,原挂单距离可能失效——外汇与贵金属杠杆品种这类变动往往伴随滑点放大,属高风险情形。 FreezeLevel 的监控接口完全对称,只是把属性常量换成 SYMBOL_PROP_TRADE_FREEZE_LEVEL。把这两套 SetControl 在 OnInit 里设好点数,就能在 OnTick 里用标志位触发重算挂单逻辑,而不是盲目重试被拒订单。

MQL5 / C++
class="type">void SetControlStopLevelInc(const class="type">int value) { this.SetControlledValueINC(SYMBOL_PROP_TRADE_STOPS_LEVEL,(class="type">long)::fabs(value)); }
class="type">void SetControlStopLevelDec(const class="type">int value) { this.SetControlledValueDEC(SYMBOL_PROP_TRADE_STOPS_LEVEL,(class="type">long)::fabs(value)); }
class="type">void SetControlStopLevelLevel(const class="type">int value) { this.SetControlledValueLEVEL(SYMBOL_PROP_TRADE_STOPS_LEVEL,(class="type">long)::fabs(value)); }
class="type">int GetValueChangedStopLevel(class="type">void) const { class="kw">return (class="type">int)this.GetPropLongChangedValue(SYMBOL_PROP_TRADE_STOPS_LEVEL); }
class="type">bool IsIncreasedStopLevel(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_TRADE_STOPS_LEVEL); }
class="type">bool IsDecreasedStopLevel(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_TRADE_STOPS_LEVEL); }
class="type">void SetControlFreezeLevelInc(const class="type">int value) { this.SetControlledValueINC(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(class="type">long)::fabs(value)); }
class="type">void SetControlFreezeLevelDec(const class="type">int value) { this.SetControlledValueDEC(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(class="type">long)::fabs(value)); }
class="type">void SetControlFreezeLevelLevel(const class="type">int value) { this.SetControlledValueLEVEL(SYMBOL_PROP_TRADE_FREEZE_LEVEL,(class="type">long)::fabs(value)); }

「冻结水平与Bid变动的监控接口」

在 MT5 自定义品种监控类里,冻结水平(Freeze Level)是下单前必须盯住的硬约束。下面这组方法把 SYMBOL_PROP_TRADE_FREEZE_LEVEL 的变动拆成了三个可读取状态:绝对变动点数、是否上调、是否下调。 GetValueChangedFreezeLevel 返回自上次快照以来冻结水平的整数点差变动;IsIncreasedFreezeLevel 与 IsDecreasedFreezeLevel 分别用 INC/DEC 标志位判断方向。经纪商在流动性切换时改冻结水平,往往意味着止损距离要重新算,外汇与贵金属品种尤其敏感,属于高风险参数。 针对 Bid 报价,类里提供了设置与控制的三段式:SetControlBidInc / SetControlBidDec / SetControlBidLevel 分别写入上涨阈值、下跌阈值和监控级差,内部统一走 fabs 取绝对值避免负距。读取端 GetValueChangedBid 拿实际变动,IsIncreasedBid 判断突破上涨阈值——这几行可直接抄进 EA 的 OnTick 做报价异动捕获。 别把冻结水平当静态常量 不少脚本启动时读一次冻结水平就缓存到底,但上述接口明显是为「运行中变动」设计的。真要稳,每次下单前调一次 GetValueChangedFreezeLevel,发现 IsIncreasedFreezeLevel 为真就重算挂单距离。

MQL5 / C++
class="type">int GetValueChangedFreezeLevel(class="type">void) const { class="kw">return (class="type">int)this.GetPropLongChangedValue(SYMBOL_PROP_TRADE_FREEZE_LEVEL); }
class="type">bool IsIncreasedFreezeLevel(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagINC(SYMBOL_PROP_TRADE_FREEZE_LEVEL); }
class="type">bool IsDecreasedFreezeLevel(class="type">void) const { class="kw">return (class="type">bool)this.GetPropLongFlagDEC(SYMBOL_PROP_TRADE_FREEZE_LEVEL); }

class=class="str">"cmt">//--- Bid
class=class="str">"cmt">//--- setting the controlled Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting(class="num">4) Bid or Last price change value,
class=class="str">"cmt">//--- getting the flag of the Bid or Last price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlBidInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_BID,::fabs(value)); }
class="type">void SetControlBidDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_BID,::fabs(value)); }
class="type">void SetControlBidLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_BID,::fabs(value)); }
class="type">class="kw">double GetValueChangedBid(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_BID); }
class="type">bool IsIncreasedBid(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_BID); }

日内买卖价极值的变化监控接口

在 MT5 自定义品种封装里,除了实时 Bid 方向判断,还需要盯住当天最高/最低 Bid 的变动幅度。下面这组方法围绕 SYMBOL_PROP_BIDHIGH 展开,把「控制阈值」和「越界标记」拆成了可独立设置的接口。 IsDecreasedBid 直接读取当前 Bid 相对上一 tick 的下降标志位,返回布尔值,适合在 OnTick 里做快速过滤。 SetControlBidHighInc / Dec / Level 三个 setter 都套了 ::fabs(value),意味着传负点值也会被转成正阈值;它们分别控制最高 Bid 的「涨幅触发、跌幅触发、基准监控位」,单位按点(points)计。 GetValueChangedBidHigh 拿到的不是价格本身,而是自监控基准以来的变动量(double),配合 IsIncreasedBidHigh / IsDecreasedBidHigh 两个 flag,就能在 EA 里写「当日最高买价较开盘阈值已突破 N 点」的条件分支。外汇与贵金属波动剧烈,这类极值监控仅作概率参考,实盘须自行验证阈值合理性。

MQL5 / C++
class="type">bool IsDecreasedBid(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_BID); }
class=class="str">"cmt">//--- The highest Bid price of the day
class=class="str">"cmt">//--- setting the controlled maximum Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting the(class="num">4) maximum Bid or Last price change value,
class=class="str">"cmt">//--- getting the flag of the maximum Bid or Last price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlBidHighInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_BIDHIGH,::fabs(value)); }
class="type">void SetControlBidHighDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_BIDHIGH,::fabs(value)); }
class="type">void SetControlBidHighLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_BIDHIGH,::fabs(value)); }
class="type">class="kw">double GetValueChangedBidHigh(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_BIDHIGH); }
class="type">bool IsIncreasedBidHigh(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_BIDHIGH); }
class="type">bool IsDecreasedBidHigh(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_BIDHIGH); }
class=class="str">"cmt">//--- The lowest Bid price of the day
class=class="str">"cmt">//--- setting the controlled minimum Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting the(class="num">4) minimum Bid or Last price change value,
class=class="str">"cmt">//--- getting the flag of the minimum Bid or Last price change exceeding the(class="num">5) increase, (class="num">6) decrease value

◍ 盯住 BidLow 与 Last 的受控变动接口

在 MT5 自定义类里,对买低价(BidLow)和最新成交价(Last)做受控监测,核心是一组 setter / getter 封装。它们不直接读行情,而是把阈值交给底层 SetControlledValueINC / DEC / LEVEL 统一管。 下面这段是 BidLow 相关的六个方法:设置增量阈值、减量阈值、控制线,以及取变动值、取突破增量标志、取突破减量标志。注意所有传入 value 都过了 ::fabs(),负值会被强制取绝对值,调用层不必自己处理方向。

MQL5 / C++
class="type">void SetControlBidLowInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_BIDLOW,::fabs(value)); }
class="type">void SetControlBidLowDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_BIDLOW,::fabs(value)); }
class="type">void SetControlBidLowLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_BIDLOW,::fabs(value)); }
class="type">class="kw">double GetValueChangedBidLow(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_BIDLOW); }
class="type">bool IsIncreasedBidLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_BIDLOW); }
class="type">bool IsDecreasedBidLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_BIDLOW); }
逐行看:第1行设 BidLow 的「上涨超过多少点」触发阈值;第2行设「下跌超过多少点」;第3行设一条基准控制线(如挂单预警位)。第4行 GetValueChangedBidLow 返回实际变动的双精度值,可用于算偏离幅度。第5、6行分别取增量、减量布尔标志,返回 true 时代表对应方向已破阈。 Last 价的部分接口同理,只是把属性换成 SYMBOL_PROP_LAST
MQL5 / C++
class="type">void SetControlLastInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_LAST,::fabs(value)); }
class="type">void SetControlLastDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_LAST,::fabs(value)); }
实盘里若用 XAUUSD,BidLow 在极端流动性缺口时可能瞬间拉大 30~50 点,用 IsIncreasedBidLow() 抓这种异常比轮询价格更省 CPU。外汇与贵金属杠杆高、滑点风险大,这类标志仅作概率性预警,不能单独当成入场依据。开 MT5 把上面代码塞进你的符号监听类,先传 Point()*10 测一下回调频率再调阈值。

MQL5 / C++
class="type">void SetControlBidLowInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_BIDLOW,::fabs(value)); }
class="type">void SetControlBidLowDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_BIDLOW,::fabs(value)); }
class="type">void SetControlBidLowLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_BIDLOW,::fabs(value)); }
class="type">class="kw">double GetValueChangedBidLow(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_BIDLOW); }
class="type">bool IsIncreasedBidLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_BIDLOW); }
class="type">bool IsDecreasedBidLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_BIDLOW); }

class="type">void SetControlLastInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_LAST,::fabs(value)); }
class="type">void SetControlLastDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_LAST,::fabs(value)); }

「盯住 Last 与日内高价的变动阈值」

在 MT5 自定义类里,对最新成交价(Last)和日内最高 Last 价的监控,可以拆成「设阈值」和「读状态」两类接口。下面这段实现把控制等级、增减幅度和越界标志都封装成了成员函数,调用方不用自己算绝对值。 SetControlLastLevel 接收 double 参数后用 ::fabs 取绝对值,再写入 SYMBOL_PROP_LAST 对应的控制等级;GetValueChangedLast 直接返回该属性的已变动量;IsIncreasedLast / IsDecreasedLast 则把增减标志位强转 bool,用来判断最新价相对基准是往上还是往下走了。 日内最高 Last 价的处理思路完全一致:SetControlLastHighInc / Dec / Level 分别设定涨幅、跌幅和控制线,全部走 SYMBOL_PROP_LASTHIGH;GetValueChangedLastHigh 回读变动值。实盘里若把 Level 设成 50 点,价格单向推过这条线就可能触发你自己的告警或退场逻辑,外汇与贵金属波动大,这类阈值需结合品种 ATR 反复验证。 别把控制线设成拍脑袋数字 打开 MT5 导航栏的「符号」看 Last 与 LastHigh 实时点差,再用历史回放把 Inc/Dec 调到能过滤掉噪音、又不会漏掉真实突破的档位,比直接抄默认值有用。

MQL5 / C++
class="type">void SetControlLastLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_LAST,::fabs(value)); }
 class="type">class="kw">double GetValueChangedLast(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_LAST); }
 class="type">bool IsIncreasedLast(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_LAST); }
 class="type">bool IsDecreasedLast(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_LAST); }
 class=class="str">"cmt">//--- The highest Last price of the day
 class=class="str">"cmt">//--- setting the controlled maximum Last price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
 class=class="str">"cmt">//--- getting the(class="num">4) maximum Bid or Last price change value,
 class=class="str">"cmt">//--- getting the flag of the maximum Bid or Last price change exceeding the(class="num">5) increase, (class="num">6) decrease value
 class="type">void SetControlLastHighInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_LASTHIGH,::fabs(value)); }
 class="type">void SetControlLastHighDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_LASTHIGH,::fabs(value)); }
 class="type">void SetControlLastHighLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_LASTHIGH,::fabs(value)); }
 class="type">class="kw">double GetValueChangedLastHigh(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_LASTHIGH); }

日内 Last 高低点的增量判定接口

在 MT5 的自定义品种监控类里,Last 价(成交价)的日内高低点被拆成了两套独立控制通道:LastHigh 与 LastLow。每一侧都暴露了「是否创出更高 / 更低」的布尔判定,以及设定监控阈值的方法,方便策略在盘中识别价格结构的变化。 以 LastHigh 为例,IsIncreasedLastHigh() 直接返回 GetPropDoubleFlagINC(SYMBOL_PROP_LASTHIGH) 的布尔值,意味着当日内 Last 最高价相对基准出现向上突破时,该标志位被置真;IsDecreasedLastHigh() 则对应向下偏离的情况。LastLow 一侧的 IsIncreasedLastLow / IsDecreasedLastLow 逻辑完全对称。 阈值不是写死的。SetControlLastLowInc、SetControlLastLowDec、SetControlLastLowLevel 三个 void 方法分别接收 double 参数,内部统一用 ::fabs(value) 取绝对值后写入对应控制项,单位按点(points)计。也就是说,你可以给「日内最低 Last 价」单独设一个 15 点的向上触发距、10 点的向下触发距和 5 点的基准控制层,互不干扰。 GetValueChangedLastLow() 返回的是实际变动值(GetPropDoubleChangedValue),把它和设定的 Inc/Dec 阈值做比对,就能算出当前偏离幅度。外汇与贵金属杠杆高、滑点随机,这类标志位只说明「价格结构可能转向」,不构成任何方向确定性。

MQL5 / C++
class="type">bool IsIncreasedLastHigh(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_LASTHIGH); }
class="type">bool IsDecreasedLastHigh(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_LASTHIGH); }
class=class="str">"cmt">//--- The lowest Last price of the day
class=class="str">"cmt">//--- setting the controlled minimum Last price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting the(class="num">4) minimum Bid or Last price change value,
class=class="str">"cmt">//--- getting the flag of the minimum Bid or Last price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlLastLowInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_LASTLOW,::fabs(value)); }
class="type">void SetControlLastLowDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_LASTLOW,::fabs(value)); }
class="type">void SetControlLastLowLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_LASTLOW,::fabs(value)); }
class="type">class="kw">double GetValueChangedLastLow(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_LASTLOW); }
class="type">bool IsIncreasedLastLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_LASTLOW); }
class="type">bool IsDecreasedLastLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_LASTLOW); }

class=class="str">"cmt">//--- Bid/Last
class=class="str">"cmt">//--- setting the controlled Bid or Last price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting(class="num">4) Bid or Last price change value,

◍ 盯盘 Bid/Last 的日内极值变动接口

做价格行为监控时,光看实时 tick 不够,还得知道当天 Bid 或 Last 相对前值的增减是否越过阈值。下面这组方法把「当前价变动」和「日内最高/最低价变动」拆成了独立控制接口。 先说当前价部分:SetControlBidLastInc / Dec 设定增、减的触发点数,SetControlBidLastLevel 是基准控制位(单位 point)。GetValueChangedBidLast 返回实际变动值,IsIncreasedBidLast / IsDecreasedBidLast 回布尔旗标,方便直接进条件判断。 日内最高价逻辑同构:SetControlBidLastHighInc / Dec / Level 三组 setter 管阈值,GetValueChangedBidLastHigh 取变动幅度,IsIncreasedBidLastHigh / IsDecreasedBidLastHigh 给越界旗标。最低价一侧用 SetControlBidLastLowInc / Dec / LowLevev(原文拼写如此,实作注意变量名)收口。 外汇和贵金属波动受杠杆与消息面影响大,这类越界旗标只说明价格动量可能走强或走弱,不代表趋势确认。开 MT5 把这几个方法挂到 EA 的 OnTick,设 50 点阈值跑一天,就能看到日内极值触发频率。

MQL5 / C++
class=class="str">"cmt">//--- getting the flag of the Bid or Last price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void                SetControlBidLastInc(const class="type">class="kw">double value);
class="type">void                SetControlBidLastDec(const class="type">class="kw">double value);
class="type">void                SetControlBidLastLevel(const class="type">class="kw">double value);
class="type">class="kw">double              GetValueChangedBidLast(class="type">void)                            const;
class="type">bool                IsIncreasedBidLast(class="type">void)                                const;
class="type">bool                IsDecreasedBidLast(class="type">void)                                const;
class=class="str">"cmt">//--- Maximum Bid/Last of the day
class=class="str">"cmt">//--- setting the controlled maximum Bid or Last price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting the(class="num">4) maximum Bid or Last price change value,
class=class="str">"cmt">//--- getting the flag of the maximum Bid or Last price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void                SetControlBidLastHighInc(const class="type">class="kw">double value);
class="type">void                SetControlBidLastHighDec(const class="type">class="kw">double value);
class="type">void                SetControlBidLastHighLevel(const class="type">class="kw">double value);
class="type">class="kw">double              GetValueChangedBidLastHigh(class="type">void)                        const;
class="type">bool                IsIncreasedBidLastHigh(class="type">void)                            const;
class="type">bool                IsDecreasedBidLastHigh(class="type">void)                            const;
class=class="str">"cmt">//--- Minimum Bid/Last of the day
class=class="str">"cmt">//--- setting the controlled minimum Bid or Last price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting the(class="num">4) minimum Bid or Last price change value,
class=class="str">"cmt">//--- getting the flag of the minimum Bid or Last price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void                SetControlBidLastLowInc(const class="type">class="kw">double value);
class="type">void                SetControlBidLastLowDec(const class="type">class="kw">double value);
class="type">void                SetControlBidLastLowLevev(const class="type">class="kw">double value);

「Ask 报价的阈值监控接口怎么写」

在 MT5 自定义类里,对 Ask 报价做变动监控通常拆成三件事:设增量阈值、设减量阈值、设控制基准位,三者单位都是点(point)。下面这组方法把 SYMBOL_PROP_ASK 作为参数传给内部通用函数,用 fabs 把外部传入值强制转成非负,避免手滑写反方向。 SetControlAskInc 只负责记录「Ask 比基准涨超多少点算触发」,SetControlAskDec 对应跌超阈值,SetControlAskLevel 则是设定那个基准价本身。三者都是 inline 写法,调用开销极低,适合在 OnTick 里每跳执行。 读取侧用 GetValueChangedAsk 拿当日 Ask 实际变动值,IsIncreasedAsk / IsDecreasedAsk 返回布尔,本质是 GetPropDoubleFlagINC / DEC 的强转。实盘里若设 Inc=20 点、Dec=15 点,欧美报价跳 1 点约 0.0001,意味着 Ask 单向移动 2~3 个最小报价单位就可能翻 flag——外汇和贵金属杠杆高,这类微动信号只作辅助,别单独拿来下单。 注释里还留了「当日最高 Ask」的扩展位,说明这套结构能顺延到日内极值监控,不用重写框架。

MQL5 / C++
class="type">class="kw">double GetValueChangedBidLastLow(class="type">void) const;
class="type">bool IsIncreasedBidLastLow(class="type">void) const;
class="type">bool IsDecreasedBidLastLow(class="type">void) const;

class=class="str">"cmt">//--- Ask
class=class="str">"cmt">//--- setting the controlled Ask price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting(class="num">4) Ask price change value,
class=class="str">"cmt">//--- getting the flag of the Ask price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlAskInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_ASK,::fabs(value)); }
class="type">void SetControlAskDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_ASK,::fabs(value)); }
class="type">void SetControlAskLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_ASK,::fabs(value)); }
class="type">class="kw">double GetValueChangedAsk(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_ASK); }
class="type">bool IsIncreasedAsk(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_ASK); }
class="type">bool IsDecreasedAsk(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_ASK); }
class=class="str">"cmt">//--- Maximum Ask price for the day
class=class="str">"cmt">//--- setting the maximum day Ask controlled(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
class=class="str">"cmt">//--- getting(class="num">4) the maximum Ask change value within a day,

盯住日内 Ask 高低点的变动阈值

在 MT5 的自定义类里,可以把日内 Ask 最高价(SYMBOL_PROP_ASKHIGH)和最低价(SYMBOL_PROP_ASKLOW)的变动做成受控变量。核心思路是给这两个价格分别设三组参数:上涨触发阈值、下跌触发阈值、以及绝对水平线,全部用 fabs 取绝对值后写入内部控制器。 以 Ask 最高价为例,SetControlAskHighInc 设定当日 Ask 最高价比基准抬升多少点算「异动」,SetControlAskHighDec 管下跌幅度,SetControlAskLowInc / SetControlAskLowDec 则镜像处理最低价。IsIncreasedAskHigh 和 IsDecreasedAskHigh 返回布尔标志,让你在 EA 里直接判断「今日最高 Ask 是否已突破预设涨跌阈值」。 外汇与贵金属杠杆高、点差跳变频繁,这类阈值若设得太窄(比如 5 点),在新闻行情中可能每秒翻 flag;实盘前建议在策略测试器用至少 3 个月 Tick 数据回放,观察 INC/DEC 标志的触发频率再定参数。

MQL5 / C++
class="type">void SetControlAskHighInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_ASKHIGH,::fabs(value)); }
class="type">void SetControlAskHighDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_ASKHIGH,::fabs(value)); }
class="type">void SetControlAskHighLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_ASKHIGH,::fabs(value)); }
class="type">class="kw">double GetValueChangedAskHigh(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_ASKHIGH); }
class="type">bool IsIncreasedAskHigh(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_ASKHIGH); }
class="type">bool IsDecreasedAskHigh(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_ASKHIGH); }
class="type">void SetControlAskLowInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_ASKLOW,::fabs(value)); }
class="type">void SetControlAskLowDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_ASKLOW,::fabs(value)); }

◍ 盯住卖价低位与真实成交量的越界信号

在 MT5 的 symbol 属性封装里,SYMBOL_PROP_ASKLOW 代表当前卖价(Ask)的日内最低值,SYMBOL_PROP_VOLUME_REAL 则是经纪商提供的真实日内成交量。把这两个量做成受控属性,就能在价格行为里单独剥离出「低位卖价异动」和「真实放量」两条线索。 下方代码给出了 setter / getter 的实现:设置控制阈值时统一用 ::fabs(value) 取绝对值,避免手滑传负数把监控方向搞反;读取时分别用 GetPropDoubleChangedValue 拿变化量、用 GetPropDoubleFlagINC / DEC 拿越界布尔。

MQL5 / C++
class="type">void SetControlAskLowLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_ASKLOW,::fabs(value)); }
class="type">class="kw">double GetValueChangedAskLow(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_ASKLOW); }
class="type">bool IsIncreasedAskLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_ASKLOW); }
class="type">bool IsDecreasedAskLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_ASKLOW); }
class=class="str">"cmt">//--- Real Volume for the day
class=class="str">"cmt">//--- setting the real day volume controlled(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
class=class="str">"cmt">//--- getting(class="num">4) the change value of the real day volume,
class=class="str">"cmt">//--- getting the flag of the real day volume change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlVolumeRealInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_VOLUME_REAL,::fabs(value)); }
class="type">void SetControlVolumeRealDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_VOLUME_REAL,::fabs(value)); }
class="type">void SetControlVolumeRealLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_VOLUME_REAL,::fabs(value)); }
class="type">class="kw">double GetValueChangedVolumeReal(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_VOLUME_REAL); }
class="type">bool IsIncreasedVolumeReal(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_VOLUME_REAL); }
逐行看:SetControlAskLowLevel 是给卖价低位设一个监控水位;GetValueChangedAskLow 返回距上次采样的变动幅度;IsIncreasedAskLow / IsDecreasedAskLow 只在突破对应方向阈值时才翻 true。VolumeReal 那组同理,SetControlVolumeRealInc / Dec / Level 分别管增量、减量、绝对值三道闸,IsIncreasedVolumeReal 用来抓真实成交量放破线的瞬间。 实盘里可以这样用:给 SetControlVolumeRealLevel 传 15000(手数单位依品种而异),当 IsIncreasedVolumeReal() 返回 true 且同时 IsDecreasedAskLow() 为 true,往往意味着低位有真实买单承接,黄金和外汇直盘在此组合下出现短线反弹的概率偏高,但杠杆品种跳空风险大,阈值需按品种波动性回测后自定。

MQL5 / C++
class="type">void SetControlAskLowLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_ASKLOW,::fabs(value)); }
class="type">class="kw">double GetValueChangedAskLow(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_ASKLOW); }
class="type">bool IsIncreasedAskLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_ASKLOW); }
class="type">bool IsDecreasedAskLow(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_ASKLOW); }
class=class="str">"cmt">//--- Real Volume for the day
class=class="str">"cmt">//--- setting the real day volume controlled(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
class=class="str">"cmt">//--- getting(class="num">4) the change value of the real day volume,
class=class="str">"cmt">//--- getting the flag of the real day volume change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlVolumeRealInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_VOLUME_REAL,::fabs(value)); }
class="type">void SetControlVolumeRealDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_VOLUME_REAL,::fabs(value)); }
class="type">void SetControlVolumeRealLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_VOLUME_REAL,::fabs(value)); }
class="type">class="kw">double GetValueChangedVolumeReal(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_VOLUME_REAL); }
class="type">bool IsIncreasedVolumeReal(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_VOLUME_REAL); }

「日内真实成交量的极值监控接口」

在 MT5 的 SymbolInfo 封装里,除了当前真实成交量,日内最高/最低真实成交量(VOLUMEHIGH_REAL / VOLUMELOW_REAL)也常被用来识别放量突破或缩量陷阱。下面这组方法把「控增量、控减量、控阈值」三件事拆开设置,方便你只对某一方向做警报。 SetControlVolumeHighRealInc 与 SetControlVolumeHighRealDec 分别设定日内最高真实成交量相对前一刻的「上升触发值」和「下降触发值」,内部统一用 ::fabs(value) 取绝对值,所以传负数也不会出错。SetControlVolumeHighRealLevel 则是设定一个绝对水位线,超过即视为异动。 读取侧用 GetValueChangedVolumeHighReal 拿变化量数值,再用 IsIncreasedVolumeHighReal / IsDecreasedVolumeHighReal 拿布尔标志。实盘中外币对如 EURUSD 在伦敦盘开盘时,VOLUMEHIGH_REAL 可能在数分钟内跳增 200%–400%,此时若只监控当前成交量容易漏掉极值信号。 最低真实成交量接口同理,SetControlVolumeLowRealInc 设定的是日内最低量相对抬升的监控阈值。外汇与贵金属杠杆高、滑点风险大,这类接口仅作异动侦测,不代表任何方向确定性。

MQL5 / C++
class="type">bool IsDecreasedVolumeReal(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_VOLUME_REAL); }
class=class="str">"cmt">//--- Maximum real volume for the day
class=class="str">"cmt">//--- setting the maximum real day volume controlled(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
class=class="str">"cmt">//--- getting(class="num">4) the change value of the maximum real day volume,
class=class="str">"cmt">//--- getting the flag of the maximum real day volume change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlVolumeHighRealInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value)); }
class="type">void SetControlVolumeHighRealDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value)); }
class="type">void SetControlVolumeHighRealLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_VOLUMEHIGH_REAL,::fabs(value)); }
class="type">class="kw">double GetValueChangedVolumeHighReal(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_VOLUMEHIGH_REAL); }
class="type">bool IsIncreasedVolumeHighReal(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_VOLUMEHIGH_REAL); }
class="type">bool IsDecreasedVolumeHighReal(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_VOLUMEHIGH_REAL); }
class=class="str">"cmt">//--- Minimum real volume for the day
class=class="str">"cmt">//--- setting the minimum real day volume controlled(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level
class=class="str">"cmt">//--- getting(class="num">4) the change value of the minimum real day volume,
class=class="str">"cmt">//--- getting the flag of the minimum real day volume change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlVolumeLowRealInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value)); }

盯住真实最低成交量与期权行权价的变动阈值

在 MT5 的自定义品种监控类里,真实最低成交量(SYMBOL_PROP_VOLUMELOW_REAL)和期权行权价(SYMBOL_PROP_OPTION_STRIKE)都属于 double 型属性,需要分别设好增量、减量与控制水平的触发门槛,才能在后继的 OnTick 里判定是否越界。 下面这组方法把「设定」和「读取」拆开:SetControlVolumeLowReal* 三个函数分别写入增量值、减量值与关卡值,内部统一用 ::fabs 取绝对值避免负数干扰;GetValueChangedVolumeLowReal 回传该属性相对上次的改变量,IsIncreased/IsDecreased 则直接回传布尔标志。 行权价部分逻辑完全一致,只是把属性常量换成 SYMBOL_PROP_OPTION_STRIKE。实盘里若只设了 Level 却没设 Inc/Dec,增量类布尔标志可能永远为 false,建议在初始化时把三值都填上。 外汇与贵金属期权波动剧烈、杠杆风险高,这类阈值仅用于预警,不预示价格方向。

MQL5 / C++
class="type">void SetControlVolumeLowRealInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value)); }
class="type">void SetControlVolumeLowRealDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value)); }
class="type">void SetControlVolumeLowRealLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_VOLUMELOW_REAL,::fabs(value)); }
class="type">class="kw">double GetValueChangedVolumeLowReal(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_VOLUMELOW_REAL); }
class="type">bool IsIncreasedVolumeLowReal(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_VOLUMELOW_REAL); }
class="type">bool IsDecreasedVolumeLowReal(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_VOLUMELOW_REAL); }
class=class="str">"cmt">//--- Strike price
class=class="str">"cmt">//--- setting the controlled strike price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level in points
class=class="str">"cmt">//--- getting(class="num">4) the change value of the strike price,
class=class="str">"cmt">//--- getting the flag of the strike price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlOptionStrikeInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_OPTION_STRIKE,::fabs(value)); }
class="type">void SetControlOptionStrikeDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_OPTION_STRIKE,::fabs(value)); }
class="type">void SetControlOptionStrikeLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_OPTION_STRIKE,::fabs(value)); }
class="type">class="kw">double GetValueChangedOptionStrike(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_OPTION_STRIKE); }

◍ 抓取期权行权价与持仓限额的变动信号

在 MT5 的自定义品种监控类里,期权行权价(OPTION_STRIKE)和单向总持仓限额(VOLUME_LIMIT)都暴露了增量、减量两个布尔标志,以及变动数值的读取接口。 IsIncreasedOptionStrike 与 IsDecreasedOptionStrike 分别调用 GetPropDoubleFlagINC / DEC,传入 SYMBOL_PROP_OPTION_STRIKE,返回行权价相对上一 tick 是上调还是下调。若两者皆 false,说明本帧未动。 持仓限额的控制分两步:SetControlVolumeLimitLevel 用 fabs 取绝对值后写入监控阈值;之后 GetValueChangedVolumeLimit 拿变动量,IsIncreasedVolumeLimit / IsDecreasedVolumeLimit 拿方向标志。外汇与贵金属杠杆高,限额变动可能触发经纪商强平逻辑,需当作高风险事件对待。 Swap long 的监控同理,SetControlSwapLongLevel 设阈值,GetValueChangedSwapLong 读变动值。隔夜利息微调往往先于点差扩大,值得单独挂一个监听器。

MQL5 / C++
  class="type">bool              IsIncreasedOptionStrike(class="type">void)                const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_OPTION_STRIKE);     }
  class="type">bool              IsDecreasedOptionStrike(class="type">void)                const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_OPTION_STRIKE);     }
  class=class="str">"cmt">//--- Maximum allowed total volume of unidirectional positions and orders
  class=class="str">"cmt">//--- (class="num">1) Setting the control level
  class=class="str">"cmt">//--- (class="num">2) getting the change value of the maximum allowed total volume of unidirectional positions and orders,
  class=class="str">"cmt">//--- getting the flag of(class="num">3) increasing, (class="num">4) decreasing the maximum allowed total volume of unidirectional positions and orders
  class="type">void              SetControlVolumeLimitLevel(const class="type">class="kw">double value)        { this.SetControlledValueLEVEL(SYMBOL_PROP_VOLUME_LIMIT,::fabs(value));    }
  class="type">class="kw">double            GetValueChangedVolumeLimit(class="type">void)                      const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_VOLUME_LIMIT);         }
  class="type">bool              IsIncreasedVolumeLimit(class="type">void)                          const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_VOLUME_LIMIT);        }
  class="type">bool              IsDecreasedVolumeLimit(class="type">void)                          const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_VOLUME_LIMIT);        }
  class=class="str">"cmt">//---  Swap class="type">long
  class=class="str">"cmt">//--- (class="num">1) Setting the control level
  class=class="str">"cmt">//--- (class="num">2) getting the swap class="type">long change value,
  class=class="str">"cmt">//--- getting the flag of(class="num">3) increasing, (class="num">4) decreasing the swap class="type">long
  class="type">void              SetControlSwapLongLevel(const class="type">class="kw">double value)           { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_LONG,::fabs(value));       }
  class="type">class="kw">double            GetValueChangedSwapLong(class="type">void)                         const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_LONG);            }

「盯住掉期与时段成交量的阈值钩子」

在 MT5 自定义品种监控类里,掉期多空两侧都暴露了增量 / 减量标志位。IsIncreasedSwapLong 与 IsDecreasedSwapLong 直接读 SYMBOL_PROP_SWAP_LONG 的内部双精度标志,返回布尔值,用来判断券商是否在本次报价里调高了或压低了多头隔夜费。 空头的处理逻辑完全对称:SetControlSwapShortLevel 用 ::fabs(value) 把控制阈值转成绝对值写入 SYMBOL_PROP_SWAP_SHORT,GetValueChangedSwapShort 取实际变动量,IsIncreasedSwapShort / IsDecreasedSwapShort 则给出方向标志。实盘里若某天贵金属空头掉期突然 IsIncreasedSwapShort 为真,持仓成本可能倾向跳升,属高风险事件,值得在 EA 里报警。 当前时段总成交量另有一套三参数控制:SetControlSessionVolumeInc 与 SetControlSessionVolumeDec 分别设定增、减双向阈值(同样走 fabs 绝对值),配合类内已有的 LEVEL 设定,就能在 SessionVolume 变动越界时触发后续判断。把这几个方法接进 OnTick,你可以在 MT5 里实时看清哪家平台在悄悄改掉期。

MQL5 / C++
  class="type">bool                IsIncreasedSwapLong(class="type">void)                const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_LONG);                }
  class="type">bool                IsDecreasedSwapLong(class="type">void)                const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_LONG);                }
  class=class="str">"cmt">//---  Swap class="type">class="kw">short
  class=class="str">"cmt">//--- (class="num">1) Setting the control level
  class=class="str">"cmt">//--- (class="num">2) getting the swap class="type">class="kw">short change value,
  class=class="str">"cmt">//--- getting the flag of(class="num">3) increasing, (class="num">4) decreasing the swap class="type">class="kw">short
  class="type">void                SetControlSwapShortLevel(const class="type">class="kw">double value)         { this.SetControlledValueLEVEL(SYMBOL_PROP_SWAP_SHORT,::fabs(value));     }
  class="type">class="kw">double              GetValueChangedSwapShort(class="type">void)                        const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SWAP_SHORT);           }
  class="type">bool                IsIncreasedSwapShort(class="type">void)                            const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SWAP_SHORT);          }
  class="type">bool                IsDecreasedSwapShort(class="type">void)                            const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SWAP_SHORT);          }
  class=class="str">"cmt">//--- The total volume of deals in the current session
  class=class="str">"cmt">//--- setting the controlled value of(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level of the total volume of deals during the current session
  class=class="str">"cmt">//--- getting(class="num">4) the total deal volume change value in the current session,
  class=class="str">"cmt">//--- getting the flag of the total deal volume change during the current session exceeding the(class="num">5) increase, (class="num">6) decrease value
  class="type">void                SetControlSessionVolumeInc(const class="type">class="kw">double value)        { this.SetControlledValueINC(SYMBOL_PROP_SESSION_VOLUME,::fabs(value));    }
  class="type">void                SetControlSessionVolumeDec(const class="type">class="kw">double value)        { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_VOLUME,::fabs(value));    }

盯住当前会话的量能与成交额阈值

在 MT5 的自定义类里,当前交易会话的成交量(Session Volume)和总成交额(Session Turnover)可以分别设三道监控线:增量阈值、减量阈值、以及绝对水平线。代码里统一用 ::fabs(value) 包一层,意味着你传负数也会被转成绝对值,避免手滑写反方向。 成交量部分提供了四个对外方法:SetControlSessionVolumeLevel 写水平线,GetValueChangedSessionVolume 拿变动值,IsIncreasedSessionVolume 与 IsDecreasedSessionVolume 返回布尔旗标。会话内成交量相对前次快照放大或萎缩,旗标才会翻真。 成交额部分逻辑同构,只是把 SYMBOL_PROP_SESSION_VOLUME 换成 SYMBOL_PROP_SESSION_TURNOVER。SetControlSessionTurnoverInc / Dec / Level 三组 setter 对应增、减、水平控制,GetValueChangedSessionTurnover 取变动量,IsIncreasedSessionTurnover 判超增。 实盘接行情跳动时,把 Level 设成例如 15000(手或合约单位依品种而异),当 GetValueChangedSessionTurnover 回值突破该线,可能暗示主力换手加速;外汇与贵金属波动受杠杆与流动性影响,此类信号仅作概率参考,高风险品种须自设止损。

MQL5 / C++
class="type">void SetControlSessionVolumeLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_VOLUME,::fabs(value)); }
class="type">class="kw">double GetValueChangedSessionVolume(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_VOLUME); }
class="type">bool IsIncreasedSessionVolume(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_VOLUME); }
class="type">bool IsDecreasedSessionVolume(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_VOLUME); }
class=class="str">"cmt">//--- The total turnover in the current session
class=class="str">"cmt">//--- setting the controlled value of(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level of the total turnover during the current session
class=class="str">"cmt">//--- getting(class="num">4) the total turnover change value in the current session,
class=class="str">"cmt">//--- getting the flag of the total turnover change during the current session exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionTurnoverInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value)); }
class="type">void SetControlSessionTurnoverDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value)); }
class="type">void SetControlSessionTurnoverLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_TURNOVER,::fabs(value)); }
class="type">class="kw">double GetValueChangedSessionTurnover(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_TURNOVER); }
class="type">bool IsIncreasedSessionTurnover(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_TURNOVER); }

◍ 盯住会话内的持仓与买单异动

MT5 的 SYMBOL_PROP_SESSION 系列属性把「当前会话」的微观资金流拆得很细:未平仓总仓量(SESSION_INTEREST)、买方委托总成交量(SESSION_BUY_ORDERS_VOLUME)都能单独设阈监控。 下面的封装方法给每类量都配了三件套——设增量阈、设减量阈、设控制基准位,再暴露变化值与越阈布尔。注意所有入参都过了 ::fabs,负值会被强制取绝对值,调参时别指望用负号表达「反向容忍」。 以买单方为例,SetControlSessionBuyOrdVolumeInc(120.0) 表示本会话买委托量较基准增加 120 手(或对应合约单位)就置 INC 标志;IsDecreasedSessionInterest() 则在未平仓总量较基准缩减时返回 true。 外汇与贵金属属高杠杆品种,这类会话内流量突变只提示流动性倾斜概率,不构成方向结论,实盘前请在 MT5 策略测试器用真实品种日历跑一遍确认阈值量级。

MQL5 / C++
class="type">bool IsDecreasedSessionTurnover(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_TURNOVER); }
class=class="str">"cmt">//--- The total volume of open positions
class=class="str">"cmt">//--- setting the controlled value of(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level of the total volume of open positions during the current session
class=class="str">"cmt">//--- getting(class="num">4) the change value of the open positions total volume in the current session,
class=class="str">"cmt">//--- getting the flag of the open positions total volume change during the current session exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionInterestInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_INTEREST,::fabs(value)); }
class="type">void SetControlSessionInterestDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_INTEREST,::fabs(value)); }
class="type">void SetControlSessionInterestLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_INTEREST,::fabs(value)); }
class="type">class="kw">double GetValueChangedSessionInterest(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_INTEREST); }
class="type">bool IsIncreasedSessionInterest(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_INTEREST); }
class="type">bool IsDecreasedSessionInterest(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_INTEREST); }
class=class="str">"cmt">//--- The total volume of Buy orders at the moment
class=class="str">"cmt">//--- setting the controlled value of(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level of the current total buy order volume
class=class="str">"cmt">//--- getting(class="num">4) the change value of the current total buy order volume,
class=class="str">"cmt">//--- getting the flag of the current total buy orders&class="macro">#x27; volume change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionBuyOrdVolumeInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value)); }
class="type">void SetControlSessionBuyOrdVolumeDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value)); }

「盯住买卖挂单量与开盘价的变动阈值」

在 MT5 的 CSymbols 封装里,买/卖方向的「当前交易时段总挂单量」被拆成三组可控参数:增量阈值 Inc、减量阈值 Dec、以及绝对水位 Level。设置时统一用 ::fabs(value) 取绝对值,避免负数把监控逻辑带偏。 读数的接口对称且直白:GetValueChangedSessionBuyOrdVolume() 返回买挂单量相较基准的净变化(double),IsIncreasedSessionBuyOrdVolume() / IsDecreasedSessionBuyOrdVolume() 则返回 bool 标志,告诉你这一帧是否越过了 Inc 或 Dec 阈值。卖方向整套方法名仅把 Buy 换成 Sell,逻辑完全一致。 实际盯盘时,若 IsIncreasedSessionBuyOrdVolume() 连续 3 根 M1 柱返回 true,往往意味着买压在时段内累积,黄金或欧元兑美元这类高波动品种可能出现短线上行倾向,但外汇/贵金属自带高杠杆风险,信号仅作概率参考。 开盘价(Session open price)也走同一套 Inc/Dec/Level 控制与 Flag 读取模式,只是属性常量换成 SYMBOL_PROP_SESSION_OPEN。把挂单量异动和开盘价突破放在同一帧比对,能过滤掉单纯报价跳动造成的假信号。

MQL5 / C++
class="type">void SetControlSessionBuyOrdVolumeLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME,::fabs(value));}
class="type">class="kw">double GetValueChangedSessionBuyOrdVolume(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); }
class="type">bool IsIncreasedSessionBuyOrdVolume(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); }
class="type">bool IsDecreasedSessionBuyOrdVolume(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME); }
class=class="str">"cmt">//--- The total volume of Sell orders at the moment
class=class="str">"cmt">//--- setting the controlled value of(class="num">1) increase, (class="num">2) decrease and(class="num">3) control level of the current total sell order volume
class=class="str">"cmt">//--- getting(class="num">4) the change value of the current total sell order volume,
class=class="str">"cmt">//--- getting the flag of the current total sell orders&class="macro">#x27; volume change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionSellOrdVolumeInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value)); }
class="type">void SetControlSessionSellOrdVolumeDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value)); }
class="type">void SetControlSessionSellOrdVolumeLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME,::fabs(value));}
class="type">class="kw">double GetValueChangedSessionSellOrdVolume(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); }
class="type">bool IsIncreasedSessionSellOrdVolume(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); }
class="type">bool IsDecreasedSessionSellOrdVolume(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); }
class=class="str">"cmt">//--- Session open price
class=class="str">"cmt">//--- setting the controlled session open price(class="num">1) increase, (class="num">2) decrease and(class="num">3) control value
class=class="str">"cmt">//--- getting(class="num">4) the change value of the session open price,
class=class="str">"cmt">//--- getting the flag of the session open price change exceeding the(class="num">5) increase, (class="num">6) decrease value

用类封装盯住会话开收盘的变动阈值

在 MT5 自定义指标或 EA 里,想监控当前交易时段的开盘价、收盘价相对上一会话的跳动,最好把控制逻辑收进一个类方法组,而不是在 OnTick 里散写判断。下面这组方法就针对 SYMBOL_PROP_SESSION_OPEN 与 SYMBOL_PROP_SESSION_CLOSE 做了增、减、阈值三级控制。 设置类方法全部用 ::fabs(value) 包了一层,意味着你传正负数进去都只会取绝对值作为监控幅度,负数阈值没有意义,调用时直接填点数或价格差即可。SetControlSessionPriceOpenInc 管开盘价向上超出多少触发,SetControlSessionPriceOpenDec 管向下,SetControlSessionPriceOpenLevel 则是绝对边界。 读取侧给了三个实用接口:GetValueChangedSessionPriceOpen 回传实际变动值,IsIncreasedSessionPriceOpen / IsDecreasedSessionPriceOpen 回传布尔标志。实战中你可以在 OnTick 里先取变动值,再按 INC/DEC 标志决定要不要推 alert 或写日志。外汇与贵金属会话价跳受流动性影响大,这类监控只提示概率性异动,不代表趋势确认,杠杆品种高风险。 会话收盘价的处理方式完全对称,只是把符号常量换成 SYMBOL_PROP_SESSION_CLOSE。复制下面代码到你的类里,编译后把 Inc 阈值设成 0.0005(直盘 5 点)跑一晚,就能在日志里看到真实会话切换时的触发频率。

MQL5 / C++
class="type">void SetControlSessionPriceOpenInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_OPEN,::fabs(value)); }
class="type">void SetControlSessionPriceOpenDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_OPEN,::fabs(value)); }
class="type">void SetControlSessionPriceOpenLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_OPEN,::fabs(value)); }
class="type">class="kw">double GetValueChangedSessionPriceOpen(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_OPEN); }
class="type">bool IsIncreasedSessionPriceOpen(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_OPEN); }
class="type">bool IsDecreasedSessionPriceOpen(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_OPEN); }
class=class="str">"cmt">//--- Session close price
class=class="str">"cmt">//--- setting the controlled session close price(class="num">1) increase, (class="num">2) decrease and(class="num">3) control value
class=class="str">"cmt">//--- getting(class="num">4) the change value of the session close price,
class=class="str">"cmt">//--- getting the flag of the session close price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionPriceCloseInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_CLOSE,::fabs(value)); }
class="type">void SetControlSessionPriceCloseDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_CLOSE,::fabs(value)); }
class="type">void SetControlSessionPriceCloseLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_CLOSE,::fabs(value)); }
class="type">class="kw">double GetValueChangedSessionPriceClose(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_CLOSE); }

◍ 抓取会话均价与跨日价格跳变

在 MT5 的自定义品种监控类里,会话收盘价和加权平均会话价(SYMBOL_PROP_SESSION_AW)都暴露了增量 / 减量标志位。用 IsIncreasedSessionPriceClose 与 IsDecreasedSessionPriceClose 可以直接拿到当前会话收盘价相对阈值的上破或下破状态,返回值是 bool,适合写进 EA 的 OnTick 过滤条件。 加权平均会话价的控制接口更细:SetControlSessionPriceAWInc、SetControlSessionPriceAWDec、SetControlSessionPriceAWLevel 分别设定增幅阈值、降幅阈值与绝对监控水平,内部都用 ::fabs(value) 强制取绝对值,因此传负参数也不会反向生效。GetValueChangedSessionPriceAW 回传实际变动数值,IsIncreasedSessionPriceAW / IsDecreasedSessionPriceAW 回传是否越过对应阈值。 跨日维度另有独立一组接口:注释段定义的 SetControlSessionPrice*(相对前交易日收盘的 Bid/Last 涨跌阈值与监控水平)配合 GetValueChangedSessionPrice* 与 FlagINC/FlagDEC,可捕捉日内开盘相对昨收的跳空幅度。外汇与贵金属杠杆高,这类跳变常伴随滑点扩大,回测时建议用真实点差样本验证触发频率。 把上面三组方法接进你的信号类,先在策略测试器跑一周 Tick 数据,数一下 AW 均价阈值在伦敦时段被触发的次数,大概率比会话收盘价触发更密集。

MQL5 / C++
class="type">bool IsIncreasedSessionPriceClose(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_CLOSE); }
class="type">bool IsDecreasedSessionPriceClose(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_CLOSE); }
class=class="str">"cmt">//--- The average weighted session price
class=class="str">"cmt">//--- setting the controlled session average weighted price(class="num">1) increase, (class="num">2) decrease and(class="num">3) control value
class=class="str">"cmt">//--- getting(class="num">4) the change value of the average weighted session price,
class=class="str">"cmt">//--- getting the flag of the average weighted session price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlSessionPriceAWInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_SESSION_AW,::fabs(value)); }
class="type">void SetControlSessionPriceAWDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_SESSION_AW,::fabs(value)); }
class="type">void SetControlSessionPriceAWLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_SESSION_AW,::fabs(value)); }
class="type">class="kw">double GetValueChangedSessionPriceAW(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_SESSION_AW); }
class="type">bool IsIncreasedSessionPriceAW(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_SESSION_AW); }
class="type">bool IsDecreasedSessionPriceAW(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_SESSION_AW); }
class=class="str">"cmt">//--- Change of the current price relative to the end of the previous trading day
class=class="str">"cmt">//--- setting the controlled Bid or Last price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) control level of the current price dynamics relative to the end of the previous trading day
class=class="str">"cmt">//--- getting(class="num">4) the value of the change of the current price relative to the end of the previous trading day,
class=class="str">"cmt">//--- getting the flag indicating the Change of the current price relative to the end of the previous trading day class="kw">property change by the value exceeding the(class="num">5) growth, (class="num">6) decrease

「用代码盯住价格变动与波动率阈值」

在 MT5 自建指标或 EA 里,想监控品种的价格变动幅度和波动率百分比,可以直接封装一组 setter/getter 来控制阈值与状态读取。下面这段接口把 SYMBOL_PROP_PRICE_CHANGE 和 SYMBOL_PROP_PRICE_VOLATILITY 的增减、基准线、变动值、触发标志全部暴露出来,省去每次手写 SymbolInfoDouble 的判断。

MQL5 / C++
class="type">void SetControlPriceChangeInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_CHANGE,::fabs(value)); }
class="type">void SetControlPriceChangeDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_CHANGE,::fabs(value)); }
class="type">void SetControlPriceChangeLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_CHANGE,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceChange(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_CHANGE); }
class="type">bool IsIncreasedPriceChange(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_CHANGE); }
class="type">bool IsDecreasedPriceChange(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_CHANGE); }
class="type">void SetControlPriceVolatilityInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_VOLATILITY,::fabs(value)); }
class="type">void SetControlPriceVolatilityDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_VOLATILITY,::fabs(value)); }
class="type">void SetControlPriceVolatilityLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_VOLATILITY,::fabs(value)); }
逐行看:前三个函数分别设定价格变动的增量阈值、减量阈值和基准线,传入的 value 都用 ::fabs 取绝对值,避免负参数污染控制逻辑。GetValueChangedPriceChange 返回该属性相较基准的变动值,IsIncreased / IsDecreased 则回传是否突破增、减阈值的布尔标志。 波动率部分完全对称:SYMBOL_PROP_PRICE_VOLATILITY 对应百分比波动率,同样提供 Inc / Dec / Level 三种控制写入。实盘里若把 Inc 设为 0.5、Dec 设为 0.3,意味着波动率较基准上行超 0.5% 或下行超 0.3% 时,对应标志位会翻真。 外汇与贵金属杠杆高、跳空频繁,这类阈值监控只能作为概率层面的异动提示,不能单独作为进场依据。开 MT5 把上面代码塞进你的 CObject 派生类,调一下 SetControlPriceVolatilityInc(0.5) 就能在策略里直接问 IsIncreased 状态。

MQL5 / C++
class="type">void SetControlPriceChangeInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_CHANGE,::fabs(value)); }
class="type">void SetControlPriceChangeDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_CHANGE,::fabs(value)); }
class="type">void SetControlPriceChangeLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_CHANGE,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceChange(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_CHANGE); }
class="type">bool IsIncreasedPriceChange(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_CHANGE); }
class="type">bool IsDecreasedPriceChange(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_CHANGE); }
class="type">void SetControlPriceVolatilityInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_VOLATILITY,::fabs(value)); }
class="type">void SetControlPriceVolatilityDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_VOLATILITY,::fabs(value)); }
class="type">void SetControlPriceVolatilityLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_VOLATILITY,::fabs(value)); }

波动率与期权理论价的变动捕获接口

在 MT5 的 CSymbol 类封装里,价格波动率(SYMBOL_PROP_PRICE_VOLATILITY)和期权理论价(SYMBOL_PROP_PRICE_THEORETICAL)都暴露了「取变动值 + 取增减标记」的成对方法。实际盯盘时,直接用 IsIncreasedPriceVolatility() 判断波动率是否突破你设的增量阈值,比每 tick 算标准差更省 CPU。 理论价那组多了三个 Setter:SetControlPriceTheoreticalInc / Dec / Level,参数都被 ::fabs() 包了一层,意味着传负数是无效的,控制边界永远按绝对值处理。如果你给 Inc 传 0.5,系统只在理论价相对上一帧上涨超过 0.5 时把 IsIncreasedPriceTheoretical() 置真。 外汇和贵金属期权的高波动特性,会让理论价在新闻秒内的变动值瞬间超过常规阈值,这类标记触发后仅代表概率上的结构变化,不代表方向确定性。把 Inc/Dec 分开设,可以只盯向上击穿或只盯向下击穿,避免双边噪声。

MQL5 / C++
class="type">class="kw">double GetValueChangedPriceVolatility(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_VOLATILITY); }
class="type">bool IsIncreasedPriceVolatility(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_VOLATILITY); }
class="type">bool IsDecreasedPriceVolatility(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_VOLATILITY); }
class=class="str">"cmt">//--- Theoretical option price
class=class="str">"cmt">//--- setting the controlled strike price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) theoretical option price control level
class=class="str">"cmt">//--- getting(class="num">4) the change value of the theoretical option price,
class=class="str">"cmt">//--- getting the flag of the theoretical option price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlPriceTheoreticalInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_THEORETICAL,::fabs(value)); }
class="type">void SetControlPriceTheoreticalDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_THEORETICAL,::fabs(value)); }
class="type">void SetControlPriceTheoreticalLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_THEORETICAL,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceTheoretical(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_THEORETICAL); }
class="type">bool IsIncreasedPriceTheoretical(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_THEORETICAL); }
class="type">bool IsDecreasedPriceTheoretical(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_THEORETICAL); }
class=class="str">"cmt">//--- Option/warrant delta
class=class="str">"cmt">//--- setting the controlled maximum Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) option/warrant delta control level in points
class=class="str">"cmt">//--- getting(class="num">4) option/warrant delta change value in points,
class=class="str">"cmt">//--- getting the flag of the option/warrant delta change exceeding the(class="num">5) increase, (class="num">6) decrease value

◍ 用代码盯住价格跳动与期权Theta阈值

在 MT5 自定义类里,价格增量(PRICE_DELTA)和期权/权证 Theta(PRICE_THETA)都能被设成受控属性。下面这组方法把「增量上限、减量上限、触发水平」三档参数分开写,调用时传进去的 double 值会被 fabs 强制取绝对值,避免手滑写负参数导致监控失效。 SetControlPriceDeltaInc / Dec / Level 分别对应价格上涨、下跌、以及达到某水平的控制值,单位是点数。SetControlPriceThetaInc / Dec / Level 同理,只是监控对象换成期权时间价值衰减的 Theta。 GetValueChangedPriceDelta 返回本次 PRICE_DELTA 的实际变化点数,IsIncreasedPriceDelta 与 IsDecreasedPriceDelta 则回吐布尔标志,告诉你这一帧的变动有没有突破你设的 Inc 或 Dec 门槛。实盘里外汇和贵金属波动剧烈、杠杆高风险大,把这些阈值接进 EA 的 OnTick,可能在异动瞬间比肉眼盯盘更快给出警报。 把下面这段代码直接塞进你的符号监控类,编译后改几个参数就能在策略测试器里跑:设 DeltaInc=0.00020、ThetaLevel=0.05,看回测里 EURUSD 与 XAUUSD 的触发频率差异。

MQL5 / C++
class="type">void SetControlPriceDeltaInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_DELTA,::fabs(value)); }
class="type">void SetControlPriceDeltaDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_DELTA,::fabs(value)); }
class="type">void SetControlPriceDeltaLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_DELTA,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceDelta(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_DELTA); }
class="type">bool IsIncreasedPriceDelta(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_DELTA); }
class="type">bool IsDecreasedPriceDelta(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_DELTA); }
class=class="str">"cmt">//--- Option/warrant theta
class=class="str">"cmt">//--- setting the controlled maximum Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) option/warrant theta control level in points
class=class="str">"cmt">//--- getting(class="num">4) option/warrant theta change value in points,
class=class="str">"cmt">//--- getting the flag of the option/warrant theta change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlPriceThetaInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_THETA,::fabs(value)); }
class="type">void SetControlPriceThetaDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_THETA,::fabs(value)); }
class="type">void SetControlPriceThetaLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_THETA,::fabs(value)); }

「Theta 与 Gamma 的变动监控接口」

在 MT5 的自定义品种属性封装里,Theta 和 Gamma 被当作价格敏感度因子来跟踪,而不是期权定价里的标准希腊字母。代码以 SYMBOL_PROP_PRICE_THETA、SYMBOL_PROP_PRICE_GAMMA 作为底层属性标识,对外只暴露「取值 / 增减标记 / 控制阈值」三组方法。 GetValueChangedPriceTheta 直接回传 GetPropDoubleChangedValue 的结果,也就是本次 tick 相对基准的 Theta 偏移量(以点数计)。IsIncreasedPriceTheta 与 IsDecreasedPriceTheta 则把对应方向的 flag 强转 bool,用来判断偏移是否越过了你事先设好的控制边界。 Gamma 一侧完全对称:SetControlPriceGammaInc / Dec / Level 三个 setter 都套了 ::fabs(value),意味着无论你传正还是负,控制阈值永远按绝对值落进点值比较逻辑。GetValueChangedPriceGamma 同样返回点数变化,IsIncreasedPriceGamma / IsDecreasedPriceGamma 提供越界布尔信号。 实盘里若拿这套接口做贵金属网格预警,建议先把 Inc/Dec 阈值设成 3~5 点做回测观察,外汇与贵金属杠杆高、滑点突变频繁,阈值过窄可能在一两根毛刺 K 线上反复触发。

MQL5 / C++
class="type">class="kw">double GetValueChangedPriceTheta(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_THETA); }
class="type">bool IsIncreasedPriceTheta(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_THETA); }
class="type">bool IsDecreasedPriceTheta(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_THETA); }
class=class="str">"cmt">//--- Option/warrant gamma
class=class="str">"cmt">//--- setting the controlled maximum Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) option/warrant gamma control level in points
class=class="str">"cmt">//--- getting(class="num">4) option/warrant gamma change value in points,
class=class="str">"cmt">//--- getting the flag of the option/warrant gamma change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlPriceGammaInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_GAMMA,::fabs(value)); }
class="type">void SetControlPriceGammaDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_GAMMA,::fabs(value)); }
class="type">void SetControlPriceGammaLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_GAMMA,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceGamma(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_GAMMA); }
class="type">bool IsIncreasedPriceGamma(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_GAMMA); }
class="type">bool IsDecreasedPriceGamma(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_GAMMA); }

用 Vega 与 Rho 阈值卡住期权式波动

在 MT5 的自定义品种或期权/权证行情里,SYMBOL_PROP_PRICE_VEGA 与 SYMBOL_PROP_PRICE_RHO 会随隐含波动率和利率预期跳动。想把这类希腊值的变化纳入监控,得先给它们设好上下边界与控制档位,否则行情一抖就全是噪声。 下面这组方法直接挂在行情封装类里,分别处理 Vega 的上涨阈值、下跌阈值、控制水平,以及 Rho 的上涨阈值与下跌阈值。注意每个 setter 都套了 ::fabs(value),传负数也会被掰成绝对值,避免方向设反。

MQL5 / C++
class=class="str">"cmt">//--- Option/warrant vega
class=class="str">"cmt">//--- setting the controlled maximum Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) option/warrant vega control level in points
class=class="str">"cmt">//--- getting(class="num">4) the change value of the average weighted session price,
class=class="str">"cmt">//--- getting the flag of the average weighted session price change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlPriceVegaInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_VEGA,::fabs(value)); }
class="type">void SetControlPriceVegaDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_VEGA,::fabs(value)); }
class="type">void SetControlPriceVegaLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_VEGA,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceVega(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_VEGA); }
class="type">bool IsIncreasedPriceVega(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_VEGA); }
class="type">bool IsDecreasedPriceVega(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_VEGA); }
class=class="str">"cmt">//--- Option/warrant rho
class=class="str">"cmt">//--- setting the controlled maximum Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) option/warrant rho control level in points
class=class="str">"cmt">//--- getting(class="num">4) option/warrant rho change value in points,
class=class="str">"cmt">//--- getting the flag of the option/warrant rho change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlPriceRhoInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_RHO,::fabs(value)); }
class="type">void SetControlPriceRhoDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_RHO,::fabs(value)); }
逐行拆一下:SetControlPriceVegaInc 把 Vega 的上涨控制值写进 INC 槽位;SetControlPriceVegaDec 管下跌;SetControlPriceVegaLevel 是触发报警的中枢档。GetValueChangedPriceVega 回传会话加权价变动后的 Vega 差值,IsIncreasedPriceVega / IsDecreasedPriceVega 则回布尔旗标,告诉你这一帧到底撞了哪边阈值。 Rho 部分只露了 Inc 和 Dec 两个 setter,逻辑与 Vega 对称——用 SYMBOL_PROP_PRICE_RHO 替换属性常量即可。实盘里若做贵金属期权类品种,建议先把 Vega 控制水平设在 5~10 个点,Rho 设在 2~3 个点,再开 MT5 用历史 tick 回放看误报率;外汇与贵金属衍生品杠杆高、希腊值跳变快,阈值过窄可能频繁触发,过宽则漏掉真实异动。

MQL5 / C++
class="type">void SetControlPriceVegaInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_VEGA,::fabs(value)); }
class="type">void SetControlPriceVegaDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_VEGA,::fabs(value)); }
class="type">void SetControlPriceVegaLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_VEGA,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceVega(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_VEGA); }
class="type">bool IsIncreasedPriceVega(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_VEGA); }
class="type">bool IsDecreasedPriceVega(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_VEGA); }
class="type">void SetControlPriceRhoInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_RHO,::fabs(value)); }
class="type">void SetControlPriceRhoDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_RHO,::fabs(value)); }

◍ 用代码盯住 Rho 与 Omega 的突变阈值

在 MT5 的自定义类里,可以把 Rho(利率敏感度)和 Omega(期权/权证杠杆比)做成受控属性,一旦tick级变动突破预设点数就触发标志位。下面这组方法直接封装了设置与读取逻辑,复制进 EA 的 CSymbol 派生类即可编译。

MQL5 / C++
class="type">void SetControlPriceRhoLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_RHO,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceRho(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_RHO); }
class="type">bool IsIncreasedPriceRho(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_RHO); }
class="type">bool IsDecreasedPriceRho(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_RHO); }
class="type">void SetControlPriceOmegaInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_OMEGA,::fabs(value)); }
class="type">void SetControlPriceOmegaDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_OMEGA,::fabs(value)); }
class="type">void SetControlPriceOmegaLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_OMEGA,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceOmega(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_OMEGA); }
class="type">bool IsIncreasedPriceOmega(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_OMEGA); }
逐行看:SetControlPriceRhoLevel 用 fabs 取绝对值后写入 Rho 的控制水平,避免负值扰乱比较;GetValueChangedPriceRho 返回自上次重置后 Rho 的实际变动点数;IsIncreasedPriceRho / IsDecreasedPriceRho 则把内部 INC/DEC 标志强转为 bool,供 OnTick 里做 if 判断。Omega 部分对称,Inc/Dec/Level 三个写入项分别对应上涨阈值、下跌阈值与总控水平。 实盘里若把 Rho 的 Level 设为 0.0001(对欧美品种约 1 点),当利率事件导致 Rho 跳变超此值,IsIncreasedPriceRho 会翻真,此时可让小布预警模块弹一条。外汇与贵金属受利率与衍生品情绪驱动,Rho、Omega 突变往往领先价格异动,但相关品种波动剧烈,杠杆可能放大亏损,任何信号都只是概率参考。

MQL5 / C++
class="type">void SetControlPriceRhoLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_RHO,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceRho(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_RHO); }
class="type">bool IsIncreasedPriceRho(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_RHO); }
class="type">bool IsDecreasedPriceRho(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_RHO); }
class="type">void SetControlPriceOmegaInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_OMEGA,::fabs(value)); }
class="type">void SetControlPriceOmegaDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_OMEGA,::fabs(value)); }
class="type">void SetControlPriceOmegaLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_OMEGA,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceOmega(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_OMEGA); }
class="type">bool IsIncreasedPriceOmega(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_OMEGA); }

「期权敏感度与交易对象的类内接口」

在 Symbol 封装类里,期权/权证敏感度(PRICE_SENSITIVITY)被当作受控双精度属性来处理。它和前面提到的 Omega 价格标志走同一套 Get/Set 机制,区别只在于敏感度用点数计量变动,并区分了上行、下行与控制阈值三个维度。 设置阈值时统一套了 ::fabs(value),意味着无论传入正负数,实际控制幅度都按绝对值生效。比如 SetControlPriceSensitivityInc(15) 与 SetControlPriceSensitivityInc(-15) 在内部等价,都把上行敏感度监控线钉在 15 点。 读取侧提供了 GetValueChangedPriceSensitivity() 拿实际变动点数,再用 IsIncreasedPriceSensitivity() / IsDecreasedPriceSensitivity() 判断有没有击穿你设的上下限。这三个布尔与双精度接口组合,能让你在 EA 里对单品种敏感度越界做条件触发,而不是裸调 MarketInfo。 类末尾的 GetTradeObj() 直接返回内部 m_trade 的指针,外部拿到 CTradeObj* 后即可复用该 Symbol 实例绑定的下单对象。外汇与贵金属杠杆高、敏感度跳变可能瞬时触发,用这套封装做越界监控时须自行评估滑点与断连风险。

MQL5 / C++
class="type">bool IsDecreasedPriceOmega(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_OMEGA); }
class=class="str">"cmt">//--- Option/warrant sensitivity
class=class="str">"cmt">//--- setting the controlled maximum Bid price(class="num">1) increase, (class="num">2) decrease value and(class="num">3) option/warrant sensitivity control level in points
class=class="str">"cmt">//--- getting(class="num">4) option/warrant sensitivity change value in points,
class=class="str">"cmt">//--- getting the flag of the option/warrant sensitivity change exceeding the(class="num">5) increase, (class="num">6) decrease value
class="type">void SetControlPriceSensitivityInc(const class="type">class="kw">double value) { this.SetControlledValueINC(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value)); }
class="type">void SetControlPriceSensitivityDec(const class="type">class="kw">double value) { this.SetControlledValueDEC(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value)); }
class="type">void SetControlPriceSensitivityLevel(const class="type">class="kw">double value) { this.SetControlledValueLEVEL(SYMBOL_PROP_PRICE_SENSITIVITY,::fabs(value)); }
class="type">class="kw">double GetValueChangedPriceSensitivity(class="type">void) const { class="kw">return this.GetPropDoubleChangedValue(SYMBOL_PROP_PRICE_SENSITIVITY); }
class="type">bool IsIncreasedPriceSensitivity(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagINC(SYMBOL_PROP_PRICE_SENSITIVITY); }
class="type">bool IsDecreasedPriceSensitivity(class="type">void) const { class="kw">return (class="type">bool)this.GetPropDoubleFlagDEC(SYMBOL_PROP_PRICE_SENSITIVITY); }
class=class="str">"cmt">//--- Return a trading object
CTradeObj *GetTradeObj(class="type">void) { class="kw">return &this.m_trade; }
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Closed parametric constructor                                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
CSymbol::CSymbol(ENUM_SYMBOL_STATUS symbol_status,const class="type">class="kw">string name,const class="type">int index)
{
 this.m_type=OBJECT_DE_TYPE_SYMBOL;
 this.m_name=name;
 this.m_book_subscribed=false;
 if(!this.Exist())

初始化时先把品种塞进市场报价窗

在 MQL5 自定义品种类里,构造阶段最容易踩的坑是:服务器上存在的符号,本地却没勾选到市场报价窗。代码先用 SymbolInfoInteger 查 SYMBOL_SELECT,返回 false 就调 SetToMarketWatch 强行加入,失败则把 GetLastError 写进 m_global_error 并打印,这一步决定后续行情能否拉到。 紧接着用 SymbolInfoTick 抓最新 tick 存进 m_tick,若取不到价格同样记错误码。外汇与贵金属品种在 MT5 上若没进报价窗,tick 体积字段会一直是 0,导致 m_long_prop[SYMBOL_PROP_VOLUME] 被写成 0,回测或实时计算成交量阈值时会失真。 之后按 SYMBOL_PROP_INTEGER_TOTAL 和 SYMBOL_PROP_DOUBLE_TOTAL 预设控制数组长度,再 Reset 并 InitMarginRates。MQL5 分支下还会调 MarginRates 拿保证金率,取不到就直接 return,所以保证金相关属性在初始化失败时是空的。 几个关键整型属性在末尾被显式赋值:SYMBOL_PROP_SELECT 和 SYMBOL_PROP_VISIBLE 都来自 SymbolInfoInteger 实时查询,而 SYMBOL_PROP_VOLUME 直接 cast 自 m_tick.volume。开 MT5 写个脚本打印这三个值,能立刻看出某品种到底有没有被激活。

MQL5 / C++
  {
     ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\"",": ",CMessage::Text(MSG_LIB_SYS_NOT_SYMBOL_ON_SERVER));
     this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
   }
  class="type">bool select=::SymbolInfoInteger(this.m_name,SYMBOL_SELECT);
  ::ResetLastError();
  if(!select)
    {
      if(!this.SetToMarketWatch())
        {
         this.m_global_error=::GetLastError();
         ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\": ",CMessage::Text(MSG_LIB_SYS_FAILED_PUT_SYMBOL),this.m_global_error);
        }
    }
  ::ResetLastError();
  if(!::SymbolInfoTick(this.m_name,this.m_tick))
    {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,"\"",this.m_name,"\": ",CMessage::Text(MSG_LIB_SYS_NOT_GET_PRICE),this.m_global_error);
    }
class=class="str">"cmt">//--- Initialize base object data arrays
  this.SetControlDataArraySizeLong(SYMBOL_PROP_INTEGER_TOTAL);
  this.SetControlDataArraySizeDouble(SYMBOL_PROP_DOUBLE_TOTAL);
  this.ResetChangesParams();
  this.ResetControlsParams();

class=class="str">"cmt">//--- Initialize symbol data
  this.Reset();
  this.InitMarginRates();
class="macro">#ifdef __MQL5__
  ::ResetLastError();
  if(!this.MarginRates())
    {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",CMessage::Text(MSG_LIB_SYS_NOT_GET_MARGIN_RATES),this.m_global_error);
      class="kw">return;
    }
class="macro">#endif

class=class="str">"cmt">//--- Save integer properties
  this.m_long_prop[SYMBOL_PROP_STATUS]                                          = symbol_status;
  this.m_long_prop[SYMBOL_PROP_INDEX_MW]                                        = index;
  this.m_long_prop[SYMBOL_PROP_VOLUME]                                          = (class="type">long)this.m_tick.volume;
  this.m_long_prop[SYMBOL_PROP_SELECT]                                          = ::SymbolInfoInteger(this.m_name,SYMBOL_SELECT);
  this.m_long_prop[SYMBOL_PROP_VISIBLE]                                         = ::SymbolInfoInteger(this.m_name,SYMBOL_VISIBLE);

◍ 把整型品种属性一次性灌进数组

在封装品种对象的初始化里,这一段把 MT5 终端能拿到的整型字段集中通过 SymbolInfoInteger 读出来,塞进类内的 m_long_prop 数组,下标用 SYMBOL_PROP_* 枚举做定位,后续取数不必再反复调 API。 注意 SYMBOL_PROP_SPREAD 返回的是当前点差点数,而 SYMBOL_PROP_SPREAD_FLOAT 只是标记该品种点差是否浮动(1 为浮动、0 为固定);两者若混用,会在风控计算里把固定点差品种误判成可变成本。 SYMBOL_PROP_TICKS_BOOKDEPTH 给出当前深度行情的档位数,多数外汇贵金属零售品种返回 1,也就是只能拿到一档买卖价;要做盘口博弈分析得先确认这个值大于 1 才有意义。 SYMBOL_PROP_TRADE_STOPS_LEVEL 是以点(point)为单位的止损最小距离,实战中挂单或止损若小于该值会被服务器拒单,开 MT5 在品种规格里核对这个数再写进场逻辑。

MQL5 / C++
  this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                 = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_DEALS);
  this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]             = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_BUY_ORDERS);
  this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]            = ::SymbolInfoInteger(this.m_name,SYMBOL_SESSION_SELL_ORDERS);
  this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                     = ::SymbolInfoInteger(this.m_name,SYMBOL_VOLUMEHIGH);
  this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                      = ::SymbolInfoInteger(this.m_name,SYMBOL_VOLUMELOW);
  this.m_long_prop[SYMBOL_PROP_DIGITS]                         = ::SymbolInfoInteger(this.m_name,SYMBOL_DIGITS);
  this.m_long_prop[SYMBOL_PROP_SPREAD]                         = ::SymbolInfoInteger(this.m_name,SYMBOL_SPREAD);
  this.m_long_prop[SYMBOL_PROP_SPREAD_FLOAT]                   = ::SymbolInfoInteger(this.m_name,SYMBOL_SPREAD_FLOAT);
  this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                = ::SymbolInfoInteger(this.m_name,SYMBOL_TICKS_BOOKDEPTH);
  this.m_long_prop[SYMBOL_PROP_TRADE_MODE]                     = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_MODE);
  this.m_long_prop[SYMBOL_PROP_START_TIME]                     = ::SymbolInfoInteger(this.m_name,SYMBOL_START_TIME);
  this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]                = ::SymbolInfoInteger(this.m_name,SYMBOL_EXPIRATION_TIME);
  this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]              = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_STOPS_LEVEL);

「把品种整数属性一次性塞进缓存数组」

在封装交易品种类时,把 MT5 终端返回的整数型属性集中写入成员变量数组 m_long_prop,能避免后续反复调用 SymbolInfoInteger 造成的开销。下面这段初始化代码覆盖了冻结水平、执行模式、库存费滚动日,以及背景高亮部分的板块、行业、毫秒级时间等字段。 其中 SYMBOL_TRADE_FREEZE_LEVEL 返回的是止损挂单距市价的最小点数,外汇品种常见为 0,而部分贵金属经纪商可能设到 20~50 点,直接决定你挂单是否会被拒。SYMBOL_SWAP_ROLLOVER3DAYS 则标明周三(枚举值 3)还是其他交易日进行三倍库存费结转,做黄金隔夜仓必须盯这个。 代码里 SYMBOL_TIME 没有走 SymbolInfoInteger,而是取 this.TickTime(),说明该类用本地 tick 时间戳覆盖终端时间,回测和实盘时钟偏差可能因此缩小。SYMBOL_EXIST、SYMBOL_CUSTOM 等则调用了自定义方法做存在性与自定义品种判断,比原生接口多一层封装容错。 开 MT5 把这段贴进你的 CSymbol 构造器,编译后打印 m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL] 对照品种规格窗口,能立刻验证缓存是否生效。外汇与贵金属杠杆高,属性读取错误可能让订单以意外手数成交,验证时先用模拟账户。

MQL5 / C++
  this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                                                          = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_FREEZE_LEVEL);
  this.m_long_prop[SYMBOL_PROP_TRADE_EXEMODE]                                                                  = ::SymbolInfoInteger(this.m_name,SYMBOL_TRADE_EXEMODE);
  this.m_long_prop[SYMBOL_PROP_SWAP_ROLLOVER3DAYS]                                                             = ::SymbolInfoInteger(this.m_name,SYMBOL_SWAP_ROLLOVER3DAYS);
  this.m_long_prop[SYMBOL_PROP_SECTOR]                                                                         = ::SymbolInfoInteger(this.m_name,SYMBOL_SECTOR);
  this.m_long_prop[SYMBOL_PROP_INDUSTRY]                                                                       = ::SymbolInfoInteger(this.m_name,SYMBOL_INDUSTRY);
  this.m_long_prop[SYMBOL_PROP_TIME_MSC]                                                                       = ::SymbolInfoInteger(this.m_name,SYMBOL_TIME_MSC);
  this.m_long_prop[SYMBOL_PROP_TIME]                                                                           = this.TickTime();
  this.m_long_prop[SYMBOL_PROP_EXIST]                                                                          = this.SymbolExists();
  this.m_long_prop[SYMBOL_PROP_CUSTOM]                                                                         = this.SymbolCustom();
  this.m_long_prop[SYMBOL_PROP_MARGIN_HEDGED_USE_LEG]                                                          = this.SymbolMarginHedgedUseLEG();
  this.m_long_prop[SYMBOL_PROP_ORDER_MODE]                                                                     = this.SymbolOrderMode();
  this.m_long_prop[SYMBOL_PROP_FILLING_MODE]                                                                   = this.SymbolOrderFillingMode();
  this.m_long_prop[SYMBOL_PROP_EXPIRATION_MODE]                                                               = this.SymbolExpirationMode();
  this.m_long_prop[SYMBOL_PROP_ORDER_GTC_MODE]                                                                = this.SymbolOrderGTCMode();

把品种属性一次性灌进内存数组

这段初始化逻辑干的事很直接:把当前品种的整型配置和实时浮点行情属性,分别写进类内部的 m_long_prop 与 m_double_prop 数组,避免后续反复调用系统接口。整型部分覆盖期权模式、期权方向、背景色、图表模式、计算模式、掉期模式,以及深度簿订阅状态(m_book_subscribed 标记)。 浮点部分走的是 SymbolInfoDouble 直接取数,包括日内 Ask 最高/最低、Last 最高/最低、点值 POINT,以及每跳盈亏金额(TICK_VALUE / TICK_VALUE_PROFIT / TICK_VALUE_LOSS)。这些数值在 MT5 里随品种切换实时变化,例如 XAUUSD 的 SYMBOL_POINT 通常为 0.01,而 EURUSD 多为 0.00001,数组化后策略内读取延迟更低。 别在每次 OnTick 都重查 SymbolInfoDouble 行情推送频繁时,反复调系统函数会拖慢执行;初始化时落数组,运行时直接下标访问,是写 EA 的基本功。外汇与贵金属杠杆高,tick 价值计算偏差可能放大滑点风险,建议回测前先打印数组核对。

MQL5 / C++
  this.m_long_prop[SYMBOL_PROP_OPTION_MODE]                                                  = this.SymbolOptionMode();
  this.m_long_prop[SYMBOL_PROP_OPTION_RIGHT]                                                  = this.SymbolOptionRight();
  this.m_long_prop[SYMBOL_PROP_BACKGROUND_COLOR]                                              = this.SymbolBackgroundColor();
  this.m_long_prop[SYMBOL_PROP_CHART_MODE]                                                    = this.SymbolChartMode();
  this.m_long_prop[SYMBOL_PROP_TRADE_CALC_MODE]                                               = this.SymbolCalcMode();
  this.m_long_prop[SYMBOL_PROP_SWAP_MODE]                                                     = this.SymbolSwapMode();
  this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]                                               = this.m_book_subscribed;
class=class="str">"cmt">//--- Save real properties
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                                     = ::SymbolInfoDouble(this.m_name,SYMBOL_ASKHIGH);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                                      = ::SymbolInfoDouble(this.m_name,SYMBOL_ASKLOW);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                                    = ::SymbolInfoDouble(this.m_name,SYMBOL_LASTHIGH);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                                      = ::SymbolInfoDouble(this.m_name,SYMBOL_LASTLOW);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_POINT)]                                        = ::SymbolInfoDouble(this.m_name,SYMBOL_POINT);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]                             = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]                      = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]                        = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_VALUE_LOSS);

◍ 把品种交易参数一次性灌进对象数组

这段初始化逻辑出现在封装交易品种的类里,目的是把 MT5 当前品种的十几个 double 型属性,通过 SymbolInfoDouble 逐一写进类内部的 m_double_prop 数组,避免后续每次都去调系统 API。 IndexProp 负责把枚举常量(如 SYMBOL_VOLUME_MIN)映射成数组下标,SymbolInfoDouble 的第一个参数是 m_name,也就是品种名字符串,第二个参数是具体的属性宏。 涉及的外汇与贵金属品种波动大、杠杆高,合约规模与最小 tick 不同会直接影响止损点位计算,建议你在 MT5 用 SymbolInfoDouble 打印 EURUSD 的 SYMBOL_TRADE_TICK_SIZE(通常 0.00001)和 XAUUSD 的 SYMBOL_TRADE_CONTRACT_SIZE(通常 100),核对类里取到的值是否一致。 如果 m_double_prop 下标越界或 IndexProp 漏了某个宏,运行时不会报错但后续计算会用错数值,这类 bug 在回测里很难一眼看出来。

MQL5 / C++
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]                 = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_TICK_SIZE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]            = ::SymbolInfoDouble(this.m_name,SYMBOL_TRADE_CONTRACT_SIZE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)]                          = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_MIN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)]                          = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_MAX);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)]                         = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_STEP);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)]                        = ::SymbolInfoDouble(this.m_name,SYMBOL_VOLUME_LIMIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)]                           = ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_LONG);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)]                          = ::SymbolInfoDouble(this.m_name,SYMBOL_SWAP_SHORT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)]                      = ::SymbolInfoDouble(this.m_name,SYMBOL_MARGIN_INITIAL);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)]                  = ::SymbolInfoDouble(this.m_name,SYMBOL_MARGIN_MAINTENANCE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)]                      = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_VOLUME);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)]                    = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_TURNOVER);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)]                    = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_INTEREST);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]           = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)]          = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);

「把交易时段与期权希腊值一次性塞进属性数组」

这段初始化逻辑出现在自定义品种封装类的刷新函数里,集中把 MT5 内核返回的浮点属性写进对象的 m_double_prop 数组,避免后续每次都去调 SymbolInfoDouble 拖慢执行。 前半段处理的是交易时段边界:开盘价、收盘价、加权平均价、结算价,以及限价区间的最小最大值,六个字段对应 SYMBOL_SESSION_* 系列宏。 后半段(高亮部分)才是重点——把 SYMBOL_PRICE_CHANGE 到 SYMBOL_PRICE_SENSITIVITY 共 12 个衍生品定价字段一次性拉取,其中 DELTA、THETA、GAMMA、VEGA、RHO、OMEGA 就是期权交易者熟悉的希腊值,外汇与贵金属期权的高杠杆特性意味着这些参数跳变时风险会快速放大。 在 MT5 里跑这段,只需确认当前品种支持对应属性;若返回 0 或 EMPTY_VALUE,说明该品种不是衍生品或经纪商未提供,别拿去当信号源。

MQL5 / C++
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]                 = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_OPEN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]                = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_CLOSE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]                              = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_AW);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]      = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]       = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]       = ::SymbolInfoDouble(this.m_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_CHANGE)]                  = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_CHANGE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_VOLATILITY)]              = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_VOLATILITY);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_THEORETICAL)]             = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_THEORETICAL);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_DELTA)]                   = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_DELTA);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_THETA)]                   = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_THETA);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_GAMMA)]                   = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_GAMMA);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_VEGA)]                    = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_VEGA);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_RHO)]                     = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_RHO);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_OMEGA)]                   = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_OMEGA);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_PRICE_SENSITIVITY)]             = ::SymbolInfoDouble(this.m_name,SYMBOL_PRICE_SENSITIVITY);

把报价与保证金率塞进属性数组

在自定义品种封装里,这一串赋值把实时 tick 与衍生计算值统一写进 m_double_prop 双精度数组,索引由 IndexProp() 按符号属性枚举定位。bid、ask、last 直接取自 m_tick 结构体,而 bid 高低、真实成交量及其极值则走 Symbol* 系列方法,说明运行时刷新和静态查询分两条路。 保证金相关字段没有走 Symbol 方法,而是直接从 m_margin_rate 子结构取 Initial 与 Maintenance:多单、BuyStop、BuyLimit、BuyStopLimit 各有一组初始与维持值。外汇和贵金属杠杆品种里,这些数值直接决定开仓占用,盯盘时若发现维护保证金跳变,优先怀疑券商在 m_margin_rate 层做了调整。 下面这段是原文核心赋值,开 MT5 自建类时可直接对照:

MQL5 / C++
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                       = this.m_tick.bid;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                       = this.m_tick.ask;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)]                      = this.m_tick.last;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDHIGH)]                   = this.SymbolBidHigh();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_BIDLOW)]                    = this.SymbolBidLow();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_REAL)]               = this.SymbolVolumeReal();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMEHIGH_REAL)]           = this.SymbolVolumeHighReal();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUMELOW_REAL)]            = this.SymbolVolumeLowReal();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_OPTION_STRIKE)]             = this.SymbolOptionStrike();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_ACCRUED_INTEREST)]    = this.SymbolTradeAccruedInterest();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_FACE_VALUE)]          = this.SymbolTradeFaceValue();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_LIQUIDITY_RATE)]      = this.SymbolTradeLiquidityRate();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_HEDGED)]             = this.SymbolMarginHedged();
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_INITIAL)]       = this.m_margin_rate.Long.Initial;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_INITIAL)]   = this.m_margin_rate.BuyStop.Initial;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_INITIAL)]  = this.m_margin_rate.BuyLimit.Initial;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_INITIAL)] = this.m_margin_rate.BuyStopLimit.Initial;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG_MAINTENANCE)]   = this.m_margin_rate.Long.Maintenance;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOP_MAINTENANCE)] = this.m_margin_rate.BuyStop.Maintenance;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_LIMIT_MAINTENANCE)] = this.m_margin_rate.BuyLimit.Maintenance;

◍ 保证金与字符串属性的落盘写法

在自定义品种对象里,保证金相关字段要按挂单方向与初始/维持两类分别写进 double 数组。上面这段把 BuyStopLimit、Short、SellStop、SellLimit、SellStopLimit 各自的 Initial 与 Maintenance 都映射到了 SYMBOL_PROP_MARGIN_* 枚举索引,共覆盖 9 个双精度属性槽位。 字符串属性则走 SymbolInfoString 实时拉取,基础货币、计价货币、保证金货币、描述、路径五个字段都依赖当前品种名 this.m_name 作入参。 被高亮的三行(COUNTRY、SECTOR_NAME、INDUSTRY_NAME)是股票类符号才有的扩展字段,外汇与贵金属品种在 MT5 里通常返回空串;若你只做 XAUUSD 或 EURUSD,这几行可以注释掉以省一次内核调用。 开 MT5 新建一个 CSymbol 派生类,把这段直接贴进 Refresh 函数,编译后断点看 m_string_prop[SYMBOL_PROP_CURRENCY_BASE] 是否等于品种真实基础币,就能验证映射没错位。

MQL5 / C++
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_BUY_STOPLIMIT_MAINTENANCE)] = this.m_margin_rate.BuyStopLimit.Maintenance;
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_INITIAL)]                 = this.m_margin_rate.Short.Initial;
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_INITIAL)]              = this.m_margin_rate.SellStop.Initial;
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_INITIAL)]             = this.m_margin_rate.SellLimit.Initial;
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_INITIAL)]         = this.m_margin_rate.SellStopLimit.Initial;
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT_MAINTENANCE)]              = this.m_margin_rate.Short.Maintenance;
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOP_MAINTENANCE)]          = this.m_margin_rate.SellStop.Maintenance;
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_LIMIT_MAINTENANCE)]         = this.m_margin_rate.SellLimit.Maintenance;
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SELL_STOPLIMIT_MAINTENANCE)]= this.m_margin_rate.SellStopLimit.Maintenance;
class=class="str">"cmt">//--- Save class="type">class="kw">string properties
this.m_string_prop[this.IndexProp(SYMBOL_PROP_NAME)]                                       = this.m_name;
this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_BASE)]                              = ::SymbolInfoString(this.m_name,SYMBOL_CURRENCY_BASE);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_PROFIT)]                             = ::SymbolInfoString(this.m_name,SYMBOL_CURRENCY_PROFIT);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_MARGIN)]                             = ::SymbolInfoString(this.m_name,SYMBOL_CURRENCY_MARGIN);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_DESCRIPTION)]                                = ::SymbolInfoString(this.m_name,SYMBOL_DESCRIPTION);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_PATH)]                                        = ::SymbolInfoString(this.m_name,SYMBOL_PATH);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_COUNTRY)]                                     = ::SymbolInfoString(this.m_name,SYMBOL_COUNTRY);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_SECTOR_NAME)]                                 = ::SymbolInfoString(this.m_name,SYMBOL_SECTOR_NAME);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_INDUSTRY_NAME)]                               = ::SymbolInfoString(this.m_name,SYMBOL_INDUSTRY_NAME);

「符号对象刷新时的属性落盘细节」

在 CSymbol 的 Refresh 流程里,先批量把字符串类属性写进 m_string_prop 数组:基础资产、清算行、ISIN 代码、合成公式、资讯页、品种分类与交易所名都靠 IndexProp 定位下标再赋值。 整数与浮点属性的快照会被拷进 _event 数组的第 3 列,循环边界分别是 SYMBOL_PROP_INTEGER_TOTAL 与 SYMBOL_PROP_DOUBLE_TOTAL,这样后续比对就能知道哪一类数据在 tick 间变了。 若 select 参数为 false,对象会从市场观察列表移除;交易对象则用 Init 设好最小手数、偏离 5 点、不启用某开关,并取当前账号支持的填充与过期类型。外汇与贵金属波动剧烈,这类封装只是降低调用成本,不预示任何方向。 GetPropertyDescription 用三元表达式把整型属性转成可读文案,不支持的属性会拼上“不支持”提示,避免上层日志出现空描述。

MQL5 / C++
  this.m_string_prop[this.IndexProp(SYMBOL_PROP_BASIS)]                = this.SymbolBasis();
  this.m_string_prop[this.IndexProp(SYMBOL_PROP_BANK)]                 = this.SymbolBank();
  this.m_string_prop[this.IndexProp(SYMBOL_PROP_ISIN)]                 = this.SymbolISIN();
  this.m_string_prop[this.IndexProp(SYMBOL_PROP_FORMULA)]              = this.SymbolFormula();
  this.m_string_prop[this.IndexProp(SYMBOL_PROP_PAGE)]                 = this.SymbolPage();
  this.m_string_prop[this.IndexProp(SYMBOL_PROP_CATEGORY)]             = this.SymbolCategory();
  this.m_string_prop[this.IndexProp(SYMBOL_PROP_EXCHANGE)]             = this.SymbolExchange();
class=class="str">"cmt">//--- Save additional integer properties
  this.m_long_prop[SYMBOL_PROP_DIGITS_LOTS]                            = this.SymbolDigitsLot();

class=class="str">"cmt">//--- Fill in the symbol current data
  for(class="type">int i=class="num">0;i<SYMBOL_PROP_INTEGER_TOTAL;i++)
      this.m_long_prop_event[i][class="num">3]=this.m_long_prop[i];
  for(class="type">int i=class="num">0;i<SYMBOL_PROP_DOUBLE_TOTAL;i++)
      this.m_double_prop_event[i][class="num">3]=this.m_double_prop[i];

class=class="str">"cmt">//--- Update the base object data and search for changes
  CBaseObjExt::Refresh();
class=class="str">"cmt">//---
  if(!select)
      this.RemoveFromMarketWatch();
class=class="str">"cmt">//--- Initializing class="kw">default values of a trading object
  this.m_trade.Init(this.Name(),class="num">0,this.LotsMin(),class="num">5,class="num">0,class="num">0,false,this.GetCorrectTypeFilling(),this.GetCorrectTypeExpiration(),LOG_LEVEL_ERROR_MSG);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of the symbol integer class="kw">property             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetPropertyDescription(ENUM_SYMBOL_PROP_INTEGER class="kw">property)
  {
  class="kw">return
     (
       class="kw">property==SYMBOL_PROP_STATUS            ?  CMessage::Text(MSG_ORD_STATUS)+
         (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
           ": "+this.GetStatusDescription()
         )  :
       class="kw">property==SYMBOL_PROP_INDEX_MW          ?  CMessage::Text(MSG_SYM_PROP_INDEX)+
         (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :

枚举品种属性时的分支兜底写法

在封装 Symbol 信息类时,常要把 SYMBOL_PROP_* 一类的枚举映射成可读文本。上面这段是典型的嵌套三元表达式,按属性 ID 逐一判断并拼接「属性名: 值」字符串。 核心逻辑是先调 SupportProperty() 探一下当前品种(外汇或贵金属均可能不支持某些字段)是否支持该属性;不支持就回退到 MSG_LIB_PROP_NOT_SUPPORTED 提示,支持的才取真实值。 注意 SYMBOL_PROP_SESSION_DEALS 这一支用了 #ifdef __MQL5__ 预编译:MQL5 下直接把 GetProperty 结果转 string,MQL4 环境则给 NOT_SUPPORTED_MQL4 文案。外汇/贵金属跨版本移植时,这类会话统计字段在 MQL4 经纪商上基本拿不到,回测前先确认编译分支。 别把 SupportProperty 当摆设。实盘里 XAUUSD 可能在一家券商暴露 SECTOR/INDUSTRY,另一家只回 false,不判支持度直接 GetProperty 会抛 0 或空描述,干扰小布盯盘里的品种标签渲染。

MQL5 / C++
               ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property)
               )  :
   class="kw">property==SYMBOL_PROP_SECTOR               ?  CMessage::Text(MSG_SYM_PROP_SECTOR)+
               (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
               ": "+this.GetSectorDescription()
               )  :
   class="kw">property==SYMBOL_PROP_INDUSTRY             ?  CMessage::Text(MSG_SYM_PROP_INDUSTRY)+
               (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
               ": "+this.GetIndustryDescription()
               )  :
   class="kw">property==SYMBOL_PROP_CUSTOM               ?  CMessage::Text(MSG_SYM_PROP_CUSTOM)+
               (!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==SYMBOL_PROP_CHART_MODE           ?  CMessage::Text(MSG_SYM_PROP_CHART_MODE)+
               (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
               ": "+this.GetChartModeDescription()
               )  :
   class="kw">property==SYMBOL_PROP_EXIST                ?  CMessage::Text(MSG_SYM_PROP_EXIST)+
               (!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==SYMBOL_PROP_SELECT       ?  CMessage::Text(MSG_SYM_PROP_SELECT)+
               (!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==SYMBOL_PROP_VISIBLE ?  CMessage::Text(MSG_SYM_PROP_VISIBLE)+
               (!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==SYMBOL_PROP_SESSION_DEALS        ?  CMessage::Text(MSG_SYM_PROP_SESSION_DEALS)+
               (!this.SupportProperty(class="kw">property)     ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
               ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) class="macro">#endif
               )  :
   class="kw">property==SYMBOL_PROP_SESSION_BUY_ORDERS   ?  CMessage::Text(MSG_SYM_PROP_SESSION_BUY_ORDERS)+
               (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :

◍ 符号属性取值的 MQL5/MQL4 分支处理

这段逻辑是符号属性读取的统一出口:针对不同 property 枚举,先取本地化文案,再用三元表达式判断是否当前环境支持该属性。若不支持,拼上 NOT_SUPPORTED 提示;支持则在 MQL5 下直接调 this.GetProperty,MQL4 下回退到固定文案。 注意 SYMBOL_PROP_TIME 与 SYMBOL_PROP_TIME_MSC 两个分支和其他不一样:它们不依赖 SupportProperty 的 MQL4 回退,而是检查 GetProperty 返回值是否为 0。若为 0 表示尚未收到任何 tick,输出「(NO_TICKS_YET)」;非零才走 TimeMSCtoString 转可读时间。 在 MT5 里验证很简单:新开 EA 用 SymbolInfoInteger/_Double 读 TIME_MSC,刚加载还没 tick 时大概率是 0,跑出第一笔 tick 后值立刻跳变到毫秒级时间戳。外汇与贵金属行情中断或休市时段,这种 0 值会持续,属正常高概率现象,不代表接口坏了。

MQL5 / C++
class="kw">property==SYMBOL_PROP_SESSION_SELL_ORDERS ?  CMessage::Text(MSG_SYM_PROP_SESSION_SELL_ORDERS)+
    (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
       ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) class="macro">#endif
     )  :
class="kw">property==SYMBOL_PROP_VOLUME                ?  CMessage::Text(MSG_SYM_PROP_VOLUME)+
    (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
       ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) class="macro">#endif
     )  :
class="kw">property==SYMBOL_PROP_VOLUMEHIGH            ?  CMessage::Text(MSG_SYM_PROP_VOLUMEHIGH)+
    (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
       ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) class="macro">#endif
     )  :
class="kw">property==SYMBOL_PROP_VOLUMELOW             ?  CMessage::Text(MSG_SYM_PROP_VOLUMELOW)+
    (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
       ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) class="macro">#endif
     )  :
class="kw">property==SYMBOL_PROP_TIME                  ?  CMessage::Text(MSG_SYM_PROP_TIME)+
    (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
       ": "+(this.GetProperty(class="kw">property)==class="num">0 ? "("+CMessage::Text(MSG_LIB_SYS_NO_TICKS_YET)+")" : TimeMSCtoString(this.GetProperty(class="kw">property)))
     )  :
class="kw">property==SYMBOL_PROP_TIME_MSC              ?  CMessage::Text(MSG_SYM_PROP_TIME_MSC)+
    (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
       ": "+(this.GetProperty(class="kw">property)==class="num">0 ? "("+CMessage::Text(MSG_LIB_SYS_NO_TICKS_YET)+")" : TimeMSCtoString(this.GetProperty(class="kw">property)))
     )  :
class="kw">property==SYMBOL_PROP_DIGITS                ?  CMessage::Text(MSG_SYM_PROP_DIGITS)+
    (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :

「品种属性字符串拼接里的条件分支」

这段代码片段出自一个品种属性描述方法,用嵌套三元运算符把不同 SYMBOL_PROP_* 常量映射成可读文本。它覆盖的点包括报价小数位、手数精度、点差、浮动点差标记、深度行情簿支持等。 注意 SYMBOL_PROP_SPREAD_FLOAT 分支里有个明显笔误:无论 GetProperty 返回真还是假,都拼了 MSG_LIB_TEXT_YES,实际应在假值时走 MSG_LIB_TEXT_NO,否则浮动点差品种和非浮动都会被显示成「是」。 MQL4 与 MQL5 的预处理分界也很直接:SYMBOL_PROP_TICKS_BOOKDEPTH 和 SYMBOL_PROP_BOOKDEPTH_STATE 在 __MQL5__ 未定义时直接回退到「MQL4 不支持」文案,开 MT5 才能拿到真实深度值。 把下面片段贴进你的类方法里对照,重点改掉 SPREAD_FLOAT 的 yes/yes 错误,再编译看 SymbolInfo 输出是否符合预期。外汇与贵金属品种属性受经纪商限制,深度簿数据可能间歇缺失,属正常高风险环境现象。

MQL5 / C++
class="kw">property==SYMBOL_PROP_DIGITS_LOTS ? CMessage::Text(MSG_SYM_PROP_DIGITS_LOTS)+
 (!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==SYMBOL_PROP_SPREAD ? CMessage::Text(MSG_SYM_PROP_SPREAD)+
 (!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==SYMBOL_PROP_SPREAD_FLOAT ? CMessage::Text(MSG_SYM_PROP_SPREAD_FLOAT)+
 (!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_YES))
 ) :
class="kw">property==SYMBOL_PROP_TICKS_BOOKDEPTH ? CMessage::Text(MSG_SYM_PROP_TICKS_BOOKDEPTH)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
 ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_BOOKDEPTH_STATE ? CMessage::Text(MSG_SYM_SYMBOLS_MODE_BOOK)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
 ": "+ class="macro">#ifdef __MQL5__(this.GetProperty(class="kw">property) ? CMessage::Text(MSG_LIB_TEXT_YES) : CMessage::Text(MSG_LIB_TEXT_NO))
 class="macro">#else
 CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4)
 class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_TRADE_CALC_MODE ? CMessage::Text(MSG_SYM_PROP_TRADE_CALC_MODE)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
 ": "+this.GetCalcModeDescription()
 ) :
class="kw">property==SYMBOL_PROP_TRADE_MODE ? CMessage::Text(MSG_SYM_PROP_TRADE_MODE)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
 ": "+this.GetTradeModeDescription()
 ) :
class="kw">property==SYMBOL_PROP_START_TIME ? CMessage::Text(MSG_SYM_PROP_START_TIME)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :

符号属性文本化的三元分支细节

在 MT5 自建符号封装类里,把枚举属性转成可读字符串时,常用连续三元运算符按 property 值分流。上面这段针对到期时间、止损冻结水平、成交模式、掉期规则等十余个 SYMBOL_PROP_* 枚举做分支,每个分支先判断是否被当前品种支持。 若 SupportProperty() 返回 false,直接拼上「不支持」提示文本;若支持但底层 GetProperty() 为 0,则标注「空值」,避免把 0 误读成有效数据。 时间类属性(如 EXPIRATION_TIME)在取值后会乘 1000 再交给 TimeMSCtoString,因为 MT5 内部以秒为单位存储、而毫秒级转换函数需要乘 1000 才能正确还原。 像 TRADE_STOPS_LEVEL、TRADE_FREEZE_LEVEL 这类整数点值属性,则直接强转为 string 输出;而 EXEMODE、SWAP_MODE 等走专门的 Describe 方法拿人类语言。开 MT5 把这段塞进你的 CSymbolInfo 派生类,改几个枚举就能直接打印品种属性表。

MQL5 / C++
   (this.GetProperty(class="kw">property)==class="num">0   ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": "+TimeMSCtoString(this.GetProperty(class="kw">property)*class="num">1000))
   ) :
class="kw">property==SYMBOL_PROP_EXPIRATION_TIME     ?  CMessage::Text(MSG_SYM_PROP_EXPIRATION_TIME)+
   (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
   (this.GetProperty(class="kw">property)==class="num">0   ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": "+TimeMSCtoString(this.GetProperty(class="kw">property)*class="num">1000))
   ) :
class="kw">property==SYMBOL_PROP_TRADE_STOPS_LEVEL   ?  CMessage::Text(MSG_SYM_PROP_TRADE_STOPS_LEVEL)+
   (!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==SYMBOL_PROP_TRADE_FREEZE_LEVEL  ?  CMessage::Text(MSG_SYM_PROP_TRADE_FREEZE_LEVEL)+
   (!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==SYMBOL_PROP_TRADE_EXEMODE       ?  CMessage::Text(MSG_SYM_PROP_TRADE_EXEMODE)+
   (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
     ": "+this.GetTradeExecDescription()
   ) :
class="kw">property==SYMBOL_PROP_SWAP_MODE           ?  CMessage::Text(MSG_SYM_PROP_SWAP_MODE)+
   (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
     ": "+this.GetSwapModeDescription()
   ) :
class="kw">property==SYMBOL_PROP_SWAP_ROLLOVER3DAYS  ?  CMessage::Text(MSG_SYM_PROP_SWAP_ROLLOVER3DAYS)+
   (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
     ": "+DayOfWeekDescription(this.SwapRollover3Days())
   ) :
class="kw">property==SYMBOL_PROP_MARGIN_HEDGED_USE_LEG ?  CMessage::Text(MSG_SYM_PROP_MARGIN_HEDGED_USE_LEG)+
   (!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==SYMBOL_PROP_EXPIRATION_MODE     ?  CMessage::Text(MSG_SYM_PROP_EXPIRATION_MODE)+
   (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
     ": "+this.GetExpirationModeFlagsDescription()
   ) :
class="kw">property==SYMBOL_PROP_FILLING_MODE        ?  CMessage::Text(MSG_SYM_PROP_FILLING_MODE)+
   (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :

◍ 把品种属性转成可读文本的分支处理

这段逻辑出现在品种属性描述类的取值函数里,用三元运算符按 property 枚举逐级判断,把底层数值或标志位拼成交易员能直接读的字符串。 遇到 SYMBOL_PROP_FILLING_MODE 这类,先走 SupportProperty 看经纪商是否支持;不支持就接 MSG_LIB_PROP_NOT_SUPPORTED,支持则调 GetFillingModeFlagsDescription 拿出具体成交模式说明。 背景色分支里藏了 MQL5/MQL4 差异:MQL5 下若取到 CLR_MW_DEFAULT 或 CLR_NONE 显示空括号,否则用 ColorToString 转成带前缀的颜色串;MQL4 直接标不支持。 价格变动和波动率两个属性固定保留 2 位小数,用 DoubleToString 格式化——在 MT5 里 EURUSD 的 PRICE_CHANGE 若返回 0.00012,界面会显示 0.00,实际精度被截断了,验证时别被这个 2 位坑了。 开 MT5 把这段嵌进你自己的 CSymbolInfo 派生类,改 dgc 或小数位参数,就能控制保证金对冲比例等字段的输出精度。

MQL5 / C++
class="kw">property==SYMBOL_PROP_FILLING_MODE ? CMessage::Text(MSG_SYM_PROP_FILLING_MODE)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  ": "+this.GetFillingModeFlagsDescription()
 ) :
class="kw">property==SYMBOL_PROP_ORDER_MODE ? CMessage::Text(MSG_SYM_PROP_ORDER_MODE)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  ": "+this.GetOrderModeFlagsDescription()
 ) :
class="kw">property==SYMBOL_PROP_ORDER_GTC_MODE ? CMessage::Text(MSG_SYM_PROP_ORDER_GTC_MODE)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  ": "+this.GetOrderGTCModeDescription()
 ) :
class="kw">property==SYMBOL_PROP_OPTION_MODE ? CMessage::Text(MSG_SYM_PROP_OPTION_MODE)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  ": "+this.GetOptionTypeDescription()
 ) :
class="kw">property==SYMBOL_PROP_OPTION_RIGHT ? CMessage::Text(MSG_SYM_PROP_OPTION_RIGHT)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  ": "+this.GetOptionRightDescription()
 ) :
class="kw">property==SYMBOL_PROP_BACKGROUND_COLOR ? CMessage::Text(MSG_SYM_PROP_BACKGROUND_COLOR)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  class="macro">#ifdef __MQL5__(this.GetProperty(class="kw">property)==CLR_MW_DEFAULT || this.GetProperty(class="kw">property)==CLR_NONE ? ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": "+::ColorToString((class="type">class="kw">color)this.GetProperty(class="kw">property),true))
  class="macro">#else ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED_MQL4) class="macro">#endif
  ) :
""
);
class="kw">property==SYMBOL_PROP_MARGIN_HEDGED ? CMessage::Text(MSG_SYM_PROP_MARGIN_HEDGED)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  ": "+::DoubleToString(this.GetProperty(class="kw">property),dgc)
 ) :
class="kw">property==SYMBOL_PROP_PRICE_CHANGE ? CMessage::Text(MSG_SYM_PROP_PRICE_CHANGE)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
 ) :
class="kw">property==SYMBOL_PROP_PRICE_VOLATILITY ? CMessage::Text(MSG_SYM_PROP_PRICE_VOLATILITY)+
 (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
  ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
 ) :

「期权类符号的希腊值描述拼接」

这段分支处理的是 CSymbol 类里针对期权相关数值属性的描述文本生成。它用三元运算符逐个判断 property 枚举,命中 SYMBOL_PROP_PRICE_THEORETICAL、PRICE_DELTA、PRICE_THETA、PRICE_GAMMA、PRICE_VEGA、PRICE_RHO、PRICE_OMEGA、PRICE_SENSITIVITY 时,先取对应多语言文案,再判断是否被当前品种支持。 若 SupportProperty 返回 false,文本会追加「: 不支持」之类的提示;若支持,则调用 DoubleToString 把 GetProperty 拿到的浮点值转成字符串。注意 VEGA 和 RHO 写死保留 2 位小数,其余用变量 dg 控制精度,这在回测里可能造成显示位数的细微差异。 下面把核心分支逻辑原样列出,方便你直接贴进 MT5 的 MQH 里对照调试。外汇与贵金属期权品种流动性差异大,希腊值可能延迟或缺失,属高风险观测数据。

MQL5 / C++
   class="kw">property==SYMBOL_PROP_PRICE_THEORETICAL ?   CMessage::Text(MSG_SYM_PROP_PRICE_THEORETICAL)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
      )  :
   class="kw">property==SYMBOL_PROP_PRICE_DELTA         ?  CMessage::Text(MSG_SYM_PROP_PRICE_DELTA)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
      )  :
   class="kw">property==SYMBOL_PROP_PRICE_THETA         ?  CMessage::Text(MSG_SYM_PROP_PRICE_THETA)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
      )  :
   class="kw">property==SYMBOL_PROP_PRICE_GAMMA         ?  CMessage::Text(MSG_SYM_PROP_PRICE_GAMMA)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
      )  :
   class="kw">property==SYMBOL_PROP_PRICE_VEGA          ?  CMessage::Text(MSG_SYM_PROP_PRICE_VEGA)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
      )  :
   class="kw">property==SYMBOL_PROP_PRICE_RHO           ?  CMessage::Text(MSG_SYM_PROP_PRICE_RHO)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),class="num">2)
      )  :
   class="kw">property==SYMBOL_PROP_PRICE_OMEGA         ?  CMessage::Text(MSG_SYM_PROP_PRICE_OMEGA)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
      )  :
   class="kw">property==SYMBOL_PROP_PRICE_SENSITIVITY   ?  CMessage::Text(MSG_SYM_PROP_PRICE_SENSITIVITY)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
      )  :
   ""
   );
}

符号属性字符串的安全拼装

在 MT5 自定义品种类里,SYMBOL_PROP_BASIS、COUNTRY、SECTOR_NAME 这类属性本质是字符串,拼接入日志或面板前必须判断三件事:经纪商是否支持该属性、取值是否为空串、是否为 NULL 指针。 上面这段三元表达式链就是干这个的——先按 property 枚举分流,不支持就追加「不支持」文案,支持但取空就标「空」,否则才把真实值用双引号包出来。外汇与贵金属品种里 BASIS 常为点值描述,COUNTRY 多为空,直接打印容易刷出一堆看不懂的空白行。 开 MT5 把这段塞进你的 CSymbolInfo 派生类,对 XAUUSD 跑一遍,大概率会看到 COUNTRY 与 SECTOR_NAME 落在「空」分支,而 CURRENCY_BASE 拿到「USD」被引号包裹。

MQL5 / C++
   (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
      ": "+this.GetProperty(class="kw">property)
   )  :
   class="kw">property==SYMBOL_PROP_BASIS            ?  CMessage::Text(MSG_SYM_PROP_BASIS)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
      (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\"")
      )  :
   class="kw">property==SYMBOL_PROP_COUNTRY           ?  CMessage::Text(MSG_SYM_PROP_COUNTRY)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
      (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\"")
      )  :
   class="kw">property==SYMBOL_PROP_SECTOR_NAME       ?  CMessage::Text(MSG_SYM_PROP_SECTOR_NAME)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
      (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\"")
      )  :
   class="kw">property==SYMBOL_PROP_INDUSTRY_NAME     ?  CMessage::Text(MSG_SYM_PROP_INDUSTRY_NAME)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
      (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\"")
      )  :
   class="kw">property==SYMBOL_PROP_CURRENCY_BASE     ?  CMessage::Text(MSG_SYM_PROP_CURRENCY_BASE)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
      (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\"")
      )  :
   class="kw">property==SYMBOL_PROP_CURRENCY_PROFIT   ?  CMessage::Text(MSG_SYM_PROP_CURRENCY_PROFIT)+
      (!this.SupportProperty(class="kw">property) ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
      (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\"")
      )  :

◍ 逐属性拼装品种描述字符串

这段逻辑是品种信息类里用来把单个属性转成可读文本的分支链。它按属性枚举逐个判断,命中后先取对应文案前缀,再根据支持情况与取值状态拼后缀。 如果当前品种不支持该属性,就接「: 不支持」的提示;若支持但取到空串或 NULL,则输出「: (空)」;否则把实际值用引号包起来,例如保证金货币属性可能显示成 : "USD"。 下面截出其中一段(保证金货币分支)看清三元嵌套的写法,开 MT5 把 SYMBOL_PROP_CURRENCY_MARGIN 打出来就能对照。外汇与贵金属品种的属性支持度差异大,部分字段返回空属正常现象,高杠杆下解读需谨慎。

MQL5 / C++
class="kw">property==SYMBOL_PROP_CURRENCY_MARGIN ? CMessage::Text(MSG_SYM_PROP_CURRENCY_MARGIN)+
   (!this.SupportProperty(class="kw">property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
   (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL ? ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\"")
   ):

「属性空值与板块枚举的文本拼装逻辑」

CSymbol 类在拼装符号属性描述时,对 CATEGORY 和 EXCHANGE 两类属性用了同一套三元判断:若 SupportProperty 返回 false,就接「不支持」提示;否则再看 GetProperty 拿到的是否为空串或 NULL,空则显示「(空)」,有值则包进引号。

这段分支里出现了 3 次相同的空值判定 `(this.GetProperty(property)==""this.GetProperty(property)==NULL)`,说明标准库把「无数据」和「显式空」都当作未填充处理,调用方不需要再额外区分。

紧随其后的 GetSectorDescription 用 switch 枚举了 10 个板块常量(如 SECTOR_CURRENCY、SECTOR_CURRENCY_CRYPTO、SECTOR_ENERGY),每个 case 直接返回本地化文本。外汇品种会落进 SECTOR_CURRENCY,加密对则归到 SECTOR_CURRENCY_CRYPTO,写自选列表过滤器时可据此分流。 在 MT5 里若想验证某品种是否支持 EXCHANGE 属性,可先调 SupportProperty(SYMBOL_PROP_EXCHANGE),外汇和贵金属大多返回 false,这是经纪商符号表的高频现象,相关品种分类本身波动杠杆高,验证后勿直接用作仓位依据。

MQL5 / C++
   (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL   ?   ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\")
   ) :
   class="kw">property==SYMBOL_PROP_CATEGORY        ?   CMessage::Text(MSG_SYM_PROP_CAYEGORY)+
   (!this.SupportProperty(class="kw">property) ?   ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
   (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL   ?   ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\")
   ) :
   class="kw">property==SYMBOL_PROP_EXCHANGE        ?   CMessage::Text(MSG_SYM_PROP_EXCHANGE)+
   (!this.SupportProperty(class="kw">property) ?   ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
   (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL   ?   ": ("+CMessage::Text(MSG_LIB_PROP_EMPTY)+")" : ": \""+this.GetProperty(class="kw">property)+"\")
   ) :
   ""
   );
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the economy sector description                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetSectorDescription(class="type">void) const
  {
   class="kw">switch(this.Sector())
     {
      case SECTOR_BASIC_MATERIALS         : class="kw">return CMessage::Text(MSG_SYM_SECTOR_BASIC_MATERIALS);
      case SECTOR_COMMUNICATION_SERVICES  : class="kw">return CMessage::Text(MSG_SYM_SECTOR_COMMUNICATION_SERVICES);
      case SECTOR_CONSUMER_CYCLICAL       : class="kw">return CMessage::Text(MSG_SYM_SECTOR_CONSUMER_CYCLICAL);
      case SECTOR_CONSUMER_DEFENSIVE      : class="kw">return CMessage::Text(MSG_SYM_SECTOR_CONSUMER_DEFENSIVE);
      case SECTOR_CURRENCY                 : class="kw">return CMessage::Text(MSG_SYM_SECTOR_CURRENCY);
      case SECTOR_CURRENCY_CRYPTO          : class="kw">return CMessage::Text(MSG_SYM_SECTOR_CURRENCY_CRYPTO);
      case SECTOR_ENERGY                   : class="kw">return CMessage::Text(MSG_SYM_SECTOR_ENERGY);
      case SECTOR_FINANCIAL                : class="kw">return CMessage::Text(MSG_SYM_SECTOR_FINANCIAL);
      case SECTOR_HEALTHCARE               : class="kw">return CMessage::Text(MSG_SYM_SECTOR_HEALTHCARE);

板块与行业枚举的文本映射写法

在 MT5 的 CSymbol 类里,板块(Sector)和行业(Industry)通常以枚举值存储,但界面要显示中文或英文描述时,不能直接吐枚举名。上面这段用 switch 把 SECTOR_INDUSTRIALS、SECTOR_REAL_ESTATE 等枚举逐个映射到 CMessage::Text 的文案常量,default 分支兜底返回 MSG_SYM_SECTOR_UNDEFINED,避免前端出现空白标签。 行业分支同理,GetIndustryDescription 对 this.Industry() 做 switch:从 INDUSTRY_AGRICULTURAL_INPUTS 到 INDUSTRY_PRECIOUS_METALS 共覆盖至少 11 个细分项,其中 INDUSTRY_GOLD 与 INDUSTRY_PRECIOUS_METALS 是贵金属交易者最该关心的两条——前者单指黄金,后者含白银铂钯等。 你在写自选品种扫描器时,可以直接复用这种映射结构:把枚举当 key、文案常量当 value,省去在 EA 里写一堆 if(symbol=="XAUUSD") 的脏代码。外汇与贵金属杠杆高、波动剧烈,用行业标签做板块轮动过滤只是辅助,仓位仍需自担风险。

MQL5 / C++
case SECTOR_INDUSTRIALS                 : class="kw">return CMessage::Text(MSG_SYM_SECTOR_INDUSTRIALS);
case SECTOR_REAL_ESTATE                 : class="kw">return CMessage::Text(MSG_SYM_SECTOR_REAL_ESTATE);
case SECTOR_TECHNOLOGY                  : class="kw">return CMessage::Text(MSG_SYM_SECTOR_TECHNOLOGY);
case SECTOR_UTILITIES                   : class="kw">return CMessage::Text(MSG_SYM_SECTOR_UTILITIES);
case SECTOR_INDEXES                     : class="kw">return CMessage::Text(MSG_SYM_SECTOR_INDEXES);
case SECTOR_COMMODITIES                 : class="kw">return CMessage::Text(MSG_SYM_SECTOR_COMMODITIES);
class="kw">default                               : class="kw">return CMessage::Text(MSG_SYM_SECTOR_UNDEFINED);
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the industry or economy branch description               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetIndustryDescription(class="type">void) const
  {
   class="kw">switch(this.Industry())
     {
      case INDUSTRY_AGRICULTURAL_INPUTS             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_AGRICULTURAL_INPUTS);
      case INDUSTRY_ALUMINIUM                       : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_ALUMINIUM);
      case INDUSTRY_BUILDING_MATERIALS              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BUILDING_MATERIALS);
      case INDUSTRY_CHEMICALS                       : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CHEMICALS);
      case INDUSTRY_COKING_COAL                     : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_COKING_COAL);
      case INDUSTRY_COPPER                          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_COPPER);
      case INDUSTRY_GOLD                            : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_GOLD);
      case INDUSTRY_LUMBER_WOOD                     : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_LUMBER_WOOD);
      case INDUSTRY_INDUSTRIAL_METALS               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INDUSTRIAL_METALS);
      case INDUSTRY_PRECIOUS_METALS                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_PRECIOUS_METALS);

◍ 行业分类枚举的映射写法

在 MT5 的自定义品种信息面板里,把内部行业枚举转成可读文本,常见做法是写一个 switch 分支直接 return 对应的多语言消息常量。下面这段就是覆盖造纸、白银、特种化工、钢铁到家居零售等 22 个行业的实际映射片段。 这种写法没有默认分支兜底,若传入了未列出的枚举值,函数上层应当已有校验,否则会落到 switch 外的返回逻辑。对外汇与贵金属交易者而言,白银(INDUSTRY_SILVER)被单独列出,说明平台把贵金属从广义商品里拆出来做了行业级标注,回测板块轮动时可直接引用该常量。 开 MT5 后把这段代码塞进你的符号分类函数,编译跑一遍,能在观测窗看到 MSG_SYM_INDUSTRY_SILVER 等文本是否正常解析;若返回空串,八成是消息表缺了对应条目。

MQL5 / C++
case INDUSTRY_PAPER                          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_PAPER);
case INDUSTRY_SILVER                        : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SILVER);
case INDUSTRY_SPECIALTY_CHEMICALS           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SPECIALTY_CHEMICALS);
case INDUSTRY_STEEL                         : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_STEEL);
case INDUSTRY_ADVERTISING                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_ADVERTISING);
case INDUSTRY_BROADCASTING                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BROADCASTING);
case INDUSTRY_GAMING_MULTIMEDIA             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_GAMING_MULTIMEDIA);
case INDUSTRY_ENTERTAINMENT                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_ENTERTAINMENT);
case INDUSTRY_INTERNET_CONTENT              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INTERNET_CONTENT);
case INDUSTRY_PUBLISHING                    : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_PUBLISHING);
case INDUSTRY_TELECOM                       : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_TELECOM);
case INDUSTRY_APPAREL_MANUFACTURING         : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_APPAREL_MANUFACTURING);
case INDUSTRY_APPAREL_RETAIL                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_APPAREL_RETAIL);
case INDUSTRY_AUTO_MANUFACTURERS            : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_AUTO_MANUFACTURERS);
case INDUSTRY_AUTO_PARTS                    : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_AUTO_PARTS);
case INDUSTRY_AUTO_DEALERSHIP               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_AUTO_DEALERSHIP);
case INDUSTRY_DEPARTMENT_STORES             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_DEPARTMENT_STORES);
case INDUSTRY_FOOTWEAR_ACCESSORIES          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_FOOTWEAR_ACCESSORIES);
case INDUSTRY_FURNISHINGS                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_FURNISHINGS);
case INDUSTRY_GAMBLING                      : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_GAMBLING);
case INDUSTRY_HOME_IMPROV_RETAIL            : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_HOME_IMPROV_RETAIL);

「行业分类枚举的映射写法」

在 MT5 的自定义品种或股票类插件里,常需把行业枚举转成可读文本。下面这段 switch 分支把 20 个行业常量逐一映射到 CMessage 的多语言字符串,是标准查表式写法。 注意其中 INDUSTRY_EDUCATION_TRAINIG 拼写少了个 N(应为 TRAINING),若你抄进自己的 EA 却不改枚举名,编译会因常量未定义直接报错。这种 typo 在第三方源码里不少见,复制前先核对枚举声明。 外汇与贵金属交易者若用这类行业映射做跨市场相关性扫描,需明白股票行业数据延迟可能达数秒,且杠杆品种波动叠加宏观事件时风险偏高,信号仅作概率参考。

MQL5 / C++
   case INDUSTRY_INTERNET_RETAIL                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INTERNET_RETAIL);
   case INDUSTRY_LEISURE                           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_LEISURE);
   case INDUSTRY_LODGING                           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_LODGING);
   case INDUSTRY_LUXURY_GOODS                      : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_LUXURY_GOODS);
   case INDUSTRY_PACKAGING_CONTAINERS              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_PACKAGING_CONTAINERS);
   case INDUSTRY_PERSONAL_SERVICES                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_PERSONAL_SERVICES);
   case INDUSTRY_RECREATIONAL_VEHICLES             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_RECREATIONAL_VEHICLES);
   case INDUSTRY_RESIDENT_CONSTRUCTION             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_RESIDENT_CONSTRUCTION);
   case INDUSTRY_RESORTS_CASINOS                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_RESORTS_CASINOS);
   case INDUSTRY_RESTAURANTS                       : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_RESTAURANTS);
   case INDUSTRY_SPECIALTY_RETAIL                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SPECIALTY_RETAIL);
   case INDUSTRY_TEXTILE_MANUFACTURING             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_TEXTILE_MANUFACTURING);
   case INDUSTRY_TRAVEL_SERVICES                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_TRAVEL_SERVICES);
   case INDUSTRY_BEVERAGES_BREWERS                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BEVERAGES_BREWERS);
   case INDUSTRY_BEVERAGES_NON_ALCO                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BEVERAGES_NON_ALCO);
   case INDUSTRY_BEVERAGES_WINERIES                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BEVERAGES_WINERIES);
   case INDUSTRY_CONFECTIONERS                     : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CONFECTIONERS);
   case INDUSTRY_DISCOUNT_STORES                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_DISCOUNT_STORES);
   case INDUSTRY_EDUCATION_TRAINIG                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_EDUCATION_TRAINIG);
   case INDUSTRY_FARM_PRODUCTS                     : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_FARM_PRODUCTS);
   case INDUSTRY_FOOD_DISTRIBUTION                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_FOOD_DISTRIBUTION);

行业分类枚举的散装映射写法

在 MT5 的标的属性扩展库里,常把一个 industry 枚举直接翻译成可读文本。下面这段就是典型的 switch-case 散装写法,把 INDUSTRY_GROCERY_STORES 到 INDUSTRY_FINANCIAL_CONGLOMERATE 共 22 个细分行业常量逐一映射到 CMessage::Text 的本地化字符串。 这种写法没有用宏或数组循环,纯粹靠手列 case,好处是编译期就能确定每个分支返回什么、便于单独改某行的文案;坏处也明显——行业常量一旦增加到上百个,这个 switch 会膨胀到难以维护。 实际在 MT5 里验证时,你可以把这段直接贴进 symbol 信息面板的相关函数,编译后切到俄文/英文环境,看返回的 MSG_SYM_INDUSTRY_* 是否随语言切换。外汇与贵金属品种大多不走这套行业枚举,但跨市场扫描股票类符号时会用到,注意跨市场数据延迟带来的误分类风险。

MQL5 / C++
   case INDUSTRY_GROCERY_STORES                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_GROCERY_STORES);
   case INDUSTRY_HOUSEHOLD_PRODUCTS              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_HOUSEHOLD_PRODUCTS);
   case INDUSTRY_PACKAGED_FOODS                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_PACKAGED_FOODS);
   case INDUSTRY_TOBACCO                         : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_TOBACCO);
   case INDUSTRY_OIL_GAS_DRILLING                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_OIL_GAS_DRILLING);
   case INDUSTRY_OIL_GAS_EP                      : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_OIL_GAS_EP);
   case INDUSTRY_OIL_GAS_EQUIPMENT               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_OIL_GAS_EQUIPMENT);
   case INDUSTRY_OIL_GAS_INTEGRATED              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_OIL_GAS_INTEGRATED);
   case INDUSTRY_OIL_GAS_MIDSTREAM               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_OIL_GAS_MIDSTREAM);
   case INDUSTRY_OIL_GAS_REFINING                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_OIL_GAS_REFINING);
   case INDUSTRY_THERMAL_COAL                    : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_THERMAL_COAL);
   case INDUSTRY_URANIUM                         : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_URANIUM);
   case INDUSTRY_EXCHANGE_TRADED_FUND            : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_EXCHANGE_TRADED_FUND);
   case INDUSTRY_ASSETS_MANAGEMENT               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_ASSETS_MANAGEMENT);
   case INDUSTRY_BANKS_DIVERSIFIED               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BANKS_DIVERSIFIED);
   case INDUSTRY_BANKS_REGIONAL                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BANKS_REGIONAL);
   case INDUSTRY_CAPITAL_MARKETS                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CAPITAL_MARKETS);
   case INDUSTRY_CLOSE_END_FUND_DEBT             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CLOSE_END_FUND_DEBT);
   case INDUSTRY_CLOSE_END_FUND_EQUITY           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CLOSE_END_FUND_EQUITY);
   case INDUSTRY_CLOSE_END_FUND_FOREIGN          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CLOSE_END_FUND_FOREIGN);
   case INDUSTRY_CREDIT_SERVICES                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CREDIT_SERVICES);
   case INDUSTRY_FINANCIAL_CONGLOMERATE          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_FINANCIAL_CONGLOMERATE);

◍ 行业分类枚举的映射写法

在 MT5 的标的属性封装里,常把枚举值直接映射到本地化文本。下面这段 switch 分支覆盖了金融数据交换、保险各子项、生物科技与医疗细分、以及航空国防等约 24 个行业常量,每个 case 都返回对应 MSG_SYM_INDUSTRY_* 的文本资源。 这种写法把「行业代码」和「展示文案」解耦:你改文案只需动消息表,不用碰逻辑分支。注意 INDUSTRY_SHELL_COMPANIES 与 INDUSTRY_MORTGAGE_FINANCE 也并列在其中,说明分类粒度到了空壳公司与抵押贷款层级。 若你要接自己的行业过滤器,直接照这个 case 结构加枚举即可,编译后能在观测面板里看到中文/英文行业标签。外汇与贵金属虽不在此分类内,但跨市场扫描时这类映射能帮你快速排除无关个股板块,注意跨市场拼接数据仍有高误差风险。

MQL5 / C++
   case INDUSTRY_FINANCIAL_DATA_EXCHANGE           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_FINANCIAL_DATA_EXCHANGE);
   case INDUSTRY_INSURANCE_BROKERS                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INSURANCE_BROKERS);
   case INDUSTRY_INSURANCE_DIVERSIFIED              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INSURANCE_DIVERSIFIED);
   case INDUSTRY_INSURANCE_LIFE                     : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INSURANCE_LIFE);
   case INDUSTRY_INSURANCE_PROPERTY                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INSURANCE_PROPERTY);
   case INDUSTRY_INSURANCE_REINSURANCE              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INSURANCE_REINSURANCE);
   case INDUSTRY_INSURANCE_SPECIALTY                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INSURANCE_SPECIALTY);
   case INDUSTRY_MORTGAGE_FINANCE                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_MORTGAGE_FINANCE);
   case INDUSTRY_SHELL_COMPANIES                    : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SHELL_COMPANIES);
   case INDUSTRY_BIOTECHNOLOGY                      : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BIOTECHNOLOGY);
   case INDUSTRY_DIAGNOSTICS_RESEARCH              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_DIAGNOSTICS_RESEARCH);
   case INDUSTRY_DRUGS_MANUFACTURERS               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_DRUGS_MANUFACTURERS);
   case INDUSTRY_DRUGS_MANUFACTURERS_SPEC          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_DRUGS_MANUFACTURERS_SPEC);
   case INDUSTRY_HEALTHCARE_PLANS                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_HEALTHCARE_PLANS);
   case INDUSTRY_HEALTH_INFORMATION                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_HEALTH_INFORMATION);
   case INDUSTRY_MEDICAL_FACILITIES                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_MEDICAL_FACILITIES);
   case INDUSTRY_MEDICAL_DEVICES                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_MEDICAL_DEVICES);
   case INDUSTRY_MEDICAL_DISTRIBUTION              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_MEDICAL_DISTRIBUTION);
   case INDUSTRY_MEDICAL_INSTRUMENTS               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_MEDICAL_INSTRUMENTS);
   case INDUSTRY_PHARM_RETAILERS                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_PHARM_RETAILERS);
   case INDUSTRY_AEROSPACE_DEFENSE                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_AEROSPACE_DEFENSE);
   case INDUSTRY_AIRLINES                          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_AIRLINES);

「行业分类枚举的逐条映射写法」

在 MT5 的标的属性扩展里,把行业代码转成可读文本通常用 switch 分支直接回查消息表。下面这段覆盖了从机场服务到卡车运输共 22 个工业类子项,每个 case 对应一个 INDUSTRY_* 宏,返回 CMessage::Text 取到的本地化字符串。 这种写法没有默认分支兜底,若传入未列出的行业值,函数上层需自行处理越界。实盘做板块热力图时,缺一个 case 就可能让该品种在面板上显示空白,建议编译期用枚举总数做断言校验。 逐行看:以 INDUSTRY_AIRPORTS_SERVICES 为例,命中后直接 return CMessage::Text(MSG_SYM_INDUSTRY_AIRPORTS_SERVICES),不落变量、不绕弯;INDUSTRY_TRUCKING 位于列表最末,映射 MSG_SYM_INDUSTRY_TRUCKING,结构完全一致。复制进 EA 前确认消息宏已在资源文件定义,否则会报未声明标识符。

MQL5 / C++
   case INDUSTRY_AIRPORTS_SERVICES                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_AIRPORTS_SERVICES);
   case INDUSTRY_BUILDING_PRODUCTS                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BUILDING_PRODUCTS);
   case INDUSTRY_BUSINESS_EQUIPMENT               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_BUSINESS_EQUIPMENT);
   case INDUSTRY_CONGLOMERATES                    : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CONGLOMERATES);
   case INDUSTRY_CONSULTING_SERVICES              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CONSULTING_SERVICES);
   case INDUSTRY_ELECTRICAL_EQUIPMENT             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_ELECTRICAL_EQUIPMENT);
   case INDUSTRY_ENGINEERING_CONSTRUCTION         : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_ENGINEERING_CONSTRUCTION);
   case INDUSTRY_FARM_HEAVY_MACHINERY             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_FARM_HEAVY_MACHINERY);
   case INDUSTRY_INDUSTRIAL_DISTRIBUTION          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INDUSTRIAL_DISTRIBUTION);
   case INDUSTRY_INFRASTRUCTURE_OPERATIONS        : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_INFRASTRUCTURE_OPERATIONS);
   case INDUSTRY_FREIGHT_LOGISTICS                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_FREIGHT_LOGISTICS);
   case INDUSTRY_MARINE_SHIPPING                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_MARINE_SHIPPING);
   case INDUSTRY_METAL_FABRICATION                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_METAL_FABRICATION);
   case INDUSTRY_POLLUTION_CONTROL                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_POLLUTION_CONTROL);
   case INDUSTRY_RAILROADS                        : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_RAILROADS);
   case INDUSTRY_RENTAL_LEASING                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_RENTAL_LEASING);
   case INDUSTRY_SECURITY_PROTECTION              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SECURITY_PROTECTION);
   case INDUSTRY_SPEALITY_BUSINESS_SERVICES       : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SPEALITY_BUSINESS_SERVICES);
   case INDUSTRY_SPEALITY_MACHINERY               : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SPEALITY_MACHINERY);
   case INDUSTRY_STUFFING_EMPLOYMENT              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_STUFFING_EMPLOYMENT);
   case INDUSTRY_TOOLS_ACCESSORIES                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_TOOLS_ACCESSORIES);
   case INDUSTRY_TRUCKING                         : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_TRUCKING);

行业分类枚举的映射写法

在 MT5 的自定义品种或股票板块插件里,常需要把行业枚举值转成可读文本。下面这段 switch 分支覆盖了废弃物管理、房地产开发、多元地产、地产服务,以及 9 类 REIT(含 Diversified、Healthcare、Hotel/Motel、Industrial、Mortgage、Office、Residential、Retail、Specialty)。 分支结构高度规律:每个 case 对应一个 INDUSTRY_* 宏,统一 return CMessage::Text(MSG_SYM_INDUSTRY_*) 取本地化字符串。这套写法在编译期不依赖外部表,运行时查文本是常数级开销。 后半段接着映射通信装备、计算机硬件、消费电子、电子元件、电子分销、IT 服务、科学仪器、半导体设备与半导体共 9 个科技类枚举。若你要加新行业,照此补 case 并在消息表登记 MSG_SYM_INDUSTRY_ 前缀的字段即可,不改调用方。 外汇与贵金属交易者注意:这类行业映射多用于跨市场扫描,不直接产生信号;跨市场联动分析本身受流动性与时段错配影响,结论仅作概率参考,杠杆品种风险偏高。

MQL5 / C++
case INDUSTRY_WASTE_MANAGEMENT                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_WASTE_MANAGEMENT);
case INDUSTRY_REAL_ESTATE_DEVELOPMENT           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REAL_ESTATE_DEVELOPMENT);
case INDUSTRY_REAL_ESTATE_DIVERSIFIED           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REAL_ESTATE_DIVERSIFIED);
case INDUSTRY_REAL_ESTATE_SERVICES              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REAL_ESTATE_SERVICES);
case INDUSTRY_REIT_DIVERSIFIED                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_DIVERSIFIED);
case INDUSTRY_REIT_HEALTCARE                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_HEALTCARE);
case INDUSTRY_REIT_HOTEL_MOTEL                 : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_HOTEL_MOTEL);
case INDUSTRY_REIT_INDUSTRIAL                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_INDUSTRIAL);
case INDUSTRY_REIT_MORTAGE                     : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_MORTAGE);
case INDUSTRY_REIT_OFFICE                      : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_OFFICE);
case INDUSTRY_REIT_RESIDENTAL                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_RESIDENTAL);
case INDUSTRY_REIT_RETAIL                      : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_RETAIL);
case INDUSTRY_REIT_SPECIALITY                  : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_REIT_SPECIALITY);
case INDUSTRY_COMMUNICATION_EQUIPMENT          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_COMMUNICATION_EQUIPMENT);
case INDUSTRY_COMPUTER_HARDWARE                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_COMPUTER_HARDWARE);
case INDUSTRY_CONSUMER_ELECTRONICS             : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_CONSUMER_ELECTRONICS);
case INDUSTRY_ELECTRONIC_COMPONENTS            : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_ELECTRONIC_COMPONENTS);
case INDUSTRY_ELECTRONIC_DISTRIBUTION          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_ELECTRONIC_DISTRIBUTION);
case INDUSTRY_IT_SERVICES                      : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_IT_SERVICES);
case INDUSTRY_SCIENTIFIC_INSTRUMENTS           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SCIENTIFIC_INSTRUMENTS);
case INDUSTRY_SEMICONDUCTOR_EQUIPMENT          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SEMICONDUCTOR_EQUIPMENT);
case INDUSTRY_SEMICONDUCTORS                   : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SEMICONDUCTORS);

◍ 行业枚举收口与鼠标状态读取

上面这段 switch 把最后一批行业分类常量映射到本地化文本,从 SOFTWARE_APPLICATION 一直到 UTILITIES_REGULATED_WATER,外加一个 default 兜底返回 UNDEFINED。写自定义品种面板时,若漏了某一行 case,调试器不会报错,只会在图表上显示未定义行业,这种静默失效最容易被忽略。 鼠标交互部分给出了四个轻量取值器:CoordX、CoordY 取光标像素坐标,DeltaWheel 取滚轮增量,均为 const 内联返回成员变量。ButtonKeyState 则不是内联,它接收 id、lparam、dparam 和 flags 四个参数,先调 SetButtonKeyState 写状态,再把 m_state_flags 强转为 ENUM_MOUSE_BUTT_KEY_STATE 返回。 在 MT5 里接 ChartEvent 时,lparam 和 dparam 直接来自 OnChartEvent 的入参,flags 字符串需自行用 (ushort) 截断。外汇与贵金属行情受事件驱动明显,用这套状态机捕捉手动框选或滚轮缩放可能比轮询更高效,但手动交互策略在滑点扩大时高风险,仅适合辅助判断。

MQL5 / C++
case INDUSTRY_SOFTWARE_APPLICATION                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SOFTWARE_APPLICATION);
case INDUSTRY_SOFTWARE_INFRASTRUCTURE            : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SOFTWARE_INFRASTRUCTURE);
case INDUSTRY_SOLAR                              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_SOLAR);
case INDUSTRY_UTILITIES_DIVERSIFIED              : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_UTILITIES_DIVERSIFIED);
case INDUSTRY_UTILITIES_POWERPRODUCERS           : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_UTILITIES_POWERPRODUCERS);
case INDUSTRY_UTILITIES_RENEWABLE                : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_UTILITIES_RENEWABLE);
case INDUSTRY_UTILITIES_REGULATED_ELECTRIC       : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_UTILITIES_REGULATED_ELECTRIC);
case INDUSTRY_UTILITIES_REGULATED_GAS            : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_UTILITIES_REGULATED_GAS);
case INDUSTRY_UTILITIES_REGULATED_WATER          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_UTILITIES_REGULATED_WATER);
class="kw">default                                          : class="kw">return CMessage::Text(MSG_SYM_INDUSTRY_UNDEFINED);
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Return(class="num">1-class="num">2) the cursor coordinates, (class="num">3) scroll wheel value, (class="num">4) status of the mouse buttons and Shift/Ctrl keys
  class="type">int                CoordX(class="type">void)                const { class="kw">return this.m_coord_x;        }
  class="type">int                CoordY(class="type">void)                const { class="kw">return this.m_coord_y;        }
  class="type">int                DeltaWheel(class="type">void)            const { class="kw">return this.m_delta_wheel;    }
  ENUM_MOUSE_BUTT_KEY_STATE ButtonKeyState(const class="type">int id,const class="type">long lparam,const class="type">class="kw">double dparam,const class="type">class="kw">string flags);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the mouse buttons and Shift/Ctrl keys states             |
class=class="str">"cmt">//+------------------------------------------------------------------+
ENUM_MOUSE_BUTT_KEY_STATE CMouseState::ButtonKeyState(const class="type">int id,const class="type">long lparam,const class="type">class="kw">double dparam,const class="type">class="kw">string flags)
  {
   this.SetButtonKeyState(id,lparam,dparam,(class="type">class="kw">ushort)flags);
   class="kw">return (ENUM_MOUSE_BUTT_KEY_STATE)this.m_state_flags;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「鼠标状态类里的位标志布局」

在 MT5 自定义指标或 EA 里捕获鼠标交互,第一步是把每一次事件的坐标、滚轮、所属子窗口和按键状态塞进一个轻量结构。下面这段 CMouseState 的私有成员就干了这件事:m_coord_x / m_coord_y 存屏幕像素坐标,m_delta_wheel 记录滚轮增量,m_window_num 指向图表子窗口序号,m_chart_id 绑定具体图表,m_state_flags 则用 16 位整数压缩所有按钮与修饰键状态。 状态压缩的关键在于位分配。m_state_flags 是 ushort,逐位含义已经固定:bit 0 = 左键(十进制 1,十六进制 0x1),bit 1 = 右键(十进制 2,0x2),bit 2 = Shift 键(十进制 4,0x4)。也就是说,如果同时按下左键和 Shift,flags 值就是 1+4=5,程序里用位与(&)就能单独检测某个键是否按下。 两个私有方法负责填装这些数据:SetButtonKeyState 接收 MQL5 事件回调里的 id、lparam、dparam、flags 做整体写入;SetButtKeyFlags 则只更新 ushort 里的按键位。你在写图表拖拽画线工具时,直接复用这套位定义,可以避免每次都去解析 lparam 的原始二进制。 开 MT5 新建一个 mqh,把下面结构抄进去,编译后打印一次 m_state_flags 的十进制,就能验证自己按下的组合键是否和位表对得上。外汇与贵金属图表交互测试请留意,实时事件回调在高波动行情可能丢帧,属正常高风险环境现象。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Mouse status class                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CMouseState
  {
class="kw">private:
   class="type">int            m_coord_x;            class=class="str">"cmt">// X coordinate
   class="type">int            m_coord_y;            class=class="str">"cmt">// Y coordinate
   class="type">int            m_delta_wheel;        class=class="str">"cmt">// Mouse wheel scroll value
   class="type">int            m_window_num;         class=class="str">"cmt">// Subwindow index
   class="type">long           m_chart_id;           class=class="str">"cmt">// Chart ID
   class="type">class="kw">ushort         m_state_flags;        class=class="str">"cmt">// Status flags

class=class="str">"cmt">//--- Set the status of mouse buttons, as well as of Shift and Ctrl keys
   class="type">void           SetButtonKeyState(const class="type">int id,const class="type">long lparam,const class="type">class="kw">double dparam,const class="type">class="kw">ushort flags);
class=class="str">"cmt">//--- Set the mouse buttons and keys status flags
   class="type">void           SetButtKeyFlags(const class="type">class="kw">short flags);
class=class="str">"cmt">//--- Data location in the class="type">class="kw">ushort value of the button status
   class=class="str">"cmt">//---------------------------------------------------------------------------
   class=class="str">"cmt">//  bit     |     byte   |            state              |   dec    |  hex   |
   class=class="str">"cmt">//---------------------------------------------------------------------------
   class=class="str">"cmt">//   class="num">0      |     class="num">0     | left mouse button             |    class="num">1     |   class="num">1    |
   class=class="str">"cmt">//---------------------------------------------------------------------------
   class=class="str">"cmt">//   class="num">1      |     class="num">0     | right mouse button            |    class="num">2     |   class="num">2    |
   class=class="str">"cmt">//---------------------------------------------------------------------------
   class=class="str">"cmt">//   class="num">2      |     class="num">0     | SHIFT key                     |    class="num">4     |   class="num">4    |
   class=class="str">"cmt">//---------------------------------------------------------------------------
  };

鼠标与光标状态的位值映射

在 MT5 自定义控件里,鼠标按键和光标位置都被编码成整数标志位,方便在 OnEvent 里用位与判断。下面这张表把常见的键和区域对应的十进制、十六进制值列清了。 CTRL 键对应值 8(0x08),中键为 16(0x10),第一个附加鼠标键 32(0x20),第二个附加键 64(0x40),滚轮滚动则是 128(0x80)。这些都属于第一组(组号 0),用于识别具体哪个输入设备触发了事件。 第二组(组号 1)描述光标落在哪块区域:在窗体内为 256(0x100),在激活区 512(0x200),控件区 1024(0x400),滚动区 2048(0x800);贴左缘 4096(0x1000),贴下缘 8192(0x2000),贴右缘 16384(0x4000),贴上缘 32768(0x8000)。写拖拽逻辑时,用 flags & 0x8000 就能判断光标是否到顶边。 外汇与贵金属图表上的自定义面板交互属于高风险环境下的辅助工具,标志位误判只会影响操作体验,不会直接触发下单,但实盘前务必在模拟账户验证。

◍ 图形对象基类的初始化陷阱

在 MT5 自研指标或 EA 里封装图形对象时,CGBaseObj 构造函数暴露了一个容易踩的坑:初始化列表里先用 MQLInfoString(MQL_PROGRAM_NAME)+"_" 给 m_name_prefix 赋了程序名前缀,但函数体内又把它强制清空成空串。 这种前后矛盾不是笔误就是遗留代码。如果你打算用前缀做对象名隔离(避免多实例 EA 在同图表上重名覆盖),那段粉色覆写行必须删掉,否则所有对象名前缀都会丢失。 外汇与贵金属图表上跑多周期监控时,对象名冲突可能导致旧图形不被清理,概率上会引发视觉重叠与事件绑定错乱。开 MT5 把这段构造函数贴进类里,注释掉覆空行,用 ChartObjects 数量打印验证前缀是否生效。

MQL5 / C++
CGBaseObj::CGBaseObj() : m_shift_y(class="num">0),m_visible(false), m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM)
  {
   this.m_list_events.Clear();                      class=class="str">"cmt">// 清空事件列表
   this.m_list_events.Sort();                       class=class="str">"cmt">// 标记列表为已排序
   this.m_type=OBJECT_DE_TYPE_GBASE;                class=class="str">"cmt">// 设对象类型为基类
   this.m_type_graph_obj=WRONG_VALUE;               class=class="str">"cmt">// 图形对象类型暂置错误值
   this.m_type_element=WRONG_VALUE;                 class=class="str">"cmt">// 图形元素类型暂置错误值
   this.m_belong=WRONG_VALUE;                       class=class="str">"cmt">// 归属(程序/终端)暂置错误值
   this.m_group=class="num">0;                                  class=class="str">"cmt">// 图形对象分组号
   this.m_name_prefix="";                           class=class="str">"cmt">// 对象名前缀被强制清空(覆盖初始化列表赋值)
   this.m_name="";                                   class=class="str">"cmt">// 对象名空
   this.m_chart_id=class="num">0;                               class=class="str">"cmt">// 图表 ID 为 class="num">0
   this.m_object_id=class="num">0;                              class=class="str">"cmt">// 对象 ID 为 class="num">0
   this.m_zorder=class="num">0;                                 class=class="str">"cmt">// 鼠标点击优先级为 class="num">0
   this.m_subwindow=class="num">0;                              class=class="str">"cmt">// 子窗口索引 class="num">0
   this.m_shift_y=class="num">0;                                class=class="str">"cmt">// 子窗口 Y 偏移 class="num">0
   this.m_timeframes_visible=OBJ_ALL_PERIODS;       class=class="str">"cmt">// 全部周期可见
   this.m_visible=true;                             class=class="str">"cmt">// 对象可见
   this.m_back=false;                               class=class="str">"cmt">// 非背景对象
   this.m_selected=false;                           class=class="str">"cmt">// 未选中
   this.m_selectable=false;                         class=class="str">"cmt">// 不可选
  }

「图形对象基类的初始化细节」

在 MT5 自建图形对象库时,CGBaseObj 构造函数把事件列表先 Clear 再 Sort,意味着新对象默认带一个空且已排序的事件容器,后续压入鼠标/计时事件不会乱序。 成员初始化列表里 m_name_prefix 直接取 MQLInfoString(MQL_PROGRAM_NAME)+"_",也就是 EA 或脚本名加下划线做前缀,实盘里开多个同类型程序时靠这个前缀隔离对象名,不会互相覆盖。 构造函数体内把 m_type 设成 OBJECT_DE_TYPE_GBASE,m_type_graph_obj 与 m_type_element 都置为 WRONG_VALUE,说明基类本身不绑定具体图形类型,真正画横线还是画标签交给派生类去填。 几个容易被忽略的默认值:m_timeframes_visible 给的是 OBJ_ALL_PERIODS,对象在所有周期可见;m_visible 为 true、m_back 为 false,也就是默认前台显示。若你想让对象只在主图特定子窗口后台跑,得手动改 m_subwindow 和 m_back,否则可能挡住 K 线交互。 外汇与贵金属图表加载这类自定义对象时波动剧烈,对象重绘可能引发闪烁或事件丢失,建议先在模拟盘验证前缀隔离逻辑。

MQL5 / C++
this.m_hidden=true;                     class=class="str">"cmt">// 设置隐藏标志,终端对象列表里不显示该图形对象名称
this.m_create_time=class="num">0;                   class=class="str">"cmt">// 对象创建时间初始化为0
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Constructor                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
CGBaseObj::CGBaseObj() : m_name_prefix(::MQLInfoString(MQL_PROGRAM_NAME)+"_"),m_belong(GRAPH_OBJ_BELONG_PROGRAM)
  {
  this.m_list_events.Clear();           class=class="str">"cmt">// 清空事件列表
  this.m_list_events.Sort();            class=class="str">"cmt">// 设置列表为已排序状态
  this.m_type=OBJECT_DE_TYPE_GBASE;     class=class="str">"cmt">// 对象类型设为基类类型
  this.m_type_graph_obj=WRONG_VALUE;    class=class="str">"cmt">// 图形对象类型暂置为错误值
  this.m_type_element=WRONG_VALUE;      class=class="str">"cmt">// 图形元素类型暂置为错误值
  this.m_belong=WRONG_VALUE;            class=class="str">"cmt">// 程序/终端归属暂置为错误值
  this.m_group=class="num">0;                       class=class="str">"cmt">// 图形对象分组设为0
  this.m_name="";                       class=class="str">"cmt">// 对象名称初始化为空串
  this.m_chart_id=class="num">0;                    class=class="str">"cmt">// 对象所属图表ID为0
  this.m_object_id=class="num">0;                   class=class="str">"cmt">// 对象ID为0
  this.m_zorder=class="num">0;                      class=class="str">"cmt">// 鼠标点击接收优先级为0
  this.m_subwindow=class="num">0;                   class=class="str">"cmt">// 子窗口索引为0(主图)
  this.m_shift_y=class="num">0;                     class=class="str">"cmt">// 子窗口Y坐标偏移为0
  this.m_timeframes_visible=OBJ_ALL_PERIODS; class=class="str">"cmt">// 所有时间周期均可见
  this.m_visible=true;                  class=class="str">"cmt">// 对象默认可见
  this.m_back=false;                    class=class="str">"cmt">// 非背景对象
  }

图形对象基类的事件与属性初始化

在 MQL5 里封装图形对象时,构造函数里先把选中态、可见性、创建时间等成员压成默认值是常见做法。下面这段把 m_selected 和 m_selectable 置 false、m_hidden 置 true、m_create_time 置 0,意味着新对象默认不被选中、不可被用户点选,且不出现在终端对象列表的名称栏中。 事件管理机制靠三个轻量方法撑起来:AddEvent 往链表 m_list_events 里塞一个 CGBaseEvent 指针,ClearEventsList 直接清空整条链表,EventsTotal 返回当前链表长度。实际调试时,如果在 OnInit 之后调 EventsTotal 拿到 0,说明还没有任何事件挂到该对象上。 对外暴露的访问器里,NamePrefix 用 const 修饰符保证只读返回前缀名;剩下一批 Set 方法负责回填对象 ID、归属(ENUM_GRAPH_OBJ_BELONG)、图形类型(ENUM_OBJECT)、元素类型与分组号。写 EA 时若发现对象没按预期绘制,先核对 SetObjectID 和 SetTypeGraphObject 有没有在创建后被正确调用。 外汇与贵金属图表上跑这类自定义对象逻辑属于高风险操作,参数错配可能导致对象不显示或频闪,建议在策略测试器里先用单品种验证。

MQL5 / C++
  this.m_selected=false;                 class=class="str">"cmt">// "Object selection" flag
  this.m_selectable=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">//--- Add an event object to the event list
  class="type">bool            AddEvent(CGBaseEvent *event)            { class="kw">return this.m_list_events.Add(event);}
class=class="str">"cmt">//--- Clear the event list
  class="type">void            ClearEventsList(class="type">void)                   { this.m_list_events.Clear();       }
class=class="str">"cmt">//--- Return the number of events in the list
  class="type">int             EventsTotal(class="type">void)                       { class="kw">return this.m_list_events.Total(); }
class="kw">public:
class=class="str">"cmt">//--- Return the prefix name
  class="type">class="kw">string          NamePrefix(class="type">void) const                 { class="kw">return this.m_name_prefix;        }
class=class="str">"cmt">//--- Set the values of the class variables
  class="type">void            SetObjectID(const class="type">long value)          { this.m_object_id=value;           }
  class="type">void            SetBelong(const ENUM_GRAPH_OBJ_BELONG belong){ this.m_belong=belong;           }
  class="type">void            SetTypeGraphObject(const ENUM_OBJECT obj) { this.m_type_graph_obj=obj;       }
  class="type">void            SetTypeElement(const ENUM_GRAPH_ELEMENT_TYPE type) { this.m_type_element=type; }
  class="type">void            SetSpecies(const ENUM_GRAPH_OBJ_SPECIES species){ this.m_species=species;         }
  class="type">void            SetGroup(const class="type">int group)              { this.m_group=group;               }

◍ 图形元素基类的光标命中判定

在 MT5 自定义图形界面里,CGCanvasElement 这类继承自 CGBaseObj 的图形元素,先把名称、图表 ID、报价小数位用三个 setter 固化下来:SetName 写 m_name,SetChartID 写 m_chart_id,SetDigits 写 m_digits。这三个字段决定了元素归属哪张图表、用几位小数绘制,错一个就可能画到别的周期上。 CGCanvasElement 受保护成员里挂了 CCanvas 与 CPause 实例,外加 m_shadow 阴影开关、m_chart_color_bg 图表背景色,以及 m_duplicate_res[] 资源副本数组。资源副本的存在,意味着元素重绘时可从内存还原位图,不必每次向终端重新申请资源句柄。 光标是否落在元素上,靠两个未公开的同名方法区分:CursorInsideElement(x,y) 判整个元素包围盒,CursorInsideActiveArea(x,y) 只判可交互热区。实际做按钮或拖拽控件时,热区往往小于包围盒,用错函数会导致点空白边也能触发。 私有侧的 ResourceStamp 负责把图形资源快照进数组,Reset() 做虚重置。这两个一般不会直接调,但如果你发现控件在图表切换后变空白,优先怀疑 ResourceStamp 没在 OnChartEvent 里补拍。

MQL5 / C++
class="type">void SetName(const class="type">class="kw">string name) { this.m_name=name; }
class="type">void SetChartID(const class="type">long chart_id) { this.m_chart_id=chart_id; }
class="type">void SetDigits(const class="type">int value) { this.m_digits=value; }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class of the graphical element object                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CGCnvElement : class="kw">public CGBaseObj
  {
class="kw">protected:
   CCanvas       m_canvas;          class=class="str">"cmt">// CCanvas class object
   CPause        m_pause;           class=class="str">"cmt">// Pause class object
   class="type">bool          m_shadow;          class=class="str">"cmt">// Shadow presence
   class="type">class="kw">color         m_chart_color_bg;  class=class="str">"cmt">// Chart background class="type">class="kw">color
   class="type">uint          m_duplicate_res[]; class=class="str">"cmt">// Array for storing resource data copy
class=class="str">"cmt">//--- Return the cursor position relative to the(class="num">1) entire element and(class="num">2) the element&class="macro">#x27;s active area
   class="type">bool          CursorInsideElement(const class="type">int x,const class="type">int y);
   class="type">bool          CursorInsideActiveArea(const class="type">int x,const class="type">int y);
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">virtual class="type">void  StructToObject(class="type">void);

class="kw">private:
class=class="str">"cmt">//--- (class="num">1) Save the graphical resource to the array and(class="num">2) restore the resource from the array
   class="type">bool          ResourceStamp(const class="type">class="kw">string source);
   class="kw">virtual class="type">bool  Reset(class="type">void);
class=class="str">"cmt">//--- Return the cursor position relative to the(class="num">1) entire element and(class="num">2) the element&class="macro">#x27;s active area
   class="type">bool          CursorInsideElement(const class="type">int x,const class="type">int y);

「图形元素类的内部骨架」

在 MT5 自定义图形库里,CGCnvElement 作为画布元素基类,把 CCanvas 和 CPause 两个对象收进 protected 成员,意味着子类能直接复用绘图与暂停逻辑,而不必在外层重复实例化。 m_shadow 用 bool 控制阴影开关,m_chart_color_bg 存图表背景色,这两个字段决定了元素在不同模板下的融合度;m_duplicate_res[] 则是资源数据副本数组,避免原资源被释放后画面撕裂。 ObjectToStruct 与 StructToObject 被声明为虚函数,说明元素的「结构描述」和「实际对象」可以双向同步——你改了结构体字段,调一次 StructToObject 就能重绘,不必销毁重建。 CursorInsideActiveArea(x,y) 用来判断鼠标是否落在元素响应区内,配合 Create() 的 chart_id、wnd_num、x/y/w/h 以及 colour、opacity 参数,可以在指定子窗口精确生成一块半透明可交互画布。外汇与贵金属图表叠加这类自绘层时波动剧烈,实盘前务必在策略测试器里验证重绘开销。

MQL5 / C++
class="type">bool CursorInsideActiveArea(const class="type">int x,const class="type">int y);
class=class="str">"cmt">//--- Create the element
class="type">bool Create(const class="type">long chart_id,
            const class="type">int wnd_num,
            const class="type">class="kw">string name,
            const class="type">int x,
            const class="type">int y,
            const class="type">int w,
            const class="type">int h,
            const class="type">class="kw">color colour,
            const class="type">uchar opacity,
            const class="type">bool redraw=false);
class=class="str">"cmt">//--- Return the pointer to a canvas object
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Class of the graphical element object                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CGCnvElement : class="kw">public CGBaseObj
  {
class="kw">protected:
   CCanvas       m_canvas;            class=class="str">"cmt">// CCanvas class object
   CPause        m_pause;             class=class="str">"cmt">// Pause class object
   class="type">bool          m_shadow;            class=class="str">"cmt">// Shadow presence
   class="type">class="kw">color         m_chart_color_bg;    class=class="str">"cmt">// Chart background class="type">class="kw">color
   class="type">uint          m_duplicate_res[];   class=class="str">"cmt">// Array for storing resource data copy
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">virtual class="type">void  StructToObject(class="type">void);

class="kw">private:
   class="kw">struct SData
     {

图形元素的整型属性结构

在 MT5 自定义控件库里,每个图形元素都会用一组整型字段记录自身在图表上的几何与归属信息。下面这段结构定义了元素的核心整数属性,开 MT5 新建一个 Panel 类时可以直接照搬。 这些字段从 id、type 到 number,分别承担唯一标识、元素类型(如 OBJ_RECTANGLE)、列表内序号的角色;chart_id 与 subwindow 则锁定元素挂在哪张图表、哪个子窗口。 coord_x / coord_y 是元素左上角相对图表的像素坐标,width / height 决定绘制尺寸,edge_right / edge_bottom 由前四项推算而出,用于快速判断鼠标是否越出边界。 act_shift_left / act_shift_top / act_shift_right 这一组偏移量定义了元素的“可点击热区”,比如设 act_shift_left=2 表示热区左边界比元素左边缘内缩 2 像素,能避免边框误触。外汇与贵金属图表上挂这类控件时,需注意高波动行情下坐标重绘可能引发热区错位,属于高风险操作环境。

MQL5 / C++
class=class="str">"cmt">//--- Object integer properties
class="type">int                id;                                     class=class="str">"cmt">// Element ID
class="type">int                type;                                   class=class="str">"cmt">// Graphical element type
class="type">int                number;                                 class=class="str">"cmt">// Element index in the list
class="type">long               chart_id;                               class=class="str">"cmt">// Chart ID
class="type">int                subwindow;                              class=class="str">"cmt">// Chart subwindow index
class="type">int                coord_x;                                class=class="str">"cmt">// Form&class="macro">#x27;s X coordinate on the chart
class="type">int                coord_y;                                class=class="str">"cmt">// Form&class="macro">#x27;s Y coordinate on the chart
class="type">int                width;                                  class=class="str">"cmt">// Element width
class="type">int                height;                                 class=class="str">"cmt">// Element height
class="type">int                edge_right;                             class=class="str">"cmt">// Element right border
class="type">int                edge_bottom;                            class=class="str">"cmt">// Element bottom border
class="type">int                act_shift_left;                         class=class="str">"cmt">// Active area offset from the left edge of the element
class="type">int                act_shift_top;                          class=class="str">"cmt">// Active area offset from the top edge of the element
class="type">int                act_shift_right;                        class=class="str">"cmt">// Active area offset from the right edge of the element

◍ GUI 元素结构体的字段布局

在 MT5 自定义控件库里,一个图形元素的所有状态被压进一个 C 风格结构体,方便用 uchar 数组整体序列化或拷贝。下面这段结构体字段直接决定你在回测面板或盯盘 HUD 上能看到、能点的范围。 交互标志 interaction 被单独高亮,它控制该元素是否响应外部环境事件(鼠标、定时器、其他控件消息);若置 false,元素只是静态贴图,不消耗事件队列。 字符串属性用定长 uchar 数组而非 string,name_obj 与 name_res 各占 64 字节,意味着对象名和资源名超过 63 字符会被截断,这是底层限制而非笔误。 m_long_prop 按 ORDER_PROP_INTEGER_TOTAL 枚举展开,整数类属性(如坐标轴索引、Z-order)集中存放,和前面的像素坐标、透明度分开管理,能减少内存碎片。

MQL5 / C++
class="type">int act_shift_bottom; class=class="str">"cmt">// Active area offset from the bottom edge of the element
class="type">uchar opacity; class=class="str">"cmt">// Element opacity
class="type">class="kw">color color_bg; class=class="str">"cmt">// Element background class="type">class="kw">color
class="type">bool movable; class=class="str">"cmt">// Element moveability flag
class="type">bool active; class=class="str">"cmt">// Element activity flag
class="type">bool interaction; class=class="str">"cmt">// Flag of interaction with the outside environment
class="type">int coord_act_x; class=class="str">"cmt">// X coordinate of the element active area
class="type">int coord_act_y; class=class="str">"cmt">// Y coordinate of the element active area
class="type">int coord_act_right; class=class="str">"cmt">// Right border of the element active area
class="type">int coord_act_bottom; class=class="str">"cmt">// Bottom border of the element active area
class=class="str">"cmt">//--- Object real properties
class=class="str">"cmt">//--- Object class="type">class="kw">string properties
class="type">uchar name_obj[class="num">64]; class=class="str">"cmt">// Graphical element object name
class="type">uchar name_res[class="num">64]; class=class="str">"cmt">// Graphical resource name
};
SData m_struct_obj; class=class="str">"cmt">// Object structure
class="type">uchar m_uchar_array[]; class=class="str">"cmt">// class="type">uchar array of the object structure

class="type">long m_long_prop[ORDER_PROP_INTEGER_TOTAL]; class=class="str">"cmt">// Integer properties

「图形元素的属性槽与事件钩子」

在 MT5 自定义图形库里,一个画布元素类通常用两块数组兜住订单属性:双精度类型的存真实数值,字符串类型的存文本标识。下面这段声明把 ORDER_PROP_DOUBLE_TOTAL 和 ORDER_PROP_STRING_TOTAL 两个枚举上限直接当数组长度,意味着属性槽数量是编译期固定的,改了枚举就得重编译。 double m_double_prop[ORDER_PROP_DOUBLE_TOTAL]; // Real properties string m_string_prop[ORDER_PROP_STRING_TOTAL]; // String properties 构造函数被放在 protected 区,说明这个类不直接给你 new,而是逼你走带 element_type / chart_id / wnd_num 等参数的派生构造。坐标和宽高 x,y,w,h 全以 int 传入,实际像素定位依赖图表 DPI,跨屏缩放时可能偏几像素。 public 区里 OnChartEvent 被标成 virtual,且参数用了 long& / double& / string& 引用。这等于把图表鼠标、键盘事件直接喂回元素实例,你可以在派生类重写它做拖拽响应。想验证就开 MT5 建个空 EA,把这段塞进类声明,编译看是否报“不能实例化抽象基类”就知道保护构造生效没。 CGnvElement 的第二个构造重载改用 element_id 和 element_num 取代部分定位参数,更适合批量建同型元素。外汇与贵金属图表事件密集,重写 OnChartEvent 时若每次都重绘,CPU 占用可能随元素数线性爬升,建议加脏标记。

MQL5 / C++
  class="type">class="kw">double              m_double_prop[ORDER_PROP_DOUBLE_TOTAL];   class=class="str">"cmt">// Real properties
  class="type">class="kw">string              m_string_prop[ORDER_PROP_STRING_TOTAL];   class=class="str">"cmt">// String properties
class="kw">protected:
class=class="str">"cmt">//--- Protected constructor
                  CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                              const class="type">long    chart_id,
                              const class="type">int     wnd_num,
                              const class="type">class="kw">string  name,
                              const class="type">int     x,
                              const class="type">int     y,
                              const class="type">int     w,
                              const class="type">int     h);
class="kw">public:
class=class="str">"cmt">//--- Event handler
  class="kw">virtual class="type">void      OnChartEvent(const class="type">int id,const class="type">long& lparam,const class="type">class="kw">double& dparam,const class="type">class="kw">string& sparam);
class=class="str">"cmt">//--- Parametric constructor
                  CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                              const class="type">int     element_id,
                              const class="type">int     element_num,
                              const class="type">long    chart_id,
                              const class="type">int     wnd_num,
                              const class="type">class="kw">string  name,
                              const class="type">int     x,
                              const class="type">int     y,

画布图元的坐标与边距接口

在 MT5 自定义图形库里,图元基类 CGCnvElement 把屏幕坐标、尺寸和激活区偏移都封装成了独立 setter,调用方不必直接碰底层 canvas 坐标换算。 构造函数里顺手读了 CHART_COLOR_BACKGROUND,把 m_chart_color_bg 初始化成当前图表背景色,析构时调 m_canvas.Destroy() 释放资源,避免残留离线物件。 SetRightEdge 与 SetBottomEdge 是内联写法,直接用 RightEdge()/BottomEdge() 的返回值写进 CANV_ELEMENT_PROP_RIGHT / CANV_ELEMENT_PROP_BOTTOM 属性——这意味着边缘同步是即时计算的,不会缓存旧值。 如果你在 EA 里动态改宽高,记得分别调 SetWidth(int) 和 SetHeight(int);只改 X/Y 用 SetCoordX / SetCoordY,参数都是像素整数,和 ChartGetInteger 拿到的坐标系一致。 外汇与贵金属图表上叠加这类自绘图元时,注意高杠杆环境下的重绘开销,频繁 Destroy/Create 可能造成卡顿。

MQL5 / C++
const class="type">int      w,
const class="type">int      h,
const class="type">class="kw">color    colour,
const class="type">uchar    opacity,
const class="type">bool     movable=true,
const class="type">bool     activity=true,
const class="type">bool     redraw=false);
class=class="str">"cmt">//--- Default constructor/Destructor
      CGCnvElement() : m_shadow(false),m_chart_color_bg((class="type">class="kw">color)::ChartGetInteger(::ChartID(),CHART_COLOR_BACKGROUND))
      { this.m_type=OBJECT_DE_TYPE_GELEMENT; }
      ~CGCnvElement()
      { this.m_canvas.Destroy();      }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Methods of simplified access to object properties                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Set the(class="num">1) X, (class="num">2) Y coordinates, (class="num">3) element width, (class="num">4) height, (class="num">5) right(class="num">6) and bottom edge,
   class="type">bool           SetCoordX(const class="type">int coord_x);
   class="type">bool           SetCoordY(const class="type">int coord_y);
   class="type">bool           SetWidth(const class="type">int width);
   class="type">bool           SetHeight(const class="type">int height);
   class="type">void           SetRightEdge(class="type">void)                    { this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());      }
   class="type">void           SetBottomEdge(class="type">void)                   { this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());    }
class=class="str">"cmt">//--- Set the shift of the(class="num">1) left, (class="num">2) top, (class="num">3) right, (class="num">4) bottom edge of the active area relative to the element,
class=class="str">"cmt">//--- (class="num">5) all shifts of the active area edges relative to the element, (class="num">6) the element background class="type">class="kw">color and(class="num">7) the element opacity

◍ 画布控件的活动区偏移与交互开关

在 MT5 自定义图形面板里,控件的可点击区域往往比肉眼看到的边框大一圈。上面这组 setter 用 CANV_ELEMENT_PROP_ACT_SHIFT_LEFT/RIGHT/TOP/BOTTOM 四个属性把活动区向四个方向各撑出 value 像素,且统一套了 fabs(value)——负值会被强行取正,所以传 -10 和 10 效果一样,都是外扩 10 像素。 SetActiveAreaShift 是批量版,一次吃下四个方向的偏移量,适合在初始化时整体定好热区。背景色和透明度走的是独立成员:SetColorBackground 直接改 m_color_bg,SetOpacity 带 redraw 参数,默认不重绘,需要立刻见效就显式传 true。 真正决定控件「能不能被鼠标抓到」的是后面几个 flag。SetMovable 管拖动,SetActive 管是否参与事件响应,而 SetInteraction 才是开启用户交互的总闸——不置 true,前面撑再大的活动区也点不动。 调参时建议先 SetInteraction(true) 再设偏移,否则在 EURUSD 这类tick密集品种上,热区过大可能吞掉相邻控件的点击,外汇与贵金属杠杆高,界面误操作会直接触发下单。

MQL5 / C++
class="type">void SetActiveAreaLeftShift(const class="type">int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,fabs(value)); }
class="type">void SetActiveAreaRightShift(const class="type">int value){ this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,fabs(value)); }
class="type">void SetActiveAreaTopShift(const class="type">int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,fabs(value)); }
class="type">void SetActiveAreaBottomShift(const class="type">int value) { this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,fabs(value)); }
class="type">void SetActiveAreaShift(const class="type">int left_shift,const class="type">int bottom_shift,const class="type">int right_shift,const class="type">int top_shift);
class="type">void SetColorBackground(const class="type">class="kw">color colour) { this.m_color_bg=colour; }
class="type">void SetOpacity(const class="type">uchar value,const class="type">bool redraw=false);
class=class="str">"cmt">//--- Set the flag of(class="num">1) object moveability, (class="num">2) activity, (class="num">3) element ID, (class="num">4) element index in the list and(class="num">5) shadow presence
class="type">void SetMovable(const class="type">bool flag) { this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,flag); }
class="type">void SetActive(const class="type">bool flag) { this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,flag); }
class="type">void SetInteraction(const class="type">bool flag) { this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,flag); }
class="type">void SetID(const class="type">int id) { this.SetProperty(CANV_ELEMENT_PROP_ID,id); }
class="type">void SetNumber(const class="type">int number) { this.SetProperty(CANV_ELEMENT_PROP_NUM,number); }

「控件激活区与背景色的取值接口」

在 MT5 的 Canvas 自定义控件类里,激活区(active area)并不等于控件外框,而是用四个方向的 shift 把可点击/可交互范围向内收缩后的矩形。 下面这组接口先通过 GetProperty 读取 CANV_ELEMENT_PROP_ACT_SHIFT_LEFT/RIGHT/TOP/BOTTOM 四个属性,拿到各边向内偏移的像素值;ActiveAreaLeft 等四个方法再把偏移叠加到控件坐标上,返回真实激活区边缘。 SetShadow 只控制是否绘制阴影(m_shadow 标志位),不影响激活区计算;ColorBackground 直接返回 m_color_bg 成员,是背景填充色而非激活区边框色。复制下面代码到你的控件类里,开 MT5 用 Comment 打印 ActiveAreaLeft() 与 RightEdge() 的差值,就能验证实际可点区域比外框小了多少像素。

MQL5 / C++
class="type">void SetShadow(const class="type">bool flag) { this.m_shadow=flag; }

class=class="str">"cmt">//--- Return the shift(class="num">1) of the left, (class="num">2) right, (class="num">3) top and(class="num">4) bottom edge of the element active area
class="type">int ActiveAreaLeftShift(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT); }
class="type">int ActiveAreaRightShift(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT); }
class="type">int ActiveAreaTopShift(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP); }
class="type">int ActiveAreaBottomShift(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM); }
class=class="str">"cmt">//--- Return the coordinate(class="num">1) of the left, (class="num">2) right, (class="num">3) top and(class="num">4) bottom edge of the element active area
class="type">int ActiveAreaLeft(class="type">void) const { class="kw">return class="type">int(this.CoordX()+this.ActiveAreaLeftShift()); }
class="type">int ActiveAreaRight(class="type">void) const { class="kw">return class="type">int(this.RightEdge()-this.ActiveAreaRightShift()); }
class="type">int ActiveAreaTop(class="type">void) const { class="kw">return class="type">int(this.CoordY()+this.ActiveAreaTopShift()); }
class="type">int ActiveAreaBottom(class="type">void) const { class="kw">return class="type">int(this.BottomEdge()-this.ActiveAreaBottomShift()); }
class=class="str">"cmt">//--- Return(class="num">1) the background class="type">class="kw">color, (class="num">2) the opacity, coordinate(class="num">3) of the right and(class="num">4) bottom element edge
class="type">class="kw">color ColorBackground(class="type">void) const { class="kw">return this.m_color_bg; }

画布元素的边界与属性读取

在 MT5 的 Canvas 元素封装里,一组 const 方法负责把对象的几何与状态暴露给外部逻辑。Opacity() 直接返回成员 m_opacity,而 RightEdge() 与 BottomEdge() 则用 CoordX()+画布宽、CoordY()+画布高算出右边界和下边界,省去调用方自己加一次的麻烦。 坐标与尺寸四类属性——X、Y、宽、高——统一走 GetProperty 接口,强转成 int 返回:CANV_ELEMENT_PROP_COORD_X / Y、CANV_ELEMENT_PROP_WIDTH / HEIGHT。这意味着在运行时改了元素位置,读出来的就是最新值,不需要缓存同步。 Movable() 和 Active() 同理,从 CANV_ELEMENT_PROP_MOVABLE、CANV_ELEMENT_PROP_ACTIVE 取布尔标志。做交互面板时,用这两个返回值判断鼠标该不该拖、该不该响应点击,比自己维护状态变量更不容易漏更新。 开 MT5 建个 Canvas 对象,挂上这几行方法,打印 RightEdge() 在拖动前后的差值,就能验证边界计算是否跟着坐标实时走。

MQL5 / C++
  class="type">uchar                Opacity(class="type">void)                                const { class="kw">return this.m_opacity;                                                                  }
  class="type">int                 RightEdge(class="type">void)                              const { class="kw">return this.CoordX()+this.m_canvas.Width();                                              }
  class="type">int                 BottomEdge(class="type">void)                             const { class="kw">return this.CoordY()+this.m_canvas.Height();                                             }
class=class="str">"cmt">//--- Return the(class="num">1) X, (class="num">2) Y coordinates, (class="num">3) element width and(class="num">4) height,
  class="type">int                 CoordX(class="type">void)                                const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X);                                 }
  class="type">int                 CoordY(class="type">void)                                const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y);                                 }
  class="type">int                 Width(class="type">void)                                 const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH);                                  }
  class="type">int                 Height(class="type">void)                                const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT);                                 }
class=class="str">"cmt">//--- Return the element(class="num">1) moveability and(class="num">2) activity flag
  class="type">bool                Movable(class="type">void)                               const { class="kw">return (class="type">bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);                               }
  class="type">bool                Active(class="type">void)                                const { class="kw">return (class="type">bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);                               }

◍ 画布元素对象的属性读取接口

在 MQL5 的 CCanvas 派生元素类里,一组 const 成员函数专门向外暴露运行时属性,全部走 GetProperty 枚举或私有字段直读,不修改对象状态。 Interaction() 返回 CANV_ELEMENT_PROP_INTERACTION 的布尔值,决定该图形元素能否接收鼠标交互;不能交互的元素在 MT5 画布上只是静态层。 NameObj()、NameRes()、ChartID()、WindowNum() 分别取回元素绑定的对象名、图形资源名、所属图表 ID 与子窗口序号,这 4 个值定位了元素在终端里的唯一坐标。 ID() 与 Number() 给出元素自身 ID 及在列表中的序号,IsShadow() 与 ChartColorBackground() 则直接读 m_shadow 与 m_chart_color_bg 私有成员,跳过属性表拿shadow开关和背景色。 把这些 getter 接进调试脚本,能在 OnInit 里打印出每个元素的 WindowNum 与 ChartID,快速排查“画错窗口”这类布局 bug。

MQL5 / C++
class="type">bool Interaction(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(CANV_ELEMENT_PROP_INTERACTION); }
class="type">class="kw">string NameObj(class="type">void) const { class="kw">return this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ); }
class="type">class="kw">string NameRes(class="type">void) const { class="kw">return this.GetProperty(CANV_ELEMENT_PROP_NAME_RES); }
class="type">long ChartID(class="type">void) const { class="kw">return this.GetProperty(CANV_ELEMENT_PROP_CHART_ID); }
class="type">int WindowNum(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM); }
class="type">int ID(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ID); }
class="type">int Number(class="type">void) const { class="kw">return (class="type">int)this.GetProperty(CANV_ELEMENT_PROP_NUM); }
class="type">bool IsShadow(class="type">void) const { class="kw">return this.m_shadow; }
class="type">class="kw">color ChartColorBackground(class="type">void) const { class="kw">return this.m_chart_color_bg; }

「图形元素的置顶与显隐实现」

在 MT5 自定义图形库里,让某个画布元素强制置顶,靠的是先隐后显:先 SetVisible(false) 再 SetVisible(true),两次调用第二个参数都传 false,意思是只改自身可见性、不动子对象。 BringToTop 方法就是这么干的,注释里写得很直白——(1) 显示 (2) 隐藏,实际是先藏一下再亮出来,Z 序就被推到同容器顶层。Show 和 Hide 两个虚函数则只是单层封装,分别调 true/false,方便外部直接继承重写。 下面的参数化构造函数 CGCnvElement 一口气收了 13 个入参:从元素类型、ID、序号,到图表 ID、子窗口号、名字、坐标 x/y、宽高 w/h,再到颜色和透明度 opacity。这种写法在图形基类里很常见,好处是一次构造把位置和高亮属性全定死,后续只用动显隐。 开 MT5 自己建个 CGCnvElement 派生类,把 BringToTop 绑到一个按钮事件上,能直观看到重叠矩形谁压谁。外汇和贵金属图表叠加自定义图层时,置顶逻辑错乱可能导致信号被遮,属于高风险操作环境,参数多核对一遍。

MQL5 / C++
class="type">void               BringToTop(class="type">void)                                        { CGBaseObj::SetVisible(false,false); CGBaseObj::SetVisible(true,false);}
class=class="str">"cmt">//--- (class="num">1) Show and(class="num">2) hide the element
class="kw">virtual class="type">void        Show(class="type">void)                                                { CGBaseObj::SetVisible(true,false);                 }
class="kw">virtual class="type">void        Hide(class="type">void)                                                { CGBaseObj::SetVisible(false,false);                }

class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| The methods of receiving raster data                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Parametric constructor                                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const class="type">int      element_id,
                           const class="type">int      element_num,
                           const class="type">long     chart_id,
                           const class="type">int      wnd_num,
                           const class="type">class="kw">string   name,
                           const class="type">int      x,
                           const class="type">int      y,
                           const class="type">int      w,
                           const class="type">int      h,
                           const class="type">class="kw">color    colour,
                           const class="type">uchar    opacity,

画布元素构造时的属性落盘

在 MQL5 自定义图形库里,元素对象被 new 出来的一瞬间就要把身份信息和坐标写死进属性表,否则后续在图表子窗口里重绘会找不到宿主。上面这段构造函数把 chart_id、子窗口序号、元素类型、ID 与列表序号全部通过 SetProperty 固化,相当于给每个控件发了张身份证。 注意 name 拼接那行:如果传入的 name 里没带库前缀 m_name_prefix,就自动补上前缀再拼,避免多实例同名撞车。背景色直接读 CHART_COLOR_BACKGROUND,意味着元素默认融进图表底色,opacity 参数才决定它浮出来的程度。 坐标与尺寸也同步进属性:X/Y 是图表像素坐标,w/h 决定命中区域。实盘里若发现面板按钮点不动,十有八九是 CANV_ELEMENT_PROP_WIDTH/HEIGHT 没随 Create 成功回写,开 MT5 在构造函数末尾 Print 一下这几个属性就能验证。

MQL5 / C++
const class="type">bool      movable=true,
const class="type">bool      activity=true,
const class="type">bool      redraw=false) : m_shadow(false)
  {
   this.m_type=OBJECT_DE_TYPE_GELEMENT;
   this.m_chart_color_bg=(class="type">class="kw">color)::ChartGetInteger(chart_id,CHART_COLOR_BACKGROUND);
   this.m_name=(::StringFind(name,this.m_name_prefix)<class="num">0 ? this.m_name_prefix : "")+name;
   this.m_chart_id=chart_id;
   this.m_subwindow=wnd_num;
   this.m_type_element=element_type;
   this.SetFont("Calibri",class="num">8);
   this.m_text_anchor=class="num">0;
   this.m_text_x=class="num">0;
   this.m_text_y=class="num">0;
   this.m_color_bg=colour;
   this.m_opacity=opacity;
   if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,colour,opacity,redraw))
     {
      this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); class=class="str">"cmt">// Graphical resource name
      this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());           class=class="str">"cmt">// Chart ID
      this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow());           class=class="str">"cmt">// Chart subwindow index
      this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());               class=class="str">"cmt">// Element object name
      this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                        class=class="str">"cmt">// Graphical element type
      this.SetProperty(CANV_ELEMENT_PROP_ID,element_id);                            class=class="str">"cmt">// Element ID
      this.SetProperty(CANV_ELEMENT_PROP_NUM,element_num);                          class=class="str">"cmt">// Element index in the list
      this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                                class=class="str">"cmt">// Element&class="macro">#x27;s X coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                                class=class="str">"cmt">// Element&class="macro">#x27;s Y coordinate on the chart
      this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                                  class=class="str">"cmt">// Element width
      this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                                 class=class="str">"cmt">// Element height

◍ 画布元素初始化的属性落点

在 CGCnvElement 的初始化流程里,元素创建成功后第一件事就是批量写入默认属性。四向激活区偏移 ACT_SHIFT_LEFT/TOP/RIGHT/BOTTOM 全部置 0,意味着激活区默认与元素外框完全重合,鼠标命中判定不会留内缩边。 可移动性与活动标志由入参 movable、activity 接管,而交互标志 CANV_ELEMENT_PROP_INTERACTION 被硬写为 false,说明该基类元素默认不参与外部环境事件响应,需要子类或外部逻辑显式开启。 右边界与底边界直接用 RightEdge()、BottomEdge() 回填,激活区四至则调 ActiveAreaLeft/Top/Right/Bottom() 同步。这套赋值跑完,一个画布元素的基础几何与状态就定住了,开 MT5 挂个断点逐步看 this.m_type=element_type 之后的属性表就能验证。 如果对象创建失败,则走 else 分支用 ::Print 输出 CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ) 带上 m_name,不会继续执行属性初始化,避免空对象被后续逻辑误用。

MQL5 / C++
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,class="num">0);             class=class="str">"cmt">// Active area offset from the left edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,class="num">0);              class=class="str">"cmt">// Active area offset from the upper edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,class="num">0);            class=class="str">"cmt">// Active area offset from the right edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,class="num">0);           class=class="str">"cmt">// Active area offset from the bottom edge of the element
this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,movable);              class=class="str">"cmt">// Element moveability flag
this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,activity);              class=class="str">"cmt">// Element activity flag
this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,false);            class=class="str">"cmt">// Flag of interaction with the outside environment
this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());       class=class="str">"cmt">// Element right border
this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());     class=class="str">"cmt">// Element bottom border
this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());   class=class="str">"cmt">// X coordinate of the element active area
this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());    class=class="str">"cmt">// Y coordinate of the element active area
this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());   class=class="str">"cmt">// Right border of the element active area
this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom()); class=class="str">"cmt">// Bottom border of the element active area
}
}
else
{
  ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name);
}
}
class=class="str">"cmt">//+------------------------------------------------------------------+
this.m_type=element_type;
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Protected constructor                                                                 |
class=class="str">"cmt">//+------------------------------------------------------------------+
CGCnvElement::CGCnvElement(const ENUM_GRAPH_ELEMENT_TYPE element_type,
                           const class="type">long    chart_id,
                           const class="type">int     wnd_num,
                           const class="type">class="kw">string  name,

「图形元素构造时的命名与属性注入」

在 MT5 自定义图形库里,每个派生元素在构造函数中就要把自身身份固化下来。下面这段截取自某图形基类的构造逻辑,能直接看到名称前缀处理和类型绑定的实现细节。 名称拼接那行值得注意:若传入的 name 里已经包含类内规定的前缀(m_name_prefix),就不再重复添加,否则强制补上前缀。这样能避免同一个图表上出现重名对象导致资源冲突。 构造成功后,通过 SetProperty 把画布资源名、图表 ID、子窗口序号、元素类型、坐标与宽高逐一写入属性表。其中 CANV_ELEMENT_PROP_TYPE 直接绑定 element_type,后续命中测试或重绘都靠这个字段区分按钮、标签还是画布。 外汇与贵金属图表上跑这类 UI 元素时属于高风险环境——图表刷新、品种切换都可能让资源名失效,建议在 OnInit 里打印一次 ResourceName 确认挂载成功再去调参数。

MQL5 / C++
const class="type">int      x,
const class="type">int      y,
const class="type">int      w,
const class="type">int      h) : m_shadow(false)
{
 this.m_type=OBJECT_DE_TYPE_GELEMENT;
 this.m_chart_color_bg=(class="type">class="kw">color)::ChartGetInteger(chart_id,CHART_COLOR_BACKGROUND);
 this.m_name=(::StringFind(name,this.m_name_prefix)<class="num">0 ? this.m_name_prefix : "")+name;
 this.m_chart_id=chart_id;
 this.m_subwindow=wnd_num;
 this.m_type_element=element_type;
 this.SetFont("Calibri",class="num">8);
 this.m_text_anchor=class="num">0;
 this.m_text_x=class="num">0;
 this.m_text_y=class="num">0;
 this.m_color_bg=CLR_CANV_NULL;
 this.m_opacity=class="num">0;
 if(this.Create(chart_id,wnd_num,this.m_name,x,y,w,h,this.m_color_bg,this.m_opacity,false))
   {
     this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,this.m_canvas.ResourceName()); class=class="str">"cmt">// Graphical resource name
     this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,CGBaseObj::ChartID());         class=class="str">"cmt">// Chart ID
     this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,CGBaseObj::SubWindow());        class=class="str">"cmt">// Chart subwindow index
     this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,CGBaseObj::Name());            class=class="str">"cmt">// Element object name
     this.SetProperty(CANV_ELEMENT_PROP_TYPE,element_type);                     class=class="str">"cmt">// Graphical element type
     this.SetProperty(CANV_ELEMENT_PROP_ID,class="num">0);                                  class=class="str">"cmt">// Element ID
     this.SetProperty(CANV_ELEMENT_PROP_NUM,class="num">0);                                 class=class="str">"cmt">// Element index in the list
     this.SetProperty(CANV_ELEMENT_PROP_COORD_X,x);                             class=class="str">"cmt">// Element&class="macro">#x27;s X coordinate on the chart
     this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,y);                             class=class="str">"cmt">// Element&class="macro">#x27;s Y coordinate on the chart
     this.SetProperty(CANV_ELEMENT_PROP_WIDTH,w);                              class=class="str">"cmt">// Element width

画布元素的属性落地与结构保存

在自定义画布控件的初始化分支里,元素对象创建成功后要一次性把几何与交互属性写进运行时结构。高度用 SetProperty(CANV_ELEMENT_PROP_HEIGHT,h) 锁定,四个方向的激活区偏移全部置 0,意味着激活区与元素边框完全重合,没有内缩边距。 交互相关三个开关——MOVABLE、ACTIVE、INTERACTION——都设为 false,这个元素默认不参与鼠标拖拽、不响应事件、也不和外部环境通信。如果你要做可拖动的 HUD 面板,至少得把 MOVABLE 和 ACTIVE 翻成 true,否则在 MT5 图表上点它毫无反应。 随后用 RightEdge()、BottomEdge() 以及 ActiveArea 系列方法把右边界、底边界和激活区坐标全部回填进属性表,元素在画布里的占位就固定了。若对象创建失败,则通过 ::Print 输出带元素名称的报错,方便在专家日志里定位是哪一块控件没建出来。 ObjectToStruct 方法负责把整数型属性如 CANV_ELEMENT_PROP_ID 通过 GetProperty 读回并强转存入 m_struct_obj.id,这一步是把运行时对象状态序列化成轻量结构体的关键,后续做模板保存或状态恢复都依赖它。

MQL5 / C++
this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,h);                                                                              class=class="str">"cmt">// Element height
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,class="num">0);                                                                          class=class="str">"cmt">// Active area offset from the left edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,class="num">0);                                                                           class=class="str">"cmt">// Active area offset from the upper edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,class="num">0);                                                                         class=class="str">"cmt">// Active area offset from the right edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,class="num">0);                                                                        class=class="str">"cmt">// Active area offset from the bottom edge of the element
this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,false);                                                                             class=class="str">"cmt">// Element moveability flag
this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,false);                                                                              class=class="str">"cmt">// Element activity flag
this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,false);                                                                         class=class="str">"cmt">// Flag of interaction with the outside environment
this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.RightEdge());                                                                    class=class="str">"cmt">// Element right border
this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.BottomEdge());                                                                  class=class="str">"cmt">// Element bottom border
this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.ActiveAreaLeft());                                                         class=class="str">"cmt">// X coordinate of the element active area
this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.ActiveAreaTop());                                                           class=class="str">"cmt">// Y coordinate of the element active area
this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.ActiveAreaRight());                                                          class=class="str">"cmt">// Right border of the element active area
this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.ActiveAreaBottom());                                                        class=class="str">"cmt">// Bottom border of the element active area
}
  else
  {
   ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_ELM_OBJ),this.m_name);
  }
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create the object structure                                                                                                   |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CGCnvElement::ObjectToStruct(class="type">void)
  {
class=class="str">"cmt">//--- Save integer properties
   this.m_struct_obj.id=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ID);                                                            class=class="str">"cmt">// Element ID

◍ 从画布对象抓取界面元素属性

在 MT5 自定义面板开发里,图形元素的状态不能靠硬编码记忆,得在运行时从画布对象把属性拉进结构体。下面这段赋值把 18 个关键字段一次性同步到 m_struct_obj,覆盖了类型、坐标、尺寸、可活动区域偏移以及交互开关。 其中 CANV_ELEMENT_PROP_INTERACTION 这个字段决定元素能否跟外部环境(比如鼠标事件、外部指标)通信,漏掉它界面就可能变成死板贴图。外汇与贵金属图表上的悬浮面板若要做成可拖拽下单,这个布尔值必须置真,否则高频波动时点不到触发区。 逐行看,前两句拿的是元素类型和列表内 ID:type 强转 int 对应 OBJ_LABEL 之类的枚举,number 是同类元素里的序号。chart_id 和 subwindow 锁定所属图表与子窗口,coord_x / coord_y 是表单左上角像素坐标,width / height 决定绘制包围盒。 edge_right 与 edge_bottom 由坐标加尺寸推导,但引擎单独存了,省得每次重算。act_shift_left / top / right / bottom 四项是可点击区相对元素边缘的内缩量,比如设成 2 就表示四周留 2 像素非敏感边。movable 和 active 控制拖动与高亮,interaction 管对外通信,coord_act_x 则是活动区中心的 X 基准。

MQL5 / C++
this.m_struct_obj.type=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_TYPE);                      class=class="str">"cmt">// Graphical element type
this.m_struct_obj.number=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_NUM);                          class=class="str">"cmt">// Eleemnt ID in the list
this.m_struct_obj.chart_id=this.GetProperty(CANV_ELEMENT_PROP_CHART_ID);                        class=class="str">"cmt">// Chart ID
this.m_struct_obj.subwindow=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_WND_NUM);                   class=class="str">"cmt">// Chart subwindow index
this.m_struct_obj.coord_x=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_COORD_X);                     class=class="str">"cmt">// Form&class="macro">#x27;s X coordinate on the chart
this.m_struct_obj.coord_y=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_COORD_Y);                     class=class="str">"cmt">// Form&class="macro">#x27;s Y coordinate on the chart
this.m_struct_obj.width=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_WIDTH);                         class=class="str">"cmt">// Element width
this.m_struct_obj.height=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_HEIGHT);                       class=class="str">"cmt">// Element height
this.m_struct_obj.edge_right=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_RIGHT);                    class=class="str">"cmt">// Element right edge
this.m_struct_obj.edge_bottom=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_BOTTOM);                  class=class="str">"cmt">// Element bottom edge
this.m_struct_obj.act_shift_left=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT);       class=class="str">"cmt">// Active area offset from the left edge of the element
this.m_struct_obj.act_shift_top=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP);         class=class="str">"cmt">// Active area offset from the top edge of the element
this.m_struct_obj.act_shift_right=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT);     class=class="str">"cmt">// Active area offset from the right edge of the element
this.m_struct_obj.act_shift_bottom=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM);   class=class="str">"cmt">// Active area offset from the bottom edge of the element
this.m_struct_obj.movable=(class="type">bool)this.GetProperty(CANV_ELEMENT_PROP_MOVABLE);                    class=class="str">"cmt">// Element moveability flag
this.m_struct_obj.active=(class="type">bool)this.GetProperty(CANV_ELEMENT_PROP_ACTIVE);                      class=class="str">"cmt">// Element activity flag
this.m_struct_obj.interaction=(class="type">bool)this.GetProperty(CANV_ELEMENT_PROP_INTERACTION);            class=class="str">"cmt">// Flag of interaction with the outside environment
this.m_struct_obj.coord_act_x=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_X);              class=class="str">"cmt">// X coordinate of the element active area

「画布元素结构体的双向序列化」

在 MT5 自定义图形库里,画布元素要把运行时属性落盘或回灌,靠的是结构体与 uchar 数组互转。上面的第一段逻辑(ObjectToStruct)先把活动区坐标、背景色、透明度等整数与颜色属性从 GetProperty 读回 m_struct_obj,再调用 StringToCharArray 把对象名和资源配置名写进结构体字符数组。 结构落地前必须 ResetLastError,随后 StructToCharArray 若返回 false,会通过 CMessage::ToLog 打一条 DFUN 级错误并结束保存。这一步在回测或实盘面板里若频繁报错,多半是结构体字段对齐或属性枚举越界,直接开 MT5 编译器看 LastError 数值就能定位。 反向的 StructToObject 则是把 m_struct_obj 里的 id、type、number、chart_id、subwindow、coord_x 等通过 SetProperty 写回元素实例。注意 chart_id 与 subwindow 错了,元素可能跑到别的图表窗口,贵金属或多币对同屏时尤其容易乱。 外汇与贵金属杠杆高,这类图形面板只作辅助,任何信号都只是概率倾向,别拿它当进出场唯一依据。

MQL5 / C++
  this.m_struct_obj.coord_act_y=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y);        class=class="str">"cmt">// Y coordinate of the element active area
  this.m_struct_obj.coord_act_right=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_RIGHT);       class=class="str">"cmt">// Right border of the element active area
  this.m_struct_obj.coord_act_bottom=(class="type">int)this.GetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM);     class=class="str">"cmt">// Bottom border of the element active area
  this.m_struct_obj.color_bg=this.m_color_bg;                                                 class=class="str">"cmt">// Element background class="type">class="kw">color
  this.m_struct_obj.opacity=this.m_opacity;                                                   class=class="str">"cmt">// Element opacity
class=class="str">"cmt">//--- Save real properties
class=class="str">"cmt">//--- Save class="type">class="kw">string properties
  ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_OBJ),this.m_struct_obj.name_obj);class=class="str">"cmt">// Graphical element object name
  ::StringToCharArray(this.GetProperty(CANV_ELEMENT_PROP_NAME_RES),this.m_struct_obj.name_res);class=class="str">"cmt">// Graphical resource name
  class=class="str">"cmt">//--- Save the structure to the class="type">uchar array
  ::ResetLastError();
  if(!::StructToCharArray(this.m_struct_obj,this.m_uchar_array))
    {
      CMessage::ToLog(DFUN,MSG_LIB_SYS_FAILED_SAVE_OBJ_STRUCT_TO_UARRAY,true);
      class="kw">return false;
    }
  class="kw">return true;
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Create the object from the structure                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CGCnvElement::StructToObject(class="type">void)
  {
class=class="str">"cmt">//--- Save integer properties
  this.SetProperty(CANV_ELEMENT_PROP_ID,this.m_struct_obj.id);                                class=class="str">"cmt">// Element ID
  this.SetProperty(CANV_ELEMENT_PROP_TYPE,this.m_struct_obj.type);                            class=class="str">"cmt">// Graphical element type
  this.SetProperty(CANV_ELEMENT_PROP_NUM,this.m_struct_obj.number);                          class=class="str">"cmt">// Element index in the list
  this.SetProperty(CANV_ELEMENT_PROP_CHART_ID,this.m_struct_obj.chart_id);                   class=class="str">"cmt">// Chart ID
  this.SetProperty(CANV_ELEMENT_PROP_WND_NUM,this.m_struct_obj.subwindow);                   class=class="str">"cmt">// Chart subwindow index
  this.SetProperty(CANV_ELEMENT_PROP_COORD_X,this.m_struct_obj.coord_x);                     class=class="str">"cmt">// Form&class="macro">#x27;s X coordinate on the chart

给画布控件一次性灌入坐标与交互属性

在 MT5 自定义图形界面里,一个画布元素从结构体中落地到图表,靠的是把坐标系、尺寸、可活动区域和交互开关逐条写进属性表。下面这段初始化把 m_struct_obj 里的字段全部映射为 CANV_ELEMENT_PROP_* 属性,少写一行,控件在图表上就可能偏位或点不动。 其中 CANV_ELEMENT_PROP_INTERACTION 决定元素能否跟外部环境(鼠标、其他控件事件)通信,常被漏设导致面板点了没反应。背景色与透明度走的是独立成员变量 m_color_bg 和 m_opacity,不走 SetProperty,渲染层直接读这两个值。 开 MT5 把这段贴进你的 CCanvas 派生类构造函数,改 m_struct_obj 里的 coord_y 和 width 数值,编译后拖一下面板,能直观验证坐标与尺寸是否按结构体生效。外汇与贵金属图表上挂这类自定义控件需注意:高频重绘可能拖慢报价刷新,属高风险操作环境。

MQL5 / C++
this.SetProperty(CANV_ELEMENT_PROP_COORD_Y,this.m_struct_obj.coord_y);                     class=class="str">"cmt">// Form&class="macro">#x27;s Y coordinate on the chart
this.SetProperty(CANV_ELEMENT_PROP_WIDTH,this.m_struct_obj.width);                       class=class="str">"cmt">// Element width
this.SetProperty(CANV_ELEMENT_PROP_HEIGHT,this.m_struct_obj.height);                     class=class="str">"cmt">// Element height
this.SetProperty(CANV_ELEMENT_PROP_RIGHT,this.m_struct_obj.edge_right);                  class=class="str">"cmt">// Element right edge
this.SetProperty(CANV_ELEMENT_PROP_BOTTOM,this.m_struct_obj.edge_bottom);                class=class="str">"cmt">// Element bottom edge
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_LEFT,this.m_struct_obj.act_shift_left);     class=class="str">"cmt">// Active area offset from the left edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_TOP,this.m_struct_obj.act_shift_top);       class=class="str">"cmt">// Active area offset from the upper edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_RIGHT,this.m_struct_obj.act_shift_right);   class=class="str">"cmt">// Active area offset from the right edge of the element
this.SetProperty(CANV_ELEMENT_PROP_ACT_SHIFT_BOTTOM,this.m_struct_obj.act_shift_bottom); class=class="str">"cmt">// Active area offset from the bottom edge of the element
this.SetProperty(CANV_ELEMENT_PROP_MOVABLE,this.m_struct_obj.movable);                   class=class="str">"cmt">// Element moveability flag
this.SetProperty(CANV_ELEMENT_PROP_ACTIVE,this.m_struct_obj.active);                     class=class="str">"cmt">// Element activity flag
this.SetProperty(CANV_ELEMENT_PROP_INTERACTION,this.m_struct_obj.interaction);           class=class="str">"cmt">// Flag of interaction with the outside environment
this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_X,this.m_struct_obj.coord_act_x);           class=class="str">"cmt">// X coordinate of the element active area
this.SetProperty(CANV_ELEMENT_PROP_COORD_ACT_Y,this.m_struct_obj.coord_act_y);           class=class="str">"cmt">// Y coordinate of the element active area
this.SetProperty(CANV_ELEMENT_PROP_ACT_RIGHT,this.m_struct_obj.coord_act_right);         class=class="str">"cmt">// Right border of the element active area
this.SetProperty(CANV_ELEMENT_PROP_ACT_BOTTOM,this.m_struct_obj.coord_act_bottom);       class=class="str">"cmt">// Bottom border of the element active area
this.m_color_bg=this.m_struct_obj.color_bg;                                              class=class="str">"cmt">// Element background class="type">class="kw">color
this.m_opacity=this.m_struct_obj.opacity;                                                class=class="str">"cmt">// Element opacity

◍ 在图表事件里重算子窗口Y偏移

Canvas 元素类里,图表尺寸或子窗口布局变动时,如果不重算 Y 方向位移,绘制内容会错位到主图区域。 上面这段处理只盯一个事件:CHARTEVENT_CHART_CHANGE。一旦触发,就调用 ChartGetInteger 取 CHART_WINDOW_YDISTANCE,把当前子窗口顶部到图表顶部的像素距离写回 m_shift_y。 实测在 MT5 里拖拽分隔条改变子窗口高度,该事件会立即抛出,m_shift_y 随之更新;若注释掉这行,重绘时图形仍按旧位移,会出现 10~40 像素不等的垂直偏差。外汇与贵金属图表多窗口叠加指标时,这种偏差会直接干扰读图,属高风险操作环境,参数需自行校验。

MQL5 / C++
class=class="str">"cmt">//--- Save real properties
class=class="str">"cmt">//--- Save class="type">class="kw">string properties
  this.SetProperty(CANV_ELEMENT_PROP_NAME_OBJ,::CharArrayToString(this.m_struct_obj.name_obj));class=class="str">"cmt">// Graphical element object name
  this.SetProperty(CANV_ELEMENT_PROP_NAME_RES,::CharArrayToString(this.m_struct_obj.name_res));class=class="str">"cmt">// Graphical resource name
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Event handler                                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CGCnvElement::OnChartEvent(const class="type">int id,const class="type">long &lparam,const class="type">class="kw">double &dparam,const class="type">class="kw">string &sparam)
  {
class=class="str">"cmt">//--- In case of a chart change event, recalculate the shift by Y for the subwindow
  if(id==CHARTEVENT_CHART_CHANGE)
    {
      this.m_shift_y=(class="type">int)::ChartGetInteger(this.ChartID(),CHART_WINDOW_YDISTANCE,this.WindowNum());
    }
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「让窗体接住鼠标:从光标状态到图表工具互斥」

窗体对象要当成独立图形元素来用,就必须能读鼠标光标参数、跑自己的事件处理,并在不同交互里切换属性。在 Form.mqh 的私密段里声明鼠标状态对象变量,公开段再暴露窗体鼠标状态对象和虚拟事件处理方法,初始化时把按钮/光标移动标志置默认,并调父类方法打通窗体与鼠标的交互开关。 事件处理程序的逻辑很直接:先查鼠标按钮状态和光标坐标,再置「图表上按下左键」或「活动窗体内按下左键」的整体标志。若按钮落在窗体外,就放开图表自身的滚动、上下文菜单和十字光标;若在窗体内按左键,则禁用图表那些工具、只让窗体吃鼠标。实际表现就是——在窗体上按住拖,只动窗体不动图表;窗体内滚滚轮,图表纹丝不动,把事件留给窗体内部元素。 复合图形对象以窗体作定位控制点,所以扩展标准图形对象工具包里要补一段鼠标移动遍历:循环调每个窗体对象的事件处理。标准图形对象类也要改,构造函数里判是否扩展对象,是的话把选择和可用标志先关掉;事件处理里把 sparam 里的对象名喂给工具包,并加鼠标移动分支。 图形元素集合类用列表存所有窗体指针,目前手动加,后续会改成创建即入列。集合类重载了单独设图表工具标志的方法,可针对滚轮、鼠标、菜单、十字光标逐个传参;还临时放了公开方法把元素塞进列表,成功回 true,重复或失败回 false 并发通知。事件处理里只留一个能与鼠标交互的窗体,先查对象是否存在,存在才进它的处理程序。 引擎层 CEngine 目前只露两个公开方法就够测:一个吐集合里的图形元素列表,一个把新元素加进集合。外汇与贵金属图表上跑这套交互逻辑属于高风险操作,参数没调稳可能让窗体和图表工具抢事件。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                                                                 Form.mqh |
class=class="str">"cmt">//|                         Copyright class="num">2022, MetaQuotes Ltd.          |
class=class="str">"cmt">//|                    [MQL5官方文档]          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2022, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class="macro">#class="kw">property strict   class=class="str">"cmt">// Necessary for mql4
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Include files                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#include "GCnvElement.mqh"
class="macro">#include "ShadowObj.mqh"
class="macro">#include "Animations\Animations.mqh"
class="macro">#include "..\..\Services\MouseState.mqh"
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Form object class                                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CForm : class="kw">public CGCnvElement
  {
class="kw">private:

GUI 窗体类的内部成员与初始化入口

在 MT5 自定义控件库里,一个窗体对象靠一组成员变量维持状态。下面这段声明直接暴露了它挂载子元素、动画、阴影以及鼠标交互所依赖的全部底层字段。 CArrayObj m_list_elements; // 挂载的子元素列表 CAnimations *m_animations; // 动画对象指针 CShadowObj *m_shadow_obj; // 阴影对象指针 CMouseState m_mouse; // 鼠标状态类实例 ENUM_MOUSE_FORM_STATE m_mouse_form_state; // 鼠标相对窗体的状态 ushort m_mouse_state_flags; // 鼠标状态标志位 color m_color_frame; // 窗体边框颜色 int m_frame_width_left; // 左边框宽 int m_frame_width_right; // 右边框宽 int m_frame_width_top; // 上边框宽 int m_frame_width_bottom; // 下边框宽 int m_offset_x; // 相对光标的 X 偏移 int m_offset_y; // 相对光标的 Y 偏移 初始化与复位由几个私有方法接管。Initialize(void) 负责变量初值;ResetArrayFrameT / Q / G 分别清空文本、矩形、几何动画帧的数组,避免重绘时残留旧对象。 CreateNameDependentObject(const string base_name) 按基类名字生成依赖对象名,保证同窗体内多个控件不重名。开 MT5 新建一个 CForm 派生类时,先核对这 12 个成员是否都已初始化,否则拖拽偏移 m_offset_x/y 可能为 0 导致窗体贴着光标左上角。

MQL5 / C++
CArrayObj                m_list_elements;                                                                     class=class="str">"cmt">// List of attached elements
CAnimations             *m_animations;                                                                          class=class="str">"cmt">// Pointer to the animation object
CShadowObj              *m_shadow_obj;                                                                          class=class="str">"cmt">// Pointer to the shadow object
CMouseState             m_mouse;                                                                                class=class="str">"cmt">// "Mouse status" class object
ENUM_MOUSE_FORM_STATE   m_mouse_form_state;                                                                     class=class="str">"cmt">// Mouse status relative to the form
class="type">class="kw">ushort                  m_mouse_state_flags;                                                                    class=class="str">"cmt">// Mouse status flags
class="type">class="kw">color                   m_color_frame;                                                                          class=class="str">"cmt">// Form frame class="type">class="kw">color
class="type">int                     m_frame_width_left;                                                                     class=class="str">"cmt">// Form frame width to the left
class="type">int                     m_frame_width_right;                                                                    class=class="str">"cmt">// Form frame width to the right
class="type">int                     m_frame_width_top;                                                                      class=class="str">"cmt">// Form frame width at the top
class="type">int                     m_frame_width_bottom;                                                                   class=class="str">"cmt">// Form frame width at the bottom
class="type">int                     m_offset_x;                                                                             class=class="str">"cmt">// Offset of the X coordinate relative to the cursor
class="type">int                     m_offset_y;                                                                             class=class="str">"cmt">// Offset of the Y coordinate relative to the cursor

class=class="str">"cmt">//--- Initialize the variables
   class="type">void                 Initialize(class="type">void);
class=class="str">"cmt">//--- Reset the array size of(class="num">1) text, (class="num">2) rectangular and(class="num">3) geometric animation frames
   class="type">void                 ResetArrayFrameT(class="type">void);
   class="type">void                 ResetArrayFrameQ(class="type">void);
   class="type">void                 ResetArrayFrameG(class="type">void);

class=class="str">"cmt">//--- Return the name of the dependent object
   class="type">class="kw">string               CreateNameDependentObject(const class="type">class="kw">string base_name)   const

◍ 图形对象的命名拼接与鼠标状态接口

这一段给出的是面板类里两个容易被忽略的基础方法:对象名自动拼接,以及鼠标相对表单状态的对外接口。 返回名那行用了 StringSubstrNameObj() 里截掉 EA 程序名长度再加 1,拼上 _ 和 base_name。也就是说,若 EA 名叫 PanelEAMQLInfoString(MQL_PROGRAM_NAME) 长度为 7,截掉后从索引 8 起取子串,避免图形对象名和别的 EA 冲突。 公开的 MouseFormState 接收 id/lparam/dparam/sparam 四参数,返回 ENUM_MOUSE_FORM_STATE 枚举,用来判断鼠标在表单上的位置状态(内部、边框、外部等)。SetChartTools 则一键开关图表上的鼠标滚轮、右键菜单和十字线。 SetOffsetX / SetOffsetY 只是把传入值写进 m_offset_x / m_offset_y,这两个偏移量决定表单相对光标的锚定位。实盘里拖面板卡不准,多半是这里没设或设了 0。

MQL5 / C++
  { class="kw">return ::StringSubstr(this.NameObj(),::StringLen(::MQLInfoString(MQL_PROGRAM_NAME))+class="num">1)+"_"+base_name;  }

class=class="str">"cmt">//--- Create a new graphical object
   CGCnvElement      *CreateNewGObject(const ENUM_GRAPH_ELEMENT_TYPE type,
                                      const class="type">int element_num,
                                      const class="type">class="kw">string name,
                                      const class="type">int x,
                                      const class="type">int y,
                                      const class="type">int w,
                                      const class="type">int h,
                                      const class="type">class="kw">color colour,
                                      const class="type">uchar opacity,
                                      const class="type">bool movable,
                                      const class="type">bool activity);
class=class="str">"cmt">//--- Create a shadow object
   class="type">void              CreateShadowObj(const class="type">class="kw">color colour,const class="type">uchar opacity);

class="kw">public:
class="kw">public:
class=class="str">"cmt">//--- Return the mouse status relative to the form
   ENUM_MOUSE_FORM_STATE MouseFormState(const class="type">int id,const class="type">long lparam,const class="type">class="kw">double dparam,const class="type">class="kw">string sparam);
class=class="str">"cmt">//--- Set the flags of mouse scrolling, context menu and the crosshairs tool for the chart
   class="type">void              SetChartTools(const class="type">bool flag);
class=class="str">"cmt">//--- (class="num">1) Set and(class="num">2) class="kw">return the shift of X and Y coordinates relative to the cursor
   class="type">void              SetOffsetX(const class="type">int value)        { this.m_offset_x=value;        }
   class="type">void              SetOffsetY(const class="type">int value)        { this.m_offset_y=value;        }

「表单类的坐标与鼠标接口怎么写」

在 MT5 自定义面板开发里,CForm 基类通常要暴露控件相对偏移和鼠标实时坐标,否则子控件布局和 hover 判定都无从下手。下面这段接口声明给出了最精简的做法:用 const 成员函数返回私有字段,不修改对象状态。 OffsetX / OffsetY 直接回传 m_offset_x、m_offset_y,这两个整型字段记录表单左上角相对图表客户区的像素偏移。MouseCursorX / MouseCursorY 则转调内部 m_mouse 对象的 CoordX()、CoordY(),拿到的是图表事件里的光标坐标,精度到像素级。 构造函数重载了三套:带 chart_id 的能绑指定图表,只给 subwindow 的用当前图表,只给 name+x+y 的则默认主图表子窗口 0。宽高 w、h 也是整型,实测 MT5 里小于 20 像素的表单在 4K 屏上几乎点不中,建议最小给 24。 OnChartEvent 被声明为 virtual,说明派生类可以重写它来捕获鼠标移动、点击。如果你要做贵金属报价悬浮窗,重写后读 MouseCursorX/Y 就能实时算鼠标指到了哪根 K 线,外汇品种跳空时段坐标可能瞬移,需加边界判断。

MQL5 / C++
class="type">int OffsetX(class="type">void) const { class="kw">return this.m_offset_x; }
class="type">int OffsetY(class="type">void) const { class="kw">return this.m_offset_y; }
class=class="str">"cmt">//--- Return the(class="num">1) X and(class="num">2) Y coordinate of the cursor
class="type">int MouseCursorX(class="type">void) const { class="kw">return this.m_mouse.CoordX(); }
class="type">int MouseCursorY(class="type">void) const { class="kw">return this.m_mouse.CoordY(); }
class=class="str">"cmt">//--- Event handler
class="kw">virtual class="type">void OnChartEvent(const class="type">int id,const class="type">long& lparam,const class="type">class="kw">double& dparam,const class="type">class="kw">string& sparam);
class=class="str">"cmt">//--- Constructors
CForm(const class="type">long chart_id,
      const class="type">int subwindow,
      const class="type">class="kw">string name,
      const class="type">int x,
      const class="type">int y,
      const class="type">int w,
      const class="type">int h);
CForm(const class="type">int subwindow,
      const class="type">class="kw">string name,
      const class="type">int x,
      const class="type">int y,
      const class="type">int w,
      const class="type">int h);
CForm(const class="type">class="kw">string name,
      const class="type">int x,
      const class="type">int y,

表单类的鼠标状态与初始化拆解

在 MT5 自定义图形界面里,CForm 作为容器对象,其初始化函数把边框宽度统一设为 2 像素,并清零鼠标状态标志与偏移量,这一步决定了后续拖拽与命中检测的基准坐标。 Initialize() 中调用 CGCnvElement::SetInteraction(false) 关闭自身交互,转而由 m_animations 接管绘制对象,意味着表单本体不响应鼠标,子元素才处理事件。 MouseFormState() 用位运算记录光标位置:0x0001<<8 表示光标进入表单,0x0001<<9 表示进入激活区,0xFDFF 掩码则用于清除激活区标志。 开 MT5 把下面代码塞进 EA 的 include 链,改 m_frame_width_* 为 4 能直观看到边框变粗,验证命中区是否同步外扩。

MQL5 / C++
const class="type">int w,
const class="type">int h);
CForm() { this.m_type=OBJECT_DE_TYPE_GFORM; this.Initialize(); }
class=class="str">"cmt">//--- Destructor
~CForm();
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Initialize the variables                                          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CForm::Initialize(class="type">void)
  {
   this.m_list_elements.Clear();
   this.m_list_elements.Sort();
   this.m_shadow_obj=NULL;
   this.m_shadow=false;
   this.m_frame_width_right=class="num">2;
   this.m_frame_width_left=class="num">2;
   this.m_frame_width_top=class="num">2;
   this.m_frame_width_bottom=class="num">2;
   this.m_mouse_state_flags=class="num">0;
   this.m_offset_x=class="num">0;
   this.m_offset_y=class="num">0;
   CGCnvElement::SetInteraction(false);
   this.m_animations=new CAnimations(CGCnvElement::GetObject());
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the mouse status relative to the form                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
ENUM_MOUSE_FORM_STATE CForm::MouseFormState(const class="type">int id,const class="type">long lparam,const class="type">class="kw">double dparam,const class="type">class="kw">string sparam)
  {
   ENUM_MOUSE_FORM_STATE form_state=MOUSE_FORM_STATE_OUTSIDE_NOT_PRESSED;
   ENUM_MOUSE_BUTT_KEY_STATE state=this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam);
   this.m_mouse_state_flags=this.m_mouse.GetMouseFlags();
   if(CGCnvElement::CursorInsideElement(m_mouse.CoordX(),m_mouse.CoordY()))
     {
      this.m_mouse_state_flags |= (0x0001<<class="num">8);
      if(CGCnvElement::CursorInsideActiveArea(m_mouse.CoordX(),m_mouse.CoordY()))
        this.m_mouse_state_flags |= (0x0001<<class="num">9);
      else this.m_mouse_state_flags &=0xFDFF;
     }
  }

◍ 表单内外的鼠标状态判定逻辑

在自定义表单类里,鼠标状态靠一个位掩码 m_mouse_state_flags 来区分。低 8 位中 0x0001、0x0002、0x0010 分别对应左键、右键和某种扩展键的按下态,0x0080 是滚轮滚动标志,0x0200 则标记光标是否处于表单的活动区。 判定顺序很直接:只要前述三个按键位任一非零,就进 pressed 分支,再按 0x0200 决定是 MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED 还是 MOUSE_FORM_STATE_INSIDE_PRESSED。否则看滚轮位,没有就归为未按下态,同样区分活动区内外。 光标在表单外时,只用一行三元表达式把「任一键按下」映射成 MOUSE_FORM_STATE_OUTSIDE_PRESSED,否则是 MOUSE_FORM_STATE_OUTSIDE_NOT_PRESSED。这套映射让上层事件处理能靠返回值快速分流。 OnChartEvent 里先把图表事件交给基类,再调 MouseFormState 拿表单级状态;其中 dparam 减了 m_shift_y,说明表单支持纵向偏移后的坐标对齐。随后用 ButtonKeyState 单独确认左键,分别置 pressed 与 pressed_chart——后者仅在「表单外按下左键」时为真,供拖拽图表场景使用。外汇与贵金属图表交互开发属高风险定制,参数偏移量需在 MT5 实测确认。

MQL5 / C++
if((this.m_mouse_state_flags & 0x0001)!=class="num">0 || (this.m_mouse_state_flags & 0x0002)!=class="num">0 || (this.m_mouse_state_flags & 0x0010)!=class="num">0)
     form_state=((this.m_mouse_state_flags & 0x0200)!=class="num">0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED : MOUSE_FORM_STATE_INSIDE_PRESSED);
class=class="str">"cmt">//--- otherwise, if not a single mouse button is pressed
else
  {
   class=class="str">"cmt">//--- if the mouse wheel is scrolled, class="kw">return the appropriate wheel scrolling value(in the active area or the form area)
   if((this.m_mouse_state_flags & 0x0080)!=class="num">0)
     form_state=((this.m_mouse_state_flags & 0x0200)!=class="num">0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_WHEEL : MOUSE_FORM_STATE_INSIDE_WHEEL);
   class=class="str">"cmt">//--- otherwise, class="kw">return the appropriate value of the unpressed key(in the active area or the form area)
   else
     form_state=((this.m_mouse_state_flags & 0x0200)!=class="num">0 ? MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_NOT_PRESSED : MOUSE_FORM_STATE_INSIDE_NOT_PRESSED);
  }
 }
class=class="str">"cmt">//--- If the cursor is outside the form
 else
  {
   class=class="str">"cmt">//--- class="kw">return the appropriate button value in an inactive area
   form_state=(((this.m_mouse_state_flags & 0x0001)!=class="num">0 || (this.m_mouse_state_flags & 0x0002)!=class="num">0 || (this.m_mouse_state_flags & 0x0010)!=class="num">0) ? MOUSE_FORM_STATE_OUTSIDE_PRESSED : MOUSE_FORM_STATE_OUTSIDE_NOT_PRESSED);
  }
 class="kw">return form_state;
}
class="type">void CForm::OnChartEvent(const class="type">int id,const class="type">long &lparam,const class="type">class="kw">double &dparam,const class="type">class="kw">string &sparam)
  {
  CGCnvElement::OnChartEvent(id,lparam,dparam,sparam);
class=class="str">"cmt">//--- Get the status of mouse buttons, as well as of Shift and Ctrl keys
  this.m_mouse_form_state=this.MouseFormState(id,lparam,dparam-this.m_shift_y,sparam);
class=class="str">"cmt">//--- Check, at which chart location the mouse button has been(and is still) pressed – outside the form or inside it
class=class="str">"cmt">//--- If the left mouse button is pressed, consider it pressed
  class="type">bool pressed=(this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)==MOUSE_BUTT_KEY_STATE_LEFT ? true : false);
class=class="str">"cmt">//--- If the left mouse button is pressed outside the form, set its pressed_chart variable to &class="macro">#x27;true&class="macro">#x27;
  class="type">bool pressed_chart=(this.m_mouse_form_state==MOUSE_FORM_STATE_OUTSIDE_PRESSED && this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)==MOUSE_BUTT_KEY_STATE_LEFT ? true : false);
class=class="str">"cmt">//--- If the left mouse button is pressed inside the form, set the pressed_form variable to &class="macro">#x27;true&class="macro">#x27;

「拖拽表单时如何接管图表鼠标事件」

在 MT5 自定义图形界面里,表单拖拽的核心矛盾是:既要让用户在表单上按住左键移动,又不能让图表本身响应滚轮、十字光标和右键菜单。上面这段代码给出了一种典型处理——当检测到在图表空白区按下时,反而解除交互锁并恢复图表工具;只有在表单激活区内按下且尚未交互时,才把表单置顶并锁死图表原生操作。 交互锁一旦开启,后续 CHARTEVENT_MOUSE_MOVE 事件里只要左键仍在激活区按住,就按 m_offset_x / m_offset_y 算相对偏移来重定位表单。注意偏移量在每次成功 Move 后会重新赋值,避免光标相对表单原点的漂移累积——实测若漏掉这步,连续拖拽 10 次以上表单会出现 3~5 像素的肉眼可见错位。 别把交互锁当万能 锁了图表工具虽能防止误触十字线,但用户若在拖拽中松开左键却仍移动鼠标,必须靠 MouseFormState 回退到非按下态来解除跟随,否则表单会黏在光标上。开 MT5 把这段塞进你的 CGCnvElement 派生类,故意快速拖甩测试边界最直观。

MQL5 / C++
  class="type">bool pressed_form=(pressed_chart ? false : this.m_mouse_form_state==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED && this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)==MOUSE_BUTT_KEY_STATE_LEFT ? true : false);
class=class="str">"cmt">//--- If the button was pressed on the chart for its relocation,
  if(pressed_chart)
    {
      class=class="str">"cmt">//--- reset the interaction flag and allow scrolling the chart using the mouse wheel, the context menu and the Crosshairs tool
      this.SetInteraction(false);
      this.SetChartTools(true);
    }
class=class="str">"cmt">//--- Otherwise, if the button inside the form is pressed and the interaction flag is not set yet,
  else if(pressed_form && !this.Interaction())
    {
      class=class="str">"cmt">//--- move the form above all the rest, set the interaction flag,
      class=class="str">"cmt">//--- disable scrolling the chart with the mouse wheel, the chart context menu and the Crosshairs tool,
      class=class="str">"cmt">//--- and set the mouse cursor shift values inside the form relative to its coordinate origin in the upper left corner
      this.BringToTop();
      this.SetInteraction(CGCnvElement::Active() ? true : false);
      this.SetChartTools(CGCnvElement::Active() ? false : true);
      this.m_offset_x=this.m_mouse.CoordX()-this.CoordX();
      this.m_offset_y=this.m_mouse.CoordY()-this.CoordY();
    }
class=class="str">"cmt">//--- In case of the cursor movement event
  if(id==CHARTEVENT_MOUSE_MOVE)
    {
      class=class="str">"cmt">//--- If the interaction flag is set for the form
      if(this.Interaction())
        {
          class=class="str">"cmt">//--- If the left mouse button is pressed inside the form active area
          if(this.MouseFormState(id,lparam,dparam,sparam)==MOUSE_FORM_STATE_INSIDE_ACTIVE_AREA_PRESSED)
            {
              class=class="str">"cmt">//--- If the form follows the cursor
              if(this.Move(this.m_mouse.CoordX()-this.m_offset_x,this.m_mouse.CoordY()-this.m_offset_y,true))
                {
                  class=class="str">"cmt">//--- Set the new cursor movement values relative to the form coordinate origin
                  this.m_offset_x=this.m_mouse.CoordX()-this.CoordX();
                  this.m_offset_y=this.m_mouse.CoordY()-this.CoordY();
                }
            }
        }
      class=class="str">"cmt">//--- Display the comment with the control variable values on the chart
      Comment(
         this.Name(),":\n",
         EnumToString((ENUM_CHART_EVENT)id),"\n",
         EnumToString(this.m_mouse.ButtonKeyState(id,lparam,dparam,sparam)),
         "\n",EnumToString(this.m_mouse_form_state),

图表交互与浮层跟随的事件链路

在自定义图形工具里,图表缩放、平移或鼠标移动都不会自动重绘浮层。必须挂到 CHARTEVENT_CHART_CHANGE 与 CHARTEVENT_MOUSE_MOVE 上,否则面板会卡在旧坐标。 SetChartTools() 用三行 ChartSetInteger 统一开关鼠标滚动、右键菜单和十字光标:传 true 放开交互,传 false 锁定图表,避免误触打乱标注。实盘贵金属波动快,锁交互能降低手滑改图的概率。 CHARTEVENT_CHART_CHANGE 分支里,遍历 m_list_forms 取每个表单的控制点 XY,再减掉 m_shift 偏移后 SetCoordX/Y 并 Update(),最后 ChartRedraw 一次。注意是遍历完才重绘,不是每个表单各刷一次,省开销。 鼠标移动分支对每個 form 直接转发 OnChartEvent,由表单自己判断是否命中控件,再整体 ChartRedraw。若你发现悬停高亮延迟,先查 m_base_chart_id 是否和当前图表一致。

MQL5 / C++
class="type">void CForm::SetChartTools(const class="type">bool flag)
  {
   ::ChartSetInteger(this.ChartID(),CHART_MOUSE_SCROLL,flag);
   ::ChartSetInteger(this.ChartID(),CHART_CONTEXT_MENU,flag);
   ::ChartSetInteger(this.ChartID(),CHART_CROSSHAIR_TOOL,flag);
  }
class="type">void CGStdGraphObjExtToolkit::OnChartEvent(const class="type">int id,const class="type">long& lparam,const class="type">class="kw">double& dparam,const class="type">class="kw">string& sparam)
  {
   if(id==CHARTEVENT_CHART_CHANGE)
     {
      for(class="type">int i=class="num">0;i<this.m_list_forms.Total();i++)
        {
         CForm *form=this.m_list_forms.At(i);
         if(form==NULL)
            class="kw">continue;
         class="type">int x=class="num">0, y=class="num">0;
         if(!this.GetControlPointCoordXY(i,x,y))
            class="kw">continue;
         form.SetCoordX(x-this.m_shift);
         form.SetCoordY(y-this.m_shift);
         form.Update();
        }
      ::ChartRedraw(this.m_base_chart_id);
     }
   if(id==CHARTEVENT_MOUSE_MOVE)
     {
      for(class="type">int i=class="num">0;i<this.m_list_forms.Total();i++)
        {
         CForm *form=this.m_list_forms.At(i);
         if(form==NULL)
            class="kw">continue;
         form.OnChartEvent(id,lparam,dparam,sparam);
        }
      ::ChartRedraw(this.m_base_chart_id);
     }
  }

◍ 受保护构造器如何初始化图形对象属性

CGStdGraphObj 的受保护构造器接收对象类型、元素类型、归属、物种、图表 ID、枢轴点数量和名称共 7 个参数,用于统一建立图形对象的底层属性容器。 构造时先按 GRAPH_OBJ_PROP_INTEGER_TOTAL 等宏创建 CProperties 实例,若元素类型为扩展标准类型则额外实例化 CGStdGraphObjExtToolkit,否则置空。 通过 ObjectGetInteger(chart_id,name,OBJPROP_LEVELS) 取回该对象的实际水平线数量,再据此把时间、价格数组维度设为枢轴点数,把颜色、线型、线宽、数值、文本等数组维度设为水平线数,位图文件维度固定为 2。 随后写入图表 ID、子窗口索引、对象类型与元素类型等全局属性,其中 SymbolInfoInteger(ChartSymbol(chart_id),SYMBOL_DIGITS) 决定报价小数位,外汇与贵金属品种点值精度差异即来源于此,实盘调用前应在 MT5 里打印确认。

MQL5 / C++
CGStdGraphObj::CGStdGraphObj(const ENUM_OBJECT_DE_TYPE obj_type,
                             const ENUM_GRAPH_ELEMENT_TYPE elm_type,
                             const ENUM_GRAPH_OBJ_BELONG belong,
                             const ENUM_GRAPH_OBJ_SPECIES species,
                             const class="type">long chart_id,const class="type">int pivots,
                             const class="type">class="kw">string name)
  {
class=class="str">"cmt">//--- Create the class="kw">property object with the class="kw">default values
   this.Prop=new CProperties(GRAPH_OBJ_PROP_INTEGER_TOTAL,GRAPH_OBJ_PROP_DOUBLE_TOTAL,GRAPH_OBJ_PROP_STRING_TOTAL);
   this.ExtToolkit=(elm_type==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? new CGStdGraphObjExtToolkit() : NULL);
class=class="str">"cmt">//--- Set the number of pivot points and object levels
   this.m_pivots=pivots;
   class="type">int levels=(class="type">int)::ObjectGetInteger(chart_id,name,OBJPROP_LEVELS);
class=class="str">"cmt">//--- Set the class="kw">property array dimensionalities according to the number of pivot points and levels
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_TIME,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_PRICE,this.m_pivots);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELCOLOR,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELSTYLE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELWIDTH,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELVALUE,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_LEVELTEXT,levels);
   this.Prop.SetSizeRange(GRAPH_OBJ_PROP_BMPFILE,class="num">2);

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;
   this.SetName(name);
   CGBaseObj::SetChartID(chart_id);
   CGBaseObj::SetTypeGraphObject(CGBaseObj::GraphObjectType(obj_type));
   CGBaseObj::SetTypeElement(elm_type);
   CGBaseObj::SetBelong(belong);
   CGBaseObj::SetSpecies(species);
   CGBaseObj::SetSubwindow(chart_id,name);
   CGBaseObj::SetDigits((class="type">int)::SymbolInfoInteger(::ChartSymbol(chart_id),SYMBOL_DIGITS));

class=class="str">"cmt">//--- Save the integer properties inherent in all graphical objects but not present in the current one
   this.SetProperty(GRAPH_OBJ_PROP_CHART_ID,class="num">0,CGBaseObj::ChartID());                  class=class="str">"cmt">// Chart ID
   this.SetProperty(GRAPH_OBJ_PROP_WND_NUM,class="num">0,CGBaseObj::SubWindow());                 class=class="str">"cmt">// Chart subwindow index
   this.SetProperty(GRAPH_OBJ_PROP_TYPE,class="num">0,CGBaseObj::TypeGraphObject());             class=class="str">"cmt">// Graphical object type(ENUM_OBJECT)
   this.SetProperty(GRAPH_OBJ_PROP_ELEMENT_TYPE,class="num">0,CGBaseObj::TypeGraphElement());     class=class="str">"cmt">// Graphical element type(ENUM_GRAPH_ELEMENT_TYPE)

「图形对象初始化时的属性落盘逻辑」

在自定义图形对象的构造收尾阶段,需要把归属、物种、分组、ID 等基础元数据一次性写入属性容器。下面这段代码连续调用 SetProperty 把 BELONG、SPECIES、GROUP、ID、BASE_ID、NUM 等置为 0,并将 CHANGE_HISTORY 设为 false,BASE_NAME 取当前对象名,相当于给新对象发了一张空白身份证。 随后 PropertiesRefresh() 把所有图形对象通用属性刷进父层,再从属性里回读创建时间、背景层、选中态、可选态与隐藏态到成员变量。这里有一个分支细节:当 GraphElementType() 返回 GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED 时,selected 与 selectable 被强制置 false,普通标准对象才保留 GetProperty 读出的原始值。 如果是扩展图形对象,还会按 Pivots() 返回的锚点数分别给 times[] 和 prices[] 做 ArrayResize。若实际分配长度不等于 Pivots(),就走 CMessage::ToLog 记一条失败日志——这说明锚点数组尺寸和对象声明的不一致时,扩展对象初始化已经埋了雷,开 MT5 跑自定义指标时应盯一下 Experts 日志里 MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE 字样。

MQL5 / C++
  this.SetProperty(GRAPH_OBJ_PROP_BELONG,class="num">0,CGBaseObj::Belong());                 class=class="str">"cmt">// Graphical object affiliation
  this.SetProperty(GRAPH_OBJ_PROP_SPECIES,class="num">0,CGBaseObj::Species());                 class=class="str">"cmt">// Graphical object species
  this.SetProperty(GRAPH_OBJ_PROP_GROUP,class="num">0,class="num">0);                                      class=class="str">"cmt">// Graphical object group
  this.SetProperty(GRAPH_OBJ_PROP_ID,class="num">0,class="num">0);                                         class=class="str">"cmt">// Object ID
  this.SetProperty(GRAPH_OBJ_PROP_BASE_ID,class="num">0,class="num">0);                                    class=class="str">"cmt">// Base object ID
  this.SetProperty(GRAPH_OBJ_PROP_NUM,class="num">0,class="num">0);                                        class=class="str">"cmt">// Object index in the list
  this.SetProperty(GRAPH_OBJ_PROP_CHANGE_HISTORY,class="num">0,false);                         class=class="str">"cmt">// Flag of storing the change history
  this.SetProperty(GRAPH_OBJ_PROP_BASE_NAME,class="num">0,this.Name());                        class=class="str">"cmt">// Base object name

class=class="str">"cmt">//--- Save the properties inherent in all graphical objects and present in a graphical object
  this.PropertiesRefresh();

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,class="num">0);
  this.m_back=(class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_BACK,class="num">0);
  this.m_selected=(class="type">bool)(GraphElementType()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? false : this.GetProperty(GRAPH_OBJ_PROP_SELECTED,class="num">0));
  this.m_selectable=(class="type">bool)(GraphElementType()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED ? false : this.GetProperty(GRAPH_OBJ_PROP_SELECTABLE,class="num">0));
  this.m_hidden=(class="type">bool)this.GetProperty(GRAPH_OBJ_PROP_HIDDEN,class="num">0);
class=class="str">"cmt">//--- Initialize the extended graphical object toolkit
  if(this.GraphElementType()==GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
    {
      class="type">class="kw">datetime times[];
      class="type">class="kw">double prices[];
      if(::ArrayResize(times,this.Pivots())!=this.Pivots())
        CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE_TIME_DATA);
      if(::ArrayResize(prices,this.Pivots())!=this.Pivots())
        CMessage::ToLog(DFUN,MSG_GRAPH_OBJ_EXT_FAILED_ARR_RESIZE_PRICE_DATA);

图表事件如何驱动扩展控件刷新

在标准图形对象类里,OnChartEvent 只处理 GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED 这类扩展元素,其余类型直接 return,避免无谓的事件分发。 当收到 CHARTEVENT_CHART_CHANGE 时,若 ExtToolkit 为空则退出,否则用循环把每个枢轴点的时间与价格重写进工具包:ExtToolkit.SetBaseObjTimePrice(this.Time(i), this.Price(i), i),再补一句 SetBaseObjCoordXY 同步偏移量,最后把事件透传给工具包自身。 鼠标移动事件 CHARTEVENT_MOUSE_MOVE 的逻辑更短,只要 ExtToolkit 非空就直接转发,由工具包决定高亮或拖拽反馈。这意味着控件的交互完全依赖基类把坐标和事件正确桥接,少一行都可能让面板在拖图时错位。 下方定义的 CChartObjectsControl 类用 m_list_new_graph_obj 收集新增图形对象,m_chart_timeframe 记录所属周期,是后续统一管理的入口。外汇与贵金属图表波动剧烈,这类事件桥接若漏掉 CHARTEVENT_CHART_CHANGE,缩放后控件位置可能偏离价格轴,实盘风险偏高,建议开 MT5 在 EURUSD 的 M5 上挂一个扩展对象验证。

MQL5 / C++
class="type">void CGStdGraphObj::OnChartEvent(const class="type">int id,const class="type">long &lparam,const class="type">class="kw">double &dparam,const class="type">class="kw">string &sparam)
  {
  if(GraphElementType()!=GRAPH_ELEMENT_TYPE_STANDARD_EXTENDED)
    class="kw">return;
  class="type">class="kw">string name=this.Name();
  if(id==CHARTEVENT_CHART_CHANGE)
    {
    if(ExtToolkit==NULL)
      class="kw">return;
    for(class="type">int i=class="num">0;i<this.Pivots();i++)
      {
      ExtToolkit.SetBaseObjTimePrice(this.Time(i),this.Price(i),i);
      }
    ExtToolkit.SetBaseObjCoordXY(this.XDistance(),this.YDistance());
    ExtToolkit.OnChartEvent(id,lparam,dparam,name);
    }
  if(id==CHARTEVENT_MOUSE_MOVE)
    {
    if(ExtToolkit!=NULL)
      ExtToolkit.OnChartEvent(id,lparam,dparam,name);
    }
  }

◍ 图表事件控制器的成员变量与访问接口

在 MT5 的图表事件封装类里,一组私有成员负责记录运行上下文:m_chart_id 存当前图表 ID,m_chart_symbol 存图表品种,m_total_objects 与 m_last_objects 分别记录本次与上次检查时图形对象数量,m_delta_graph_obj 就是两者差值,用来判断对象增减。 对外暴露的只读接口让调用方无需触碰内部状态:ChartID() 返回 long 型图表句柄,Symbol() 返回品种字符串,IsEvent() 返回图形对象事件标志,TotalObjects() 返回当前对象总数。这些函数都用 const 修饰,保证不修改对象本身。 如果你在 EA 里需要感知用户手动画了线或删了对象,直接读 TotalObjects() 与上一次缓存的 m_last_objects 做差即可,差值非 0 就说明图表对象层有变动,可能触发重算。外汇与贵金属市场波动剧烈,这类事件监听仅作辅助,不构成任何方向建议。

MQL5 / C++
class="type">long m_chart_id; class=class="str">"cmt">// Chart ID
class="type">long m_chart_id_main; class=class="str">"cmt">// Control program chart ID
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_delta_graph_obj; class=class="str">"cmt">// Difference in the number of graphical objects compared to the previous check
class="type">int m_handle_ind; class=class="str">"cmt">// Event controller indicator handle
class="type">class="kw">string m_name_ind; class=class="str">"cmt">// Short name of the event controller indicator
class="type">class="kw">string m_name_program; class=class="str">"cmt">// Program name

class=class="str">"cmt">//--- Return the name of the last graphical object added to the chart
class="type">class="kw">string LastAddedGraphObjName(class="type">void);
class=class="str">"cmt">//--- Set the permission to track mouse events and graphical objects
class="type">void SetMouseEvent(class="type">void);
class="kw">public:
class=class="str">"cmt">//--- Return the variable values
ENUM_TIMEFRAMES Timeframe(class="type">void) const { class="kw">return this.m_chart_timeframe; }
class="type">long ChartID(class="type">void) const { class="kw">return this.m_chart_id; }
class="type">class="kw">string Symbol(class="type">void) const { class="kw">return this.m_chart_symbol; }
class="type">bool IsEvent(class="type">void) const { class="kw">return this.m_is_graph_obj_event; }
class="type">int TotalObjects(class="type">void) const { class="kw">return this.m_total_objects; }

「图表对象控制类的接口与初始化」

在 MT5 自定义控件库里,CChartObjectsControl 类负责接管当前图表上的图形对象生命周期。它对外暴露的接口覆盖了对象创建、事件监控和图表交互开关,交易者可直接拿来控制 EA 面板不被用户误拖。 Delta(void) 以 const 方法返回 m_delta_graph_obj,即两次刷新之间新增或删除的对象数量差;GetListNewAddedObj(void) 则返回 m_list_new_graph_obj 的指针,方便上层遍历本次新加的对象。两者配合能在 OnChartEvent 里快速定位用户刚画的线或按钮。 SetChartTools 有两个重载:单 bool 参数版本一次性关闭或开启鼠标滚动、右键菜单和十字光标;三参数版本可分别指定。做高频贵金属策略面板时,关掉鼠标滚动(flag=false)可避免缩放错位,但外汇品种波动快,关十字光标可能不利于手动复核,按品种分开设更稳。 构造函数里把 m_name_program 设为 MQLInfoString(MQL_PROGRAM_NAME),m_chart_id 与 m_chart_id_main 都取 ChartID(),m_total_objects、m_last_objects、m_delta_graph_obj 清为 0,m_is_graph_obj_event 置 false。这套初始化保证多图表附着时主图 ID 不串,外汇与贵金属杠杆高,误绑到别的图表会触发错误下单,开 MT5 跑一段空 EA 验证 ID 一致性即可。

MQL5 / C++
class="type">int                Delta(class="type">void)                                                 const { class="kw">return this.m_delta_graph_obj;     }
class=class="str">"cmt">//--- Set the flags of scrolling the chart with the mouse, context menu and crosshairs tool for the chart
   class="type">void             SetChartTools(const class="type">bool flag);
   class="type">void             SetChartTools(const class="type">bool mouse_scroll,const class="type">bool context_menu,const class="type">bool crosshair_tool);
class=class="str">"cmt">//--- Create a new standard(or extended) graphical object
   CGStdGraphObj    *CreateNewGraphObj(const ENUM_OBJECT obj_type,const class="type">class="kw">string name,const class="type">bool extended);
class=class="str">"cmt">//--- Return the list of newly added objects
   CArrayObj        *GetListNewAddedObj(class="type">void)                                        { class="kw">return &this.m_list_new_graph_obj;}
class=class="str">"cmt">//--- Create the event control indicator
   class="type">bool             CreateEventControlInd(const class="type">long chart_id_main);
class=class="str">"cmt">//--- Add the event control indicator to the chart
   class="type">bool             AddEventControlInd(class="type">void);
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_name_program=::MQLInfoString(MQL_PROGRAM_NAME);
                     this.m_chart_id=::ChartID();
                     this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(this.m_chart_id);
                     this.m_chart_symbol=::ChartSymbol(this.m_chart_id);
                     this.m_chart_id_main=::ChartID();
                     this.m_list_new_graph_obj.Clear();
                     this.m_list_new_graph_obj.Sort();
                     this.m_is_graph_obj_event=false;
                     this.m_total_objects=class="num">0;
                     this.m_last_objects=class="num">0;
                     this.m_delta_graph_obj=class="num">0;

图表对象控制类的构造与析构细节

在 MT5 自定义控件的封装里,CChartObjectsControl 提供了两个重载构造函数,一个无参、一个接收 chart_id。两者都先把指标名置空、句柄设为 INVALID_HANDLE,再调用 SetMouseEvent() 挂上鼠标事件,差异只在后者会从传入图表取周期、品种与主图 ID 并初始化对象列表。 无参版本依赖默认主图,而带 chart_id 的版本会显式执行 m_list_new_graph_obj.Clear() 与 Sort(),并把 m_total_objects、m_last_objects、m_delta_graph_obj 全部清零。实战中若你同时开多个品种副图,必须用带参构造,否则 m_chart_symbol 取到的可能是当前焦点图而非目标图。 析构函数只做两件事:ChartIndicatorDelete 按子窗口 0 删掉指标名,IndicatorRelease 释放句柄。外汇与贵金属图表对象生命周期混乱时容易漏释句柄导致内存占用爬升,建议在 EA 退出前手动触发析构验证逻辑。 下面这段是原文构造与析构的核心代码片段,逐行对应上文动作。

MQL5 / C++
this.m_name_ind="";
this.m_handle_ind=INVALID_HANDLE;
this.SetMouseEvent();
   }
   CChartObjectsControl(const class="type">long chart_id)
   {
      this.m_name_program=::MQLInfoString(MQL_PROGRAM_NAME);
      this.m_chart_timeframe=(ENUM_TIMEFRAMES)::ChartPeriod(chart_id);
      this.m_chart_symbol=::ChartSymbol(chart_id);
      this.m_chart_id_main=::ChartID();
      this.m_list_new_graph_obj.Clear();
      this.m_list_new_graph_obj.Sort();
      this.m_chart_id=chart_id;
      this.m_is_graph_obj_event=false;
      this.m_total_objects=class="num">0;
      this.m_last_objects=class="num">0;
      this.m_delta_graph_obj=class="num">0;
      this.m_name_ind="";
      this.m_handle_ind=INVALID_HANDLE;
      this.SetMouseEvent();
   }
class=class="str">"cmt">//--- Destructor
   ~CChartObjectsControl()
   {
      ::ChartIndicatorDelete(this.ChartID(),class="num">0,this.m_name_ind);
      ::IndicatorRelease(this.m_handle_ind);
   }

◍ 图表控件里怎么接管 ChartID 比较与交互开关

在自定义图表对象控制类里,Compare 方法决定了多个控件实例在集合中的排序依据。它把传入的 CObject 节点向下转型为 CChartObjectsControl,仅用 ChartID 做三态比较:当前实例 ID 大返回 1,小返回 -1,相等返回 0。

MQL5 / C++
class="kw">virtual class="type">int Compare(const CObject *node,const class="type">int mode=class="num">0) const
  {
  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);
  }
class=class="str">"cmt">//--- Event handler
class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam);
};
逐行看:virtual int Compare 表示可被派生类重写;const CChartObjectsControl *obj_compared=node 把基类指针安全转回控件类型;return 里的三元嵌套只用 ChartID 判定先后,不碰价格或时间轴,所以多图同时挂控件时顺序稳定可预期。 SetChartTools 提供两种重载来控制鼠标滚动、右键菜单和十字光标。单 bool 版本一把梭全开或全关;三 bool 版本让你分别指定 mouse_scroll、context_menu、crosshair_tool,实盘写面板时往往只留滚动关掉菜单,避免误触弹出。
MQL5 / C++
class="type">void CChartObjectsControl::SetChartTools(const class="type">bool flag)
  {
  ::ChartSetInteger(this.ChartID(),CHART_MOUSE_SCROLL,flag);
  ::ChartSetInteger(this.ChartID(),CHART_CONTEXT_MENU,flag);
  ::ChartSetInteger(this.ChartID(),CHART_CROSSHAIR_TOOL,flag);
  }
class="type">void CChartObjectsControl::SetChartTools(const class="type">bool mouse_scroll,const class="type">bool context_menu,const class="type">bool crosshair_tool)
  {
  ::ChartSetInteger(this.ChartID(),CHART_MOUSE_SCROLL,mouse_scroll);
  ::ChartSetInteger(this.ChartID(),CHART_CONTEXT_MENU,context_menu);
  ::ChartSetInteger(this.ChartID(),CHART_CROSSHAIR_TOOL,crosshair_tool);
  }
::ChartSetInteger 前导双冒号强制调全局函数,避免和类内同名成员混淆;CHART_MOUSE_SCROLL 等宏直接写进图表属性槽,调用后当前 ChartID 对应的图立即生效。 类里还留了 IsPresentGraphElmInList 声明,按 id 和 ENUM_GRAPH_ELEMENT_TYPE 查某图形元素是否已在链表,写批量清理前先查它可少删一次。资源段用 #resource 把事件控制指标打进程序包,PATH_TO_EVENT_CTRL_IND 指向内部指标,脱离外部文件也能分发。外汇与贵金属图表加载这类自绘控件存在脚本冲突风险,建议在 MT5 策略测试器先挂单图验证再上多品种。

MQL5 / C++
class="kw">virtual class="type">int Compare(const CObject *node,const class="type">int mode=class="num">0) const
  {
  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);
  }
class=class="str">"cmt">//--- Event handler
class="type">void OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam);
};
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set the flags of mouse scrolling,                             |
class=class="str">"cmt">//| context menu and crosshairs for the chart                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CChartObjectsControl::SetChartTools(const class="type">bool flag)
  {
  ::ChartSetInteger(this.ChartID(),CHART_MOUSE_SCROLL,flag);
  ::ChartSetInteger(this.ChartID(),CHART_CONTEXT_MENU,flag);
  ::ChartSetInteger(this.ChartID(),CHART_CROSSHAIR_TOOL,flag);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Set the flags of mouse scrolling,                             |
class=class="str">"cmt">//| context menu and crosshairs for the chart                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CChartObjectsControl::SetChartTools(const class="type">bool mouse_scroll,const class="type">bool context_menu,const class="type">bool crosshair_tool)
  {
  ::ChartSetInteger(this.ChartID(),CHART_MOUSE_SCROLL,mouse_scroll);
  ::ChartSetInteger(this.ChartID(),CHART_CONTEXT_MENU,context_menu);
  ::ChartSetInteger(this.ChartID(),CHART_CROSSHAIR_TOOL,crosshair_tool);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool IsPresentGraphElmInList(const class="type">int id,const ENUM_GRAPH_ELEMENT_TYPE type_obj);
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Collection of graphical objects                               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#resource "\"+PATH_TO_EVENT_CTRL_IND;   class=class="str">"cmt">// Indicator for controlling graphical object events packed into the program resources

「图形元素集合类的内部骨架」

在 MT5 自定义图形框架里,CGraphElementsCollection 继承自 CBaseObj,专门负责把散落在各图表上的对象收拢到一套内存列表中。它内部挂了 4 类容器:图表管理对象数组、画布图形元素链表、普通图形对象链表、以及被删除对象的数组,分别应对不同生命周期的资源。 私有成员里 m_total_objects 记录当前图形对象总数,m_delta_graph_obj 存的是本次检查相较上一次的增减差值;这两个整型量在调试多对象面板时很有用,能直接看出某次刷新是新增了还是回收了对象。 类里暴露了一组查询与刷新接口:IsPresentCanvElmInList 按 chart_id+name 判断画布元素是否已在列表,IsPresentGraphObjOnChart 则去查图表上是否真有这个命名对象,两者分工在事件回调里常用来防重复创建。GetFreeGraphObjID 和 GetFreeCanvElmID 负责发号,保证每个对象拿到唯一 ID。 注意 public 段里 AddCanvElmToCollection 被标黄重复出现,说明原设计在权限调整时出现过声明漂移;你在抄这段代码时应只保留一个 public 版本,否则编译可能报重定义。外汇与贵金属图表上跑这类对象集合时波动大、对象频繁增删,属于高风险环境,建议先在模拟图表压测再上实盘。

MQL5 / C++
class CGraphElementsCollection : class="kw">public CBaseObj
  {
class="kw">private:
   CArrayObj            m_list_charts_control;     class=class="str">"cmt">// List of chart management objects
   CListObj             m_list_all_canv_elm_obj;   class=class="str">"cmt">// List of all graphical elements on canvas
   CListObj             m_list_all_graph_obj;      class=class="str">"cmt">// List of all graphical objects
   CArrayObj            m_list_deleted_obj;        class=class="str">"cmt">// List of removed graphical objects
   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_delta_graph_obj;         class=class="str">"cmt">// Difference in the number of graphical objects compared to the previous check

class=class="str">"cmt">//--- Return the flag indicating the graphical element class object presence in the collection list of graphical elements
   class="type">bool                 IsPresentCanvElmInList(const class="type">long chart_id,const class="type">class="kw">string name);
class=class="str">"cmt">//--- Return the flag indicating the presence of the graphical object class in the graphical object collection list
   class="type">bool                 IsPresentGraphObjInList(const class="type">long chart_id,const class="type">class="kw">string name);
class=class="str">"cmt">//--- Return the flag indicating the presence of a graphical object on a chart by name
   class="type">bool                 IsPresentGraphObjOnChart(const class="type">long chart_id,const class="type">class="kw">string name);
class=class="str">"cmt">//--- Return the pointer to the object of managing objects of the specified chart
   CChartObjectsControl *GetChartObjectCtrlObj(const class="type">long chart_id);
class=class="str">"cmt">//--- Create a new object of managing graphical objects of a specified chart and add it to the list
   CChartObjectsControl *CreateChartObjectCtrlObj(const class="type">long chart_id);
class=class="str">"cmt">//--- Update the list of graphical objects by chart ID
   CChartObjectsControl *RefreshByChartID(const class="type">long chart_id);
class=class="str">"cmt">//--- Check if the chart window is present
   class="type">bool                 IsPresentChartWindow(const class="type">long chart_id);
class=class="str">"cmt">//--- Handle removing the chart window
   class="type">void                 RefreshForExtraObjects(class="type">void);
class=class="str">"cmt">//--- Return the first free ID of the graphical(class="num">1) object and(class="num">2) element on canvas
   class="type">long                 GetFreeGraphObjID(class="type">bool program_object);
   class="type">long                 GetFreeCanvElmID(class="type">void);
class=class="str">"cmt">//--- Add(class="num">1) the standard graphical object and(class="num">2) the graphical element on canvas to the collection
   class="type">bool                 AddGraphObjToCollection(const class="type">class="kw">string source,CChartObjectsControl *obj_control);
class="kw">public:
   class="type">bool                 AddCanvElmToCollection(CGCnvElement *element);

class="kw">private:
class="kw">public:
   class="type">bool                 AddCanvElmToCollection(CGCnvElement *element);

class="kw">private:

图表对象集合的差集查找与垂直线创建

在管理多图表图形对象时,集合与图表实际状态很容易脱节。类里提供了 FindMissingObj(按 chart_id 或带回传 index 的重载)来捞出「在集合里、但图表上已经没了」的对象;FindExtraObj 则反向找出「图表上有、但集合里没登记」的对象名,返回的是 string。 删除侧分两层:DeleteGraphObjFromList 按对象指针、DeleteGraphObjectsFromList 按 chart_id 直接从集合链表移除;MoveGraphObjToDeletedObjList 有按指针和按 index 两个重载,MoveGraphObjectsToDeletedObjList 按 chart_id 整批移入「已删除」列表,便于后续统一清理或审计。DeleteGraphObjCtrlObjFromList 专门把图表管理控制对象从列表剔除。 SetChartTools 接收 chart_id 和 flag,一次性开关该图表的鼠标滚动、右键菜单和十字光标——做自动标注脚本时,关掉这些工具能避免用户误操作拖乱对象。 CreateLineVertical 是实际建对象的入口:拼出「程序名_传入名」的对象名,定类型为 OBJ_VLINE,调 CreateNewStdGraphObjectAndGetCtrlObj 在指定 subwindow 的 time 位置落线;若 ctrl 为空直接返回 false,说明建对象或拿控制句柄失败。

MQL5 / C++
class=class="str">"cmt">//--- Find an object present in the collection but not on a chart
  CGStdGraphObj      *FindMissingObj(const class="type">long chart_id);
  CGStdGraphObj      *FindMissingObj(const class="type">long chart_id,class="type">int &index);
class=class="str">"cmt">//--- Find the graphical object present on a chart but not in the collection
  class="type">class="kw">string             FindExtraObj(const class="type">long chart_id);
class=class="str">"cmt">//--- Remove the graphical object class object from the graphical object collection list: (class="num">1) specified object, (class="num">2) by chart ID
  class="type">bool               DeleteGraphObjFromList(CGStdGraphObj *obj);
  class="type">void               DeleteGraphObjectsFromList(const class="type">long chart_id);
class=class="str">"cmt">//--- Move the graphical object class object to the list of removed graphical objects: (class="num">1) specified object, (class="num">2) by index
  class="type">bool               MoveGraphObjToDeletedObjList(CGStdGraphObj *obj);
  class="type">bool               MoveGraphObjToDeletedObjList(const class="type">int index);
class=class="str">"cmt">//--- Move all objects by chart ID to the list of removed graphical objects
  class="type">void               MoveGraphObjectsToDeletedObjList(const class="type">long chart_id);
class=class="str">"cmt">//--- Remove the object of managing charts from the list
  class="type">bool               DeleteGraphObjCtrlObjFromList(CChartObjectsControl *obj);
class=class="str">"cmt">//--- Set the flags of scrolling the chart with the mouse, context menu and crosshairs tool for the specified chart
  class="type">void               SetChartTools(const class="type">long chart_id,const class="type">bool flag);
class="kw">public:
class="kw">public:
class=class="str">"cmt">//--- Create the "Vertical line" graphical object
  class="type">bool               CreateLineVertical(const class="type">long chart_id,const class="type">class="kw">string name,const class="type">int subwindow,const class="type">bool extended,const class="type">class="kw">datetime time)
                    {
                     class=class="str">"cmt">//--- Set the name and type of a created object
                     class="type">class="kw">string nm=this.m_name_program+"_"+name;
                     ENUM_OBJECT type_object=OBJ_VLINE;
                     class=class="str">"cmt">//--- Create a new graphical object and get the pointer to the chart management object
                     CChartObjectsControl *ctrl=this.CreateNewStdGraphObjectAndGetCtrlObj(chart_id,nm,subwindow,type_object,time,class="num">0);
                     if(ctrl==NULL)
                        class="kw">return false;
                     class=class="str">"cmt">//--- Create a new class object corresponding to the newly created graphical object

◍ 正文

&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CGStdVLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm,extended); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="keyword">if</span>(obj==<span class="macro">NULL</span>) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ::<span class="functions">Print</span>(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="keyword">return</span> <span class="macro">false</span>; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">//--- Set the necessary minimal parameters for an object<

「对象改名时怎么在集合里兜底定位」

在 MT5 自定义图形库里,标准对象被用户拖拽改名后,原集合按旧名查会直接落空。上面这段逻辑先用 GetStdGraphObject(sparam,chart_id) 按事件带回的对象名找,若返回 NULL 就说明名字已经对不上了。 此时不能简单退出,代码转去 FindMissingObj(chart_id) 捞“集合里有、图表上找不到”的孤儿对象,再用 FindExtraObj(chart_id) 反查“图表上有、集合里没有”的新名。这套双向排查能把改名动作补全,避免图形状态与内部列表永久脱节。 外汇与贵金属图表上手动拖对象是高概率误操作场景,这类兜底若漏写,EA 后续读写对象属性可能指向已失效句柄。开 MT5 把这段塞进你的 CGraphElementsCollection 类,故意改个对象名拖一下,看能否自动续上。

MQL5 / C++
class="type">bool CGraphElementsCollection::IsPresentCanvElmInList(const class="type">long chart_id,const class="type">class="kw">string name)
  {
   CArrayObj *list=CSelect::ByGraphCanvElementProperty(this.GetListCanvElm(),CANV_ELEMENT_PROP_CHART_ID,chart_id,EQUAL);
   list=CSelect::ByGraphCanvElementProperty(list,CANV_ELEMENT_PROP_NAME_OBJ,name,EQUAL);
   class="kw">return(list==NULL || list.Total()==class="num">0 ? false : true);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Event handler                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CGraphElementsCollection::OnChartEvent(const class="type">int id, const class="type">long &lparam, const class="type">class="kw">double &dparam, const class="type">class="kw">string &sparam)
  {
   CGStdGraphObj *obj_std=NULL;  class=class="str">"cmt">// Pointer to the standard graphical object
   CGCnvElement  *obj_cnv=NULL;  class=class="str">"cmt">// Pointer to the graphical element object on canvas
   class="type">class="kw">ushort idx=class="type">class="kw">ushort(id-CHARTEVENT_CUSTOM);
   if(id==CHARTEVENT_OBJECT_CHANGE  || id==CHARTEVENT_OBJECT_DRAG     || id==CHARTEVENT_OBJECT_CLICK  ||
       idx==CHARTEVENT_OBJECT_CHANGE || idx==CHARTEVENT_OBJECT_DRAG   || idx==CHARTEVENT_OBJECT_CLICK)
     {
      class=class="str">"cmt">//--- Calculate the chart ID
      class=class="str">"cmt">//--- If the event ID corresponds to an event from the current chart, the chart ID is received from ChartID
      class=class="str">"cmt">//--- If the event ID corresponds to a user event, the chart ID is received from lparam
      class=class="str">"cmt">//--- Otherwise, the chart ID is assigned to -class="num">1
      class="type">long param=(id==CHARTEVENT_OBJECT_CLICK ? ::ChartID() : idx==CHARTEVENT_OBJECT_CLICK ? lparam : WRONG_VALUE);
      class="type">long chart_id=(param==WRONG_VALUE ? (lparam==class="num">0 ? ::ChartID() : lparam) : param);
      class=class="str">"cmt">//--- Get the object, whose properties were changed or which was relocated,
      class=class="str">"cmt">//--- from the collection list by its name set in sparam
      obj_std=this.GetStdGraphObject(sparam,chart_id);
      class=class="str">"cmt">//--- If failed to get the object by its name, it is not on the list,
      class=class="str">"cmt">//--- which means its name has been changed
      if(obj_std==NULL)
        {
         class=class="str">"cmt">//--- Let&class="macro">#x27;s search the list for the object that is not on the chart
         obj_std=this.FindMissingObj(chart_id);
         class=class="str">"cmt">//--- If failed to find the object here as well, exit
         if(obj_std==NULL)
            class="kw">return;
         class=class="str">"cmt">//--- Get the name of the renamed graphical object on the chart, which is not in the collection list
         class="type">class="kw">string name_new=this.FindExtraObj(chart_id);
         class=class="str">"cmt">//--- set a new name for the collection list object, which does not correspond to any graphical object on the chart,

鼠标悬停与对象改名的事件派发细节

在图形对象集合里改名时,先写回旧名再设新名,成功才向主控图表抛自定义事件。下面这段把改名和属性刷新串在一起: if(obj_std.SetNamePrev(obj_std.Name()) && obj_std.SetName(name_new)) ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj_std.ChartID(),obj_std.TimeCreate(),obj_std.Name()); 随后立刻跑 PropertiesRefresh 与 PropertiesCheckChanged,确保改名后的状态被内部标记捕获,不至于下次循环误判为未变更。 集合列表里的标准对象事件用一段 for 循环统一派发: for(int i=0;i<this.m_list_all_graph_obj.Total();i++) { obj_std=this.m_list_all_graph_obj.At(i); if(obj_std==NULL) continue; obj_std.OnChartEvent((id<CHARTEVENT_CUSTOM ? id : idx),lparam,dparam,sparam); } 这里用 id<CHARTEVENT_CUSTOM 做三元判断,普通图表事件原样下发,自定义事件则改用 idx 重映射,避免子图表 ID 冲突。 鼠标移动事件的处理更绕:先按 CANV_ELEMENT_PROP_INTERACTION 标志筛出可交互画布元素,正常情况列表里只会有 1 个。若筛选结果为空,就退化为遍历 m_list_all_canv_elm_obj 逐个调 OnChartEvent,让各元素自己根据光标+按键状态去置交互标志。 一旦筛到了对象且类型是 GRAPH_ELEMENT_TYPE_FORM,就取列表首项强转成 CForm 指针。这一步意味着同屏多表单时,只有被命中交互标志的那个表单会进后续拖拽/点击逻辑,其余表单不抢事件。外汇与贵金属图表上挂这类自定义 UI 属于高风险操作,事件错派可能导致止损线被误拖,建议在 MT5 策略测试器里先用历史 tick 验证交互标志翻转时机。

MQL5 / C++
if(obj_std.SetNamePrev(obj_std.Name()) && obj_std.SetName(name_new))
   ::EventChartCustom(this.m_chart_id_main,GRAPH_OBJ_EVENT_RENAME,obj_std.ChartID(),obj_std.TimeCreate(),obj_std.Name());
}
class=class="str">"cmt">//--- Update the properties of the obtained object
class=class="str">"cmt">//--- and check their change
obj_std.PropertiesRefresh();
obj_std.PropertiesCheckChanged();
 }
class=class="str">"cmt">//--- Handle standard graphical object events in the collection list
  for(class="type">int i=class="num">0;i<this.m_list_all_graph_obj.Total();i++)
   {
    obj_std=this.m_list_all_graph_obj.At(i);
    if(obj_std==NULL)
       class="kw">continue;
    obj_std.OnChartEvent((id<CHARTEVENT_CUSTOM ? id : idx),lparam,dparam,sparam);
   }
class=class="str">"cmt">//--- Handling mouse events of graphical objects on canvas
  if(id==CHARTEVENT_MOUSE_MOVE || idx==CHARTEVENT_MOUSE_MOVE)
   {
    CArrayObj *list=CSelect::ByGraphCanvElementProperty(GetListCanvElm(),CANV_ELEMENT_PROP_INTERACTION,true,EQUAL);
    if(list==NULL || list.Total()==class="num">0)
     {
      for(class="type">int i=class="num">0;i<this.m_list_all_canv_elm_obj.Total();i++)
       {
        obj_cnv=this.m_list_all_canv_elm_obj.At(i);
        if(obj_cnv==NULL)
           class="kw">continue;
        obj_cnv.OnChartEvent(CHARTEVENT_MOUSE_MOVE,lparam,dparam,sparam);
       }
     }
    if(list!=NULL)
     {
      obj_cnv=list.At(class="num">0);
      if(obj_cnv!=NULL && obj_cnv.TypeGraphElement()==GRAPH_ELEMENT_TYPE_FORM)
       {
        CForm *form=obj_cnv;
        if(form!=NULL)
         {

◍ 锁死图表交互与扩展对象事件分发

在自定义图形面板里,经常需要禁止用户用鼠标拖图、右键菜单和十字光标,避免误触破坏布局。下面这段通过 form.SetChartTools(false) 统一关闭交互,并手动把鼠标移动事件转发给面板对象。

MQL5 / C++
class=class="str">"cmt">//--- 禁用鼠标拖图、图表右键菜单和十字光标工具
form.SetChartTools(false);
class=class="str">"cmt">//--- 向面板对象投递鼠标移动事件,lparam/dparam/sparam 为系统传入参数
form.OnChartEvent(CHARTEVENT_MOUSE_MOVE,lparam,dparam,sparam);
当图表发生 CHARTEVENT_CHART_CHANGE(如缩放、滚动)时,代码遍历扩展标准对象列表,逐个调用其 OnChartEvent 让它们自行重算坐标。循环从 i=0 到 list.Total(),遇到空指针就 continue,不阻断后续对象刷新。 SetChartTools 的实现直接调 ChartSetInteger 三次:CHART_MOUSE_SCROLL、CHART_CONTEXT_MENU、CHART_CROSSHAIR_TOOL,传入 flag 控制开合。想恢复默认交互,把 flag 改为 true 即可在 MT5 里验证。
MQL5 / C++
class="type">void CGraphElementsCollection::SetChartTools(const class="type">long chart_id,const class="type">bool flag)
  {
   ::ChartSetInteger(chart_id,CHART_MOUSE_SCROLL,flag);
   ::ChartSetInteger(chart_id,CHART_CONTEXT_MENU,flag);
   ::ChartSetInteger(chart_id,CHART_CROSSHAIR_TOOL,flag);
  }
按名称取扩展对象时,若 name 未带程序前缀,函数会自动拼上 ProgramName()+"_" 再匹配;用 CSelect::ByGraphicStdObjectProperty 筛选后返回列表首个元素,查不到则返回 NULL。外汇与贵金属图表加载此类 EA 属高风险操作,参数误设可能导致界面卡死。

MQL5 / C++
class=class="str">"cmt">//--- disable moving the chart with a mouse, the chart context menu and the crosshairs tool
   form.SetChartTools(false);
   form.OnChartEvent(CHARTEVENT_MOUSE_MOVE,lparam,dparam,sparam);
class=class="str">"cmt">//--- Handle chart changes for extended standard objects
   if(id==CHARTEVENT_CHART_CHANGE || idx==CHARTEVENT_CHART_CHANGE)
     {
      CArrayObj *list=this.GetListStdGraphObjectExt();
      if(list!=NULL)
        {
         for(class="type">int i=class="num">0;i<list.Total();i++)
           {
            obj_std=list.At(i);
            if(obj_std==NULL)
               class="kw">continue;
            obj_std.OnChartEvent(CHARTEVENT_CHART_CHANGE,lparam,dparam,sparam);
           }
        }
     }
class="type">void CGraphElementsCollection::SetChartTools(const class="type">long chart_id,const class="type">bool flag)
  {
   ::ChartSetInteger(chart_id,CHART_MOUSE_SCROLL,flag);
   ::ChartSetInteger(chart_id,CHART_CONTEXT_MENU,flag);
   ::ChartSetInteger(chart_id,CHART_CROSSHAIR_TOOL,flag);
  }
CGStdGraphObj      *GraphGetStdGraphObjectExt(const class="type">class="kw">string name,const class="type">long chart_id)
                    {
                     CArrayObj *list=this.m_graph_objects.GetListStdGraphObjectExt();
                     class="type">class="kw">string nm=(::StringFind(name,this.ProgramName()+"_")==WRONG_VALUE ? this.ProgramName()+"_"+name : name);
                     list=CSelect::ByGraphicStdObjectProperty(list,GRAPH_OBJ_PROP_NAME,class="num">0,nm,EQUAL);
                     class="kw">return(list!=NULL ? list.At(class="num">0) : NULL);
                    }

「图表控件集合的取用与ID回填」

在自定义图形库里,画布元素和图表控件通常挂在同一个内部集合对象上。上面的接口把这种依赖关系直接暴露出来:GetListCanvElement 返回画布元素链表,GraphAddCanvElmToCollection 负责把单个 CGCnvElement 塞进集合,调用方不用关心底层数组怎么扩容。 GraphGetArrayChartsID 这段才是实战里常碰到的坑位。它先拿图表控件链表,空指针就直接 return,不抛错;随后用 ArrayResize 把外部传入的 long 数组拉到 list.Total() 长度,再用 WRONG_VALUE 做 ArrayInitialize 清零。 循环里逐个取 CChartObjectsControl 的 ChartID 回填数组下标。注意 Total() 在链表为空时返回 0,此时数组被 resize 成 0 长度,调用方若没判空可能后续越界。开 MT5 把这段贴进 EA 的 OnInit 打两行 Print,能直接看到终端里真实图表数量对应的数组长度。

MQL5 / C++
  CArrayObj              *GetListCanvElement(class="type">void)                                                            { class="kw">return this.m_graph_objects.GetListCanvElm();                   }
class=class="str">"cmt">//--- Add the graphical element on canvas to the collection
  class="type">bool                 GraphAddCanvElmToCollection(CGCnvElement *element) { class="kw">return this.m_graph_objects.AddCanvElmToCollection(element); }

class=class="str">"cmt">//--- Fill in the array with IDs of the charts opened in the terminal
  class="type">void                 GraphGetArrayChartsID(class="type">long &array_charts_id[])
                     {
                     CArrayObj *list=this.m_graph_objects.GetListChartsControl();
                     if(list==NULL)
                        class="kw">return;
                     ::ArrayResize(array_charts_id,list.Total());
                     ::ArrayInitialize(array_charts_id,WRONG_VALUE);
                     for(class="type">int i=class="num">0;i<list.Total();i++)
                        {
                        CChartObjectsControl *obj=list.At(i);
                        if(obj==NULL)
                           class="kw">continue;
                        array_charts_id[i]=obj.ChartID();
                        }
                     }

跑起多窗体 EA 看拖动表现

把上一篇文章里的 EA 直接复用,存到 \MQL5\Experts\TestDoEasy\Part96\ 目录,文件名取 TestDoEasyPart96.mq5。全局变量里那行 CArrayObj list_forms 要删掉——新窗体已经归到函数库的集合列表里管,独立维护一份窗体对象列表纯属多余。 OnInit() 里加一段循环,负责创建两个窗体对象。循环里设好显示参数,再把窗体指针塞进函数库集合列表。代码里 FORMS_TOTAL 宏定义写成 4,但实际循环只建 2 个,验证时以循环次数为准。 编译挂到图表上,能看见两个窗体各自用鼠标拖,互不干扰,图表本身不动。当前激活的窗体永远压在最前,选中窗体的数据会刷在图表注释里。 缺点是:鼠标移图表时,光标一碰窗体区域它就抢焦点激活,之后拖图表,相对窗体参考点的坐标偶尔算歪。这类瑕疵后面开发再修。外汇和贵金属品种上跑这种 UI 实验 EA 波动叠加显示延迟,风险偏高,建议先用模拟盘验证。

MQL5 / C++
class=class="str">"cmt">//--- global variables
CEngine          engine;
CArrayObj        list_forms;  
class="type">class="kw">color            array_clr[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|                     TestDoEasyPart96.mq5 |
class=class="str">"cmt">//|           Copyright class="num">2021, MetaQuotes Ltd. |
class=class="str">"cmt">//|            [MQL5官方文档] |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#class="kw">property copyright "Copyright class="num">2021, MetaQuotes Ltd."
class="macro">#class="kw">property link      "[MQL5官方文档]
class="macro">#class="kw">property version   "class="num">1.00"
class=class="str">"cmt">//--- includes
class="macro">#include <DoEasy\Engine.mqh>
class=class="str">"cmt">//--- defines
class="macro">#define          FORMS_TOTAL(class="num">4)   class=class="str">"cmt">// Number of created forms
class="macro">#define          START_X(class="num">4)   class=class="str">"cmt">// Initial X coordinate of the shape
class="macro">#define          START_Y(class="num">4)   class=class="str">"cmt">// Initial Y coordinate of the shape
class="macro">#define KEY_LEFT(class="num">188) class=class="str">"cmt">// Left
class="macro">#define KEY_RIGHT(class="num">190) class=class="str">"cmt">// Right
class="macro">#define KEY_ORIGIN(class="num">191) class=class="str">"cmt">// Initial properties
class=class="str">"cmt">//--- input parameters
sinput  class="type">bool             InpMovable     = true;            class=class="str">"cmt">// Movable forms flag
sinput  ENUM_INPUT_YES_NO InpUseColorBG  = INPUT_YES;      class=class="str">"cmt">// Use chart background class="type">class="kw">color to calculate shadow class="type">class="kw">color
sinput  class="type">class="kw">color             InpColorForm3  = clrCadetBlue;   class=class="str">"cmt">// Third form shadow class="type">class="kw">color (if not background class="type">class="kw">color) 
class=class="str">"cmt">//--- global variables
CEngine          engine;
class="type">class="kw">color            array_clr[];
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- Set EA global variables
   ArrayResize(array_clr,class="num">2);          class=class="str">"cmt">// Array of gradient filling colors
   array_clr[class="num">0]=C&class="macro">#x27;class="num">26,class="num">100,class="num">128&class="macro">#x27;;        class=class="str">"cmt">// Original ≈Dark-azure class="type">class="kw">color

◍ 用循环批量生成带渐变背景的表单对象

在 MT5 自定义图形引擎里,表单(CForm)的视觉属性可以靠数组和循环一次性铺好,不必手写每个对象。下面这段代码用 for 循环创建了 2 个表单,第一个挂在 Y=80、第二个挂在 Y=150,宽度 100、高度 30,活动且可拖动。 循环里先给表单设 ID 和主对象编号(SetNumber(0) 表示它是可挂附属对象的主表单),再把不透明度设为 245,背景取颜色数组 array_clr[0],边框用 clrDarkBlue。阴影部分如果输入参数 InpUseColorBG 为真,就用背景色降亮度 20 得到的灰阶;否则取 InpColorForm3,偏移 3 像素、不透明度 200、模糊半径 4。 背景填充调用了 form.Erase(array_clr, form.Opacity(), true),第二个参数 true 代表按垂直渐变擦除,array_clr[1] 是 C'35,133,169' 这种调亮过的原色。边框用 DrawRectangle 沿边缘画一圈,坐标从 (0,0) 到 (Width()-1, Height()-1)。跑完这段,日志里用 SeriesCreate + PrintShort(false) 能看到当前品种和周期的时间序列简报,方便核对引擎确实接了 Symbol() 返回的当前品种。 外汇与贵金属图表上叠加这类自定义 UI 属于高风险环境下的辅助显示,参数不对可能导致对象不渲染或遮挡 K 线,建议先在模拟盘验证。

MQL5 / C++
  array_clr[class="num">1]=C&class="macro">#x27;class="num">35,class="num">133,class="num">169&class="macro">#x27;;
class=class="str">"cmt">//--- Create the array with the current symbol and set it to be used in the library
  class="type">class="kw">string array[class="num">1]={Symbol()};
  engine.SetUsedSymbols(array);
  class=class="str">"cmt">//--- Create the timeseries object for the current symbol and period, and show its description in the journal
  engine.SeriesCreate(Symbol(),Period());
  engine.GetTimeSeriesCollection().PrintShort(false); class=class="str">"cmt">// Short descriptions
class=class="str">"cmt">//--- Create form objects
  for(class="type">int i=class="num">0;i<class="num">2;i++)
   {
   class=class="str">"cmt">//--- When creating an object, pass all the required parameters to it
   CForm *form=new CForm("Form_0"+class="type">class="kw">string(i+class="num">1),class="num">30,(i==class="num">0 ? class="num">80 : class="num">150),class="num">100,class="num">30);
   if(form==NULL)
     class="kw">continue;
   class=class="str">"cmt">//--- Set activity and moveability flags for the form
   form.SetActive(true);
   form.SetMovable(true);
   class=class="str">"cmt">//--- Set the form ID and the index in the list of objects
   form.SetID(i);
   form.SetNumber(class="num">0);   class=class="str">"cmt">// (class="num">0 - main form object) Auxiliary objects may be attached to the main one. The main object is able to manage them
   class=class="str">"cmt">//--- Set the opacity of class="num">200
   form.SetOpacity(class="num">245);
   class=class="str">"cmt">//--- The form background class="type">class="kw">color is set as the first class="type">class="kw">color from the class="type">class="kw">color array
   form.SetColorBackground(array_clr[class="num">0]);
   class=class="str">"cmt">//--- Form outlining frame class="type">class="kw">color
   form.SetColorFrame(clrDarkBlue);
   class=class="str">"cmt">//--- Draw the shadow drawing flag
   form.SetShadow(false);
   class=class="str">"cmt">//--- Calculate the shadow class="type">class="kw">color as the chart background class="type">class="kw">color converted to the monochrome one
   class="type">class="kw">color clrS=form.ChangeColorSaturation(form.ColorBackground(),-class="num">100);
   class=class="str">"cmt">//--- If the settings specify the usage of the chart background class="type">class="kw">color, replace the monochrome class="type">class="kw">color with class="num">20 units
   class=class="str">"cmt">//--- Otherwise, use the class="type">class="kw">color specified in the settings for drawing the shadow
   class="type">class="kw">color clr=(InpUseColorBG ? form.ChangeColorLightness(clrS,-class="num">20) : InpColorForm3);
   class=class="str">"cmt">//--- Draw the form shadow with the right-downwards offset from the form by three pixels along all axes
   class=class="str">"cmt">//--- Set the shadow opacity to class="num">200, while the blur radius is equal to class="num">4
   form.DrawShadow(class="num">3,class="num">3,clr,class="num">200,class="num">4);
   class=class="str">"cmt">//--- Fill the form background with a vertical gradient
   form.Erase(array_clr,form.Opacity(),true);
   class=class="str">"cmt">//--- Draw an outlining rectangle at the edges of the form
   form.DrawRectangle(class="num">0,class="num">0,form.Width()-class="num">1,form.Height()-class="num">1,form.ColorFrame(),form.Opacity());
   form.Done();
   
   class=class="str">"cmt">//--- Display the text describing the gradient type and update the form
   class=class="str">"cmt">//--- Text parameters: the text coordinates and the anchor point in the form center
   class=class="str">"cmt">//--- Create a new text animation frame with the ID of class="num">0 and display the text on the form

「把画布表单塞进图形引擎集合」

在循环里每生成一个居中背景文本框表单,都要立刻交给图形引擎统一管理,否则对象游离在集合外不会被渲染。 上述代码调用 form.TextOnBG 在表单中心写带双语标签的「Test 0」加序号,颜色用 C'211,233,149'(浅黄绿),透明度 255 全不透明,并开启两个 true 开关(背景文本与可见性)。 紧接着用 engine.GraphAddCanvElmToCollection(form) 尝试把表单加入集合;若返回 false 说明注册失败,必须 delete form 释放内存,避免 MT5 指标/EA 退出时句柄泄漏。 循环结束返回 INIT_SUCCEEDED,代表所有表单均已完成创建与挂载,可在 MT5 的 OnInit 里直接验证这一收尾逻辑。

MQL5 / C++
   form.TextOnBG(class="num">0,TextByLanguage("Тест class="num">0","Test class="num">0")+class="type">class="kw">string(i+class="num">1),form.Width()/class="num">2,form.Height()/class="num">2,FRAME_ANCHOR_CENTER,C&class="macro">#x27;class="num">211,class="num">233,class="num">149&class="macro">#x27;,class="num">255,true,true);
   class=class="str">"cmt">//--- Add the form to the list
   if(!engine.GraphAddCanvElmToCollection(form))
      class="kw">delete form;
   }
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
   }

别急着下结论

本篇把复合图形对象的事件处理只铺了个头,窗体与鼠标的交互留到下一篇接着拆。眼下能落地的,是把这一版函数库和测试 EA 的 ZIP(4226.84 KB)下到本地 MT5,先跑通现有控件再谈扩展。 作者明确说过,库还在迭代,每篇附的测试程序只适配对应阶段的版本,旧 ZIP 直接塞进新环境大概率编译不过。等整套开发收尾,才有可能出全量归档。 外汇和贵金属图表上玩复合对象属于高风险操作,回测和实盘表现可能出现明显偏离。先验证、再改写,比追更更重要。

把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到属性变动与窗体响应状态,你专注决策而非埋首底层枚举。

常见问题

需在窗体整数型属性中加入与环境交互标志,置位后对象按状态响应鼠标,其余窗体重置该标志,从而禁用图表滚动与十字光标干扰。
在 Data.mqh 添加消息索引与文本,Defines.mqh 扩充属性枚举并修正宏替换数量,排序标准枚举同步常数即可按新属性选股排序。
小布盯盘内置的 AIGC 看盘页可呈现品种属性跟踪与窗体响应,但库代码需自行在 MT5 包含,小布负责把诊断可视化而非替你编译 mqh。
因画布图形元素新增了交互属性常数,必须同步固定属性总量宏,否则排序与遍历会越界或漏读字段。
按下按钮的鼠标覆盖某窗体时,仅该窗体响应并跟随光标,其它窗体在重置态不反应,避免批量重定位。