DoEasy 函数库中的图形(第九十六部分):窗体对象中的图形和鼠标事件的处理·综合运用
(3/3)· 从悬停感知到拖拽独占,142 节把窗体对象的鼠标响应与品种属性跟踪全链路打通
- 符号分类枚举里的贵金属落点
- 行业分类映射表的俄语源与英文键
- 行业分类表的金融到工业段结构
- 行业分类映射表的后半段怎么填
- 整数型符号属性的枚举清单
- 品种交易属性里藏着的下单暗礁
- 枚举里的整数与双精度属性分界
- 品种属性枚举里的保证金与掉期字段
- 保证金与盘面会话属性的枚举落地
- 抓合约属性里的日内波动与期权希腊值
- 品种字符串属性的枚举边界
- 枚举里塞满了排序维度
- 按交易属性给品种排序的枚举清单
- 市场报价窗的实值排序枚举
- 按保证金与掉期维度给品种排序的枚举
- 按挂单维持保证金与当节行情字段排序的枚举项
- 按字符串属性给交易品种排序的枚举项
- 画布图形元素的整型属性枚举
- 画布元素排序的整型属性边界
- 画布元素排序枚举与品种属性描述接口
- 扒开 CSymbol 里的板块与订单属性接口
- 从品种对象里直接抠出图表与session状态
- 从成交量到毫秒时间的符号属性读取
- 扒开品种属性里的执行与风控参数
- 从品种属性接口挖出交易规则细节
- 报价边界与成交价的封装取数
- 从真实成交量到 tick 价值的符号属性读取
- 从合约属性里抠出交易限制与成本
- 把保证金与库存费读进自定义类
- 空头挂单与维护保证金的接口取法
- 从交易品种对象直接取会话层数据
- 从限价到希腊字母:品种属性的第二批取值器
- 从符号属性里直接抓价格弹性与盘口极值
- 从交易品种对象里抠出基础属性串
- 直接从品种对象取元信息
- 盯住当前会话的成交与买单变动
- 盯住场内session挂单的增减节奏
- 成交量异动的三道监控闸
- 日内高低成交量变动的监控接口
- 用代码盯住点差与低价区成交量异动
- 盯住止损位与冻结位的突变阈值
- 冻结水平与Bid变动的监控接口
- 日内买卖价极值的变化监控接口
- 盯住 BidLow 与 Last 的受控变动接口
- 盯住 Last 与日内高价的变动阈值
- 日内 Last 高低点的增量判定接口
- 盯盘 Bid/Last 的日内极值变动接口
- Ask 报价的阈值监控接口怎么写
- 盯住日内 Ask 高低点的变动阈值
- 盯住卖价低位与真实成交量的越界信号
- 日内真实成交量的极值监控接口
- 盯住真实最低成交量与期权行权价的变动阈值
- 抓取期权行权价与持仓限额的变动信号
- 盯住掉期与时段成交量的阈值钩子
- 盯住当前会话的量能与成交额阈值
- 盯住会话内的持仓与买单异动
- 盯住买卖挂单量与开盘价的变动阈值
- 用类封装盯住会话开收盘的变动阈值
- 抓取会话均价与跨日价格跳变
- 用代码盯住价格变动与波动率阈值
- 波动率与期权理论价的变动捕获接口
- 用代码盯住价格跳动与期权Theta阈值
- Theta 与 Gamma 的变动监控接口
- 用 Vega 与 Rho 阈值卡住期权式波动
- 用代码盯住 Rho 与 Omega 的突变阈值
- 期权敏感度与交易对象的类内接口
- 初始化时先把品种塞进市场报价窗
- 把整型品种属性一次性灌进数组
- 把品种整数属性一次性塞进缓存数组
- 把品种属性一次性灌进内存数组
- 把品种交易参数一次性灌进对象数组
- 把交易时段与期权希腊值一次性塞进属性数组
- 把报价与保证金率塞进属性数组
- 保证金与字符串属性的落盘写法
- 符号对象刷新时的属性落盘细节
- 枚举品种属性时的分支兜底写法
- 符号属性取值的 MQL5/MQL4 分支处理
- 品种属性字符串拼接里的条件分支
- 符号属性文本化的三元分支细节
- 把品种属性转成可读文本的分支处理
- 期权类符号的希腊值描述拼接
- 符号属性字符串的安全拼装
- 逐属性拼装品种描述字符串
- 属性空值与板块枚举的文本拼装逻辑
- 板块与行业枚举的文本映射写法
- 行业分类枚举的映射写法
- 行业分类枚举的映射写法
- 行业分类枚举的散装映射写法
- 行业分类枚举的映射写法
- 行业分类枚举的逐条映射写法
- 行业分类枚举的映射写法
- 行业枚举收口与鼠标状态读取
- 鼠标状态类里的位标志布局
- 鼠标与光标状态的位值映射
- 图形对象基类的初始化陷阱
- 图形对象基类的初始化细节
- 图形对象基类的事件与属性初始化
- 图形元素基类的光标命中判定
- 图形元素类的内部骨架
- 图形元素的整型属性结构
- GUI 元素结构体的字段布局
- 图形元素的属性槽与事件钩子
- 画布图元的坐标与边距接口
- 画布控件的活动区偏移与交互开关
- 控件激活区与背景色的取值接口
- 画布元素的边界与属性读取
- 画布元素对象的属性读取接口
- 图形元素的置顶与显隐实现
- 画布元素构造时的属性落盘
- 画布元素初始化的属性落点
- 图形元素构造时的命名与属性注入
- 画布元素的属性落地与结构保存
- 从画布对象抓取界面元素属性
- 画布元素结构体的双向序列化
- 给画布控件一次性灌入坐标与交互属性
- 在图表事件里重算子窗口Y偏移
- 让窗体接住鼠标:从光标状态到图表工具互斥
- GUI 窗体类的内部成员与初始化入口
- 图形对象的命名拼接与鼠标状态接口
- 表单类的坐标与鼠标接口怎么写
- 表单类的鼠标状态与初始化拆解
- 表单内外的鼠标状态判定逻辑
- 拖拽表单时如何接管图表鼠标事件
- 图表交互与浮层跟随的事件链路
- 受保护构造器如何初始化图形对象属性
- 图形对象初始化时的属性落盘逻辑
- 图表事件如何驱动扩展控件刷新
- 图表事件控制器的成员变量与访问接口
- 图表对象控制类的接口与初始化
- 图表对象控制类的构造与析构细节
- 图表控件里怎么接管 ChartID 比较与交互开关
- 图形元素集合类的内部骨架
- 图表对象集合的差集查找与垂直线创建
- 正文
- 对象改名时怎么在集合里兜底定位
- 鼠标悬停与对象改名的事件派发细节
- 锁死图表交互与扩展对象事件分发
- 图表控件集合的取用与ID回填
- 跑起多窗体 EA 看拖动表现
- 用循环批量生成带渐变背景的表单对象
- 把画布表单塞进图形引擎集合
- 别急着下结论
符号分类枚举里的贵金属落点
在 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,确认你的过滤分支没写错。
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 里若要做跨市场相关性扫描,直接把这段二元组贴进 struct 或 string[][2] 数组就能用,不需要运行时翻译。注意原文用 //--- 注释做板块分隔,不是语言关键字,只是给人看的切分标记。
实际验证时数一下:消费周期品段含 27 个条目,是四块里最密的;能源段仅 9 条,做板块权重统计时别漏掉 Thermal coal 和 Uranium 这两个非油气条目。外汇与贵金属交易受宏观板块轮动影响明显,此类分类仅作背景参照,杠杆品种高风险,映射本身不预示任何价格方向。
{"Вещание","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 联动时,这类映射能帮你快速隔离风险敞口,注意跨市场操作杠杆品种属高风险行为,仓位倾向轻配。
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 直接测一遍最稳妥。
{"Железные дороги","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),直接读取可避免手填点值出错。外汇与贵金属杠杆高、滑点突变频繁,用这些属性做运行时校验,可能降低因品种参数误配导致的异常平仓概率。
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 各读一次,差距会立刻显现。
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 等。做日内振幅统计时,直接读这些属性比自己维护高低点数组更省事,且数据来自终端内核、延迟更低。外汇与贵金属杠杆品种波动剧烈,用这些属性做阈值触发时须自行评估滑点与断崖风险。
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 品种规格窗口逐项比对这几个返回值。
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(加权平均价),可以算出当前会话的被动成交占比,倾向用来识别流动性真空时段。
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 就能逐行核对,外汇与贵金属杠杆高,这些数值仅作风险参考,实际敞口可能迅速放大。
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) 计算,暗示双精度属性枚举起点由整型属性总数和跳过数推导,改头文件常量后编译期即重算偏移。
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 探一下,避免整列塌掉。外汇与贵金属杠杆高、滑点大,任何排序结果都只是概率偏好,不是方向保证。
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 时,如果你要批量扫描跨品种机会,用这些排序枚举比自己写比较函数更省事;比如优先挑点差小、冻结水平低的品种,可能降低滑点与被拒单概率。外汇与贵金属波动剧烈,高杠杆下风险显著,排序只是预处理,不等于信号。
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价格,这在多品种批量测算保证金占用时比单纯看点值更贴近实际。外汇与贵金属杠杆高,排序只是观察手段,不构成方向判断。
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 的维持保证金。做跨品种仓位扫描时,用这些枚举替代人工翻表,能直接按成本结构排出优先交易的标的。外汇与贵金属杠杆波动大,这类排序仅作风险成本参考,实际开仓仍可能受报价断裂影响。
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 暴露最高的合约。外汇与贵金属杠杆交易高风险,排序只是观察工具,不构成方向判断。
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 字样再调。
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 里能编译用的原样定义,建议复制到头文件里对照调参。
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,排序枚举会静默错位,调试时只会看到元素乱飞。
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 日志就能验证。
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(),能立刻验证你当前品种表里哪些是自定义的。
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()) 跑一下,就能验证当前品种时段挂单量是否和终端显示一致。
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 调用包装成了语义清晰的只读接口,覆盖成交量、极值成交量、报价时间与精度等维度。
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); }
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 可能短暂异常,回测与实盘取值要分开打点记录,外汇与贵金属交易杠杆高、滑点突发,参数漂移可能直接吃掉风控边界。
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 你的品种类头文件,循环打印这几个方法,就能在策略初始化阶段把规则盲区扫清。
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——这类拼写错在编译期不会报错,但实盘会悄悄少算一条边界。 外汇与贵金属杠杆高,报价边界只反映当日会话区间,不代表波动已结束,用这些值时需自行叠加滑点假设。
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 的麻烦。
class="type">class="kw">double VolumeReal(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUME_REAL); }
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 窗口,切到不同品种就能看到数值差异,比你翻规格书直观。
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 系列就是把多单和各类买单的初始、维持保证金属性透传出来。
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() 比自己手算合约规模乘杠杆要稳,能少踩经纪商自定义属性不一致的坑。
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 或定时器里高频轮询。
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); }
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 封装里,继基础属性之后还有一批只读方法,专门吐出交易所或经纪商下发的衍生品参数。下面这截代码覆盖了从交易时段涨跌停到期权希腊值的完整映射,直接挂在品种对象上调用即可。
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); }
sym.PriceDelta() 拿数值进 EA 调仓,比手动查终端更稳。外汇与贵金属杠杆高,希腊值随行情跳变,参数仅作概率参考,不构成方向判断。
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 分钟的数值差,能直观看到黄金对流动性的敏感度分布。
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 在品种切换瞬间崩在空引用上。
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 后台的品种属性透传出来,调用方不用再碰底层枚举。
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); }
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 在同一量纲,否则报警会频繁误触。
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,负值会被强转为绝对值,避免误传参数把监控方向搞反。
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); }
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 通道。把它和单笔监控叠起来看,才可能分辨是散户追单还是机构倒仓。
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 里直接编译。
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 把这几行塞进你的符号监控类,跑一晚就知自己经纪商的点差脾气。
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 里用标志位触发重算挂单逻辑,而不是盲目重试被拒订单。
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 为真就重算挂单距离。
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 点」的条件分支。外汇与贵金属波动剧烈,这类极值监控仅作概率参考,实盘须自行验证阈值合理性。
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(),负值会被强制取绝对值,调用层不必自己处理方向。
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); }
GetValueChangedBidLow 返回实际变动的双精度值,可用于算偏离幅度。第5、6行分别取增量、减量布尔标志,返回 true 时代表对应方向已破阈。
Last 价的部分接口同理,只是把属性换成 SYMBOL_PROP_LAST:
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)); }
IsIncreasedBidLow() 抓这种异常比轮询价格更省 CPU。外汇与贵金属杠杆高、滑点风险大,这类标志仅作概率性预警,不能单独当成入场依据。开 MT5 把上面代码塞进你的符号监听类,先传 Point()*10 测一下回调频率再调阈值。
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 调到能过滤掉噪音、又不会漏掉真实突破的档位,比直接抄默认值有用。
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 阈值做比对,就能算出当前偏离幅度。外汇与贵金属杠杆高、滑点随机,这类标志位只说明「价格结构可能转向」,不构成任何方向确定性。
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 点阈值跑一天,就能看到日内极值触发频率。
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」的扩展位,说明这套结构能顺延到日内极值监控,不用重写框架。
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 标志的触发频率再定参数。
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 拿越界布尔。
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); }
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 设定的是日内最低量相对抬升的监控阈值。外汇与贵金属杠杆高、滑点风险大,这类接口仅作异动侦测,不代表任何方向确定性。
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,建议在初始化时把三值都填上。 外汇与贵金属期权波动剧烈、杠杆风险高,这类阈值仅用于预警,不预示价格方向。
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 读变动值。隔夜利息微调往往先于点差扩大,值得单独挂一个监听器。
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 里实时看清哪家平台在悄悄改掉期。
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 回值突破该线,可能暗示主力换手加速;外汇与贵金属波动受杠杆与流动性影响,此类信号仅作概率参考,高风险品种须自设止损。
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 策略测试器用真实品种日历跑一遍确认阈值量级。
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。把挂单量异动和开盘价突破放在同一帧比对,能过滤掉单纯报价跳动造成的假信号。
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 点)跑一晚,就能在日志里看到真实会话切换时的触发频率。
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 均价阈值在伦敦时段被触发的次数,大概率比会话收盘价触发更密集。
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 的判断。
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)); }
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 分开设,可以只盯向上击穿或只盯向下击穿,避免双边噪声。
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 的触发频率差异。
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 线上反复触发。
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),传负数也会被掰成绝对值,避免方向设反。
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)); }
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 派生类即可编译。
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); }
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 实例绑定的下单对象。外汇与贵金属杠杆高、敏感度跳变可能瞬时触发,用这套封装做越界监控时须自行评估滑点与断连风险。
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 写个脚本打印这三个值,能立刻看出某品种到底有没有被激活。
{
::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 在品种规格里核对这个数再写进场逻辑。
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] 对照品种规格窗口,能立刻验证缓存是否生效。外汇与贵金属杠杆高,属性读取错误可能让订单以意外手数成交,验证时先用模拟账户。
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 价值计算偏差可能放大滑点风险,建议回测前先打印数组核对。
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 在回测里很难一眼看出来。
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,说明该品种不是衍生品或经纪商未提供,别拿去当信号源。
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 自建类时可直接对照:
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] 是否等于品种真实基础币,就能验证映射没错位。
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 用三元表达式把整型属性转成可读文案,不支持的属性会拼上“不支持”提示,避免上层日志出现空描述。
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 或空描述,干扰小布盯盘里的品种标签渲染。
": "+(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 值会持续,属正常高概率现象,不代表接口坏了。
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 输出是否符合预期。外汇与贵金属品种属性受经纪商限制,深度簿数据可能间歇缺失,属正常高风险环境现象。
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 派生类,改几个枚举就能直接打印品种属性表。
(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 或小数位参数,就能控制保证金对冲比例等字段的输出精度。
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 里对照调试。外汇与贵金属期权品种流动性差异大,希腊值可能延迟或缺失,属高风险观测数据。
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」被引号包裹。
(!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 打出来就能对照。外汇与贵金属品种的属性支持度差异大,部分字段返回空属正常现象,高杠杆下解读需谨慎。
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,这是经纪商符号表的高频现象,相关品种分类本身波动杠杆高,验证后勿直接用作仓位依据。
(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") 的脏代码。外汇与贵金属杠杆高、波动剧烈,用行业标签做板块轮动过滤只是辅助,仓位仍需自担风险。
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 等文本是否正常解析;若返回空串,八成是消息表缺了对应条目。
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 在第三方源码里不少见,复制前先核对枚举声明。 外汇与贵金属交易者若用这类行业映射做跨市场相关性扫描,需明白股票行业数据延迟可能达数秒,且杠杆品种波动叠加宏观事件时风险偏高,信号仅作概率参考。
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_* 是否随语言切换。外汇与贵金属品种大多不走这套行业枚举,但跨市场扫描股票类符号时会用到,注意跨市场数据延迟带来的误分类风险。
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 结构加枚举即可,编译后能在观测面板里看到中文/英文行业标签。外汇与贵金属虽不在此分类内,但跨市场扫描时这类映射能帮你快速排除无关个股板块,注意跨市场拼接数据仍有高误差风险。
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 前确认消息宏已在资源文件定义,否则会报未声明标识符。
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_ 前缀的字段即可,不改调用方。 外汇与贵金属交易者注意:这类行业映射多用于跨市场扫描,不直接产生信号;跨市场联动分析本身受流动性与时段错配影响,结论仅作概率参考,杠杆品种风险偏高。
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) 截断。外汇与贵金属行情受事件驱动明显,用这套状态机捕捉手动框选或滚轮缩放可能比轮询更高效,但手动交互策略在滑点扩大时高风险,仅适合辅助判断。
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 的十进制,就能验证自己按下的组合键是否和位表对得上。外汇与贵金属图表交互测试请留意,实时事件回调在高波动行情可能丢帧,属正常高风险环境现象。
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 数量打印验证前缀是否生效。
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 线交互。 外汇与贵金属图表加载这类自定义对象时波动剧烈,对象重绘可能引发闪烁或事件丢失,建议先在模拟盘验证前缀隔离逻辑。
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 有没有在创建后被正确调用。 外汇与贵金属图表上跑这类自定义对象逻辑属于高风险操作,参数错配可能导致对象不显示或频闪,建议在策略测试器里先用单品种验证。
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 里补拍。
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 参数,可以在指定子窗口精确生成一块半透明可交互画布。外汇与贵金属图表叠加这类自绘层时波动剧烈,实盘前务必在策略测试器里验证重绘开销。
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 像素,能避免边框误触。外汇与贵金属图表上挂这类控件时,需注意高波动行情下坐标重绘可能引发热区错位,属于高风险操作环境。
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)集中存放,和前面的像素坐标、透明度分开管理,能减少内存碎片。
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 占用可能随元素数线性爬升,建议加脏标记。
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 可能造成卡顿。
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密集品种上,热区过大可能吞掉相邻控件的点击,外汇与贵金属杠杆高,界面误操作会直接触发下单。
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() 的差值,就能验证实际可点区域比外框小了多少像素。
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() 在拖动前后的差值,就能验证边界计算是否跟着坐标实时走。
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。
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 绑到一个按钮事件上,能直观看到重叠矩形谁压谁。外汇和贵金属图表叠加自定义图层时,置顶逻辑错乱可能导致信号被遮,属于高风险操作环境,参数多核对一遍。
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 一下这几个属性就能验证。
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,不会继续执行属性初始化,避免空对象被后续逻辑误用。
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 确认挂载成功再去调参数。
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,这一步是把运行时对象状态序列化成轻量结构体的关键,后续做模板保存或状态恢复都依赖它。
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 基准。
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 错了,元素可能跑到别的图表窗口,贵金属或多币对同屏时尤其容易乱。 外汇与贵金属杠杆高,这类图形面板只作辅助,任何信号都只是概率倾向,别拿它当进出场唯一依据。
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 数值,编译后拖一下面板,能直观验证坐标与尺寸是否按结构体生效。外汇与贵金属图表上挂这类自定义控件需注意:高频重绘可能拖慢报价刷新,属高风险操作环境。
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 像素不等的垂直偏差。外汇与贵金属图表多窗口叠加指标时,这种偏差会直接干扰读图,属高风险操作环境,参数需自行校验。
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 目前只露两个公开方法就够测:一个吐集合里的图形元素列表,一个把新元素加进集合。外汇与贵金属图表上跑这套交互逻辑属于高风险操作,参数没调稳可能让窗体和图表工具抢事件。
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 导致窗体贴着光标左上角。
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
◍ 图形对象的命名拼接与鼠标状态接口
这一段给出的是面板类里两个容易被忽略的基础方法:对象名自动拼接,以及鼠标相对表单状态的对外接口。
返回名那行用了 StringSubstr 从 NameObj() 里截掉 EA 程序名长度再加 1,拼上 _ 和 base_name。也就是说,若 EA 名叫 PanelEA,MQLInfoString(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。
{ 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 线,外汇品种跳空时段坐标可能瞬移,需加边界判断。
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 能直观看到边框变粗,验证命中区是否同步外扩。
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 实测确认。
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 派生类,故意快速拖甩测试边界最直观。
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 是否和当前图表一致。
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 里打印确认。
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 字样。
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 上挂一个扩展对象验证。
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 就说明图表对象层有变动,可能触发重算。外汇与贵金属市场波动剧烈,这类事件监听仅作辅助,不构成任何方向建议。
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 一致性即可。
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 退出前手动触发析构验证逻辑。 下面这段是原文构造与析构的核心代码片段,逐行对应上文动作。
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。
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="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); }
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 版本,否则编译可能报重定义。外汇与贵金属图表上跑这类对象集合时波动大、对象频繁增删,属于高风险环境,建议先在模拟图表压测再上实盘。
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,说明建对象或拿控制句柄失败。
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
◍ 正文
CGStdVLineObj *obj=ctrl.CreateNewGraphObj(type_object,nm,extended); <span class="keyword">if</span>(obj==<span class="macro">NULL</span>) { ::<span class="functions">Print</span>(DFUN,CMessage::Text(MSG_GRAPH_STD_OBJ_ERR_FAILED_CREATE_CLASS_OBJ),StdGraphObjectTypeDescription(type_object)); <span class="keyword">return</span> <span class="macro">false</span>; } <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 类,故意改个对象名拖一下,看能否自动续上。
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 验证交互标志翻转时机。
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) 统一关闭交互,并手动把鼠标移动事件转发给面板对象。
class=class="str">"cmt">//--- 禁用鼠标拖图、图表右键菜单和十字光标工具 form.SetChartTools(false); class=class="str">"cmt">//--- 向面板对象投递鼠标移动事件,lparam/dparam/sparam 为系统传入参数 form.OnChartEvent(CHARTEVENT_MOUSE_MOVE,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); }
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,能直接看到终端里真实图表数量对应的数组长度。
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 波动叠加显示延迟,风险偏高,建议先用模拟盘验证。
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 线,建议先在模拟盘验证。
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 里直接验证这一收尾逻辑。
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 直接塞进新环境大概率编译不过。等整套开发收尾,才有可能出全量归档。 外汇和贵金属图表上玩复合对象属于高风险操作,回测和实盘表现可能出现明显偏离。先验证、再改写,比追更更重要。