轻松快捷开发 MetaTrader 程序的函数库(第十四部分):品种对象·综合运用
📘

轻松快捷开发 MetaTrader 程序的函数库(第十四部分):品种对象·综合运用

第 3/3 篇
本章目录
  1. 持仓排序枚举里那些跟交易风控直接挂钩的字段
  2. 品种排序枚举里的会话与字符串维度
  3. CSymbol 类的属性存储与索引偏移
  4. 符号属性的存取与描述接口
  5. CSymbol 类的私有数据骨架
  6. CSymbol 类里的属性仓储与下标换算
  7. 扒开 SymbolInfo 的隐藏访问接口
  8. 封装品种属性的读写与订单权限查询
  9. 用位运算卡死品种的下单权限
  10. 从到期标志位反推品种交易权限
  11. 把交易品种属性读成可读文本
  12. 把交易品种塞进观察窗口与挂接深度
  13. 品种状态与成交量的接口抽取
  14. 品种属性读取的几个底层接口
  15. 从品种属性里抠出交易约束
  16. 从订单模式到买卖报价的属性读取
  17. 报价极值与真实成交量的取数接口
  18. 从报价点差到合约面值的取数接口
  19. 手数边界与持仓成本的几个取数口
  20. 从品种类里直接抠保证金与时段成交
  21. 读取交易时段与保证金的内联取值器
  22. 品种属性里的字符串字段怎么取
  23. 符号对象构造时的校验与初始化
  24. 把 tick 和品种属性塞进长整型数组
  25. 一次性抓全交易品种的整数属性
  26. 把品种属性一次性塞进内存数组
  27. 把品种交易参数一次性塞进数组
  28. 把交易时段与盘口字段一次性灌进属性数组
  29. 把品种属性一次性灌进内存结构
  30. 按属性类别比对两个交易品种对象
  31. 从封装类看品种整数属性的取数路径
  32. 品种属性读取的 MQL5/MQL4 兼容封装
  33. 品种交易属性接口的跨版本取法
  34. 手数小数位与当日极值怎么从接口抠出来
  35. 读取真实成交量的两个边界值
  36. 对冲保证金与衍生品种元数据读取
  37. 正文
  38. 把整数属性翻成人话的映射函数
  39. 可见性与会话订单量的属性分支
  40. 把品种属性翻译成可读标签的嵌套写法
  41. 读取交易品种隐藏属性的字符串拼装
  42. 品种属性描述的分支拼装逻辑
  43. 枚举属性怎么翻成可读文本
  44. 把当日高低点与买卖报价映射到可读文本
  45. 符号属性的多语言与版本分支拼装
  46. 逐属性拼装品种字段的中文显示串
  47. 债券面值与交易量限制的字符串拼装细节
  48. 把 swap 和保证金属性翻成可读文本
  49. 挂单与时段属性的文本化读取
  50. 读取交易时段内的挂单与均价字段
  51. 会话结算价与对冲保证金的属性拼装
  52. 逐个核对交易品种的属性字段
  53. 自定义品种属性与存在性校验的底层写法
  54. 在 MQL5 里接管深度与品种身份
  55. 从品种状态反推报价结构
  56. 从计算模式反推品种的保证金逻辑
  57. 把品种交易限制与执行模式读成可读文本
  58. 掉期利息的计费维度怎么读
  59. 从代码看持仓过夜与挂单有效期的底层规则
  60. 把品种交易权限拆成可读字符串
  61. 把品种订单权限读成可读字符串
  62. 品种交易权限的多语描述封装
  63. 订单过期模式与报价规整的底层实现
  64. 把品种整数属性一次性灌进结构体
  65. 把品种属性一次性灌进数组
  66. 保证金与盘后会话字段的批量抓取
  67. 把 tick 快照灌进属性数组的刷新路数
  68. 把品种属性塞进内存的那几行
  69. 在 MQL4 里补齐 MQL5 的订单常量映射
  70. 品种属性枚举与订单标志位拆解
  71. 保证金与掉期计费的枚举映射
  72. 交易引擎构造时的集合与定时器初始化
  73. 账户事件索引的倒序取数逻辑
  74. 账户事件队列的倒序取数逻辑
  75. 在 OnInit 里快速验证品种对象
  76. 把品种属性塞进私有成员
  77. 同品种在 MT5 与 MT4 下的参数差异
  78. EURUSD 品种属性里的 MQL4 盲区
  79. 顺着库的开发脉络接着往下走

持仓排序枚举里那些跟交易风控直接挂钩的字段

在 MT5 的 Symbols 排序枚举中,有一批 SORT_BY_SYMBOL_* 常量专门按品种的交易属性做排序,而不是按名称或报价。做多品种监控时,用这些枚举可以让小布盯盘类脚本把高 swap、高保证金占用或特殊合约大小的品种排到前面。 比如 SORT_BY_SYMBOL_SWAP_LONG 与 SORT_BY_SYMBOL_SWAP_SHORT 分别按多空隔夜息排序,贵金属和交叉盘之间可能差出好几倍;SORT_BY_SYMBOL_MARGIN_INITIAL 和 SORT_BY_SYMBOL_MARGIN_MAINTENANCE 则直接对应开仓与维持保证金。外汇及贵金属杠杆高,同账户下不同品种保证金系数波动可能让可用预付款瞬间吃紧。 另外像 SORT_BY_SYMBOL_VOLUME_MIN / MAX / STEP 决定了最小开仓量和步长,SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT 与 _LOSS 则按盈亏仓的单跳价值排,回测里你会发现同样 1 手,XAUUSD 的一跳价值可能比 EURUSD 高 50 倍以上。把这些枚举喂给 SortSeries 或自定义数组排序,能省掉手工筛品种的时间。

MQL5 / C++
SORT_BY_SYMBOL_TRADE_TICK_VALUE_PROFIT,              class=class="str">"cmt">// 按盈利仓位计算出的单跳价格排序
SORT_BY_SYMBOL_TRADE_TICK_VALUE_LOSS,               class=class="str">"cmt">// 按亏损仓位计算出的单跳价格排序
SORT_BY_SYMBOL_TRADE_TICK_SIZE,                     class=class="str">"cmt">// 按最小价格变动单位排序
SORT_BY_SYMBOL_TRADE_CONTRACT_SIZE,                 class=class="str">"cmt">// 按交易合约大小排序
SORT_BY_SYMBOL_TRADE_ACCRUED_INTEREST,              class=class="str">"cmt">// 按应计利息排序
SORT_BY_SYMBOL_TRADE_FACE_VALUE,                    class=class="str">"cmt">// 按面值排序
SORT_BY_SYMBOL_TRADE_LIQUIDITY_RATE,                class=class="str">"cmt">// 按流动性比率排序
SORT_BY_SYMBOL_VOLUME_MIN,                          class=class="str">"cmt">// 按成交最小交易量排序
SORT_BY_SYMBOL_VOLUME_MAX,                          class=class="str">"cmt">// 按成交最大交易量排序
SORT_BY_SYMBOL_VOLUME_STEP,                         class=class="str">"cmt">// 按成交最小量变动步长排序
SORT_BY_SYMBOL_VOLUME_LIMIT,                        class=class="str">"cmt">// 按同方向开仓+挂单的总量上限排序
SORT_BY_SYMBOL_SWAP_LONG,                           class=class="str">"cmt">// 按多头掉期值排序
SORT_BY_SYMBOL_SWAP_SHORT,                          class=class="str">"cmt">// 按空头掉期值排序
SORT_BY_SYMBOL_MARGIN_INITIAL,                      class=class="str">"cmt">// 按初始保证金排序
SORT_BY_SYMBOL_MARGIN_MAINTENANCE,                  class=class="str">"cmt">// 按品种维持保证金排序
SORT_BY_SYMBOL_MARGIN_LONG,                         class=class="str">"cmt">// 按多头保证金系数排序
SORT_BY_SYMBOL_MARGIN_SHORT,                        class=class="str">"cmt">// 按空头保证金系数排序
SORT_BY_SYMBOL_MARGIN_STOP,                         class=class="str">"cmt">// 按止损单保证金系数排序
SORT_BY_SYMBOL_MARGIN_LIMIT,                        class=class="str">"cmt">// 按限价单保证金系数排序
SORT_BY_SYMBOL_MARGIN_STOPLIMIT,                    class=class="str">"cmt">// 按止损限价单保证金系数排序
SORT_BY_SYMBOL_SESSION_VOLUME,                      class=class="str">"cmt">// 按当前会话成交汇总量排序

「品种排序枚举里的会话与字符串维度」

在 MT5 的行情簿接口中,品种排序枚举(ENUM_SYMBOL_SORT)把可排序字段切成了数值属性和字符串属性两大块。上面这段枚举片段列出了当前交易会话(session)相关的 11 个数值排序键,以及从 SORT_BY_SYMBOL_NAME 起的 10 个字符串排序键。 会话类排序键覆盖的内容很细:从 SORT_BY_SYMBOL_SESSION_TURNOVER(按当前会话总成交额排序)到 SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX(按当前会话最高价排序),中间还包括买卖挂单量、未平仓合约汇总、开盘/收盘/均价/结算价等。做跨品种热度扫描时,直接按 SESSION_BUY_ORDERS_VOLUME 或 SESSION_SELL_ORDERS_VOLUME 排,能在 30 个品种里快速揪出多空挂单失衡的那几个。 字符串类排序从 FIRST_SYM_STR_PROP 基准开始编号。SORT_BY_SYMBOL_NAME 对应基础枚举值,其后 BASIS(标的资产)、CURRENCY_BASE(基础货币)、CURRENCY_PROFIT(盈利货币)、CURRENCY_MARGIN(保证金货币)等,适合做按账户币种或衍生品底层资产的分组。SORT_BY_SYMBOL_FORMULA 仅对自定义品种有意义,实盘外汇品种返回空串。 外汇与贵金属杠杆高、会话切换时流动性可能瞬间断层,用这些排序键做盘前扫描只是辅助,不代表任何方向确定性。打开 MT5 的 MetaEditor,全局搜 ENUM_SYMBOL_SORT 即可核对完整枚举,顺手改两行排序键验证返回顺序。

MQL5 / C++
  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 an Open price of the current session
  SORT_BY_SYMBOL_SESSION_CLOSE,                                                                        class=class="str">"cmt">// Sort by a Close price of the current session
  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 minimal price of the current session 
  SORT_BY_SYMBOL_SESSION_PRICE_LIMIT_MAX,                                                              class=class="str">"cmt">// Sort by a maximal 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
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 basic 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 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

◍ CSymbol 类的属性存储与索引偏移

在 MT5 的 Symbol.mqh 体系里,CSymbol 作为抽象品种类,把品种属性按类型拆进三个定长数组:m_long_prop 装整数属性,m_double_prop 装实数属性,m_string_prop 装字符串属性,数组大小分别由 SYMBOL_PROP_INTEGER_TOTAL、SYMBOL_PROP_DOUBLE_TOTAL、SYMBOL_PROP_STRING_TOTAL 决定。 实数与字符串属性的读取不是从 0 开始,而是用枚举值减去前面类型的总数做偏移。IndexProp 对 double 属性返回 (int)property - SYMBOL_PROP_INTEGER_TOTAL,对 string 属性再多减一个 SYMBOL_PROP_DOUBLE_TOTAL,这样三套枚举可以共用连续编号而不冲突。 类里默认构造 CSymbol(void){;} 是空实现,真正初始化走 protected 的参数化构造 CSymbol(ENUM_SYMBOL_STATUS, const string name),外部不能直接 new,只能由派生类调用。 在 MT5 里开个脚本 #include <Symbols\Symbol.mqh> 打几行 CSymbol 派生测试,能直接验证这种偏移索引是否和你本地头文件里的 _TOTAL 常量对得上。

MQL5 / C++
class CSymbol : class="kw">public CObject
  {
class="kw">private:
   class="type">long               m_long_prop[SYMBOL_PROP_INTEGER_TOTAL];       class=class="str">"cmt">// Integer properties
   class="type">class="kw">double             m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL];      class=class="str">"cmt">// Real properties
   class="type">class="kw">string             m_string_prop[SYMBOL_PROP_STRING_TOTAL];      class=class="str">"cmt">// String properties
class=class="str">"cmt">//--- Return the index of the array the symbol&class="macro">#x27;s(class="num">1) class="type">class="kw">double and(class="num">2) class="type">class="kw">string properties are located at
   class="type">int                IndexProp(ENUM_SYMBOL_PROP_DOUBLE class="kw">property)  const { class="kw">return(class="type">int)class="kw">property-SYMBOL_PROP_INTEGER_TOTAL; }
   class="type">int                IndexProp(ENUM_SYMBOL_PROP_STRING class="kw">property)  const { class="kw">return(class="type">int)class="kw">property-SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_DOUBLE_TOTAL; }
class="kw">public:
class=class="str">"cmt">//--- Default constructor
                     CSymbol(class="type">void){;}
class="kw">protected:
class=class="str">"cmt">//--- Protected parametric constructor
                     CSymbol(ENUM_SYMBOL_STATUS symbol_status,const class="type">class="kw">string name);
class="kw">public:
class=class="str">"cmt">//--- Set(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string symbol properties

符号属性的存取与描述接口

在 MT5 自建符号类里,属性读写被拆成三套重载:整型、双精度、字符串各走各的数组。整型直接用枚举当下标写进 m_long_prop,而双精度和字符串要先过 IndexProp 做一次映射再落库,这意味着后两者在底层并非连续枚举寻址。 读接口对称地提供 GetProperty 的三个 const 版本,调用方拿到的就是存进去的原值,没有运行时换算。SupportProperty 三个虚函数目前都返回 true,说明默认假设 broker 端全属性可用,实盘里若遇到黄金点差属性缺失,应当在这里做重写而不是在外层判空。 描述类方法只声明不实现:GetPropertyDescription 三重载负责把枚举翻成人类可读文案,StatusDescription 给符号状态命名,二者都是写日志或画面板前的预处理。想要在「小布盯盘」里把 XAUUSD 的 Swap 水平直接打进专家日志,就得先扩这三个描述函数,否则日志里只会出现原始枚举数字。 外汇与贵金属杠杆品种属性随交易时段变动频繁,上述封装仅降低访问成本,不消除报价中断或属性跳变风险,参数校验仍须在调用侧保留。

MQL5 / C++
class="type">void SetProperty(ENUM_SYMBOL_PROP_INTEGER class="kw">property,class="type">long value) { this.m_long_prop[class="kw">property]=value; }
class="type">void SetProperty(ENUM_SYMBOL_PROP_DOUBLE class="kw">property,class="type">class="kw">double value){ this.m_double_prop[this.IndexProp(class="kw">property)]=value; }
class="type">void SetProperty(ENUM_SYMBOL_PROP_STRING class="kw">property,class="type">class="kw">string value){ this.m_string_prop[this.IndexProp(class="kw">property)]=value; }
class=class="str">"cmt">//--- Return(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string symbol properties from the properties array
class="type">long GetProperty(ENUM_SYMBOL_PROP_INTEGER class="kw">property) const { class="kw">return this.m_long_prop[class="kw">property]; }
class="type">class="kw">double GetProperty(ENUM_SYMBOL_PROP_DOUBLE class="kw">property) const { class="kw">return this.m_double_prop[this.IndexProp(class="kw">property)]; }
class="type">class="kw">string GetProperty(ENUM_SYMBOL_PROP_STRING class="kw">property) const { class="kw">return this.m_string_prop[this.IndexProp(class="kw">property)]; }
class=class="str">"cmt">//--- Return the flag of a symbol supporting the class="kw">property
class="kw">virtual class="type">bool SupportProperty(ENUM_SYMBOL_PROP_INTEGER class="kw">property) { class="kw">return true; }
class="kw">virtual class="type">bool SupportProperty(ENUM_SYMBOL_PROP_DOUBLE class="kw">property) { class="kw">return true; }
class="kw">virtual class="type">bool SupportProperty(ENUM_SYMBOL_PROP_STRING class="kw">property) { class="kw">return true; }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Description of symbol object properties                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- Get description of a symbol(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties
class="type">class="kw">string GetPropertyDescription(ENUM_SYMBOL_PROP_INTEGER class="kw">property);
class="type">class="kw">string GetPropertyDescription(ENUM_SYMBOL_PROP_DOUBLE class="kw">property);
class="type">class="kw">string GetPropertyDescription(ENUM_SYMBOL_PROP_STRING class="kw">property);
class=class="str">"cmt">//--- Return symbol status name
class="type">class="kw">string StatusDescription(class="type">void) const;
class=class="str">"cmt">//--- Send description of symbol properties to the journal(full_prop=true - all properties, false - only supported ones)

「CSymbol 类的私有数据骨架」

在 MT5 的自定义品种封装里,CSymbol 作为抽象基类继承自 CObject,其私有段决定了后续所有行情读取的上限。 它用 MqlTick m_tick 缓存最新逐笔报价,用动态数组 MqlBookInfo m_book_info_array[] 承接市场深度(DOM)快照;这两块是高频刷价时最容易忽略的内存占用点。 品种名存于 m_symbol_name,而整数与实数属性分别落在定长数组 m_long_prop[SYMBOL_PROP_INTEGER_TOTAL] 与 m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL] 中,下标总数由标准枚举控制,意味着换品种不会动态扩容。 开 MT5 把 Symbol.mqh 调进编辑器,搜 CSymbol 私有段即可核对上述字段;外汇与贵金属杠杆品种在 DOM 跳动时,m_book_info_array 的实际长度可能瞬时超过 20 档,回测前应先限流。

MQL5 / C++
class CSymbol : class="kw">public CObject
  {
class="kw">private:
   class="type">MqlTick       m_tick;             class=class="str">"cmt">// Symbol tick structure
   MqlBookInfo   m_book_info_array[];class=class="str">"cmt">// Array of the market depth data structures
   class="type">class="kw">string        m_symbol_name;      class=class="str">"cmt">// Symbol name
   class="type">long          m_long_prop[SYMBOL_PROP_INTEGER_TOTAL];     class=class="str">"cmt">// Integer properties
   class="type">class="kw">double        m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL];    class=class="str">"cmt">// Real properties

◍ CSymbol 类里的属性仓储与下标换算

在 MQL5 的 CSymbol 封装里,品种的全部静态属性被拆成三块数组存放:整型、双精度、字符串。m_string_prop 数组长度由 SYMBOL_PROP_STRING_TOTAL 决定,专门收纳字符串类属性如品种描述;m_digits_currency 记录账户货币的小数位,用于金额类数值的格式化输出。 m_global_error 这个整型成员承载最近一次全局错误码,调试时直接读它比反复调 GetLastError() 更省事。IndexProp 两个重载把枚举值映射成数组下标:double 属性要减掉 SYMBOL_PROP_INTEGER_TOTAL,string 属性再额外减掉 SYMBOL_PROP_DOUBLE_TOTAL,这样枚举连续值就能直接定位到对应桶。 protected 区的参数化构造函数接收 ENUM_SYMBOL_STATUS 与 name,意味着对象不允许在外部随意 new,只能由工厂或派生类按状态创建。下方那一串 SymbolExists、SymbolCustom、SymbolChartMode 等 long 型内联方法,都是从参数缓存里直接返回整型属性,不触发终端符号信息重新查询,所以在 EA 的 OnTick 里高频调用也不会明显拖慢执行。 想验证映射是否越界,可在 MT5 里打印 (int)SYMBOL_VOLUME_MIN - SYMBOL_PROP_INTEGER_TOTAL,若结果为负说明该属性不在整型段,调用 IndexProp 会错桶。外汇与贵金属杠杆高,这类底层封装错了可能让仓位计算偏差放大数倍,上线前务必用真实品种跑一遍断言。

MQL5 / C++
class="type">class="kw">string m_string_prop[SYMBOL_PROP_STRING_TOTAL]; class=class="str">"cmt">// String properties
class="type">int m_digits_currency; class=class="str">"cmt">// Number of decimal places in an account currency
class="type">int m_global_error; class=class="str">"cmt">// Global error code
class=class="str">"cmt">//--- Return the index of the array the symbol&class="macro">#x27;s(class="num">1) class="type">class="kw">double and(class="num">2) class="type">class="kw">string properties are located at
class="type">int IndexProp(ENUM_SYMBOL_PROP_DOUBLE class="kw">property) const { class="kw">return(class="type">int)class="kw">property-SYMBOL_PROP_INTEGER_TOTAL; }
class="type">int IndexProp(ENUM_SYMBOL_PROP_STRING class="kw">property) const { class="kw">return(class="type">int)class="kw">property-SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_DOUBLE_TOTAL; }
class=class="str">"cmt">//--- Reset all symbol object data
class="type">void Reset(class="type">void);
class="kw">public:
class=class="str">"cmt">//--- Default constructor
CSymbol(class="type">void){;}
class="kw">protected:
class=class="str">"cmt">//--- Protected parametric constructor
CSymbol(ENUM_SYMBOL_STATUS symbol_status,const class="type">class="kw">string name);
class="kw">protected:
class=class="str">"cmt">//--- Protected parametric constructor
CSymbol(ENUM_SYMBOL_STATUS symbol_status,const class="type">class="kw">string name);
class=class="str">"cmt">//--- Get and class="kw">return integer properties of a selected symbol from its parameters
class="type">long SymbolExists(class="type">void) const;
class="type">long SymbolCustom(class="type">void) const;
class="type">long SymbolChartMode(class="type">void) const;
class="type">long SymbolMarginHedgedUseLEG(class="type">void) const;
class="type">long SymbolOrderFillingMode(class="type">void) const;
class="type">long SymbolOrderMode(class="type">void) const;
class="type">long SymbolOrderGTCMode(class="type">void) const;
class="type">long SymbolOptionMode(class="type">void) const;

扒开 SymbolInfo 的隐藏访问接口

在 MT5 的 CSymbolInfo 类封装里,除常见 Bid/Ask 外,还暴露了一批直接读取品种参数的成员函数。它们多数带 const 修饰,意味着只取不写,适合在 EA 初始化或品种切换时一次性拉取属性。 下面这段声明覆盖了整数、双精度、字符串三类返回值。整数类如 SymbolCalcMode 返回合约计算模式(0=Forex、1=CFD、2=Futures 等),SymbolSwapMode 决定隔夜息计算方式;双精度类如 SymbolBidHigh / SymbolBidLow 给出当日买价极值,SymbolVolumeReal 返回最新真实成交量。 字符串类容易被忽略:SymbolISIN 取国际证券标识码,SymbolFormula 对合成品种返回成分公式。实盘写监控脚本时,若不去调这些接口,就只能绕路用 SymbolInfoString 硬查,代码更啰嗦。 外汇与贵金属波动剧烈、杠杆风险高,用这些属性做品种过滤时,建议先在策略测试器跑一轮再上真仓。

MQL5 / C++
class="type">long      SymbolOptionRight(class="type">void)        const;
class="type">long      SymbolBackgroundColor(class="type">void)    const;
class="type">long      SymbolCalcMode(class="type">void)           const;
class="type">long      SymbolSwapMode(class="type">void)           const;
class="type">long      SymbolExpirationMode(class="type">void)     const;
class="type">long      SymbolDigitsLot(class="type">void);
class=class="str">"cmt">//--- Get and class="kw">return real properties of a selected symbol from its parameters
class="type">class="kw">double     SymbolBidHigh(class="type">void)           const;
class="type">class="kw">double     SymbolBidLow(class="type">void)            const;
class="type">class="kw">double     SymbolVolumeReal(class="type">void)        const;
class="type">class="kw">double     SymbolVolumeHighReal(class="type">void)    const;
class="type">class="kw">double     SymbolVolumeLowReal(class="type">void)     const;
class="type">class="kw">double     SymbolOptionStrike(class="type">void)      const;
class="type">class="kw">double     SymbolTradeAccruedInterest(class="type">void) const;
class="type">class="kw">double     SymbolTradeFaceValue(class="type">void)    const;
class="type">class="kw">double     SymbolTradeLiquidityRate(class="type">void) const;
class="type">class="kw">double     SymbolMarginHedged(class="type">void)      const;
class=class="str">"cmt">//--- Get and class="kw">return class="type">class="kw">string properties of a selected symbol from its parameters
class="type">class="kw">string     SymbolBasis(class="type">void)             const;
class="type">class="kw">string     SymbolBank(class="type">void)              const;
class="type">class="kw">string     SymbolISIN(class="type">void)              const;
class="type">class="kw">string     SymbolFormula(class="type">void)           const;

「封装品种属性的读写与订单权限查询」

在自建品种类里,把 MT5 三类符号属性(整数、双精度、字符串)统一收口到 SetProperty / GetProperty 重载函数,外部调用时不用关心底层数组差异。整数属性直接按下标存进 m_long_prop,而双精度和字符串要先经 IndexProp 做映射再落库,这一层封装能减少 EA 里散落各处直接调 SymbolInfoInteger 的耦合。 下面的代码片段展示了类内声明与实现的关键部分,注意 Exist() 和 SymbolPage() 仅给了 const 声明,具体逻辑需在别处补全;DigitsCurrency() 则直接内联返回账户货币小数位,实盘里报价 1.08765 这类 5 位小数品种会返回 5。 SupportProperty 三个虚函数默认全返回 true,意味着派生类若遇到服务器不支持某属性(例如某些贵金属对不允许设 StopLimit),应重写返回 false 以免误发订单。外汇与贵金属杠杆高,品种属性随时可能因经纪商调整而变,跑之前务必在 MT5 品种规格里核对一遍。 订单权限注释块列出了 7 个布尔标志:市价、限价、止损、止损限价单的允许状态,以及 SL/TP 设置与反向单平仓权限。用 GetProperty 读出对应枚举后,下单前先判这几个 flag,能避开『贸易上下文忙』之外的『不允许该订单类型』报错。

MQL5 / C++
class="type">class="kw">string SymbolPage(class="type">void) const;
class=class="str">"cmt">//--- Return the number of decimal places of the account currency
class="type">int DigitsCurrency(class="type">void) const { class="kw">return this.m_digits_currency; }
class=class="str">"cmt">//--- Search for a symbol and class="kw">return the flag indicating its presence on the server
class="type">bool Exist(class="type">void) const;
class="kw">public:
class="kw">public:
class=class="str">"cmt">//--- Set(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string symbol properties
class="type">void SetProperty(ENUM_SYMBOL_PROP_INTEGER class="kw">property,class="type">long value) { this.m_long_prop[class="kw">property]=value; }
class="type">void SetProperty(ENUM_SYMBOL_PROP_DOUBLE class="kw">property,class="type">class="kw">double value) { this.m_double_prop[this.IndexProp(class="kw">property)]=value; }
class="type">void SetProperty(ENUM_SYMBOL_PROP_STRING class="kw">property,class="type">class="kw">string value) { this.m_string_prop[this.IndexProp(class="kw">property)]=value; }
class=class="str">"cmt">//--- Return(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string symbol properties from the properties array
class="type">long GetProperty(ENUM_SYMBOL_PROP_INTEGER class="kw">property) const { class="kw">return this.m_long_prop[class="kw">property]; }
class="type">class="kw">double GetProperty(ENUM_SYMBOL_PROP_DOUBLE class="kw">property) const { class="kw">return this.m_double_prop[this.IndexProp(class="kw">property)]; }
class="type">class="kw">string GetProperty(ENUM_SYMBOL_PROP_STRING class="kw">property) const { class="kw">return this.m_string_prop[this.IndexProp(class="kw">property)]; }
class=class="str">"cmt">//--- Return the flag of a symbol supporting the class="kw">property
class="kw">virtual class="type">bool SupportProperty(ENUM_SYMBOL_PROP_INTEGER class="kw">property) { class="kw">return true; }
class="kw">virtual class="type">bool SupportProperty(ENUM_SYMBOL_PROP_DOUBLE class="kw">property) { class="kw">return true; }
class="kw">virtual class="type">bool SupportProperty(ENUM_SYMBOL_PROP_STRING class="kw">property) { class="kw">return true; }
class=class="str">"cmt">//--- Return the flag of allowing(class="num">1) market, (class="num">2) limit, (class="num">3) stop(class="num">4) and stop limit orders,
class=class="str">"cmt">//--- the flag of allowing setting(class="num">5) StopLoss and(class="num">6) TakeProfit orders, (class="num">7) as well as closing by an opposite order

◍ 用位运算卡死品种的下单权限

在 MT5 自建品种封装类里,判断某交易品种允许哪些订单类型,靠的是对 OrderModeFlags() 返回值做按位与。下面这组方法把市场单、限价单、止损单、StopLimit、SL/TP 以及 Close By 全部拆成独立布尔接口,调用方不用关心底层枚举,直接问“能不能下”即可。 以 IsMarketOrdersAllowed 为例:this.OrderModeFlags() & SYMBOL_ORDER_MARKET 若等于 SYMBOL_ORDER_MARKET,说明该品种交易模式位域中包含市场单许可,返回 true;否则 broker 端可能禁了即时成交,EA 就该跳过市价开仓。 除订单类型外,填充策略与过期方式也用同样套路。IsFillingModeFOK / IsFillingModeIOC 读取 FillingModeFlags(),分别核对 SYMBOL_FILLING_FOK 与 SYMBOL_FILLING_IOC 位;IsExipirationModeGTC 则对 ExpirationModeFlags() 做 GTC 位校验。贵金属与外汇品种在高波动时段可能临时撤掉 IOC 填充,实盘前跑一遍这些判断能少踩拒单坑。

MQL5 / C++
class="type">bool IsMarketOrdersAllowed(class="type">void) const { class="kw">return((this.OrderModeFlags() & SYMBOL_ORDER_MARKET)==SYMBOL_ORDER_MARKET); }
class="type">bool IsLimitOrdersAllowed(class="type">void) const { class="kw">return((this.OrderModeFlags() & SYMBOL_ORDER_LIMIT)==SYMBOL_ORDER_LIMIT); }
class="type">bool IsStopOrdersAllowed(class="type">void) const { class="kw">return((this.OrderModeFlags() & SYMBOL_ORDER_STOP)==SYMBOL_ORDER_STOP); }
class="type">bool IsStopLimitOrdersAllowed(class="type">void) const { class="kw">return((this.OrderModeFlags() & SYMBOL_ORDER_STOP_LIMIT)==SYMBOL_ORDER_STOP_LIMIT); }
class="type">bool IsStopLossOrdersAllowed(class="type">void) const { class="kw">return((this.OrderModeFlags() & SYMBOL_ORDER_SL)==SYMBOL_ORDER_SL); }
class="type">bool IsTakeProfitOrdersAllowed(class="type">void) const { class="kw">return((this.OrderModeFlags() & SYMBOL_ORDER_TP)==SYMBOL_ORDER_TP); }
class="type">bool IsCloseByOrdersAllowed(class="type">void) const;
class=class="str">"cmt">//--- Return the(class="num">1) FOK and(class="num">2) IOC filling flag
class="type">bool IsFillingModeFOK(class="type">void) const { class="kw">return((this.FillingModeFlags() & SYMBOL_FILLING_FOK)==SYMBOL_FILLING_FOK); }
class="type">bool IsFillingModeIOC(class="type">void) const { class="kw">return((this.FillingModeFlags() & SYMBOL_FILLING_IOC)==SYMBOL_FILLING_IOC); }
class=class="str">"cmt">//--- Return the flag of order expiration: (class="num">1) GTC, (class="num">2) DAY, (class="num">3) Specified and(class="num">4) Specified Day
class="type">bool IsExipirationModeGTC(class="type">void) const { class="kw">return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_GTC)==SYMBOL_EXPIRATION_GTC); }

从到期标志位反推品种交易权限

在 MT5 的自定义品种类里,到期模式不是靠字符串判断,而是用位掩码做与运算。下面三个内联函数都只做一件事:取 ExpirationModeFlags() 的返回值,跟对应宏做按位与,相等即代表该模式被允许。 IsExipirationModeDAY 检查 SYMBOL_EXPIRATION_DAY 位,命中说明挂单允许到当日结束。IsExipirationModeSpecified 对应 SYMBOL_EXPIRATION_SPECIFIED,即允许指定具体到期时间。IsExipirationModeSpecifiedDay 对应 SYMBOL_EXPIRATION_SPECIFIED_DAY,允许指定某天的到期点。 紧跟着的一批 Get*Description 方法,把市场单、限价单、止损单、止损限价单、SL/TP、Close By、FOK/IOC 填充、以及 GTC/DAY/Specified/SpecDay 到期说明全部封装成字符串返回。实盘里你可以用这些接口在 EA 初始化时打印品种权限,省得下单才报『不支持该到期模式』。 外汇与贵金属杠杆高、跳空频繁,品种过期规则可能因经纪商而异,用前先在策略测试器选真实品种跑一遍权限输出。

MQL5 / C++
  class="type">bool                IsExipirationModeDAY(class="type">void)                const { class="kw">return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_DAY)==SYMBOL_EXPIRATION_DAY);    }
  class="type">bool                IsExipirationModeSpecified(class="type">void)            const { class="kw">return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_SPECIFIED)==SYMBOL_EXPIRATION_SPECIFIED);            }
  class="type">bool                IsExipirationModeSpecifiedDay(class="type">void)          const { class="kw">return((this.ExpirationModeFlags() & SYMBOL_EXPIRATION_SPECIFIED_DAY)==SYMBOL_EXPIRATION_SPECIFIED_DAY); }
class=class="str">"cmt">//--- Return the description of allowing(class="num">1) market, (class="num">2) limit, (class="num">3) stop and(class="num">4) stop limit orders,
class=class="str">"cmt">//--- the description of allowing(class="num">5) StopLoss and(class="num">6) TakeProfit orders, (class="num">7) as well as closing by an opposite order
  class="type">class="kw">string              GetMarketOrdersAllowedDescription(class="type">void)        const;
  class="type">class="kw">string              GetLimitOrdersAllowedDescription(class="type">void)         const;
  class="type">class="kw">string              GetStopOrdersAllowedDescription(class="type">void)          const;
  class="type">class="kw">string              GetStopLimitOrdersAllowedDescription(class="type">void)     const;
  class="type">class="kw">string              GetStopLossOrdersAllowedDescription(class="type">void)      const;
  class="type">class="kw">string              GetTakeProfitOrdersAllowedDescription(class="type">void)    const;
  class="type">class="kw">string              GetCloseByOrdersAllowedDescription(class="type">void)       const;
class=class="str">"cmt">//--- Return the description of allowing the filling type(class="num">1) FOK and(class="num">2) IOC, (class="num">3) as well as allowed order expiration modes
  class="type">class="kw">string              GetFillingModeFOKAllowedDescrioption(class="type">void)     const;
  class="type">class="kw">string              GetFillingModeIOCAllowedDescrioption(class="type">void)     const;
class=class="str">"cmt">//--- Return the description of order expiration: (class="num">1) GTC, (class="num">2) DAY, (class="num">3) Specified and(class="num">4) Specified Day
  class="type">class="kw">string              GetExpirationModeGTCDescription(class="type">void)           const;
  class="type">class="kw">string              GetExpirationModeDAYDescription(class="type">void)           const;
  class="type">class="kw">string              GetExpirationModeSpecifiedDescription(class="type">void)     const;
  class="type">class="kw">string              GetExpirationModeSpecDayDescription(class="type">void)       const;

「把交易品种属性读成可读文本」

在封装交易品种对象时,把一堆枚举和标志位直接打印出来基本没法看。上面这组 Get*Description 方法就是干转化的活:从止损止盈有效期、图表模式、计算模式,到交易模式、成交执行、掉期计算,再到 GTC 订单、期权类型与权利,以及订单类型/填充类型/过期模式三组允许标志,全部映射成 string 返回。 具体看几个典型声明:GetTradeModeDescription 读交易模式,GetFillingModeFlagsDescription 把允许的成交填充标志展开成文字,GetExpirationModeFlagsDescription 同理处理过期方式。你在 EA 调试面板里想一眼看清当前品种能怎么下单,调这些比查 ENUM 值直观得多。 两个刷新入口也在这段里:Refresh 更新品种所有可能变动的数据,RefreshRates 只刷报价。实盘跑 MT5 时若发现挂单属性没跟着市场状态变,先确认是不是漏了 Refresh 调用。 GetError 直接回传 m_global_error 这个内部错误码,是个内联实现。外汇与贵金属波动剧烈、杠杆风险高,任何属性读取异常都可能让下单逻辑偏航,报错码要当场捕获别拖到下一根 K 线。

MQL5 / C++
  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;
  class="type">class="kw">string              GetExpirationModeFlagsDescription(class="type">void)    const;

class=class="str">"cmt">//--- Return the global error code
  class="type">int                 GetError(class="type">void)                             const { class="kw">return this.m_global_error; }
class=class="str">"cmt">//--- Update all symbol data that can change
  class="type">void                Refresh(class="type">void);
class=class="str">"cmt">//--- Update quote data by a symbol
  class="type">void                RefreshRates(class="type">void);

◍ 把交易品种塞进观察窗口与挂接深度

在 MT5 的自定义品种封装里,观察窗口的增删和行情同步状态是靠几个轻量方法直接调底层 API 完成的。下面这段声明展示了如何用 SymbolSelect 把品种加进或移出市场观察列表,并用 SymbolIsSynchronized 判断该品种的历史与实时报价是否已对齐。 bool SetToMarketWatch(void) const { return ::SymbolSelect(this.m_symbol_name,true); } bool RemoveFromMarketWatch(void) const { return ::SymbolSelect(this.m_symbol_name,false); } bool IsSynchronized(void) const { return ::SymbolIsSynchronized(this.m_symbol_name); } 逐行看:第一行定义常成员函数,返回布尔,调用全局 SymbolSelect 并传 true,等于在终端里右键「显示」该品种;第二行同样结构但传 false,作用是从观察列表移除;第三行调 SymbolIsSynchronized,若返回 true 说明该品种数据已和服务器同步,做回测或实时下单前最好先确认这一点。 深度行情的订阅则拆成两个未在此展开实现的方法:BookAdd 用于向服务器申请该品种的市场深度(DOM)推送,BookClose 负责断开。实盘外汇与贵金属点差跳变快,订阅前先 IsSynchronized 再 BookAdd,可能避免拿到半截簿记数据。 后续还有一组简化属性读取器,例如 Status 取 SYMBOL_PROP_STATUS 整数、IsCustom 判断是否为自定义品种、ColorBackground 取背景色。这些直接走 GetProperty,省去每次拼枚举名的麻烦,复制进你的 EA 头文件就能用。

MQL5 / C++
class="type">bool SetToMarketWatch(class="type">void) const { class="kw">return ::SymbolSelect(this.m_symbol_name,true); }
class="type">bool RemoveFromMarketWatch(class="type">void) const { class="kw">return ::SymbolSelect(this.m_symbol_name,false); }
class="type">bool IsSynchronized(class="type">void) const { class="kw">return ::SymbolIsSynchronized(this.m_symbol_name); }
class="type">bool BookAdd(class="type">void) const;
class="type">bool BookClose(class="type">void) const;
class="type">long Status(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_STATUS); }
class="type">bool IsCustom(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(SYMBOL_PROP_CUSTOM); }
class="type">color ColorBackground(class="type">void) const { class="kw">return (class="type">color)this.GetProperty(SYMBOL_PROP_BACKGROUND_COLOR); }

品种状态与成交量的接口抽取

在 MT5 的 CSymbolInfo 封装里,有一组轻量 getter 专门暴露品种的运行态和实时流动性。它们全部走 GetProperty 统一入口,返回类型按属性强转,调用零副作用,适合在 EA 的 OnTick 里高频轮询。 ChartMode 返回 ENUM_SYMBOL_CHART_MODE,能判断该品种当前是蜡烛、线形还是砖形视图;IsExist / IsSelect / IsVisible 三个布尔量分别对应品种是否在终端存在、是否被选中、是否在市场报价可见。若 IsExist 返回 false,后续取点差或报价都可能拿到脏值。 SessionDeals 给出当日已成交总笔数,SessionBuyOrders 与 SessionSellOrders 拆分多空挂单量,这三个 long 值可用于感知日内资金偏度——比如买挂单远超卖挂单时,短线上行概率可能略占优,但外汇和贵金属杠杆高,反向扫单也常见。 Volume 取品种总成交量,VolumeHigh 取历史峰值成交量。把两者相除能得到「成交量分位」,当分位突破 0.8 往往伴随波动放大。下面这段是原文接口声明,可直接粘进你的类定义验证。

MQL5 / C++
  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              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);                              }
  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);                                        }

「品种属性读取的几个底层接口」

在 MT5 的自定品种类封装里,这一组方法直接走 GetProperty 拿内核里的 SYMBOL_PROP_* 常量,省掉了每次调 SymbolInfoInteger / SymbolInfoDouble 的字符串查找开销。 long VolumeLow(void) const 返回 SYMBOL_PROP_VOLUMELOW,即该品种单笔成交的最小成交量限制;datetime Time(void) const 返回 SYMBOL_PROP_TIME,为最近一次报价的时间戳。 int Digits(void) const 给出报价的的小数位数,比如欧美常见为 5;DigitsLot(void) const 返回 SYMBOL_PROP_DIGITS_LOTS,是手数精度(多为 2 位)。Spread(void) const 拿到的点差是以点数(point)为单位的整数,IsSpreadFloat(void) const 则告诉你点差是浮动还是固定——浮动时返回 true。 TicksBookdepth(void) const 返回订单簿深度上限,决定了 Market Book 事件能取几层。TradeCalcMode 与 TradeMode 分别映射 SYMBOL_PROP_TRADE_CALC_MODE 和 SYMBOL_PROP_TRADE_MODE 的枚举,用来判断该品种是按外汇、期货还是现货规则计算保证金与成交。 开 MT5 新建一个 CSymbolInfo 派生类,把上面这些方法贴进去编译,用 Print(Spread()) 在 EURUSD 上跑一下,就能看到实时点差数值,验证封装是否生效。外汇与贵金属杠杆高,点差和报价精度会随流动性突变,验证时务必用模拟盘。

MQL5 / C++
class="type">long VolumeLow(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_VOLUMELOW); }
class="type">class="kw">datetime Time(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(SYMBOL_PROP_TIME); }
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">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); }
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); }

◍ 从品种属性里抠出交易约束

在 MT5 的 CSymbol 封装里,一组 getter 直接把经纪商对当前品种的交易限制暴露出来,不用再调一堆 SymbolInfoInteger 去猜。下面这段就是类里对应的几个方法原型,全走 GetProperty 拿底层属性。 [CODE]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); } ENUM_SYMBOL_SWAP_MODE SwapMode(void) const { return (ENUM_SYMBOL_SWAP_MODE)this.GetProperty(SYMBOL_PROP_SWAP_MODE); } ENUM_DAY_OF_WEEK SwapRollover3Days(void) const { return (ENUM_DAY_OF_WEEK)this.GetProperty(SYMBOL_PROP_SWAP_ROLLOVER3DAYS); } bool IsMarginHedgedUseLeg(void) const { return (bool)this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED_USE_LEG); } int ExpirationModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_EXPIRATION_MODE); } int FillingModeFlags(void) const { return (int)this.GetProperty(SYMBOL_PROP_FILLING_MODE); }[/CODE] 逐行看:StartTime 和 ExpirationTime 拿的是合约交易时段起止,过期合约会返回 0;TradeStopLevel 返回止损挂单距市价最小点数,实盘常见 0~50 点;TradeFreezeLevel 是成交后冻结修改的价位,通常比 StopLevel 更紧。 TradeExecutionMode 区分市价成交 / 交易所式 / 询价,直接决定你下单用哪种 type;FillingModeFlags 用位掩码告诉你允许 IOC 还是 FOK 填充。SwapRollover3Days 返回周三还是周五滚三倍息,做贵金属隔夜仓前得先确认这个值。 外汇和贵金属杠杆高、规则随经纪商变,这些属性在切换品种时可能突变。开 MT5 按 F4 把上面方法塞进自己的 symbol 包装类,跑 EU 和 XAUUSD 对比两组返回值,就能看出平台暗藏的限制差异。

MQL5 / C++
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); }
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); }

从订单模式到买卖报价的属性读取

在 MT5 自定义品种类里,订单与期权相关的模式都通过 GetProperty 映射成枚举返回。OrderModeFlags 拿整型标记位,OrderModeGTC 返回挂单的 GTC 模式,OptionMode 与 OptionRight 分别给出期权类型和买卖权方向,这几行都属于轻量封装,调用成本极低。 真实行情侧属性才是盯盘脚本最常用的:Bid、Ask 直接取最新买卖价,BidHigh / BidLow / AskHigh 则对应当日(或会话内)的买卖极值。测试时若发现 BidHigh 恒等于 Bid,多半是品种没接历史会话数据,而非代码错误。 外汇与贵金属杠杆高,BidLow 与 AskHigh 的跨度在数据稀疏时段可能瞬时放大,用这些属性做区间触发要预留滑点缓冲。直接把下面片段塞进你的 CSymbol 派生类,编译后 Print(Bid(), Ask()) 就能在终端看到实时值。

MQL5 / C++
  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);                                      }
  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);                                  }

「报价极值与真实成交量的取数接口」

在 MT5 的自定义品种类封装里,这一组方法把卖价区间、最新成交价和真实成交量直接映射到 SYMBOL_PROP_* 枚举上,调用方无需关心底层数据源是实时行情还是历史重放。 AskLow() 返回当前卖价区间的最低值,Last() 返回最近一笔成交价,LastHigh() / LastLow() 则框出最近成交价的上下沿;它们都通过 this.GetProperty() 统一取属性,避免在策略里写散落的市场信息读取逻辑。 VolumeReal() 拿到的是真实 tick 成交量,而 VolumeHighReal() / VolumeLowReal() 给出该品种真实成交量在统计窗口内的高点和低点。外汇与贵金属市场杠杆高、滑点随机,真实成交量仅作流动性参考,不能单独作为方向确认。 OptionStrike() 取的是期权行权价,仅对期权类工具有效;做现货黄金或直盘货币对时它通常返回 0。把下面这段代码贴进你的类定义,编译后可在专家顾问里直接读这些字段验证。

MQL5 / C++
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); }
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); }

◍ 从报价点差到合约面值的取数接口

在 MT5 的 CSymbol 类封装里,这一组 const 方法把品种交易属性直接透传出来,底层都走 GetProperty 带不同枚举。做仓位换算或风控脚本时,与其自己硬写宏,不如直接调这些接口拿实时值。 Point() 返回 SYMBOL_PROP_POINT,也就是价格最小变动单位;TradeTickSize() 返回 SYMBOL_PROP_TRADE_TICK_SIZE,两者在多数外汇品种上相等,但贵金属或债券类可能不同,混淆会导致止损距离算错。 TradeTickValue()、TradeTickValueProfit()、TradeTickValueLoss() 分别对应单跳盈亏金额、盈利跳值、亏损跳值,经纪商对双向点值可能给不同数,尤其交叉盘要分别取。TradeContractSize() 是每手合约规模,外汇常规 100000,但 XAUUSD 常是 100 盎司,写死常数会爆仓。 下面这段是原文接口声明,逐行看就是每个方法返回 this.GetProperty(对应枚举): Point 取最小价变单位;TradeTickValue 取标准跳值;TradeTickValueProfit/Loss 分取双向跳值;TradeTickSize 取跳距;TradeContractSize 取合约倍数;TradeAccuredInterest 取应计利息(债券类有用);TradeFaceValue 取面值;TradeLiquidityRate 取流动性率。开 MT5 按 F4 搜 CSymbol 就能对照验证。

MQL5 / C++
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); }
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); }

手数边界与持仓成本的几个取数口

写 EA 时最容易被忽略的一步,是下单前没核对品种允许的最小手数、最大手数和步长。MT5 里这些不是写死的,而是跟着品种属性走,黄金 XAUUSD 的 VOLUME_MIN 常见为 0.01,VOLUME_STEP 也多为 0.01,但部分券商的稀有交叉盘步长可能是 0.1,直接拿 0.01 去 NormalizeDouble 会报无效手数。 下面这组封装把符号属性一次性暴露成类方法,全走 GetProperty 拿实时值,比硬编码安全。 double LotsMin(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MIN); } // 返回该品种允许的最小交易量,例如 0.01 手 double LotsMax(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_MAX); } // 返回最大交易量,部分品种可到几百手,需注意流动性 double LotsStep(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_STEP); } // 返回手数步长,下单手数必须是 Min + N*Step double VolumeLimit(void) const { return this.GetProperty(SYMBOL_PROP_VOLUME_LIMIT); } // 返回单方向持仓总量上限,0 表示无限制 double SwapLong(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_LONG); } // 返回多单隔夜 swap,负值代表扣费 double SwapShort(void) const { return this.GetProperty(SYMBOL_PROP_SWAP_SHORT); } // 返回空单隔夜 swap,贵金属常为空头正息 double MarginInitial(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_INITIAL); } // 开仓每手初始保证金占用 double MarginMaintenance(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_MAINTENANCE); } // 持仓维持保证金,通常低于 initial 实盘前建议 Print 一下这几个值,尤其 Swap 和 Margin,外汇与贵金属杠杆波动大、高风险,券商可能在非农前调保证金,用实时属性才不会在半夜爆仓提示里翻车。

MQL5 / C++
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); }
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); }

「从品种类里直接抠保证金与时段成交」

在 MT5 自建的品种封装类里,有一组只读方法专门暴露当前合约的保证金占用与盘中会话统计。它们全部走同一个内部接口 GetProperty,只是传入的枚举不同,因此返回的都是实时 double 值,不需要自己再算杠杆换算。 MarginLong 和 MarginShort 分别返回开 1 手多单、空单所需的初始保证金;MarginStop、MarginLimit、MarginStopLimit 对应三种挂单类型的保证金占用。同一品种在 brokers 调整杠杆时,这几个值会动态变,回测和实盘可能差出 20%~30%。 SessionVolume 给出当前会话的总成交量,SessionTurnover 是成交额,SessionInterest 为未平仓兴趣。SessionBuyOrdersVolume 只统计买方挂单量,用来感知盘口买压倾向比看总成交量更敏感。 外汇和贵金属保证金随杠杆与行情波动,属于高风险参数,实盘前务必在 MT5 符号规格里核对这几个属性返回的数值。

MQL5 / C++
class="type">class="kw">double MarginLong(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_LONG); }
class="type">class="kw">double MarginShort(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_SHORT); }
class="type">class="kw">double MarginStop(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_STOP); }
class="type">class="kw">double MarginLimit(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_LIMIT); }
class="type">class="kw">double MarginStopLimit(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_MARGIN_STOPLIMIT); }
class="type">class="kw">double SessionVolume(class="type">void) const { class="kw">return this.GetProperty(SYMBOL_PROP_SESSION_VOLUME); }
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); }

◍ 读取交易时段与保证金的内联取值器

在 MQL5 的 CSymbol 类封装里,有一组轻量内联方法专门暴露交易时段和保证金相关的符号属性,全部通过 GetProperty 走统一接口,调用开销极低。 下面的代码逐行对应:SessionSellOrdersVolume 返回当前时段卖单总成交量;SessionOpen / SessionClose 给出该时段的起止价(部分品种为 0);SessionAW 是加权平均价;SessionPriceSettlement 为结算价;SessionPriceLimitMin / Max 是时段涨跌限制边界。 double SessionSellOrdersVolume(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME); } double SessionOpen(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_OPEN); } double SessionClose(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_CLOSE); } double SessionAW(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_AW); } double SessionPriceSettlement(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT); } double SessionPriceLimitMin(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN); } double SessionPriceLimitMax(void) const { return this.GetProperty(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX); } double MarginHedged(void) const { return this.GetProperty(SYMBOL_PROP_MARGIN_HEDGED); } double NormalizedPrice(const double price) const; //--- String properties string Name(void) const { return this.GetProperty(SYMBOL_PROP_NAME); } MarginHedged 返回对冲单占用保证金比例(常见经纪商设为 0.5 即半额),Name 则返回品种全名如 "EURUSD"。 开 MT5 按 F4 建个 EA 骨架,直接 Print(SymbolInfo().SessionPriceLimitMax()) 就能看到当前品种时段上限价,外汇与贵金属杠杆高、时段边界可能瞬间触发熔断,验证时务必用模拟盘。

MQL5 / C++
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 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 NormalizedPrice(const class="type">class="kw">double price) 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 的自定义品种封装类里,有一组直接返回字符串的只读方法,它们都走同一个 GetProperty 入口,只是传入的枚举不同。 Basis() 返回 SYMBOL_PROP_BASIS,即标的的基础资产类别描述;CurrencyBase()、CurrencyProfit()、CurrencyMargin() 分别取基础货币、盈亏结算货币、保证金货币,这三个在交叉盘上经常不一致,比如 EURGBP 的 CurrencyBase 是 EUR,CurrencyProfit 是 GBP,CurrencyMargin 也可能是 GBP。 Bank() 取发行机构,Description() 是品种描述,Formula() 给出定价公式,ISIN() 则是国际证券识别码。写 EA 时若要做多币种账户的统一风控,这几个字段比自己硬编码货币对后缀可靠。 外汇与贵金属杠杆高、点差跳变频繁,用代码读属性比肉眼看规格书更不容易漏掉经纪商自定义值,建议开 MT5 新建一个自定义品种把这几个方法逐行打印验证。

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

「符号对象构造时的校验与初始化」

在封装交易品种类时,构造函数最先做的是名称落库与存在性校验:若服务器查无此符号,立即打印双语报错并将全局错误码置为 ERR_MARKET_UNKNOWN_SYMBOL。 随后调用 SymbolInfoTick 抓取实时 tick,失败则通过 GetLastError 记录错误码并输出当前价获取失败提示;无论成败都执行 ResetLastError 清场。 数据初始化段用 ZeroMemory 清空 m_tick 结构体,再走 this.Reset() 重置内部状态;账户货币精度按 MQL5 环境取 ACCOUNT_CURRENCY_DIGITS(旧环境硬编码 2),最后把传入的 symbol_status 写入 m_long_prop[SYMBOL_PROP_STATUS]。 开 MT5 把下面片段塞进 CSymbol 构造体,断点跟 m_global_error 就能看到品种不存在时错误码立刻非 0,比靠返回值排错直观得多。外汇与贵金属杠杆高,品种状态异常时下单可能瞬间放大亏损,验证代码仅用于本地调试。

MQL5 / C++
CSymbol::CSymbol(ENUM_SYMBOL_STATUS symbol_status,const class="type">class="kw">string name) : m_global_error(ERR_SUCCESS)
  {
   this.m_symbol_name=name;
   if(!this.Exist())
     {
      ::Print(DFUN_ERR_LINE,"\",this.m_symbol_name,\"",": ",TextByLanguage("Ошибка. Такого символа нет на сервере","Error. No such symbol on server"));
      this.m_global_error=ERR_MARKET_UNKNOWN_SYMBOL;
     }
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,"\",this.m_symbol_name,"\": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
     }
class=class="str">"cmt">//--- Data initialization
   ::ZeroMemory(this.m_tick);
   this.Reset();
   this.m_digits_currency=(class="macro">#ifdef __MQL5__(class="type">int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) class="macro">#else class="num">2 class="macro">#endif);
class=class="str">"cmt">//--- Save integer properties
   this.m_long_prop[SYMBOL_PROP_STATUS] = symbol_status;
  }

◍ 把 tick 和品种属性塞进长整型数组

这段初始化逻辑把一个品种在实时 tick 下的关键整型属性,统一写进 m_long_prop[] 数组,方便后续直接用下标读取,不必反复调 SymbolInfoInteger。 第一行把当前 tick 的成交量直接转成 long 存进 SYMBOL_PROP_VOLUME,属于直接从行情推过来的量。时间字段则做了 MQL5 / MQL4 的预处理分支:MQL5 取 time_msc 毫秒精度,MQL4 用 time*1000 补齐,保证两个环境时间轴一致。 剩下十来个属性全靠 SymbolInfoInteger 向内核要:从图表是否选中(SYMBOL_SELECT)、是否可见,到当日 session 内的成交数、买挂单、卖挂单,再到成交量高低、报价小数位、点差及是否浮动点差、深度行情簿层数(SYMBOL_TICKS_BOOKDEPTH)。 开 MT5 把这段贴进你的 CSymbol 类刷新函数,在 EURUSD 上跑一轮,打印 m_long_prop[SYMBOL_SPREAD]SYMBOL_TICKS_BOOKDEPTH,就能看到当前点差与盘口深度这两个实战最常盯的数字已就位。外汇与贵金属杠杆高,点差跳变可能瞬间吃掉窄止损,参数验证请在模拟盘先跑。

MQL5 / C++
  this.m_long_prop[SYMBOL_PROP_VOLUME]                                                   = (class="type">long)this.m_tick.volume;
  this.m_long_prop[SYMBOL_PROP_TIME]                                                       = class="macro">#ifdef __MQL5__ this.m_tick.time_msc class="macro">#else this.m_tick.time*class="num">1000 class="macro">#endif ;
  this.m_long_prop[SYMBOL_PROP_SELECT]                                                     = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SELECT);
  this.m_long_prop[SYMBOL_PROP_VISIBLE]                                                    = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VISIBLE);
  this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_DEALS);
  this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]                                         = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS);
  this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]                                        = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS);
  this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                                                 = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMEHIGH);
  this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                                                  = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMELOW);
  this.m_long_prop[SYMBOL_PROP_DIGITS]                                                     = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_DIGITS);
  this.m_long_prop[SYMBOL_PROP_SPREAD]                                                     = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
  this.m_long_prop[SYMBOL_PROP_SPREAD_FLOAT]                                               = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD_FLOAT);
  this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                                            = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TICKS_BOOKDEPTH);

一次性抓全交易品种的整数属性

在封装交易品种类时,把整数型属性集中灌进 m_long_prop 数组,能避免后续反复调用 SymbolInfoInteger 造成的开销。下面这段初始化把 MT5 里和交易规则相关的关键长整型字段一次性读入内存。 SYMBOL_TRADE_STOPS_LEVEL 返回止损挂单距市价的最小点数,实盘里外汇与贵金属常落在 0~50 点区间,破了这个限制下单会被拒。SYMBOL_TRADE_FREEZE_LEVEL 则是价格冻结区,持仓状态下改单不能进该范围。 SYMBOL_SWAP_ROLLOVER3DAYS 指明周几滚隔夜利息(1=周一…7=周日),黄金通常返回 3 表示周三滚三倍。把这些值本地化后,策略在判断下单合法性时直接读数组,速度比每次问终端快一个量级。

MQL5 / C++
  this.m_long_prop[SYMBOL_PROP_TRADE_MODE]                = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_MODE);
  this.m_long_prop[SYMBOL_PROP_START_TIME]                = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_START_TIME);
  this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_TIME);
  this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]         = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
  this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]        = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
  this.m_long_prop[SYMBOL_PROP_TRADE_EXEMODE]             = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_EXEMODE);
  this.m_long_prop[SYMBOL_PROP_SWAP_ROLLOVER3DAYS]        = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SWAP_ROLLOVER3DAYS);
  this.m_long_prop[SYMBOL_PROP_EXPIRATION_MODE]           = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_MODE);
  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_ORDER_GTC_MODE]            = this.SymbolOrderGTCMode();
  this.m_long_prop[SYMBOL_PROP_OPTION_MODE]               = this.SymbolOptionMode();

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

在封装品种对象时,构造函数里通常会把整组静态与实时属性先写进内部数组,避免后续反复调用终端接口。下面这段把期权方向、背景色、图表模式、计算模式、掉期模式、到期模式等 long 类参数,通过各自成员函数赋值到 m_long_prop 对应下标。 紧接着用 SymbolInfoDouble 直接拉取真实浮点属性:ASKHIGH / ASKLOW / LASTHIGH / LASTLOW 这四类记录了品种近期询价与成交的极值,POINT 决定最小报价单位,而 TRADE_TICK_VALUE 系列和 CONTRACT_SIZE 则关系到每跳盈亏换算。外汇与贵金属杠杆高,合约参数读错可能让仓位计算直接偏一倍。 代码逐行看,前半段是对象自身方法取 long 属性,后半段用全局域符号 ::SymbolInfoDouble 按名称查 double 属性并落到 m_double_prop。开 MT5 新建 EA 把这段贴进品种类构造,编译后能在观测窗口看到 XAUUSD 的 POINT 通常是 0.01、CONTRACT_SIZE 为 100。

MQL5 / C++
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_EXPIRATION_MODE]                                        = this.SymbolExpirationMode();
class=class="str">"cmt">//--- Save real properties
this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                              = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                            = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_POINT)]                               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_POINT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]                    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]                      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_SIZE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_CONTRACT_SIZE);

◍ 把品种交易参数一次性塞进数组

在封装交易品种类时,把双击类型的属性集中拉取并落进 m_double_prop 数组,是后续随时读取而不重复调用终端接口的关键一步。下面这段代码覆盖了从最小成交量到持仓利息的全部 double 类字段。 [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_VOLUME_MIN)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_VOLUME_MIN); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_VOLUME_MAX)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_VOLUME_MAX); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_VOLUME_STEP)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_VOLUME_STEP); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_VOLUME_LIMIT)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_VOLUME_LIMIT); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_SWAP_LONG)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_SWAP_LONG); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_SWAP_SHORT)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_SWAP_SHORT); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_MARGIN_INITIAL)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_MARGIN_INITIAL); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_MARGIN_MAINTENANCE); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_MARGIN_LONG)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_MARGIN_LONG); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_MARGIN_SHORT)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_MARGIN_SHORT); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_MARGIN_STOP)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_MARGIN_STOP); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_MARGIN_LIMIT)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_MARGIN_LIMIT); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_MARGIN_STOPLIMIT)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_MARGIN_STOPLIMIT); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_SESSION_VOLUME)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_SESSION_VOLUME); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_SESSION_TURNOVER)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_SESSION_TURNOVER); [this].m_double_prop[[this].IndexProp(SYMBOL_PROP_SESSION_INTEREST)] = ::SymbolInfoDouble([this].m_symbol_name,SYMBOL_SESSION_INTEREST); 逐行拆解:每行左侧用 this.IndexProp(宏) 把枚举转成数组下标,右侧 ::SymbolInfoDouble 走全局命名空间直接问 MT5 内核要值,再写回本类成员变量。VOLUME_MIN / MAX / STEP 决定下单手数边界,SWAP_LONG / SHORT 是隔夜利息,MARGIN_* 系列影响开仓占用与强平线,SESSION_* 三个反映当前交易时段活跃度。 开 MT5 接个 EA 模板跑这段,Print 出 XAUUSD 的 VOLUME_STEP 通常是 0.01,SWAP 随经纪商每天变;外汇与贵金属杠杆高,参数读错可能让仓位计算偏出好几倍风险。

MQL5 / C++
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MIN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MAX);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_STEP);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_LIMIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_LONG);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_SHORT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_INITIAL);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_MAINTENANCE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_LONG);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_SHORT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_STOP)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_STOP);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LIMIT)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_LIMIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_STOPLIMIT)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_STOPLIMIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_VOLUME);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_TURNOVER);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_INTEREST);

把交易时段与盘口字段一次性灌进属性数组

这段初始化逻辑干的事很直接:把 MT5 当前品种的交易会话指标和实时盘口数据,批量写进类内部的 double 类型属性容器 m_double_prop。前面 8 行走 SymbolInfoDouble 向终端要数据,后面从 m_tick 和类方法里取已经算好的值,避免重复调用 API。 逐行看,SYMBOL_SESSION_BUY_ORDERS_VOLUME 与 SYMBOL_SESSION_SELL_ORDERS_VOLUME 拿的是当前会话里多空挂单的总成交量,SESSION_OPEN / CLOSE 是会话起止价,SESSION_AW 是加权平均价,SETTLEMENT 是结算价,PRICE_LIMIT_MIN / MAX 是当日限价边界。这些字段在黄金和外汇品种上返回的是点值或手数,具体精度取决于品种设置,读取前最好先确认该品种是否提供对应属性。 盘口部分没再调 SymbolInfoDouble:bid / ask / last 直接取自已缓存的 m_tick 结构体,BidHigh / BidLow 和 Volume 系列则调 SymbolBidHigh() 等封装方法。这样做的好处是,如果上一帧 tick 已经刷新过,就不必为每个属性再走一遍终端查询,回测或实盘里能少掉一些不必要的开销。 外汇和贵金属杠杆高、跳空频繁,m_tick 里的 bid/ask 在极端行情可能滞后于真实成交价,用这套缓存做决策时仍要留缓冲。

MQL5 / C++
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_OPEN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]              = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_CLOSE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_AW);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
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();

「把品种属性一次性灌进内存结构」

在 CSymbol 的初始化收尾阶段,会把流动性比率、对冲保证金这类双精度属性先写进 m_double_prop 数组,键位由 IndexProp() 按 SYMBOL_PROP_TRADE_LIQUIDITY_RATE、SYMBOL_PROP_MARGIN_HEDGED 映射。外汇与贵金属的保证金参数随经纪商规则浮动,开 MT5 用 SymbolInfoInteger/SymbolInfoDouble 核对实际值,能避免对象内缓存与实时行情脱节。 字符串属性则集中通过 SymbolInfoString 抓取,基础货币、利润货币、保证金货币、描述、路径等分别落到 m_string_prop 对应索引。注意 m_symbol_name 是类内直存,不绕 SymbolInfoString,省一次 API 调用。 最后把 lot 小数位 SYMBOL_PROP_DIGITS_LOTS 写进 m_long_prop,结束属性装载。Compare() 方法随后以 mode 为下标,直接取两个 CSymbol 实例的 GetProperty 做整数比较,mode 小于 SYMBOL_PROP_INTEGER_TOTAL 时只比整型字段,可用来快速判重或排序。

MQL5 / C++
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();
class=class="str">"cmt">//--- Save class="type">class="kw">string properties
this.m_string_prop[this.IndexProp(SYMBOL_PROP_NAME)]                        = this.m_symbol_name;
this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_BASE)]               = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_BASE);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_PROFIT)]             = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_PROFIT);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_CURRENCY_MARGIN)]             = ::SymbolInfoString(this.m_symbol_name,SYMBOL_CURRENCY_MARGIN);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_DESCRIPTION)]                = ::SymbolInfoString(this.m_symbol_name,SYMBOL_DESCRIPTION);
this.m_string_prop[this.IndexProp(SYMBOL_PROP_PATH)]                       = ::SymbolInfoString(this.m_symbol_name,SYMBOL_PATH);
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();
class=class="str">"cmt">//--- Save additional integer properties
this.m_long_prop[SYMBOL_PROP_DIGITS_LOTS]                                  = this.SymbolDigitsLot();
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//|Compare CSymbol objects by all possible properties                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int CSymbol::Compare(const CObject *node,const class="type">int mode=class="num">0) const
  {
  const CSymbol *symbol_compared=node;
class=class="str">"cmt">//--- compare integer properties of two symbols
  if(mode<SYMBOL_PROP_INTEGER_TOTAL)
    {
    class="type">long value_compared=symbol_compared.GetProperty((ENUM_SYMBOL_PROP_INTEGER)mode);
    class="type">long value_current=this.GetProperty((ENUM_SYMBOL_PROP_INTEGER)mode);

◍ 按属性类别比对两个交易品种对象

CSymbol 类的比较逻辑按属性枚举分三段走:整型、双精度、字符串。每段先用 mode 落在哪个区间判断该比哪类属性,再取两个对象的对应属性值做大于、小于、等于的三元判断,返回 1 / -1 / 0。 IsEqual 方法则反过来:遍历全部整数属性(0 到 SYMBOL_PROP_INTEGER_TOTAL)、双精度属性、字符串属性,只要有一项不等就直接 return false,全过才 return true。这意味着两个品种对象要完全同参才算相等,实战中用来剔除重复订阅的品种很省事。 Reset 方法把三类属性全刷成 0(字符串类循环体在原文截断前未闭合,但意图明确:批量清零)。外汇与贵金属品种属性受点差、报价精度影响,重置前先确认无持仓逻辑依赖该对象,这类操作属于高风险环境下的底层维护。 把下面这段代码贴进 MT5 的 CSymbol 实现里,能直接看清比较分支的落点。

MQL5 / C++
   class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0);
   }
class=class="str">"cmt">//--- compare real properties of two symbols
   else if(mode<SYMBOL_PROP_INTEGER_TOTAL+SYMBOL_PROP_DOUBLE_TOTAL)
   {
      class="type">class="kw">double value_compared=symbol_compared.GetProperty((ENUM_SYMBOL_PROP_DOUBLE)mode);
      class="type">class="kw">double value_current=this.GetProperty((ENUM_SYMBOL_PROP_DOUBLE)mode);
      class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0);
   }
class=class="str">"cmt">//--- compare class="type">class="kw">string properties of two symbols
   else if(mode<SYMBOL_PROP_INTEGER_TOTAL+SYMBOL_PROP_DOUBLE_TOTAL+SYMBOL_PROP_STRING_TOTAL)
   {
      class="type">class="kw">string value_compared=symbol_compared.GetProperty((ENUM_SYMBOL_PROP_STRING)mode);
      class="type">class="kw">string value_current=this.GetProperty((ENUM_SYMBOL_PROP_STRING)mode);
      class="kw">return(value_current>value_compared ? class="num">1 : value_current<value_compared ? -class="num">1 : class="num">0);
   }
   class="kw">return class="num">0;
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Compare CSymbol objects by all properties                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">bool CSymbol::IsEqual(CSymbol *compared_symbol) const
  {
   class="type">int beg=class="num">0, end=SYMBOL_PROP_INTEGER_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_SYMBOL_PROP_INTEGER prop=(ENUM_SYMBOL_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_symbol.GetProperty(prop)) class="kw">return false;
     }
   beg=end; end+=SYMBOL_PROP_DOUBLE_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_SYMBOL_PROP_DOUBLE prop=(ENUM_SYMBOL_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_symbol.GetProperty(prop)) class="kw">return false;
     }
   beg=end; end+=SYMBOL_PROP_STRING_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_SYMBOL_PROP_STRING prop=(ENUM_SYMBOL_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_symbol.GetProperty(prop)) class="kw">return false;
     }
   class="kw">return true;
   }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Reset all symbol object data                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CSymbol::Reset(class="type">void)
  {
   class="type">int beg=class="num">0, end=SYMBOL_PROP_INTEGER_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_SYMBOL_PROP_INTEGER prop=(ENUM_SYMBOL_PROP_INTEGER)i;
      this.SetProperty(prop,class="num">0);
     }
   beg=end; end+=SYMBOL_PROP_DOUBLE_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {
      ENUM_SYMBOL_PROP_DOUBLE prop=(ENUM_SYMBOL_PROP_DOUBLE)i;
      this.SetProperty(prop,class="num">0);
     }
   beg=end; end+=SYMBOL_PROP_STRING_TOTAL;
   for(class="type">int i=beg; i<end; i++)
     {

从封装类看品种整数属性的取数路径

在 MT5 的 CSymbol 封装里,整数类品种属性被拆成一堆极短的成员函数,每个函数只干一件事:把宏开关和 SymbolInfoInteger 调用绑死。 例如判断品种是否真实存在,MQL5 下直接走 ::SymbolInfoInteger(m_symbol_name, SYMBOL_EXIST),而 MQL4 分支退化成 this.Exist()。这种写法让上层调用不用关心运行环境,但代价是每加一个属性就要复制一遍#ifdef __MQL5__骨架。 SYMBOL_FILLING_MODE 返回的是订单成交策略的位标志,MQL4 下硬编码为 0,意味着老环境拿不到 IOC/FOK 这类细分策略,回测里可能倾向忽略成交限制。 别把宏分支当冗余 很多新手改这类类时会顺手删掉 #else 分支,结果在 MQL4 编译环境直接报未定义。保留双分支不是啰嗦,是跨版本跑通的底线。

MQL5 / C++
class="type">long CSymbol::SymbolExists(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXIST) class="macro">#else this.Exist() class="macro">#endif);
  }
class="type">long CSymbol::SymbolCustom(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_CUSTOM) class="macro">#else false class="macro">#endif);
  }
class="type">long CSymbol::SymbolChartMode(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_CHART_MODE) class="macro">#else SYMBOL_CHART_MODE_BID class="macro">#endif);
  }
class="type">long CSymbol::SymbolMarginHedgedUseLEG(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_MARGIN_HEDGED_USE_LEG) class="macro">#else false class="macro">#endif);
  }
class="type">long CSymbol::SymbolOrderFillingMode(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_FILLING_MODE) class="macro">#else class="num">0 class="macro">#endif );
  }

「品种属性读取的 MQL5/MQL4 兼容封装」

在自建 CSymbol 类里,这几个方法把品种层面的交易属性做了统一封装,核心目的是同一套代码在 MQL5 和 MQL4 下都能编译跑通。MQL5 走 SymbolInfoInteger 直接拿整数属性,MQL4 则退化到 MarketInfo 或写死常量,差异被收敛在预处理宏里。 以 IsCloseByOrdersAllowed 为例,MQL5 下用 OrderModeFlags() 与 SYMBOL_ORDER_CLOSEBY 做位与判断,确认该品种是否允许一键平仓反向单;MQL4 下则读 MODE_CLOSEBY_ALLOWED。外汇与贵金属品种是否开放 Close By,取决于经纪商配置,实盘前应在 MT5 品种规格里核对,这类属性误判可能导致对冲单无法批量平掉,放大高风险敞口。 SymbolOrderGTCMode 返回挂单及 SL/TP 的 GTC 模式(MQL5 用 SYMBOL_ORDER_GTC_MODE,MQL4 固定返回 SYMBOL_ORDERS_GTC),SymbolCalcMode 取保证金计算方式(MQL5 为 SYMBOL_TRADE_CALC_MODE,MQL4 映射 MODE_MARGINCALCMODE)。其余 OptionMode、OptionRight、BackgroundColor 在 MQL4 分支直接给 NONE 或 clrNONE,说明旧终端并不暴露这些字段。 写 EA 时若直接调这些类方法,比散落调用 MarketInfo 更利于跨版本维护;复制到自己的 include 里,改 m_symbol_name 就能逐个打印属性做验证。

MQL5 / C++
class="type">bool CSymbol::IsCloseByOrdersAllowed(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__(this.OrderModeFlags() & SYMBOL_ORDER_CLOSEBY)==SYMBOL_ORDER_CLOSEBY class="macro">#else (class="type">bool)::MarketInfo(this.m_symbol_name,MODE_CLOSEBY_ALLOWED)  class="macro">#endif );
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the lifetime of pending orders and placed StopLoss/TakeProfit levels |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">long CSymbol::SymbolOrderGTCMode(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_ORDER_GTC_MODE) class="macro">#else SYMBOL_ORDERS_GTC class="macro">#endif);
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the option type                                                               |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">long CSymbol::SymbolOptionMode(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_OPTION_MODE) class="macro">#else SYMBOL_OPTION_MODE_NONE class="macro">#endif);
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the option right                                                               |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">long CSymbol::SymbolOptionRight(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_OPTION_RIGHT) class="macro">#else SYMBOL_OPTION_RIGHT_NONE class="macro">#endif);
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//|Return the background class="type">color used to highlight a symbol in Market Watch|
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">long CSymbol::SymbolBackgroundColor(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_BACKGROUND_COLOR) class="macro">#else clrNONE class="macro">#endif);
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the margin calculation method                                                |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">long CSymbol::SymbolCalcMode(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_CALC_MODE) class="macro">#else (class="type">long)::MarketInfo(this.m_symbol_name,MODE_MARGINCALCMODE) class="macro">#endif);

◍ 品种交易属性接口的跨版本取法

在封装品种类时,掉期计算方式、订单过期模式与可下订单类型这三个属性,MQL4 与 MQL5 的取法和枚举名并不一致。MQL5 用 SymbolInfoInteger 配 SYMBOL_SWAP_MODE / SYMBOL_EXPIRATION_MODE / SYMBOL_ORDER_MODE;MQL4 则退化为 MarketInfo 的 MODE_SWAPTYPE,过期模式硬编码为 SYMBOL_EXPIRATION_GTC,订单类型靠宏相加拼出。 下面这段直接从类实现里摘出来,能直接看出分支写法: long CSymbol::SymbolSwapMode(void) const { return(#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SWAP_MODE) #else (long)::MarketInfo(this.m_symbol_name,MODE_SWAPTYPE) #endif); } long CSymbol::SymbolExpirationMode(void) const { return(#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_MODE) #else (long)SYMBOL_EXPIRATION_GTC #endif); } long CSymbol::SymbolOrderMode(void) const { return ( #ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_ORDER_MODE) #else (SYMBOL_ORDER_MARKET+SYMBOL_ORDER_LIMIT+SYMBOL_ORDER_STOP+SYMBOL_ORDER_SL+SYMBOL_ORDER_TP+(this.IsCloseByOrdersAllowed() ? SYMBOL_ORDER_CLOSEBY : 0)) #endif ); } SymbolDigitsLot 里有个防御逻辑:当 LotsMax / LotsMin / LotsStep 任一为 0,直接 Print 报错并把 m_global_error 置为 ERR_MARKET_UNKNOWN_SYMBOL,返回默认 2 位小数。外汇与贵金属杠杆高、点差跳动快,复制这段代码前先确认对应品种在 MT5 市场报价窗口能正常取到手数参数,否则小数位误判会影响仓位计算。

MQL5 / C++
class="type">long CSymbol::SymbolSwapMode(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SWAP_MODE) class="macro">#else (class="type">long)::MarketInfo(this.m_symbol_name,MODE_SWAPTYPE) class="macro">#endif);
  }
class="type">long CSymbol::SymbolExpirationMode(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_MODE) class="macro">#else (class="type">long)SYMBOL_EXPIRATION_GTC class="macro">#endif);
  }
class="type">long CSymbol::SymbolOrderMode(class="type">void) const
  {
   class="kw">return
     (
      class="macro">#ifdef __MQL5__
       ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_ORDER_MODE)
      class="macro">#else 
       (SYMBOL_ORDER_MARKET+SYMBOL_ORDER_LIMIT+SYMBOL_ORDER_STOP+SYMBOL_ORDER_SL+SYMBOL_ORDER_TP+(this.IsCloseByOrdersAllowed() ? SYMBOL_ORDER_CLOSEBY : class="num">0))
      class="macro">#endif
     );
  }

手数小数位与当日极值怎么从接口抠出来

写 EA 时经常要确认手数精度,不然下单数被经纪商拒掉都不知道为什么。下面这段先用手数最小值除以步长再乘回步长,把有效手数四舍五入出来,再数小数点后的位数;末尾如果最后是 0 还会减一位,避免把 0.10 算成两位有效小数。 另一种更干脆的做法是用 SYMBOL_VOLUME_STEP 直接算步长对应的十进制位数:取步长的绝对值对数除以 10 的对数,再向上取整。例如步长 0.01 时 log(0.01)/log(10) = -2,ceil 后是 2 位小数;步长 0.1 则得出 1 位。 当日真实行情边界也别自己存高低点。MQL5 里 SYMBOL_BIDHIGH / SYMBOL_BIDLOW 直接给全天 Bid 的极高极低,SYMBOL_VOLUME_REAL 给当前真实成交量,比自己维护数组省事且不会漏掉跳空。外汇和贵金属杠杆高,这些极值只反映已发生波动,不代表后续方向。

MQL5 / C++
class="type">class="kw">double val=::round(this.LotsMin()/this.LotsStep())*this.LotsStep();
class="type">class="kw">string val_str=(class="type">class="kw">string)val;
class="type">int len=::StringLen(val_str);
class="type">int n=len-::StringFind(val_str,".",class="num">0)-class="num">1;
if(::StringSubstr(val_str,len-class="num">1,class="num">1)=="class="num">0")
   n--;
class="kw">return n;
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Returns the number of decimal places in a symbol lot              |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">uint DigitsLots(const class="type">class="kw">string symbol_name)
  {
   class="kw">return (class="type">int)ceil(fabs(log(SymbolInfoDouble(symbol_name,SYMBOL_VOLUME_STEP))/log(class="num">10)));
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Real properties                                                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return maximum Bid for a day                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CSymbol::SymbolBidHigh(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_BIDHIGH) class="macro">#else ::MarketInfo(this.m_symbol_name,MODE_HIGH) class="macro">#endif);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return minimum Bid for a day                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CSymbol::SymbolBidLow(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_BIDLOW) class="macro">#else ::MarketInfo(this.m_symbol_name,MODE_LOW) class="macro">#endif);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return real Volume for a day                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CSymbol::SymbolVolumeReal(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_REAL) class="macro">#else class="num">0 class="macro">#endif);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

「读取真实成交量的两个边界值」

在 MT5 的 CSymbol 封装里,SymbolVolumeHighReal 与 SymbolVolumeLowReal 直接走 SymbolInfoDouble 拿 SYMBOL_VOLUMEHIGH_REAL / SYMBOL_VOLUMELOW_REAL。这两个字段返回的是「当日真实成交」的上下界,而不是限价单挂单量,做量能异常检测时得用这对而不是 VolumeMin/VolumeMax。 [CODE] double CSymbol::SymbolVolumeHighReal(void) const { return(#ifdef __MQL5__ ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUMEHIGH_REAL) #else 0 #endif); } double CSymbol::SymbolVolumeLowReal(void) const { return(#ifdef __MQL5__ ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUMELOW_REAL) #else 0 #endif); } [/CODE] 逐行看:函数声明为 const 成员,保证不改动对象状态;#ifdef __MQL5__ 判断编译环境,MQL5 下调用全局 ::SymbolInfoDouble 并传入实例的品种名与对应枚举,非 MQL5(如 MQL4 编译分支)直接返回 0 做安全降级;return 包裹整条条件编译表达式,无额外局部变量。 外汇与贵金属属高风险品种,真实成交量字段在部分经纪商处可能返回 0 或偏差值。开 MT5 按 F4 把上面两段塞进你的 CSymbol 派生类,编译后 Print(SymbolVolumeHighReal()) 对比行情窗口的成交量大概率能验证边界是否生效。

MQL5 / C++
class="type">class="kw">double CSymbol::SymbolVolumeHighReal(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUMEHIGH_REAL) class="macro">#else class="num">0 class="macro">#endif);
  }
class="type">class="kw">double CSymbol::SymbolVolumeLowReal(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUMELOW_REAL) class="macro">#else class="num">0 class="macro">#endif);
  }

◍ 对冲保证金与衍生品种元数据读取

在 MT5 的 CSymbol 封装里,对冲单占用保证金用 SYMBOL_MARGIN_HEDGED 直接取,返回的是「单标准手锁仓头寸」所需的保证金数值。这个值随品种和杠杆变动,黄金 XAUUSD 在 1:100 账户下通常约 1000 美元/手,但确切数字以运行时查询为准,外汇与贵金属保证金规则差异大、风险高。 下面几个字符串属性只在 MQL5 有效:SYMBOL_BASIS 取标的资产名(如 BTCUSD 的底层是 BTC),SYMBOL_BANK 取报价源,SYMBOL_ISIN 取国际证券标识码。MQL4 下这些接口不存在,代码里用预编译分支返回提示串,避免编译报错。 实际写 EA 时,别在 MQL4 工程里硬调这些函数。开 MT5 按 F4 把下面代码塞进 CSymbol 类,编译后用一个 Print(SymbolBasis()) 就能看到当前品种的底层资产,验证数据源是否如预期。

MQL5 / C++
class="type">class="kw">double CSymbol::SymbolMarginHedged(class="type">void) const
  {
   class="kw">return(class="macro">#ifdef __MQL5__ ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_HEDGED) class="macro">#else ::MarketInfo(this.m_symbol_name, MODE_MARGINHEDGED) class="macro">#endif);
  }

class="type">class="kw">string CSymbol::SymbolBasis(class="type">void) const
  {
   class="kw">return
     (
      class="macro">#ifdef __MQL5__
         ::SymbolInfoString(this.m_symbol_name,SYMBOL_BASIS)
      class="macro">#else
         ": "+TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
      class="macro">#endif
     );
  }

class="type">class="kw">string CSymbol::SymbolBank(class="type">void) const
  {
   class="kw">return
     (
      class="macro">#ifdef __MQL5__
         ::SymbolInfoString(this.m_symbol_name,SYMBOL_BANK)
      class="macro">#else
         ": "+TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
      class="macro">#endif
     );
  }

class="type">class="kw">string CSymbol::SymbolISIN(class="type">void) const
  {
   class="kw">return
     (
      class="macro">#ifdef __MQL5__
         ::SymbolInfoString(this.m_symbol_name,SYMBOL_ISIN)
      class="macro">#else
         ": "+TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4")
      class="macro">#endif
     );
  }

正文

&nbsp;&nbsp; <span class="keyword">return</span> &nbsp;&nbsp;&nbsp;&nbsp; ( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="preprocessor">#ifdef </span><span class="macro">__MQL5__</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ::<span class="functions">SymbolInfoString</span>(<span class="keyword">this</span>.m_symbol_name,<span class="macro">SYMBOL_FORMULA</span>) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="preprocessor">#else &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="string">": "</span>+TextByLanguage(<span class="string">"Свойство не поддерживается в MQL4"</span>,<span class="string">"Property not supported in MQL4"</span>) &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="preprocessor">#endif &nbsp;&nbsp;&nbsp;&nbsp; </span>); &nbsp;&nbsp;} <span class="comment">//+------------------------------------------------------------------+</span>

<span class="comment">//Return an address of a web page with a symbol data&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>

<span class="comment">//+------------------------------------------------------------------+</span> <span class="keyword">string</span> CSymbol::SymbolPage(<span class="keyword">void</span>) <span class="keyword">const</span> &nbsp;&nbsp;{ &nbsp;&nbsp; <span class="keyword">return</span> &nbsp;&nbsp;&nbsp;&nbsp; ( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="preprocessor">#ifdef </span><span class="macro">__MQL5__</span> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&n

「把整数属性翻成人话的映射函数」

在封装交易品种对象时,直接把 SYMBOL_PROP_INTEGER 枚举丢给 Print 只会看到数字,调试时根本分不清哪个状态位代表什么。下面这段 CSymbol::GetPropertyDescription 的做法,是把常见整数属性逐一映射成可读文本,并且先判断当前品种是否支持该属性。 函数用三元表达式串起 SYMBOL_PROP_STATUS、SYMBOL_PROP_CUSTOM、SYMBOL_PROP_CHART_MODE、SYMBOL_PROP_EXIST、SYMBOL_PROP_SELECT 等分支。若 SupportProperty 返回 false,就追加「Property not supported」提示;否则取具体描述或 Yes/No。 实盘里 XAUUSD 这类主流通品种一般全属性可用,但自定义品种(property==SYMBOL_PROP_CUSTOM 为真)在部分券商环境可能返回不支持。开 MT5 新建 EA 把这段粘进 CSymbol 类,调 GetPropertyDescription(SYMBOL_PROP_EXIST) 应能稳定打印「Symbol with this name exists: Yes」。

MQL5 / C++
class="type">class="kw">string CSymbol::GetPropertyDescription(ENUM_SYMBOL_PROP_INTEGER class="kw">property)
  {
   class="kw">return
     (
     class=class="str">"cmt">//--- General properties
      class="kw">property==SYMBOL_PROP_STATUS                 ?  TextByLanguage("Статус","Status")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
        ": "+(class="type">class="kw">string)this.GetStatusDescription()
       )  :
      class="kw">property==SYMBOL_PROP_CUSTOM                 ?  TextByLanguage("Пользовательский символ","Custom symbol")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
        ": "+(this.GetProperty(class="kw">property)   ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
       )  :
      class="kw">property==SYMBOL_PROP_CHART_MODE             ?  TextByLanguage("Тип цены для построения баров","Price type used for generating symbols bars")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
        ": "+this.GetChartModeDescription()
       )  :
      class="kw">property==SYMBOL_PROP_EXIST                  ?  TextByLanguage("Символ с таким именем существует","Symbol with this name exists")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
        ": "+(this.GetProperty(class="kw">property)   ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
       )  :
      class="kw">property==SYMBOL_PROP_SELECT ?  TextByLanguage("Символ выбран в Market Watch","Symbol selected in Market Watch")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :

◍ 可见性与会话订单量的属性分支

在封装交易品种信息类时,针对 SYMBOL_PROP_VISIBLE 这类布尔型属性,代码用三元表达式先判断 SupportProperty 是否支持,再调用 GetProperty 返回本地化「Yes/No」。若经纪商行情接口不提供该字段,会直接拼出「Property not supported」,避免 EA 在读取时崩在空值上。 会话维度的挂单统计(SYMBOL_PROP_SESSION_DEALS、SESSION_BUY_ORDERS、SESSION_SELL_ORDERS)只在 MQL5 下可用。片段里用 #ifdef __MQL5__ 包裹 (string)this.GetProperty(property),否则回退到「Property not supported in MQL4」——这意味着你要把同套逻辑移植到 MT4 时,这几项数据直接拿不到。 成交量类属性如 SYMBOL_PROP_VOLUME(最后一笔成交手数)、SYMBOL_PROP_VOLUMEHIGH(当日最大成交量)也走同一套「支持判断 + MQL5 取值」通道。开 MT5 随便挂个品种跑这段,能直观看到当前会话买/卖挂单数,对判断盘口热度倾向有帮助;外汇与贵金属杠杆高,会话订单倾斜不代表价格必然反转。

MQL5 / C++
class="kw">property==SYMBOL_PROP_VISIBLE ? TextByLanguage("Символ отображается в Market Watch","Symbol visible in Market Watch")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+(this.GetProperty(class="kw">property) ? TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
 ) :
class="kw">property==SYMBOL_PROP_SESSION_DEALS ? TextByLanguage("Количество сделок в текущей сессии","Number of deals in the current session")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property is not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_SESSION_BUY_ORDERS ? TextByLanguage("Общее число ордеров на покупку в текущий момент","Number of Buy orders at the moment")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property is not support") :
  ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_SESSION_SELL_ORDERS ? TextByLanguage("Общее число ордеров на продажу в текущий момент","Number of Sell orders at the moment")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_VOLUME ? TextByLanguage("Объем в последней сделке","Volume of the last deal")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_VOLUMEHIGH ? TextByLanguage("Максимальный объём за день","Maximum day volume")+

把品种属性翻译成可读标签的嵌套写法

这段逻辑干的事很直接:用三元运算符把 SYMBOL_PROP_* 常量逐个映射到双语可读字符串,顺带检查当前环境是否支持该属性。MQL5 下直接取 GetProperty,MQL4 下统一回退成「不支持」提示,避免老终端编译报错。 注意 SYMBOL_PROP_TIME 这一支比较特殊:当 GetProperty 返回 0 时,说明品种还没收到任何 tick,代码会补一句「(No ticks yet)」而不是硬转时间。实盘里欧盘开盘前挂的稀有交叉盘常出现这种情况,读面板时别误判成断线。 SYMBOL_PROP_DIGITS 和 SYMBOL_PROP_DIGITS_LOTS 都靠 (string) 强转,但前者是报价小数位、后者是手数小数位——XAUUSD 常见报价 2 位、手数 2 位,而某些券商的 BTC 手数能到 3 位,逐属性打印能帮你快速核对合约规格。 SPREAD 与 SPREAD_FLOAT 分开取:前者给点值整数,后者给布尔。浮动点差品种在新闻市可能瞬间扩到常规 5~10 倍,用这套标签把两者并列输出,比盯 MT5 默认窗口更直观。

MQL5 / C++
class="kw">property==SYMBOL_PROP_VOLUMELOW ? TextByLanguage("Минимальный объём за день","Minimum day volume")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif ) :
class="kw">property==SYMBOL_PROP_TIME ? TextByLanguage("Время последней котировки","Time of last quote")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(this.GetProperty(class="kw">property)==class="num">0 ? TextByLanguage("(Ещё не было тиков)","(No ticks yet)") : TimeMSCtoString(this.GetProperty(class="kw">property)))) ) :
class="kw">property==SYMBOL_PROP_DIGITS ? TextByLanguage("Количество знаков после запятой","Digits after decimal point")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) :
class="kw">property==SYMBOL_PROP_DIGITS_LOTS ? TextByLanguage("Количество знаков после запятой в значении лота","Digits after decimal point in lot value")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) :
class="kw">property==SYMBOL_PROP_SPREAD ? TextByLanguage("Размер спреда в пунктах","Spread value in points")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property) ) :
class="kw">property==SYMBOL_PROP_SPREAD_FLOAT ? TextByLanguage("Плавающий спред","Floating spread")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :

「读取交易品种隐藏属性的字符串拼装」

在封装交易品种信息类时,常要把 SYMBOL_PROP_* 这类枚举转成可读文本。下面这段分支专门处理深度、计算模式、执行类型、起止时间、止损位与冻结位等属性,能在 MT5 里直接跑出双语说明。 注意 SYMBOL_PROP_TICKS_BOOKDEPTH 用了 #ifdef __MQL5__ 预编译:MQL5 下取 GetProperty(property) 数值,MQL4 则直接返回“不支持”。这说明同套代码若跨平台编译,深度字段很可能拿不到数。 SYMBOL_PROP_START_TIMESYMBOL_PROP_EXPIRATION_TIME 把秒级时间乘 1000 转毫秒再交给 TimeMSCtoString,且当属性为 0 时显示“(Not set)”。外汇现货品种通常返回 0,而期货类合约才可能带具体起止时间戳。 SYMBOL_PROP_TRADE_STOPS_LEVEL 直接强转 (string)this.GetProperty(property),给出的是点数;黄金 XAUUSD 在多数券商下该值介于 0~50 点,设止损比现价偏离过小会被拒单,属高频踩坑点。贵金属与外汇杠杆品种波动剧烈,读取这些层级参数仅用于风控预判,不预示任何价格方向。

MQL5 / C++
class="kw">property==SYMBOL_PROP_TICKS_BOOKDEPTH ? TextByLanguage("Максимальное количество показываемых заявок в стакане","Maximum number of requests shown in Depth of Market")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__(class="type">class="kw">string)this.GetProperty(class="kw">property) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_TRADE_CALC_MODE ? TextByLanguage("Способ вычисления стоимости контракта","Contract price calculation mode")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+this.GetCalcModeDescription()
 ) :
class="kw">property==SYMBOL_PROP_TRADE_MODE ? TextByLanguage("Тип исполнения ордеров","Order execution type")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+this.GetTradeModeDescription()
 ) :
class="kw">property==SYMBOL_PROP_START_TIME ? TextByLanguage("Дата начала торгов по инструменту","Date of symbol trade beginning")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  (this.GetProperty(class="kw">property)==class="num">0 ? TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+TimeMSCtoString(this.GetProperty(class="kw">property)*class="num">1000))
 ) :
class="kw">property==SYMBOL_PROP_EXPIRATION_TIME ? TextByLanguage("Дата окончания торгов по инструменту","Date of symbol trade end")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  (this.GetProperty(class="kw">property)==class="num">0 ? TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+TimeMSCtoString(this.GetProperty(class="kw">property)*class="num">1000))
 ) :
class="kw">property==SYMBOL_PROP_TRADE_STOPS_LEVEL ? TextByLanguage("Минимальный отступ от цены закрытия для установки Stop ордеров","Minimum indention from close price to place Stop orders")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+(class="type">class="kw">string)this.GetProperty(class="kw">property)
 ) :
class="kw">property==SYMBOL_PROP_TRADE_FREEZE_LEVEL ? TextByLanguage("Дистанция заморозки торговых операций","Distance to freeze trade operations in points")+

◍ 品种属性描述的分支拼装逻辑

这段条件链把交易品种(symbol)的若干关键属性转成可读文本,核心思路是:先判断 property 枚举值,再检查 broker 是否支持该属性,不支持就返回双语的「Property not supported」,支持则调用对应的 Get 方法拿具体描述。 涉及 SYMBOL_PROP_TRADE_EXEMODE 时拼出成交执行模式,调 GetTradeExecDescription();SYMBOL_PROP_SWAP_MODE 走 GetSwapModeDescription() 拿掉期计算模型;SYMBOL_PROP_SWAP_ROLLOVER3DAYS 则用 DayOfWeekDescription(this.SwapRollover3Days()) 把三重掉期日转成星期名。 保证金对冲里 SYMBOL_PROP_MARGIN_HEDGED_USE_LEG 比较特殊,它返回布尔:属性为真时显示「Yes」,否则「No」,用来标识是否按较大腿计算对冲保证金。 剩下 SYMBOL_PROP_EXPIRATION_MODE、SYMBOL_PROP_FILLING_MODE、SYMBOL_PROP_ORDER_MODE 三个枚举,分别由 GetExpirationModeFlagsDescription、GetFillingModeFlagsDescription、GetOrderModeFlagsDescription 输出 broker 允许的过期、成交、订单类型标志位。开 MT5 写个 CSymbolInfo 派生类跑这段,能直接打印出当前品种的真实限制,外汇和贵金属杠杆高、规则常变,验证后再下单更稳妥。

MQL5 / C++
   (!this.SupportProperty(class="kw">property)     ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
       ": "+((class="type">class="kw">string)this.GetProperty(class="kw">property))
   )  :
   class="kw">property==SYMBOL_PROP_TRADE_EXEMODE       ?  TextByLanguage("Режим заключения сделок","Deal execution mode")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetTradeExecDescription()
       )  :
   class="kw">property==SYMBOL_PROP_SWAP_MODE           ?  TextByLanguage("Модель расчета свопа","Swap calculation model")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetSwapModeDescription()
       )  :
   class="kw">property==SYMBOL_PROP_SWAP_ROLLOVER3DAYS  ?  TextByLanguage("День недели для начисления тройного свопа","Day of week to charge class="num">3 days swap rollover")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+DayOfWeekDescription(this.SwapRollover3Days())
       )  :
   class="kw">property==SYMBOL_PROP_MARGIN_HEDGED_USE_LEG  ?  TextByLanguage("Расчет хеджированной маржи по наибольшей стороне","Calculating hedging margin using larger leg")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+(this.GetProperty(class="kw">property)  ?  TextByLanguage("Да","Yes") : TextByLanguage("Нет","No"))
       )  :
   class="kw">property==SYMBOL_PROP_EXPIRATION_MODE     ?  TextByLanguage("Флаги разрешенных режимов истечения ордера","Flags of allowed order expiration modes")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetExpirationModeFlagsDescription()
       )  :
   class="kw">property==SYMBOL_PROP_FILLING_MODE        ?  TextByLanguage("Флаги разрешенных режимов заливки ордера","Flags of allowed order filling modes")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetFillingModeFlagsDescription()
       )  :
   class="kw">property==SYMBOL_PROP_ORDER_MODE          ?  TextByLanguage("Флаги разрешенных типов ордера","Flags of allowed order types")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
          ": "+this.GetOrderModeFlagsDescription()
       )  :

枚举属性怎么翻成可读文本

在封装品种信息类时,把 MQL5 的枚举属性转成交易员能看懂的中文描述,是调试面板和日志输出的基础活。下面这段分支判断覆盖了 GTC 模式、期权类型、期权权利、市场报价背景色等整型属性,不支持时直接回退到「属性未支持」提示。 针对 SYMBOL_PROP_BACKGROUND_COLOR 有个细节:MQL5 下若取值大于 clrWhite 视为未设置,调 ColorToString 带 true 参数输出含 ARGB 的完整色值;MQL4 分支则直接声明不支持,避免跨语言编译报错。 后续还有一组双精度属性描述函数 GetPropertyDescription(ENUM_SYMBOL_PROP_DOUBLE),对 Bid 价做了防御——若 GetProperty 返回 0 说明品种还没收到过 tick,显示「(No ticks yet)」而不是乱喷一个 0.00000,Digits() 控制的精度位数保证 XAUUSD 这类 2 位和 EURUSD 5 位品种都能对齐。

MQL5 / C++
   class="kw">property==SYMBOL_PROP_ORDER_GTC_MODE       ?  TextByLanguage("Срок действия StopLoss и TakeProfit ордеров","Expiration of Stop Loss and Take Profit orders")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         ": "+this.GetOrderGTCModeDescription()
       )  :
   class="kw">property==SYMBOL_PROP_OPTION_MODE           ?  TextByLanguage("Тип опциона","Option type")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         ": "+this.GetOptionTypeDescription()
       )  :
   class="kw">property==SYMBOL_PROP_OPTION_RIGHT          ?  TextByLanguage("Право опциона","Option right")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         ": "+this.GetOptionRightDescription()
       )  :
   class="kw">property==SYMBOL_PROP_BACKGROUND_COLOR      ?  TextByLanguage("Цвет фона символа в Market Watch","Background class="type">color of symbol in Market Watch")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
        class="macro">#ifdef __MQL5__(this.GetProperty(class="kw">property)>clrWhite  ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": "+::ColorToString((class="type">color)this.GetProperty(class="kw">property),true))
        class="macro">#else TextByLanguage(": Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
       )  :
   ""
   );
}
class="type">class="kw">string CSymbol::GetPropertyDescription(ENUM_SYMBOL_PROP_DOUBLE class="kw">property)
  {
   class="type">int dg=this.Digits();
   class="type">int dgl=this.DigitsLot();
   class="type">int dgc=this.DigitsCurrency();
   class="kw">return
     (
      class="kw">property==SYMBOL_PROP_BID                ?  TextByLanguage("Цена Bid","Bid price")+
       (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         ": "+(this.GetProperty(class="kw">property)==class="num">0 ? TextByLanguage("(Ещё не было тиков)","(No ticks yet)") : ::DoubleToString(this.GetProperty(class="kw">property),dg))
       )  :
      class="kw">property==SYMBOL_PROP_BIDHIGH            ?  TextByLanguage("Максимальный Bid за день","Maximum Bid for the day")+

「把当日高低点与买卖报价映射到可读文本」

这段嵌套三元表达式负责把 SYMBOL_PROP_* 系列属性转成交易者可看的文字标签。BIDLOW 对应「当日最低买价」,ASK 对应实时卖价,ASKHIGH / ASKLOW 则是当日卖价的高低边界,LAST 系列记录最后一笔成交价。 注意 ASK 属性有个特殊分支:若 GetProperty 返回 0,说明当前品种当天还没收到任何 tick,会输出「(No ticks yet)」而非数字。外汇与贵金属市场跳空时这类空 tick 状态可能出现,属正常高波动现象。 MQL5 环境下 ASKHIGH、ASKLOW、LAST 直接用 DoubleToString 格式化;代码里用 #ifdef __MQL5__ 包裹,意味着同一套逻辑在 MQL4 下会明确提示属性不支持,移植时要留心。 开 MT5 新建脚本,把下面片段塞进 Print 调用,切换 EURUSD 与 XAUUSD 观察输出差异,能快速验证经纪商是否真提供了这些日内极值属性。

MQL5 / C++
class="kw">property==SYMBOL_PROP_BIDLOW ? TextByLanguage("Минимальный Bid за день","Minimum Bid for the day")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
 ) :
class="kw">property==SYMBOL_PROP_ASK ? TextByLanguage("Цена Ask","Ask price")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+(this.GetProperty(class="kw">property)==class="num">0 ? TextByLanguage("(Ещё не было тиков)","(No ticks yet)") : ::DoubleToString(this.GetProperty(class="kw">property),dg))
 ) :
class="kw">property==SYMBOL_PROP_ASKHIGH ? TextByLanguage("Максимальный Ask за день","Maximum Ask for the day")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_ASKLOW ? TextByLanguage("Минимальный Ask за день","Minimum Ask for the day")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_LAST ? TextByLanguage("Цена последней сделки","Price of the last deal")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_LASTHIGH ? TextByLanguage("Максимальный Last за день","Maximum Last for the day")+

◍ 符号属性的多语言与版本分支拼装

这段逻辑在做一个事:把交易品种(symbol)的某个属性值,按俄语/英语双语标签拼成可读字符串,同时处理 MQL4 不支持该属性的降级显示。 它用三元嵌套判断 property 等于哪个枚举:SYMBOL_PROP_LASTHIGH 拼“当日 Last 最高价”,SYMBOL_PROP_LASTLOW 拼“当日 Last 最低价”,SYMBOL_PROP_VOLUME_REAL 拼“真实成交量”,后面还有 VOLUMEHIGH_REAL / VOLUMELOW_REAL / OPTION_STRIKE 等分支。 每个分支先走 SupportProperty() 检查,若返回 false 就直接追加“: Property not supported”这类提示;MQL5 下则用 DoubleToString(GetProperty(property), dg 或 dgl) 把双精度值转字符串,dg 与 dgl 是两套小数位参数(前者管价格类、后者管成交量类)。 在 MT5 里打开任意品种测 SYMBOL_PROP_VOLUME_REAL,若券商提供真实成交量为 123456.0,dgl=0 时输出“Real volume of the last deal: 123456”,改 dgl=2 才会出小数。外汇与贵金属真实成交量字段是否可用取决于经纪商数据馈送,属高风险品种,字段为空时勿据此下单。

MQL5 / C++
(!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_LASTLOW ? TextByLanguage("Минимальный Last за день","Minimum Last for the day")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_VOLUME_REAL ? TextByLanguage("Реальный объём за день","Real volume of the last deal")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgl) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_VOLUMEHIGH_REAL ? TextByLanguage("Максимальный реальный объём за день","Maximum real volume for the day")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgl) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_VOLUMELOW_REAL ? TextByLanguage("Минимальный реальный объём за день","Minimum real volume for the day")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgl) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_OPTION_STRIKE ? TextByLanguage("Цена исполнения опциона","Option strike price")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :

逐属性拼装品种字段的中文显示串

这段逻辑是给一个品种对象做「属性 → 可读文本」的映射,专门处理点值、tick 价值、合约规模等交易关键字段。它用三元表达式按 property 枚举逐个分支走,不支持的属性会回退到「Property not supported」提示,MT5 下才用 DoubleToString 把数值格式化成字符串。 注意 SYMBOL_PROP_POINT 和 SYMBOL_PROP_TRADE_TICK_SIZE 用的是 dg 精度,而 TRADE_TICK_VALUE / CONTRACT_SIZE 这类带资金属性的用 dgc 精度,两者若设成同一值会丢掉 tick 价值的微小小数。外汇与贵金属杠杆高,点值计算偏差会直接放大成仓位风险,建议在 MT5 里用 SymbolInfoDouble 对照验证。 MQL4 环境直接走俄语/英语的「不支持」分支,说明这套映射本就是为 MQL5 品种模型写的;若你要在老终端跑,得自己补属性代理层。

MQL5 / C++
class="kw">property==SYMBOL_PROP_POINT ? TextByLanguage("Значение одного пункта","Symbol point value")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
 ) :
class="kw">property==SYMBOL_PROP_TRADE_TICK_VALUE ? TextByLanguage("Рассчитанная стоимость тика для позиции","Calculated tick price for position")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+::DoubleToString(this.GetProperty(class="kw">property),dgc)
 ) :
class="kw">property==SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT ? TextByLanguage("Рассчитанная стоимость тика для прибыльной позиции","Calculated tick price for profitable position")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_TRADE_TICK_VALUE_LOSS ? TextByLanguage("Рассчитанная стоимость тика для убыточной позиции","Calculated tick price for losing position")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_TRADE_TICK_SIZE ? TextByLanguage("Минимальное изменение цены","Minimum price change")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+::DoubleToString(this.GetProperty(class="kw">property),dg)
 ) :
class="kw">property==SYMBOL_PROP_TRADE_CONTRACT_SIZE ? TextByLanguage("Размер торгового контракта","Trade contract size")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+::DoubleToString(this.GetProperty(class="kw">property),dgc)
 ) :
class="kw">property==SYMBOL_PROP_TRADE_ACCRUED_INTEREST ? TextByLanguage("Накопленный купонный доход","Accumulated coupon interest")+

「债券面值与交易量限制的字符串拼装细节」

在封装交易品种属性描述时,这段代码用嵌套三元运算符把不同 SYMBOL_PROP 枚举映射到多语言文本。MQL5 下走 DoubleToString 取数,MQL4 下直接回退到“Property not supported in MQL4”,说明这些字段本就不是四系兼容的。 针对 SYMBOL_PROP_TRADE_FACE_VALUE(债券发行面值)和 SYMBOL_PROP_TRADE_LIQUIDITY_RATE(流动性系数),前者用变量 dgc 控制小数位,后者硬编码了 2 位小数;而 SYMBOL_PROP_VOLUME_MIN / MAX / STEP / LIMIT 这组成交量约束,则统一用 dgl 精度输出。 开 MT5 把 dgc 和 dgl 分别打印出来,大概率能看到面值类用 4~8 位、成交量类用 2~3 位,差一位都会在面板显示里出现截断或补零。外汇与贵金属杠杆品种波动剧烈,这类基础属性读取错误会放大下单风险。

MQL5 / C++
class="kw">property==SYMBOL_PROP_TRADE_FACE_VALUE ?  TextByLanguage("Начальная стоимость облигации, установленная эмитентом","Initial bond value set by issuer")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_TRADE_LIQUIDITY_RATE  ?  TextByLanguage("Коэффициент ликвидности","Liquidity rate")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),class="num">2) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_VOLUME_MIN        ?  TextByLanguage("Минимальный объем для заключения сделки","Minimum volume for a deal")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+::DoubleToString(this.GetProperty(class="kw">property),dgl)
 ) :
class="kw">property==SYMBOL_PROP_VOLUME_MAX        ?  TextByLanguage("Максимальный объем для заключения сделки","Maximum volume for a deal")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+::DoubleToString(this.GetProperty(class="kw">property),dgl)
 ) :
class="kw">property==SYMBOL_PROP_VOLUME_STEP       ?  TextByLanguage("Минимальный шаг изменения объема для заключения сделки","Minimum volume change step for deal execution")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
  ": "+::DoubleToString(this.GetProperty(class="kw">property),dgl)
 ) :
class="kw">property==SYMBOL_PROP_VOLUME_LIMIT     ?  TextByLanguage("Максимально допустимый общий объем позиции и отложенных ордеров в одном направлении","Maximum allowed aggregate volume of open position and pending orders in one direction")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :

◍ 把 swap 和保证金属性翻成可读文本

这段三元表达式链专门处理交易品种里最容易被忽略的成本字段:买入掉期、卖出掉期、初始保证金、维持保证金,以及多空持仓的保证金系数。每个分支都先做 SupportProperty 探测,不支持就回多语种提示,支持才用 DoubleToString 按精度格式化输出。 注意 MQL4 与 MQL5 的差异:SYMBOL_PROP_SWAP_LONG、SYMBOL_PROP_SWAP_SHORT、SYMBOL_PROP_MARGIN_INITIAL、SYMBOL_PROP_MARGIN_MAINTENANCE 在 MQL4 下靠 SupportProperty 返回假值走「Property not supported」分支;而部分属性在 MQL4 编译期直接被 #ifdef __MQL5__ 拦掉,连运行期探测都不发生。 做跨版本面板时,dgl 与 dgc 是两个不同精度变量,掉期用 dgl、保证金类用 dgc,混用会让贵金属点值显示多出无效小数位。外汇与贵金属杠杆品种保证金参数跳动频繁,读取后应在实盘前用「品种规格」窗口人工核对一次,这类品种高风险,参数误读可能放大敞口。

MQL5 / C++
class="kw">property==SYMBOL_PROP_SWAP_LONG ? TextByLanguage("Значение свопа на покупку","Long swap value")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::DoubleToString(this.GetProperty(class="kw">property),dgc) ) :
class="kw">property==SYMBOL_PROP_SWAP_SHORT ? TextByLanguage("Значение свопа на продажу","Short swap value")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::DoubleToString(this.GetProperty(class="kw">property),dgc) ) :
class="kw">property==SYMBOL_PROP_MARGIN_INITIAL ? TextByLanguage("Начальная (инициирующая) маржа","Initial margin")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::DoubleToString(this.GetProperty(class="kw">property),dgc) ) :
class="kw">property==SYMBOL_PROP_MARGIN_MAINTENANCE ? TextByLanguage("Поддерживающая маржа по инструменту","Maintenance margin")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+::DoubleToString(this.GetProperty(class="kw">property),dgc) ) :
class="kw">property==SYMBOL_PROP_MARGIN_LONG ? TextByLanguage("Коэффициент взимания маржи по длинным позициям","Coefficient of margin charging for class="type">long positions")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif ) :
class="kw">property==SYMBOL_PROP_MARGIN_SHORT ? TextByLanguage("Коэффициент взимания маржи по коротким позициям","Coefficient of margin charging for class="type">short positions")+( !this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") : ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif ) :

挂单与时段属性的文本化读取

在封装品种属性读取的类方法里,针对 Stop、Limit、StopLimit 挂单的保证金系数,以及当前交易时段的成交量、成交额和未平仓总量,代码用了一连串三元运算符做分支映射。 每个分支先调用 TextByLanguage 输出中俄双语标签,例如 Stop 单保证金系数对应「Coefficient of margin charging for Stop orders」,再判断 SupportProperty 是否支持该属性;不支持就追加「Property not supported」,支持则在 MQL5 下用 DoubleToString(this.GetProperty(property), dgc) 把数值格式化为字符串。 注意 volume 分支用的是 dgl 精度参数,而其余几个用的是 dgc,这说明时段成交量可能默认保留更多小数位,开 MT5 后可在 SymbolInfoDouble 里对照验证具体精度差异。 若编译环境是 MQL4,所有上述属性都会走 #else 分支直接返回「Property not supported in MQL4」,因为老平台根本不暴露这些符号级字段。

MQL5 / C++
class="kw">property==SYMBOL_PROP_MARGIN_STOP ? TextByLanguage("Коэффициент взимания маржи по Stop ордерам","Coefficient of margin charging for Stop orders")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_MARGIN_LIMIT ? TextByLanguage("Коэффициент взимания маржи по Limit ордерам","Coefficient of margin charging for Limit orders")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_MARGIN_STOPLIMIT ? TextByLanguage("Коэффициент взимания маржи по Stop Limit ордерам","Coefficient of margin charging for StopLimit orders")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_SESSION_VOLUME ? TextByLanguage("Cуммарный объём сделок в текущую сессию","Summary volume of the current session deals")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgl) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_SESSION_TURNOVER ? TextByLanguage("Cуммарный оборот в текущую сессию","Summary turnover of the current session")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgc) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 ) :
class="kw">property==SYMBOL_PROP_SESSION_INTEREST ? TextByLanguage("Cуммарный объём открытых позиций","Summary open interest")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :

「读取交易时段内的挂单与均价字段」

在 MT5 的 CSymbol 封装里,SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME、SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME 以及 SESSION_OPEN / CLOSE / AW 这组属性,用来取当前交易时段的买挂单总手数、卖挂单总手数、时段开盘价、收盘价与成交量加权均价。外汇与贵金属市场高杠杆、跳空频繁,这些数值仅在部分品种和经纪商环境下被支持,读取前必须用 SupportProperty() 判可用。 下面这段分支返回的是「属性中文/英文标签 + 冒号 + 数值」的字符串。MQL5 下用 DoubleToString 按 dgl(成交量类用 dgl,价格类用 dg 小数位)格式化;MQL4 不支持,直接回退到多语种提示。 别把时段成交量当全市场深度。它只覆盖当前交易时段内的挂单汇总,不代表未成交限价单的全貌,黄金跳空时段该值可能短暂为 0。

MQL5 / C++
class="kw">property==SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME ?  TextByLanguage("Общий объём ордеров на покупку в текущий момент","Current volume of Buy orders")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
   ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgl) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 )  :
class="kw">property==SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME  ?  TextByLanguage("Общий объём ордеров на продажу в текущий момент","Current volume of Sell orders")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
   ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dgl) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 )  :
class="kw">property==SYMBOL_PROP_SESSION_OPEN     ?  TextByLanguage("Цена открытия сессии","Open price of the current session")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
   ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 )  :
class="kw">property==SYMBOL_PROP_SESSION_CLOSE     ?  TextByLanguage("Цена закрытия сессии","Close price of the current session")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
   ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 )  :
class="kw">property==SYMBOL_PROP_SESSION_AW       ?  TextByLanguage("Средневзвешенная цена сессии","Average weighted price of the current session")+
 (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
   ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
 )  :

◍ 会话结算价与对冲保证金的属性拼装

这段分支把几个容易被忽视的交易品种属性塞进了统一的描述函数里。SYMBOL_PROP_SESSION_PRICE_SETTLEMENT 取的是当前交易时段的结算价,SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN / MAX 则是该时段允许的最小、最大限价,三者都依赖 SupportProperty 先判支持性,不支持就回「Property not supported」。 MQL5 下用 DoubleToString(this.GetProperty(property),dg) 把双精度值按 dg 位小数转成字符串;MQL4 分支直接返回不支持提示,说明这些会话级属性在老语言环境里读不到。 SYMBOL_PROP_MARGIN_HEDGED 返回的是对冲单每手的合约规模或保证金占用值,这里用 dgc 控制小数位,没有 MQL4 降级判断——若你的封装层在 MQL4 编译,这行可能直接报错。 下方 CSymbol::GetPropertyDescription 的字符串重载只处理 SYMBOL_PROP_NAME、SYMBOL_PROP_BASIS 等文本属性,不支持时同样走统一提示。开 MT5 把 dg 设成 2、dgc 设成 4 跑一遍,能直接看到 XAUUSD 的会话限价和对冲保证金数值差异。外汇与贵金属杠杆高,会话属性随交易所规则变动,读数仅作风控参考,不预示方向。

MQL5 / C++
   class="kw">property==SYMBOL_PROP_SESSION_PRICE_SETTLEMENT   ?  TextByLanguage("Цена поставки на текущую сессию","Settlement price of the current session")+
      (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
       ):
   class="kw">property==SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN   ?  TextByLanguage("Минимально допустимое значение цены на сессию","Minimum price of the current session")+
      (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
       ):
   class="kw">property==SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX   ?  TextByLanguage("Максимально допустимое значение цены на сессию","Maximum price of the current session")+
      (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         ": "+ class="macro">#ifdef __MQL5__::DoubleToString(this.GetProperty(class="kw">property),dg) class="macro">#else TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#endif
       ):
   class="kw">property==SYMBOL_PROP_MARGIN_HEDGED    ?  TextByLanguage("Размер контракта или маржи для одного лота перекрытых позиций","Contract size or margin value per one lot of hedged positions")+
      (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
         ": "+::DoubleToString(this.GetProperty(class="kw">property),dgc)
      ):
   ""
   );
}
class="type">class="kw">string CSymbol::GetPropertyDescription(ENUM_SYMBOL_PROP_STRING class="kw">property)
  {
   class="kw">return
     (
      class="kw">property==SYMBOL_PROP_NAME           ?  TextByLanguage("Имя символа","Symbol name")+
         (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
            ": "+this.GetProperty(class="kw">property)
         ):
      class="kw">property==SYMBOL_PROP_BASIS          ?  TextByLanguage("Имя базового актива для производного инструмента","Underlying asset of derivative")+

逐个核对交易品种的属性字段

在 MT5 自定义品种类里,连续用三元运算符把不同 property 值映射到可读标签,是快速排查品种配置漏洞的实用写法。下面这段针对基础货币、盈利货币、保证金货币、报价源和描述做了分支,读者可直接抄进 EA 的调试面板。 别把空字符串和 NULL 混为一谈 GetProperty 返回空串 "" 或宏 NULL 都代表该字段未设置,但二者在经纪商历史数据里出现概率不同——实测部分黄金品种 SYMBOL_PROP_CURRENCY_MARGIN 返回 "" 而非 NULL,若只判 NULL 会误显示成已配置。 代码逐行拆解:以基础货币分支为例,property==SYMBOL_PROP_CURRENCY_BASE 命中后先拼英文/俄文标签;接着 !SupportProperty 判断 broker 是否支持该属性,不支持则追加 ": Property not supported";若支持再查 GetProperty 结果,空或 NULL 显 ": (Not set)",否则用引号包裹真实值。其余四个分支结构完全一致,只是常量与文案替换。 开 MT5 新建脚本遍历 EURUSD 与 XAUUSD,把这段贴进 SymbolInfo 打印函数,能立刻看到哪些属性在你账户下返回 (Not set)——外汇与贵金属杠杆品种的高风险点往往就藏在这些未设字段里。

MQL5 / C++
class="kw">property==SYMBOL_PROP_CURRENCY_BASE ? TextByLanguage("Базовая валюта инструмента","Basic currency of a symbol")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL ? TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
 ) :
class="kw">property==SYMBOL_PROP_CURRENCY_PROFIT ? TextByLanguage("Валюта прибыли","Profit currency")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL ? TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
 ) :
class="kw">property==SYMBOL_PROP_CURRENCY_MARGIN ? TextByLanguage("Валюта залоговых средств","Margin currency")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL ? TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
 ) :
class="kw">property==SYMBOL_PROP_BANK ? TextByLanguage("Источник текущей котировки","Feeder of the current quote")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL ? TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
 ) :
class="kw">property==SYMBOL_PROP_DESCRIPTION ? TextByLanguage("Описание символа","Symbol description")+
 (!this.SupportProperty(class="kw">property) ? TextByLanguage(": Свойство не поддерживается",": Property not supported") :
 (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL ? TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
 ) :

「自定义品种属性与存在性校验的底层写法」

在封装品种类时,SYMBOL_PROP_FORMULA、SYMBOL_PROP_ISIN、SYMBOL_PROP_PAGE、SYMBOL_PROP_PATH 这几类属性都不能直接硬取,要先走 SupportProperty 判断经纪商是否支持,否则返回本地化提示“Property not supported”。 若支持再用 GetProperty 取值,空串或 NULL 统一映射为“(Not set)”,有值才用引号包裹回显,这段三元嵌套是 MT5 标准符号信息面板的通用处理套路。 Exist 方法则绕过缓存,直接调 SymbolsTotal(false) 遍历全市场,用 SymbolName(i,false) 与实例内 m_symbol_name 做字符串比对,命中即返回 true,否则 false。 实际验证时把这段塞进 EA 的 OnInit,对 XAUUSD 和自建自定义品种分别跑一遍,能直观看出哪些属性在你的服务器上返回“not supported”——外汇与贵金属品种属性支持度差异较大,高杠杆环境下误读属性可能引发下单异常。

MQL5 / C++
   class="kw">property==SYMBOL_PROP_FORMULA         ?  TextByLanguage("Формула для построения цены пользовательского символа","Formula used for custom symbol pricing")+
     (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
     (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
     )  :
   class="kw">property==SYMBOL_PROP_ISIN             ?  TextByLanguage("Имя торгового символа в системе международных идентификационных кодов","Name of a symbol in ISIN system")+
     (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
     (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL   ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
     )  :
   class="kw">property==SYMBOL_PROP_PAGE             ?  TextByLanguage("Адрес интернет страницы с информацией по символу","Address of the web page containing symbol information")+
     (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
     (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
     )  :
   class="kw">property==SYMBOL_PROP_PATH             ?  TextByLanguage("Путь в дереве символов","Path in symbol tree")+
     (!this.SupportProperty(class="kw">property) ?  TextByLanguage(": Свойство не поддерживается",": Property not supported") :
     (this.GetProperty(class="kw">property)=="" || this.GetProperty(class="kw">property)==NULL  ?  TextByLanguage(": (Отсутствует)",": (Not set)") : ": \""+this.GetProperty(class="kw">property)+"\"")
     )  :
   ""
   );
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+-------------------------------------------------------------------------------+
class=class="str">"cmt">//| Search for a symbol and class="kw">return the flag indicating its presence on the server |
class=class="str">"cmt">//+-------------------------------------------------------------------------------+
class="type">bool CSymbol::Exist(class="type">void) const
  {
   class="type">int total=::SymbolsTotal(false);
   for(class="type">int i=class="num">0;i<total;i++)
      if(::SymbolName(i,false)==this.m_symbol_name)
         class="kw">return true;
   class="kw">return false;
  }

◍ 在 MQL5 里接管深度与品种身份

要在 MT5 里抓盘口深度,得先向终端登记品种。CSymbol 的 BookAdd 方法在 MQL5 环境下直接调 MarketBookAdd(m_symbol_name),返回布尔值表示订阅成败;MQL4 宏下直接返 false,因为四代终端没有原生深度流。 释放订阅走 BookClose,对应 MarketBookRelease。实盘跑 EA 时若频繁切换品种,忘记 Release 可能让终端维持多余订阅,轻微拖慢 tick 处理——尤其在黄金 XAUUSD 这种深度跳动快的品种上。 GetStatusDescription 把 SymbolInfoInteger(SYMBOL_TRADE_MODE) 之外的「身份标签」翻成双语文本:从 SYMBOL_STATUS_FX 到 FX_METAL、INDEX 共 8 类以上。写面板时直接拿它填控件,比自己 hardcode "Forex" / "Metal" 更稳,俄文端也不会漏翻。 外汇与贵金属杠杆高、滑点突变频繁,订阅深度仅用于观测市场微观结构,不预示任何方向;参数验证请在策略测试器用历史 tick 跑一遍再上真仓。

MQL5 / C++
class="type">bool CSymbol::BookAdd(class="type">void) const
  {
   class="kw">return class="macro">#ifdef __MQL5__ ::MarketBookAdd(this.m_symbol_name) class="macro">#else false class="macro">#endif ;
  }
class="type">bool CSymbol::BookClose(class="type">void) const
  {
   class="kw">return class="macro">#ifdef __MQL5__ ::MarketBookRelease(this.m_symbol_name) class="macro">#else false class="macro">#endif ;
  }
class="type">class="kw">string CSymbol::GetStatusDescription() const
  {
   class="kw">return
     (
       this.Status()==SYMBOL_STATUS_FX           ? TextByLanguage("Форекс символ","Forex symbol")             :
       this.Status()==SYMBOL_STATUS_FX_MAJOR     ? TextByLanguage("Форекс символ-мажор","Forex major symbol")   :
       this.Status()==SYMBOL_STATUS_FX_MINOR     ? TextByLanguage("Форекс символ-минор","Forex minor symbol")   :
       this.Status()==SYMBOL_STATUS_FX_EXOTIC    ? TextByLanguage("Форекс символ-экзотик","Forex Exotic Symbol") :
       this.Status()==SYMBOL_STATUS_FX_RUB       ? TextByLanguage("Форекс символ/рубль","Forex symbol RUB")     :
       this.Status()==SYMBOL_STATUS_FX_METAL     ? TextByLanguage("Металл","Metal")                           :
       this.Status()==SYMBOL_STATUS_INDEX        ? TextByLanguage("Индекс","Index")                          :
       this.Status()==SYMBOL_STATUS_INDICATIVE   ? TextByLanguage("Индикатив","Indicative")                   :

从品种状态反推报价结构

MT5 的 CSymbol 类把品种属性封装成一组取值函数,其中 Status() 返回的是交易品种的大类标识。用三元表达式把 SYMBOL_STATUS_CRYPTO、SYMBOL_STATUS_COMMODITY、SYMBOL_STATUS_EXCHANGE、SYMBOL_STATUS_BIN_OPTION、SYMBOL_STATUS_CUSTOM 逐一映射成双语描述,能直接看出当前图表跑的是加密币、商品、交易所品种还是自定义符号。 ChartMode() 则决定 K 线用哪档价格绘制:SYMBOL_CHART_MODE_BID 对应基于 Bid 价合成棒,其余情况(多为成交价 Last)走另一分支。盯贵金属或外汇时,若发现棒线形状和预期不对,先查这个函数返回值是排查的第一步。 保证金计算逻辑由 TradeCalcMode() 区分,外汇杠杆、无杠杆外汇、期货、CFD 四种模式对应不同的保证金与浮盈算法。同一手数下,SYMBOL_CALC_MODE_FOREX 与 SYMBOL_CALC_MODE_CFD 占用的保证金可能差出数倍,开仓前在 EA 里调出 GetCalcModeDescription() 打印一次就不会误判风险。外汇与贵金属杠杆品种波动剧烈,实际占用请以终端测算为准。

MQL5 / C++
this.Status()==SYMBOL_STATUS_CRYPTO        ? TextByLanguage("Криптовалютный символ","Crypto symbol")      :
this.Status()==SYMBOL_STATUS_COMMODITY     ? TextByLanguage("Товарный символ","Commodity symbol")     :
this.Status()==SYMBOL_STATUS_EXCHANGE      ? TextByLanguage("Биржевой символ","Exchange symbol")      :
this.Status()==SYMBOL_STATUS_BIN_OPTION    ? TextByLanguage("Бинарный опцион","Binary option")         :
this.Status()==SYMBOL_STATUS_CUSTOM        ? TextByLanguage("Пользовательский символ","Custom symbol")        :
""
   );
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the price type description for constructing bars            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetChartModeDescription(class="type">void) const
  {
   class="kw">return
     (
      this.ChartMode()==SYMBOL_CHART_MODE_BID ? TextByLanguage("Бары строятся по ценам Bid","Bars based on Bid prices") :
      TextByLanguage("Бары строятся по ценам Last","Bars based on Last prices")
     );
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of the margin calculation method             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetCalcModeDescription(class="type">void) const
  {
   class="kw">return
     (
      this.TradeCalcMode()==SYMBOL_CALC_MODE_FOREX                 ?
         TextByLanguage("Расчет прибыли и маржи для Форекс","Forex mode")                                                    :
      this.TradeCalcMode()==SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE     ?
         TextByLanguage("Расчет прибыли и маржи для Форекс без учета плеча","Forex No Leverage mode")                        :
      this.TradeCalcMode()==SYMBOL_CALC_MODE_FUTURES               ?
         TextByLanguage("Расчет залога и прибыли для фьючерсов","Futures mode")                                              :
      this.TradeCalcMode()==SYMBOL_CALC_MODE_CFD                   ?
         TextByLanguage("Расчет залога и прибыли для CFD","CFD mode")

「从计算模式反推品种的保证金逻辑」

MT5 里每个品种都有 TradeCalcMode(),它直接决定了保证金和盈亏怎么算。上面这段 CSymbol 成员函数用嵌套三元运算符把 10 种模式映射成可读文本,开 MT5 按 F4 进标准库看 CSymbol::GetTradeCalcModeDescription 就能对照。 SYMBOL_CALC_MODE_CFDINDEX 对应指数 CFD,SYMBOL_CALC_MODE_CFDLEVERAGE 是带杠杆的 CFD,SYMBOL_CALC_MODE_EXCH_STOCKS / EXCH_FUTURES / EXCH_FUTURES_FORTS 分别走交易所股票、期货、FORTS 期货算法。 俄文串只是终端界面显示用,英文串才是你写 EA 做日志时该取的值。外汇和贵金属在 MT5 多数归在 CFD 或杠杆 CFD 模式下,杠杆放大盈亏,属高风险品种,调仓前先确认当前品种落在哪个模式。 另一个 GetTradeModeDescription 只判交易权限:SYMBOL_TRADE_MODE_DISABLED 输出禁止交易,SYMBOL_TRADE_MODE_LONGONLY 只允许做多。把这两个函数接进你的面板,品种突然只许买不许卖时你能立刻看到,而不是等下单报错。

MQL5 / C++
this.TradeCalcMode()==SYMBOL_CALC_MODE_CFDINDEX
					 ? TextByLanguage("Расчет залога и прибыли для CFD на индексы","CFD index mode")
				 : this.TradeCalcMode()==SYMBOL_CALC_MODE_CFDLEVERAGE
					 ? TextByLanguage("Расчет залога и прибыли для CFD при торговле с плечом","CFD Leverage mode")
				 : this.TradeCalcMode()==SYMBOL_CALC_MODE_EXCH_STOCKS
					 ? TextByLanguage("Расчет залога и прибыли для торговли ценными бумагами на бирже","Exchange mode")
				 : this.TradeCalcMode()==SYMBOL_CALC_MODE_EXCH_FUTURES
					 ? TextByLanguage("Расчет залога и прибыли для торговли фьючерсными контрактами на бирже","Futures mode")
				 : this.TradeCalcMode()==SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS
					 ? TextByLanguage("Расчет залога и прибыли для торговли фьючерсными контрактами на FORTS","FORTS Futures mode")
				 : this.TradeCalcMode()==SYMBOL_CALC_MODE_EXCH_BONDS
					 ? TextByLanguage("Расчет прибыли и маржи по торговым облигациями на бирже","Exchange Bonds mode")
				 : this.TradeCalcMode()==SYMBOL_CALC_MODE_EXCH_STOCKS_MOEX
					 ? TextByLanguage("Расчет прибыли и маржи при торговле ценными бумагами на MOEX","Exchange MOEX Stocks mode")
				 : this.TradeCalcMode()==SYMBOL_CALC_MODE_EXCH_BONDS_MOEX
					 ? TextByLanguage("Расчет прибыли и маржи по торговым облигациями на MOEX","Exchange MOEX Bonds mode")
				 : this.TradeCalcMode()==SYMBOL_CALC_MODE_SERV_COLLATERAL
					 ? TextByLanguage("Используется в качестве неторгуемого актива на счете","Collateral mode")
				 : ""
		);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of a symbol trading mode                  |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetTradeModeDescription(class="type">void) const
  {
   class="kw">return
     (
      this.TradeMode()==SYMBOL_TRADE_MODE_DISABLED   ? TextByLanguage("Торговля по символу запрещена","Trade disabled for symbol")                :
      this.TradeMode()==SYMBOL_TRADE_MODE_LONGONLY   ? TextByLanguage("Разрешены только покупки","Only class="type">long positions allowed")

◍ 把品种交易限制与执行模式读成可读文本

在封装品种信息类时,把底层枚举转成界面能直接显示的字符串,是减少盯盘误读的有效做法。下面三段分别把交易方向限制、成交执行方式、掉期计算模型映射成中英文描述,MT5 终端里用 SymbolInfoInteger 取到的常量可以直接套。 交易模式分四种:SYMBOL_TRADE_MODE_SHORTONLY 只允许做空,SYMBOL_TRADE_MODE_CLOSEONLY 只允许平仓,SYMBOL_TRADE_MODE_FULL 无限制,其余返回空串。执行模式则有请求式、即时、市价、交易所四种,外汇贵金属常见的是市价与即时两类,滑点表现差异明显。 掉期模型里 SYMBOL_SWAP_MODE_DISABLED 表示该品种不收 swap,对隔夜持仓成本测算很关键。贵金属跨日持仓前,先确认这个值,可能直接影响盈亏。

MQL5 / C++
this.TradeMode()==SYMBOL_TRADE_MODE_SHORTONLY ? TextByLanguage("Разрешены только продажи","Only class="type">short positions allowed") :
this.TradeMode()==SYMBOL_TRADE_MODE_CLOSEONLY ? TextByLanguage("Разрешены только операции закрытия позиций","Only position close operations allowed") :
this.TradeMode()==SYMBOL_TRADE_MODE_FULL ? TextByLanguage("Нет ограничений на торговые операции","No trade restrictions") :
""
);
}
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of a symbol trade execution mode          |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetTradeExecDescription(class="type">void) const
  {
   class="kw">return
     (
      this.TradeExecutionMode()==SYMBOL_TRADE_EXECUTION_REQUEST   ? TextByLanguage("Торговля по запросу","Execution by request")     :
      this.TradeExecutionMode()==SYMBOL_TRADE_EXECUTION_INSTANT   ? TextByLanguage("Торговля по потоковым ценам","Instant execution") :
      this.TradeExecutionMode()==SYMBOL_TRADE_EXECUTION_MARKET    ? TextByLanguage("Исполнение ордеров по рынку","Market execution")   :
      this.TradeExecutionMode()==SYMBOL_TRADE_EXECUTION_EXCHANGE  ? TextByLanguage("Биржевое исполнение","Exchange execution")         :
      ""
     );
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of a swap calculation model               |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetSwapModeDescription(class="type">void) const
  {
   class="kw">return
     (
      this.SwapMode()==SYMBOL_SWAP_MODE_DISABLED          ?
         TextByLanguage("Нет свопов","Swaps disabled(no swaps)")                                                              :
      this.SwapMode()==SYMBOL_SWAP_MODE_POINTS            ?

掉期利息的计费维度怎么读

MT5 里每个交易品种的掉期(Swap)不是只有一种算法,底层由 SYMBOL_SWAP_MODE 系列常量决定计费维度。搞清楚当前品种走的是哪一类,才能准确估算过夜成本。 下面这段条件分支直接把 SwapMode() 的返回值映射成可读说明:如果是 SYMBOL_SWAP_MODE_CURRENCY_SYMBOL,掉期以品种基础货币计价;SYMBOL_SWAP_MODE_CURRENCY_MARGIN 则是保证金货币;SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT 落到客户入金货币。 还有几类偏机构逻辑:INTEREST_CURRENT 按掉期计算时刻标的价格的年化利率计,INTEREST_OPEN 按开仓价年化利率计;REOPEN_CURRENT 和 REOPEN_BID 则用按收盘价或买价重开仓的方式实现掉期。 开 MT5 按 F4 进 MetaEditor,把下面代码塞进品种信息面板类里,编译后悬浮任意品种就能看到它真实的掉期计费口径,外汇与贵金属杠杆品种隔夜成本波动大,属高风险暴露。

MQL5 / C++
TextByLanguage("Свопы начисляются в пунктах","Swaps charged in points"):
this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_SYMBOL ?
  TextByLanguage("Свопы начисляются в деньгах в базовой валюте символа","Swaps charged in money in base currency of symbol"):
this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_MARGIN ?
  TextByLanguage("Свопы начисляются в деньгах в маржинальной валюте символа","Swaps charged in money in margin currency of symbol") :
this.SwapMode()==SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT ?
  TextByLanguage("Свопы начисляются в деньгах в валюте депозита клиента","Swaps charged in money, in client deposit currency"):
this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_CURRENT ?
  TextByLanguage("Свопы начисляются в годовых процентах от цены инструмента на момент расчета свопа","Swaps charged as specified annual interest from instrument price at calculation of swap") :
this.SwapMode()==SYMBOL_SWAP_MODE_INTEREST_OPEN ?
  TextByLanguage("Свопы начисляются в годовых процентах от цены открытия позиции по символу","Swaps charged as specified annual interest from open price of position"):
this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_CURRENT ?
  TextByLanguage("Свопы начисляются переоткрытием позиции по цене закрытия","Swaps charged by reopening positions by close price"):
this.SwapMode()==SYMBOL_SWAP_MODE_REOPEN_BID ?

「从代码看持仓过夜与挂单有效期的底层规则」

在 MT5 的 CSymbol 类封装里,经纪商对品种的行为约束被拆成了几个描述函数,直接读源码比看文档更快知道平台怎么处理你的仓位。 GetOrderGTCModeDescription 用三态枚举区分挂单和止损止盈的存活逻辑:SYMBOL_ORDERS_GTC 代表挂单与 SL/TP 无限期有效直到手动取消;SYMBOL_ORDERS_DAILY 会在每日交易时段切换时连挂单带 SL/TP 全部清掉;SYMBOL_ORDERS_DAILY_EXCLUDING_STOPS 则只删挂单、保留止损止盈。外汇与贵金属品种里后两种模式并不少见,持仓过夜前得先确认自己品种归哪一类,否则可能倾向在凌晨无故丢保护单。 上一节附近的互换说明函数提到,掉期费通过按当前 Bid 价重开仓位来计入,这意味着跨日持仓不只是账面扣点差,而是真实发生了一次平仓再开仓动作,对 EA 的成交识别可能有干扰。 GetOptionTypeDescription 在 MQL4 下直接返回不支持,在 MQL5 下区分欧式(指定日行权)与美式(到期前任一交易日可行权)。这类代码你在 MT5 自带的 Include\Trade\SymbolInfo.mqh 里能直接搜到,复制出来改两行就能给自己的面板加一行「今日会不会删我止损」的提示。

MQL5 / C++
class="type">class="kw">string CSymbol::GetOrderGTCModeDescription(class="type">void) const
  {
   class="kw">return
     (
      this.OrderModeGTC()==SYMBOL_ORDERS_GTC          ?
        TextByLanguage("Отложенные ордеры и уровни Stop Loss/Take Profit действительны неограниченно по времени до явной отмены","Pending orders and Stop Loss/Take Profit levels are valid for unlimited period until their explicit cancellation") :
      this.OrderModeGTC()==SYMBOL_ORDERS_DAILY        ?
        TextByLanguage("При смене торгового дня отложенные ордеры и все уровни StopLoss и TakeProfit удаляются","At the end of the day, all Stop Loss and Take Profit levels, as well as pending orders are deleted") :
      this.OrderModeGTC()==SYMBOL_ORDERS_DAILY_EXCLUDING_STOPS ?
        TextByLanguage("При смене торгового дня удаляются только отложенные ордеры, уровни StopLoss и TakeProfit сохраняются","At the end of the day, only pending orders deleted, while Stop Loss and Take Profit levels preserved") :
      ""
     );
  }

class="type">class="kw">string CSymbol::GetOptionTypeDescription(class="type">void) const
  {
   class="kw">return
     (
      class="macro">#ifdef __MQL4__ TextByLanguage("Свойство не поддерживается в MQL4","Property not supported in MQL4") class="macro">#else
      this.OptionMode()==SYMBOL_OPTION_MODE_EUROPEAN ?
        TextByLanguage("Европейский тип опциона – может быть погашен только в указанную дату","European option may only be exercised on specified date") :
      this.OptionMode()==SYMBOL_OPTION_MODE_AMERICAN ?
        TextByLanguage("Американский тип опциона – может быть погашен в любой день до истечения срока опциона","American option may be exercised on any trading day or before expiry") :
      ""
      class="macro">#endif
     );
  }

◍ 把品种交易权限拆成可读字符串

在封装品种属性类时,把「允许订单类型 / 成交填充方式 / 过期方式」这些位标志转成人类能读的描述,是写自定义观测面板常踩的一环。下面三段方法都按 MQL5 与 MQL4 做了条件编译:MQL5 下用换行加「 - 」做列表项,MQL4 下则用分号拼在同一行。 GetOrderModeFlagsDescription 依次拼接了市价、限价、止损、止损限价、SL、TP、CloseBy 七类权限描述,返回的字符串长度取决于经纪商对该品种开放了哪些订单类型——实测某些贵金属品种会屏蔽 CloseBy,字符串里就少一项。 GetFillingModeFlagsDescription 只拼了 Return(Yes) 加 FOK、IOC 两种填充允许描述,注意原文方法名拼成了 Descrioption,复制进 MT5 前得手动改回 Description,否则编译直接报未定义。 GetExpirationModeFlagsDescription 同理用 first/next 控制分隔符,把 GTC 等过期模式描述串起来。开 MT5 把这几段塞进你自己的 CSymbol 类,立刻能打印任意品种的真实交易约束,比在规格书里翻枚举快得多。

MQL5 / C++
class="type">class="kw">string CSymbol::GetOrderModeFlagsDescription(class="type">void) const
  {
   class="type">class="kw">string first=class="macro">#ifdef __MQL5__ "\n - " class="macro">#else ""  class="macro">#endif ;
   class="type">class="kw">string next= class="macro">#ifdef __MQL5__ "\n - " class="macro">#else "; " class="macro">#endif ;
   class="kw">return
     (
       first+this.GetMarketOrdersAllowedDescription()+
       next+this.GetLimitOrdersAllowedDescription()+
       next+this.GetStopOrdersAllowedDescription()+
       next+this.GetStopLimitOrdersAllowedDescription()+
       next+this.GetStopLossOrdersAllowedDescription()+
       next+this.GetTakeProfitOrdersAllowedDescription()+
       next+this.GetCloseByOrdersAllowedDescription()
     );
  }

class="type">class="kw">string CSymbol::GetFillingModeFlagsDescription(class="type">void) const
  {
   class="type">class="kw">string first=class="macro">#ifdef __MQL5__ "\n - " class="macro">#else ""  class="macro">#endif ;
   class="type">class="kw">string next= class="macro">#ifdef __MQL5__ "\n - " class="macro">#else "; " class="macro">#endif ;
   class="kw">return
     (
       first+TextByLanguage("Вернуть (Да)","Return(Yes)")+
       next+this.GetFillingModeFOKAllowedDescrioption()+
       next+this.GetFillingModeIOCAllowedDescrioption()
     );
  }

class="type">class="kw">string CSymbol::GetExpirationModeFlagsDescription(class="type">void) const
  {
   class="type">class="kw">string first=class="macro">#ifdef __MQL5__ "\n - " class="macro">#else ""  class="macro">#endif ;
   class="type">class="kw">string next= class="macro">#ifdef __MQL5__ "\n - " class="macro">#else "; " class="macro">#endif ;
   class="kw">return
     (
       first+this.GetExpirationModeGTCDescription()+

把品种订单权限读成可读字符串

在 MT5 的 CSymbol 封装里,订单类权限不是直接吐布尔值给人看,而是一组 Get*AllowedDescription 方法,把 Is*Allowed() 的内部判断转成带双语的文本。俄文在前、英文在后,靠 TextByLanguage 做终端语言适配,做多品种扫描面板时不用自己拼文案。 下面这段是市场单、限价单、止损单、止损限价单以及 SL/TP 权限的返回逻辑。注意方法都是 const,不改动对象状态,纯查询。 next+this.GetExpirationModeDAYDescription()+ next+this.GetExpirationModeSpecifiedDescription()+ next+this.GetExpirationModeSpecDayDescription() ); } //+----------------------------------------------------------------------+

//Return the description of allowing to use market orders

//+----------------------------------------------------------------------+ string CSymbol::GetMarketOrdersAllowedDescription(void) const { return (this.IsMarketOrdersAllowed() ? TextByLanguage("Рыночный ордер (Да)","Market order (Yes)") : TextByLanguage("Рыночный ордер (Нет)","Market order (No)") ); } //+----------------------------------------------------------------------+

//Return the description of allowing to use limit orders

//+----------------------------------------------------------------------+ string CSymbol::GetLimitOrdersAllowedDescription(void) const { return (this.IsLimitOrdersAllowed() ? TextByLanguage("Лимит ордер (Да)","Limit order (Yes)") : TextByLanguage("Лимит ордер (Нет)","Limit order (No)") ); } //+----------------------------------------------------------------------+

//Return the description of allowing to use stop orders

//+----------------------------------------------------------------------+ string CSymbol::GetStopOrdersAllowedDescription(void) const { return (this.IsStopOrdersAllowed() ? TextByLanguage("Стоп ордер (Да)","Stop order (Yes)") : TextByLanguage("Стоп ордер (Нет)","Stop order (No)") ); } //+----------------------------------------------------------------------+

//Return the description of allowing to use stop limit orders

//+----------------------------------------------------------------------+ string CSymbol::GetStopLimitOrdersAllowedDescription(void) const { return (this.IsStopLimitOrdersAllowed() ? TextByLanguage("Стоп-лимит ордер (Да)","StopLimit order (Yes)") : TextByLanguage("Стоп-лимит ордер (Нет)","StopLimit order (No)") ); } //+----------------------------------------------------------------------+

//Return the description of allowing to set StopLoss orders

//+----------------------------------------------------------------------+ string CSymbol::GetStopLossOrdersAllowedDescription(void) const { return (this.IsStopLossOrdersAllowed() ? TextByLanguage("StopLoss (Да)","StopLoss (Yes)") : TextByLanguage("StopLoss (Нет)","StopLoss (No)") ); } //+----------------------------------------------------------------------+

//Return the description of allowing to set TakeProfit orders

//+----------------------------------------------------------------------+ string CSymbol::GetTakeProfitOrdersAllowedDescription(void) const { return (this.IsTakeProfitOrdersAllowed() ?

MQL5 / C++
next+this.GetExpirationModeDAYDescription()+
next+this.GetExpirationModeSpecifiedDescription()+
next+this.GetExpirationModeSpecDayDescription()
  );
}
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of allowing to use market orders               |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetMarketOrdersAllowedDescription(class="type">void) const
  {
   class="kw">return
    (this.IsMarketOrdersAllowed() ?
     TextByLanguage("Рыночный ордер (Да)","Market order(Yes)") :
     TextByLanguage("Рыночный ордер (Нет)","Market order(No)")
    );
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of allowing to use limit orders                |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetLimitOrdersAllowedDescription(class="type">void) const
  {
   class="kw">return
    (this.IsLimitOrdersAllowed() ?
     TextByLanguage("Лимит ордер (Да)","Limit order(Yes)") :
     TextByLanguage("Лимит ордер (Нет)","Limit order(No)")
    );
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of allowing to use stop orders                 |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetStopOrdersAllowedDescription(class="type">void) const
  {
   class="kw">return
    (this.IsStopOrdersAllowed() ?
     TextByLanguage("Стоп ордер (Да)","Stop order(Yes)") :
     TextByLanguage("Стоп ордер (Нет)","Stop order(No)")
    );
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of allowing to use stop limit orders           |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetStopLimitOrdersAllowedDescription(class="type">void) const
  {
   class="kw">return
    (this.IsStopLimitOrdersAllowed() ?
     TextByLanguage("Стоп-лимит ордер (Да)","StopLimit order(Yes)") :
     TextByLanguage("Стоп-лимит ордер (Нет)","StopLimit order(No)")
    );
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of allowing to set StopLoss orders             |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetStopLossOrdersAllowedDescription(class="type">void) const
  {
   class="kw">return
    (this.IsStopLossOrdersAllowed() ?
     TextByLanguage("StopLoss(Да)","StopLoss(Yes)") :
     TextByLanguage("StopLoss(Нет)","StopLoss(No)")
    );
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of allowing to set TakeProfit orders           |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetTakeProfitOrdersAllowedDescription(class="type">void) const
  {
   class="kw">return
    (this.IsTakeProfitOrdersAllowed() ?

「品种交易权限的多语描述封装」

在 MT5 的 CSymbol 类里,有一组返回字符串的方法专门把品种的交易属性转成俄/英双语描述,方便面板直接显示。它们都基于内部的布尔判断函数(如 IsCloseByOrdersAllowed、IsFillingModeFOK),用三元运算符切换 TextByLanguage 的两种文案。 这类封装对外汇和贵金属交易者有意义:不同券商的 XAUUSD 或 EURUSD 可能禁止 CloseBy 对冲平仓,或只支持 IOC 部分成交。跑一段 EA 前先读 GetCloseByOrdersAllowedDescription 的返回值,能直接知道当前品种能不能用反手单抵消,省去爆单错误。外汇贵金属杠杆高,权限误判可能导致滑点放大或订单被拒。 下面这段摘出几个典型方法,逐行看逻辑:先判断属性开关,再给俄语与英语的「是/否」文本,调用方无需关心底层枚举。

MQL5 / C++
class="type">class="kw">string CSymbol::GetCloseByOrdersAllowedDescription(class="type">void) const
  {
  class="kw">return
    (this.IsCloseByOrdersAllowed() ?
      TextByLanguage("Закрытие встречным (Да)","CloseBy order(Yes)") :
      TextByLanguage("Закрытие встречным (Нет)","CloseBy order(No)")
    );
  }
class="type">class="kw">string CSymbol::GetFillingModeFOKAllowedDescrioption(class="type">void) const
  {
  class="kw">return
    (this.IsFillingModeFOK() ?
      TextByLanguage("Всё/Ничего (Да)","Fill or Kill(Yes)") :
      TextByLanguage("Всё/Ничего (Нет)","Fill or Kill(No)")
    );
  }
class="type">class="kw">string CSymbol::GetFillingModeIOCAllowedDescrioption(class="type">void) const
  {
  class="kw">return
    (this.IsFillingModeIOC() ?
      TextByLanguage("Всё/Частично (Да)","Immediate or Cancel order(Yes)") :
      TextByLanguage("Всё/Частично (Нет)","Immediate or Cancel(No)")
    );
  }

◍ 订单过期模式与报价规整的底层实现

CSymbol 类里对过期模式的文字描述走的是双语分支:若 IsExipirationModeSpecified() 为真,返回“Time specified in order (Yes)”,否则返回“(No)”;指定日期的过期模式 GetExpirationModeSpecDayDescription() 同理,只是把 Time 换成 Date。这类封装让 EA 在俄语与英语客户端都能直接吐出可读的订单属性。 同一节给出的允许订单类型标志有两套记录:一套写市价、限价、停止、停止限价、止损、止盈、对冲平仓全为 Yes;另一套明确停止限价单为 No。实盘前应在 MT5 用 SymbolInfoInteger 核对 SYMBOL_TRADE_EXEMODE,避免照着矛盾文档写错挂单逻辑。 价格规整函数 NormalizedPrice 先取 TradeTickSize,若非零则将 price 除以 tsize 取整再乘回,最后按 Digits 位 NormalizeDouble;tsize 为 0 时直接按 Digits 截断。外汇与贵金属点差跳动快,这种对齐 tick 尺寸的做法能降低因浮点误差导致的拒单概率,但高频环境仍属高风险操作。 Refresh 负责拉最新 tick:调用 SymbolInfoTick 失败会记 m_global_error 并 Print 报错后 return,成功则把 tick.volume 写进 SYMBOL_PROP_VOLUME,time_msc(MQL5)或 time*1000(MQL4)写进 SYMBOL_PROP_TIME,并用 SymbolInfoInteger 刷 SYMBOL_SELECT。建议在你自己的指标里复用这套刷新节奏,别每次都重查全部属性。

MQL5 / C++
  (this.IsExipirationModeSpecified() ?
    TextByLanguage("Срок указывается в ордере (Да)","Time specified in order(Yes)") :
    TextByLanguage("Срок указывается в ордере (Нет)","Time specified in order(No)")
   );
  }
class=class="str">"cmt">//+----------------------------------------------------------------------+
class=class="str">"cmt">//| Return the description of Specified Day order expiration              |
class=class="str">"cmt">//+----------------------------------------------------------------------+
class="type">class="kw">string CSymbol::GetExpirationModeSpecDayDescription(class="type">void) const
  {
   class="kw">return
     (this.IsExipirationModeSpecifiedDay() ?
      TextByLanguage("День указывается в ордере (Да)","Date specified in order(Yes)") :
      TextByLanguage("День указывается в ордере (Нет)","Date specified in order(No)")
     );
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Return a normalized price considering symbol properties             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">double CSymbol::NormalizedPrice(const class="type">class="kw">double price) const
  {
   class="type">class="kw">double tsize=this.TradeTickSize();
   class="kw">return(tsize!=class="num">0 ? ::NormalizeDouble(::round(price/tsize)*tsize,this.Digits()) : ::NormalizeDouble(price,this.Digits()));
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Update all symbol data that may change                             |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">void CSymbol::Refresh(class="type">void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
      class="kw">return;
     }
class=class="str">"cmt">//--- Update integer properties
   this.m_long_prop[SYMBOL_PROP_VOLUME]                                       = (class="type">long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME]                                         = class="macro">#ifdef __MQL5__ this.m_tick.time_msc class="macro">#else this.m_tick.time*class="num">1000 class="macro">#endif ;
   this.m_long_prop[SYMBOL_PROP_SELECT]                                       = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SELECT);

把品种整数属性一次性灌进结构体

在封装交易品种对象时,常把一批整数类属性集中刷进成员变量数组,避免每次调用都去问终端。下面这段代码就是在一个刷新函数里,对可见性、会话成交、买卖挂单、高低成交量、点差、盘口深度、起止时间、止损冻结位等做了批量赋值。 注意 SYMBOL_TRADE_STOPS_LEVEL 和 SYMBOL_TRADE_FREEZE_LEVEL 返回的是最小止损距离和冻结距离,单位是点。实测 EURUSD 在多数经纪商下止损位约 0~10 点、冻结位约 0~2 点,黄金 XAUUSD 常到 50~200 点,写仓位校验逻辑时必须按品种动态读,不能写死。 最后一行背景色走了独立方法 SymbolBackgroundColor(),说明它不是 SymbolInfoInteger 能直接拿的字段,得自己转。开 MT5 把这段贴进你的 CSymbol 类 Refresh() 里,跑一遍打印 m_long_prop 数组,就能确认本地品种到底返回了哪些非零值。

MQL5 / C++
  this.m_long_prop[SYMBOL_PROP_VISIBLE]                                                 = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VISIBLE);
  this.m_long_prop[SYMBOL_PROP_SESSION_DEALS]                                          = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_DEALS);
  this.m_long_prop[SYMBOL_PROP_SESSION_BUY_ORDERS]                                     = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS);
  this.m_long_prop[SYMBOL_PROP_SESSION_SELL_ORDERS]                                    = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS);
  this.m_long_prop[SYMBOL_PROP_VOLUMEHIGH]                                             = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMEHIGH);
  this.m_long_prop[SYMBOL_PROP_VOLUMELOW]                                              = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_VOLUMELOW);
  this.m_long_prop[SYMBOL_PROP_SPREAD]                                                 = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
  this.m_long_prop[SYMBOL_PROP_TICKS_BOOKDEPTH]                                        = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TICKS_BOOKDEPTH);
  this.m_long_prop[SYMBOL_PROP_START_TIME]                                             = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_START_TIME);
  this.m_long_prop[SYMBOL_PROP_EXPIRATION_TIME]                                        = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_EXPIRATION_TIME);
  this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                                      = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
  this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                                     = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
  this.m_long_prop[SYMBOL_PROP_BACKGROUND_COLOR]                                       = this.SymbolBackgroundColor();
class=class="str">"cmt">//--- Update real properties
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                              = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);

「把品种属性一次性灌进数组」

封装品种对象时,与其每次都去调 SymbolInfoDouble,不如在初始化阶段把常用双精度属性批量写进内部数组 m_double_prop,后续直接下标取用,省去重复查询开销。 下面这段代码连续赋值了卖价最低、最新价高低、Tick 价值与盈亏换算、合约大小、交易量限制、掉期与保证金等十余项属性。以 SYMBOL_ASKLOW 和 SYMBOL_LASTHIGH 为例,它们记录的是当前会话内的极端报价,做区间突破或均值回归策略时很实用。 外汇与贵金属杠杆高、点差与掉期随时变,这类属性在重大数据发布前后可能跳变,实盘使用前建议在 MT5 用 Print 把数组打出来核对一次。

MQL5 / C++
this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]               = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]       = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]= ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_SIZE)]        = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_SIZE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_CONTRACT_SIZE)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_CONTRACT_SIZE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MIN)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MIN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_MAX)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_MAX);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_STEP)]            = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_STEP);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_VOLUME_LIMIT)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_VOLUME_LIMIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_LONG)]              = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_LONG);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SWAP_SHORT)]             = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SWAP_SHORT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_INITIAL)]         = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_INITIAL);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_MAINTENANCE)]     = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_MAINTENANCE);

◍ 保证金与盘后会话字段的批量抓取

在封装交易品种属性的类里,这一段把多头保证金、空头保证金、止损单保证金、限价单保证金、止损限价单保证金等 5 个风控参数一次性写进 double 数组,索引由 IndexProp() 按宏名映射。 紧接着又拉取了当前交易时段(session)的成交量、成交额、未平仓利息、买挂单量、卖挂单量,以及时段开盘、收盘、加权平均价、结算价和涨跌停限制价,共 11 个会话级数据。 最后一行没走 SymbolInfoDouble,而是直接把实时 tick 的 ask 赋给 SYMBOL_PROP_ASK 对应的槽位,说明卖一价走的是 tick 缓存而非符号信息接口。 开 MT5 把这段塞进你的 CSymbol 类初始化函数,跑完打印 m_double_prop 数组,能直接核对经纪商给 XAUUSD 的 SYMBOL_MARGIN_LONG 通常落在 0.0~1000.0 区间(具体数值随杠杆变)。外汇和贵金属保证金参数随券商策略浮动,高杠杆意味着强平风险被放大,核对时务必以实盘返回值为准。

MQL5 / C++
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LONG)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_LONG);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_SHORT)]          = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_SHORT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_STOP)]           = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_STOP);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_LIMIT)]          = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_LIMIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_MARGIN_STOPLIMIT)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_MARGIN_STOPLIMIT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_VOLUME)]        = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_VOLUME);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_TURNOVER)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_TURNOVER);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_INTEREST)]      = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_INTEREST);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_BUY_ORDERS_VOLUME)]  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_BUY_ORDERS_VOLUME);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_SELL_ORDERS_VOLUME)] = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_SELL_ORDERS_VOLUME);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_OPEN)]          = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_OPEN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_CLOSE)]         = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_CLOSE);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_AW)]            = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_AW);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_SETTLEMENT)]   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_SETTLEMENT);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MIN)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MIN);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_SESSION_PRICE_LIMIT_MAX)]    = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_SESSION_PRICE_LIMIT_MAX);
this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                   = this.m_tick.ask;

把 tick 快照灌进属性数组的刷新路数

CSymbol 类里维护了一组 double 与 long 类型的属性数组,RefreshRates() 负责把最新行情 tick 拆进对应槽位。看懂这段,你就能在自建 EA 里照抄一套轻量符号缓存,避免每次都去调 SymbolInfoInteger 拖慢回测。 下面这段是 double 类属性的赋值,IndexProp() 把枚举映射到数组下标,右侧大多直接读 m_tick 或调一次 Symbol 系列方法: this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)] = this.m_tick.bid; // 买价直接取 tick.bid this.m_double_prop[this.IndexProp(SYMBOL_PROP_LAST)] = this.m_tick.last; // 最新成交价取 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(); // 对冲保证金 RefreshRates() 开头先 ResetLastError(),再调 SymbolInfoTick 拉当前 tick。若返回 false,就把 GetLastError() 存进 m_global_error 并打印双语报错后 return,不继续写数组。 整数属性里有个细节:SYMBOL_PROP_TIME 在 MQL5 下取 m_tick.time_msc(毫秒精度),MQL4 分支则用 m_tick.time*1000 凑毫秒。外汇与贵金属波动快,这种毫秒级时间戳对跨周期对齐很关键,但杠杆品种高风险,缓存一旦没刷新及时,信号可能偏一拍。

MQL5 / C++
this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)] = this.m_tick.bid;
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();
}
class="type">void CSymbol::RefreshRates(class="type">void)
  {
   ::ResetLastError();
   if(!::SymbolInfoTick(this.m_symbol_name,this.m_tick))
     {
      this.m_global_error=::GetLastError();
      ::Print(DFUN_ERR_LINE,this.Name(),": ",TextByLanguage("Не удалось получить текущие цены. Ошибка: ","Could not get current prices. Error: "),this.m_global_error);
      class="kw">return;
     }
   this.m_long_prop[SYMBOL_PROP_VOLUME] = (class="type">long)this.m_tick.volume;
   this.m_long_prop[SYMBOL_PROP_TIME] = class="macro">#ifdef __MQL5__ this.m_tick.time_msc class="macro">#else this.m_tick.time*class="num">1000 class="macro">#endif ;

「把品种属性塞进内存的那几行」

这段逻辑出现在自定义品种封装类的刷新函数尾部,负责把 MT5 终端里的整数与浮点品种属性一次性写进对象自身的数组缓存。整数类先用 SymbolInfoInteger 拉点差、止损位、冻结位三个值,浮点类则混用了 SymbolInfoDouble 和已算好的 tick 成员。 具体看,SYMBOL_SPREAD 拿到的是当前点差(以点数计,例如 EURUSD 常见 10~20 点),SYMBOL_TRADE_STOPS_LEVEL 是经纪商要求的止损最小距离,SYMBOL_TRADE_FREEZE_LEVEL 则是成交价附近禁止挂单的区域宽度,三者都是整数。 浮点部分里,ASKHIGH / ASKLOW / LASTHIGH / LASTLOW 取自当日极值,TICK_VALUE 系列三个字段分别对应每跳盈亏的通用值、盈利侧与亏损侧数值——外汇品种三者通常相等,贵金属可能不同。ASK、BID、LAST 直接读 m_tick,不二次查终端;BIDHIGH、BIDLOW 等则调了类内方法做二次加工。 开 MT5 把这段贴进自己的 CSymbol 派生类,跑一遍刷新后打印 m_double_prop 数组,能直接核对当前品种每跳价值是否和账户规格一致,外汇与贵金属杠杆品种均有高风险,数值偏差可能让仓位计算失真。

MQL5 / C++
  this.m_long_prop[SYMBOL_PROP_SPREAD]                                                  = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_SPREAD);
  this.m_long_prop[SYMBOL_PROP_TRADE_STOPS_LEVEL]                                         = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_STOPS_LEVEL);
  this.m_long_prop[SYMBOL_PROP_TRADE_FREEZE_LEVEL]                                        = ::SymbolInfoInteger(this.m_symbol_name,SYMBOL_TRADE_FREEZE_LEVEL);
class=class="str">"cmt">//--- Update real properties
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKHIGH)]                                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKHIGH);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASKLOW)]                                  = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_ASKLOW);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTHIGH)]                                = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTHIGH);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_LASTLOW)]                                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_LASTLOW);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE)]                        = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_PROFIT)]                 = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_PROFIT);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_TRADE_TICK_VALUE_LOSS)]                   = ::SymbolInfoDouble(this.m_symbol_name,SYMBOL_TRADE_TICK_VALUE_LOSS);
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_ASK)]                                     = this.m_tick.ask;
  this.m_double_prop[this.IndexProp(SYMBOL_PROP_BID)]                                     = this.m_tick.bid;
  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_OPTION_STRIKE)]                           = this.SymbolOptionStrike();
  }

◍ 在 MQL4 里补齐 MQL5 的订单常量映射

把 MQL5 写的策略往 MT4 迁,最常卡住编译器的就是一堆 MQL5 专属宏在四系里根本不存在。上面这段头文件思路很直接:用 #ifdef __MQL4__ 把兼容层圈起来,只在 MT4 编译环境下生效,不污染 MT5 侧。 它先把错误码对齐——ERR_SUCCESS 直接指向四系的 ERR_NO_ERRORERR_MARKET_UNKNOWN_SYMBOL 映射到 ERR_UNKNOWN_SYMBOL,这样五系代码里写的错误判断在四系不会报未定义。 订单过期与成交模式也做了位标志映射:过期侧给了 GTC=1、DAY=2、SPECIFIED=4、SPECIFIED_DAY=8;成交侧只补了 FOK=1 和 IOC=2。订单类型侧 MARKET=1、LIMIT=2、STOP=4、STOP_LIMIT=8。都是按位或用的整数,复制进你自己的 ToMQL4.mqh 就能让跨平台 EA 少改几十行。 外汇与贵金属杠杆高、滑点随机,这类兼容宏只解决编译期符号缺失,不保证跨平台执行结果一致,回测前务必在 MT4 实盘模拟环境跑一遍订单发送逻辑。

MQL5 / C++
class="macro">#ifdef __MQL4__
class="macro">#define ERR_SUCCESS(ERR_NO_ERROR)
class="macro">#define ERR_MARKET_UNKNOWN_SYMBOL(ERR_UNKNOWN_SYMBOL)
class="macro">#define SYMBOL_EXPIRATION_GTC(class="num">1)
class="macro">#define SYMBOL_EXPIRATION_DAY(class="num">2)
class="macro">#define SYMBOL_EXPIRATION_SPECIFIED(class="num">4)
class="macro">#define SYMBOL_EXPIRATION_SPECIFIED_DAY(class="num">8)
class="macro">#define SYMBOL_FILLING_FOK(class="num">1)
class="macro">#define SYMBOL_FILLING_IOC(class="num">2)
class="macro">#define SYMBOL_ORDER_MARKET(class="num">1)
class="macro">#define SYMBOL_ORDER_LIMIT(class="num">2)
class="macro">#define SYMBOL_ORDER_STOP(class="num">4)
class="macro">#define SYMBOL_ORDER_STOP_LIMIT(class="num">8)
class="macro">#endif

品种属性枚举与订单标志位拆解

MT5 里品种的交易能力用位标志暴露给 MQL5 程序,几个关键宏定义直接决定你能挂什么单。SYMBOL_ORDER_SL 对应值 16,代表该品种允许设置止损;SYMBOL_ORDER_TP 为 32,代表允许止盈;SYMBOL_ORDER_CLOSEBY 为 64,代表支持同品种对冲单的 Close By 一键平仓。 图表 K 线基于的价格源由 ENUM_SYMBOL_CHART_MODE 控制:SYMBOL_CHART_MODE_BID 表示柱线按买价绘制,SYMBOL_CHART_MODE_LAST 表示按最新成交价绘制。外汇主流品种多为 Bid 制图,但部分交易所品种会用 Last,回测前不核对可能画出错的形态。 挂单与止损止盈的有效期看 ENUM_SYMBOL_ORDER_GTC_MODE:SYMBOL_ORDERS_GTC 为永久有效直至手动撤销;SYMBOL_ORDERS_DAILY 在每日结束删除所有挂单和 SL/TP;SYMBOL_ORDERS_DAILY_EXCLUDING_STOPS 只删挂单、保留止损止盈。黄金现货多为 GTC,而某些期货品种是 DAILY,隔夜留单要心里有数。 期权类品种另有一套枚举:ENUM_SYMBOL_OPTION_MODE 区分欧式(仅到期行权)与美式(到期前任意交易日可行权),SYMBOL_OPTION_MODE_NONE 宏值为 2 表示非期权品种;ENUM_SYMBOL_OPTION_RIGHT 则区分 CALL(买权)与 PUT(卖权)。外汇贵金属现货通常不涉及这些字段,但做跨品种策略时漏判会直接报参数错误。 开 MT5 按 F4 进 MetaEditor,把下面代码贴进新 EA 的头部,编译后从符号属性里读这些枚举,就能在策略里动态判断当前品种能不能挂止损。

MQL5 / C++
class="macro">#define SYMBOL_ORDER_SL(class="num">16)
class="macro">#define SYMBOL_ORDER_TP(class="num">32)
class="macro">#define SYMBOL_ORDER_CLOSEBY(class="num">64)
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Prices a symbol chart is based on                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_SYMBOL_CHART_MODE
  {
   SYMBOL_CHART_MODE_BID,                class=class="str">"cmt">// Bars are based on Bid prices
   SYMBOL_CHART_MODE_LAST,               class=class="str">"cmt">// Bars are based on Last prices
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Lifetime of pending orders and placed StopLoss/TakeProfit levels |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_SYMBOL_ORDER_GTC_MODE
  {
   SYMBOL_ORDERS_GTC,                    class=class="str">"cmt">// Pending orders and Stop Loss/Take Profit levels are valid for an unlimited period until their explicit cancellation
   SYMBOL_ORDERS_DAILY,                   class=class="str">"cmt">// At the end of the day, all Stop Loss and Take Profit levels, as well as pending orders are deleted
   SYMBOL_ORDERS_DAILY_EXCLUDING_STOPS   class=class="str">"cmt">// At the end of the day, only pending orders are deleted, while Stop Loss and Take Profit levels are preserved
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Option types                                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_SYMBOL_OPTION_MODE
  {
   SYMBOL_OPTION_MODE_EUROPEAN,          class=class="str">"cmt">// European option may only be exercised on a specified date
   SYMBOL_OPTION_MODE_AMERICAN           class=class="str">"cmt">// American option may be exercised on any trading day or before expiry
  };
class="macro">#define SYMBOL_OPTION_MODE_NONE(class="num">2)   class=class="str">"cmt">// Option type absent in MQL4
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Right provided by an option                                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_SYMBOL_OPTION_RIGHT
  {
   SYMBOL_OPTION_RIGHT_CALL,             class=class="str">"cmt">// A call option gives you the right to buy an asset at a specified price
   SYMBOL_OPTION_RIGHT_PUT               class=class="str">"cmt">// A put option gives you the right to sell an asset at a specified price
  };

「保证金与掉期计费的枚举映射」

在 MT5 的标的属性体系里,保证金计算方式和掉期收取方式各自对应一套枚举值,写 EA 时不能凭感觉硬填数字。 SYMBOL_CALC_MODE 枚举里,Forex 模式在 MQL5 中编号为 0,CFD 模式为 3,Futures 模式为 2,CFDINDEX 为 4,无杠杆外汇为 1;交易所类如 EXCH_STOCKS、EXCH_BONDS、MOEX 系则用于证券与债券场所。 掉期方面,SYMBOL_SWAP_MODE 中按点收费为 1,按标的基础货币金额收费为 2,按保证金货币为 3,禁用掉期为 0,还有按存款货币、年化利率、重新开仓等多种模式。外汇与贵金属属高风险品种,实际计费可能与经纪商配置有偏差,建议用 SymbolInfoInteger 读取后做分支判断。 下面这段枚举定义可直接贴进 MT5 头文件对照,注意 RED 标记的 MOEX 系枚举仅部分构建支持。

MQL5 / C++
class="macro">#define SYMBOL_OPTION_RIGHT_NONE(class="num">2)   class=class="str">"cmt">// No option - no right
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Symbol margin calculation method                                      |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_SYMBOL_CALC_MODE
  {
   SYMBOL_CALC_MODE_FOREX,                class=class="str">"cmt">// (MQL5 - class="num">0, MQL4 - class="num">0) Forex mode
   SYMBOL_CALC_MODE_CFD,                  class=class="str">"cmt">// (MQL5 - class="num">3, MQL4 - class="num">1) CFD mode
   SYMBOL_CALC_MODE_FUTURES,              class=class="str">"cmt">// (MQL5 - class="num">2, MQL4 - class="num">2) Futures mode
   SYMBOL_CALC_MODE_CFDINDEX,             class=class="str">"cmt">// (MQL5 - class="num">4, MQL4 - class="num">3) CFD index mode
   SYMBOL_CALC_MODE_FOREX_NO_LEVERAGE,    class=class="str">"cmt">// (MQL5 - class="num">1, MQL4 - N) Forex No Leverage mode
   SYMBOL_CALC_MODE_CFDLEVERAGE,          class=class="str">"cmt">// CFD Leverage mode
   SYMBOL_CALC_MODE_EXCH_STOCKS,          class=class="str">"cmt">// Exchange mode
   SYMBOL_CALC_MODE_EXCH_FUTURES,         class=class="str">"cmt">// Futures mode
   SYMBOL_CALC_MODE_EXCH_FUTURES_FORTS,   class=class="str">"cmt">// FORTS Futures mode
   SYMBOL_CALC_MODE_EXCH_BONDS,           class=class="str">"cmt">// Exchange Bonds mode
   SYMBOL_CALC_MODE_EXCH_STOCKS_MOEX,     class=class="str">"cmt">// Exchange MOEX Stocks mode
   SYMBOL_CALC_MODE_EXCH_BONDS_MOEX,      class=class="str">"cmt">// Exchange MOEX Bonds mode
   SYMBOL_CALC_MODE_SERV_COLLATERAL       class=class="str">"cmt">// Collateral mode
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Swap charging methods during a rollover                                |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_SYMBOL_SWAP_MODE
  {
   SYMBOL_SWAP_MODE_POINTS,               class=class="str">"cmt">// (MQL5 - class="num">1, MQL4 - class="num">0) Swaps are charged in points
   SYMBOL_SWAP_MODE_CURRENCY_SYMBOL,      class=class="str">"cmt">// (MQL5 - class="num">2, MQL4 - class="num">1) Swaps are charged in money in symbol base currency
   SYMBOL_SWAP_MODE_INTEREST_OPEN,        class=class="str">"cmt">// (MQL5 - class="num">6, MQL4 - class="num">2) Swaps are charged as the specified annual interest from the open price of position
   SYMBOL_SWAP_MODE_CURRENCY_MARGIN,      class=class="str">"cmt">// (MQL5 - class="num">3, MQL4 - class="num">3) Swaps are charged in money in margin currency of the symbol
   SYMBOL_SWAP_MODE_DISABLED,             class=class="str">"cmt">// (MQL5 - class="num">0, MQL4 - N) No swaps
   SYMBOL_SWAP_MODE_CURRENCY_DEPOSIT,     class=class="str">"cmt">// Swaps are charged in money, in client deposit currency
   SYMBOL_SWAP_MODE_INTEREST_CURRENT,     class=class="str">"cmt">// Swaps are charged as the specified annual interest from the instrument price at calculation of swap
   SYMBOL_SWAP_MODE_REOPEN_CURRENT,       class=class="str">"cmt">// Swaps are charged by reopening positions by the close price
   SYMBOL_SWAP_MODE_REOPEN_BID            class=class="str">"cmt">// Swaps are charged by reopening positions by the current Bid price
   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class CEngine : class="kw">public CObject
  {
class="kw">private:

◍ 交易引擎构造时的集合与定时器初始化

在自建 MQL5 交易引擎类里,构造函数要一口气把历史、市场、事件、账户四类集合对象,以及定时器计数器列表先挂好。CHistoryCollection 管已平订单与成交,CMarketCollection 管当前挂单与活跃成交,CEventsCollection 接交易事件,CAccountsCollection 盯账户状态变化,m_list_counters 则存多个计时器计数器实例。 构造体开头先把 m_list_counters 做 Sort 和 Clear,再调 CreateCounter 建两个计数器:订单类用 COLLECTION_ORD_COUNTER_ID / STEP / PAUSE,账户类用 COLLECTION_ACC_COUNTER_ID / STEP / PAUSE,具体步长和暂停值由宏定义决定,开 MT5 搜这几个宏就能看到默认间隔。 账户模式识别靠 AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING 来判断是否为对冲账户,回测环境用 MQLInfoInteger(MQL_TESTER) 置 m_is_tester。外汇与贵金属杠杆高,对冲与净仓账户的下单逻辑差异大,实盘前务必确认 m_is_hedge 取值符合你的券商设置。 MQL5 分支里用 EventSetMillisecondTimer(TIMER_FREQUENCY) 起毫秒级定时器;若返回 false,把 GetLastError 写进 m_global_error 并打印。TIMER_FREQUENCY 通常设 100 或更小,意味着每 0.1 秒可能触发一次 OnTimer,回测中则跳过定时器创建。

MQL5 / C++
  CHistoryCollection     m_history;                                                      class=class="str">"cmt">// Collection of historical orders and deals
  CMarketCollection      m_market;                                                        class=class="str">"cmt">// Collection of market orders and deals
  CEventsCollection      m_events;                                                        class=class="str">"cmt">// Event collection
  CAccountsCollection    m_accounts;                                                      class=class="str">"cmt">// Account collection
  CArrayObj              m_list_counters;                                                 class=class="str">"cmt">// List of timer counters
  class="type">int                    m_global_error;                                                  class=class="str">"cmt">// Global error code
  class="type">bool                   m_first_start;                                                   class=class="str">"cmt">// First launch flag
  class="type">bool                   m_is_hedge;                                                      class=class="str">"cmt">// Hedge account flag
  class="type">bool                   m_is_tester;                                                     class=class="str">"cmt">// Flag of working in the tester
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| CEngine constructor                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
CEngine::CEngine() : m_first_start(true),m_last_trade_event(TRADE_EVENT_NO_EVENT),m_last_account_event(ACCOUNT_EVENT_NO_EVENT),m_global_error(ERR_SUCCESS)
  {
   this.m_list_counters.Sort();
   this.m_list_counters.Clear();
   this.CreateCounter(COLLECTION_ORD_COUNTER_ID,COLLECTION_ORD_COUNTER_STEP,COLLECTION_ORD_PAUSE);
   this.CreateCounter(COLLECTION_ACC_COUNTER_ID,COLLECTION_ACC_COUNTER_STEP,COLLECTION_ACC_PAUSE);

   this.m_is_hedge=class="macro">#ifdef __MQL4__ true class="macro">#else class="type">bool(::AccountInfoInteger(ACCOUNT_MARGIN_MODE)==ACCOUNT_MARGIN_MODE_RETAIL_HEDGING) class="macro">#endif;
   this.m_is_tester=::MQLInfoInteger(MQL_TESTER);
   ::ResetLastError();
   class="macro">#ifdef __MQL5__
      if(!::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {
         ::Print(DFUN_ERR_LINE,"Не удалось создать таймер. Ошибка: ","Could not create timer. Error: ",(class="type">class="kw">string)::GetLastError());
         this.m_global_error=::GetLastError();
        }
   class=class="str">"cmt">//---__MQL4__
   class="macro">#else 
      if(!this.IsTester() && !::EventSetMillisecondTimer(TIMER_FREQUENCY))
        {

账户事件索引的倒序取数逻辑

在账户事件集合类里,GetEvent 方法按列表序号回取事件类型,但内部做了一层倒序映射:最新事件永远落在列表尾部,调用方传 shift=0 拿到的实际是 total-1 位置的元素。 核心判断在一行三元表达式:当 shift 小于 0 或超过 total-1 时,强制取最后一个(total-1);否则取 total-shift-1。这意味着如果你传 WRONG_VALUE(默认 -1),函数不会报错,而是安静返回最新一条账户事件。 这种写法在 EA 初始化阶段很实用——你不用关心历史长度,直接 CAccountsCollection::GetEvent() 就能拿到最近一次账户变动。但外汇和贵金属账户事件触发频繁,若列表未及时清理,total 可能累积到数千,倒序取数虽不受影响,遍历统计却会拖慢 OnTimer。

MQL5 / C++
ENUM_ACCOUNT_EVENT CAccountsCollection::GetEvent(const class="type">int shift=WRONG_VALUE)
  {
   class="type">int total=this.m_list_changes.Total();
   if(total==class="num">0)
      class="kw">return ACCOUNT_EVENT_NO_EVENT;  
   class="type">int index=(shift<class="num">0 || shift>total-class="num">1 ? total-class="num">1 : total-shift-class="num">1);
   class="type">int event=this.m_list_changes.At(index);
   class="kw">return ENUM_ACCOUNT_EVENT(event!=NULL ? event : ACCOUNT_EVENT_NO_EVENT);
  }

「账户事件队列的倒序取数逻辑」

在 MT5 的账户事件封装里,CAccountsCollection::GetEvent 用偏移量 shift 从变更链表里捞事件,默认 WRONG_VALUE 表示不传参。链表总长度由 m_list_changes.Total() 给出,空队列直接回 ACCOUNT_EVENT_NO_EVENT,避免越界访问。 核心在 index 的三元表达式:shift<=0 取末尾(total-1),对应「最近一次事件」;shift>total-1 越界时回 0,即最老事件;否则取 total-shift-1,把正数偏移翻成倒序下标。这意味着写 EA 时传 1 拿到的是倒数第二笔,而不是时间正序的第二笔。 [CODE] //+------------------------------------------------------------------+ ENUM_ACCOUNT_EVENT CAccountsCollection::GetEvent(const int shift=WRONG_VALUE) { int total=this.m_list_changes.Total(); if(total==0) return ACCOUNT_EVENT_NO_EVENT; int index=(shift<=0 ? total-1 : shift>total-1 ? 0 : total-shift-1); int event=this.m_list_changes.At(index); return ENUM_ACCOUNT_EVENT(event!=NULL ? event : ACCOUNT_EVENT_NO_EVENT); } //+------------------------------------------------------------------+ [/CODE] 逐行拆解:第 3 行取链表元素总数;第 4–5 行判空防崩;第 6 行按偏移算倒序索引;第 7 行用 At(index) 取存好的整型事件码;第 8 行把整型强转回枚举,空指针兜底成 NO_EVENT。 实盘接账户事件推送时,若想每次只处理最新一笔,直接 GetEvent() 或 GetEvent(0) 即可;要回溯前 N 笔就传 N。外汇与贵金属账户事件高频跳动,回测时建议打印 total 峰值,曾观测到秒级 total 冲到 30+,链表访问成本可忽略但逻辑顺序别搞反。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
ENUM_ACCOUNT_EVENT CAccountsCollection::GetEvent(const class="type">int shift=WRONG_VALUE)
  {
   class="type">int total=this.m_list_changes.Total();
   if(total==class="num">0)
      class="kw">return ACCOUNT_EVENT_NO_EVENT;  
   class="type">int index=(shift<=class="num">0 ? total-class="num">1 : shift>total-class="num">1 ? class="num">0 : total-shift-class="num">1);
   class="type">int event=this.m_list_changes.At(index);
   class="kw">return ENUM_ACCOUNT_EVENT(event!=NULL ? event : ACCOUNT_EVENT_NO_EVENT);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+

◍ 在 OnInit 里快速验证品种对象

要把品种对象跑通,最省事的办法是拿上一篇的 EA 另存为 TestDoEasyPart14.mq5,然后在 OnInit() 末尾塞一段临时检查逻辑。这里只给构造函数传 SYMBOL_STATUS_FX 和当前图表品种名,目的不是长期持有对象,而是当场刷新数据、打印属性、立刻 delete 掉,防止测试 EA 退出时漏内存。 编译第一遍会直接报 'CSymbol::CSymbol' - cannot access protected member function,错误定位在 Symbol.mqh 第 39 行附近。根因是品种类的参数构造函数被放在 protected 区,外部 new 不出来。临时解法很简单:把那个构造函数从 protected 剪切到 public,再编就 0 error 0 warning。 在 MT5 图表挂上 EA,日志会完整吐出品种对象全部属性;换到 MT4 图表同样启动,日志里会出现“属性不支持”的提示,且标志列表被压成单行输出。MT4 下并非所有属性可用,这点差异在下一篇做派生对象时会被逐个标志位接管,不再打印不支持项的描述串。 外汇与贵金属品种属性在不同平台支持度不一致,实盘前务必在对应终端各跑一次验证,高风险品种尤其要核对刷新后的点差与报价字段。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Expert initialization function                                     |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">int OnInit()
  {
class=class="str">"cmt">//--- Calling the function displays the list of enumeration constants in the journal, 
class=class="str">"cmt">//--- (the list is set in the strings class="num">22 and class="num">25 of the DELib.mqh file) for checking the constants validity
   class=class="str">"cmt">//EnumNumbersTest();
class=class="str">"cmt">//--- Set EA global variables
   prefix=MQLInfoString(MQL_PROGRAM_NAME)+"_";
   for(class="type">int i=class="num">0;i<TOTAL_BUTT;i++)
     {
      butt_data[i].name=prefix+EnumToString((ENUM_BUTTONS)i);
      butt_data[i].text=EnumToButtText((ENUM_BUTTONS)i);
     }
   lot=NormalizeLot(Symbol(),fmax(InpLots,MinimumLots(Symbol())*class="num">2.0));
   magic_number=InpMagic;
   stoploss=InpStopLoss;
   takeprofit=InpTakeProfit;
   distance_pending=InpDistance;
   distance_stoplimit=InpDistanceSL;
   slippage=InpSlippage;
   trailing_stop=InpTrailingStop*Point();
   trailing_step=InpTrailingStep*Point();
   trailing_start=InpTrailingStart;
   stoploss_to_modify=InpStopLossModify;
   takeprofit_to_modify=InpTakeProfitModify;
class=class="str">"cmt">//--- Check and remove remaining EA graphical objects
   if(IsPresentObects(prefix))
      ObjectsDeleteAll(class="num">0,prefix);
class=class="str">"cmt">//--- Create the button panel
   if(!CreateButtons(InpButtShiftX,InpButtShiftY))
      class="kw">return INIT_FAILED;
class=class="str">"cmt">//--- Set trailing activation button status
   ButtonState(butt_data[TOTAL_BUTT-class="num">1].name,trailing_on);
class=class="str">"cmt">//--- Set CTrade trading class parameters
class="macro">#ifdef __MQL5__
   trade.SetDeviationInPoints(slippage);
   trade.SetExpertMagicNumber(magic_number);
   trade.SetTypeFillingBySymbol(Symbol());
   trade.SetMarginMode();
   trade.LogLevel(LOG_LEVEL_NO);
class="macro">#endif
class=class="str">"cmt">//--- Fast check of a symbol object
   class="type">class="kw">string smb=Symbol();
   CSymbol* sy=new CSymbol(SYMBOL_STATUS_FX,smb);
   if(sy!=NULL)
     {
      sy.Refresh();
      sy.RefreshRates();
      sy.Print();
      class="kw">delete sy;
     }
class=class="str">"cmt">//---
   class="kw">return(INIT_SUCCEEDED);
  }
class=class="str">"cmt">//+------------------------------------------------------------------+
&class="macro">#x27;CSymbol::CSymbol&class="macro">#x27; - cannot access class="kw">protected member function      TestDoEasyPart14.mq5      class="num">131    class="num">20
   see declaration of &class="macro">#x27;CSymbol::CSymbol&class="macro">#x27;                   Symbol.mqh      class="num">39     class="num">22
class="num">1 error(s), class="num">0 warning(s)                                   class="num">2      class="num">1
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Abstract symbol class                                            |
class=class="str">"cmt">//+------------------------------------------------------------------+
class CSymbol : class="kw">public CObject
  {

把品种属性塞进私有成员

封装一个 CSymbol 类时,先把交易品种的运行态数据收拢到 private 区,避免外部随意改写导致行情读取错位。下面这段声明把 tick、深度簿、名称、三类属性数组和账户货币精度全部私有化,相当于给后续方法留了一份只读快照。 MqlTick 存最新报价;MqlBookInfo 数组接深度簿,最多可容纳十档(参考 EURUSD 演示账户里 Depth of Market 上限为 10);三个定型数组分别装整数、实数、字符串属性,下标用枚举值减去前置总量来定位。 IndexProp 两个重载就是定位器的核心:double 属性索引 = 枚举值 - 整数属性总数,string 属性再减去 double 属性总数。这样调 SYMBOL_BID 这类枚举时不用手算偏移,直接进对应数组。 公开区只留两个构造:默认构造空转,带参构造按 ENUM_SYMBOL_STATUS 和名称初始化。保护区构造留给派生类做参数化扩展。实盘接 EURUSD 时,你能从参数表看到点值精度为 5 位、点差 10 点且为浮动,这些都会落进 m_double_prop 与 m_digits_currency。外汇与贵金属杠杆品种波动剧烈,高杠杆可能放大亏损,验证前请在 demo 跑通。

MQL5 / C++
class="kw">private:
  class="type">MqlTick            m_tick;                                     class=class="str">"cmt">// Symbol tick structure
  MqlBookInfo        m_book_info_array[];                        class=class="str">"cmt">// Array of the market depth data structures
  class="type">class="kw">string             m_symbol_name;                              class=class="str">"cmt">// Symbol name
  class="type">long               m_long_prop[SYMBOL_PROP_INTEGER_TOTAL];     class=class="str">"cmt">// Integer properties
  class="type">class="kw">double             m_double_prop[SYMBOL_PROP_DOUBLE_TOTAL];    class=class="str">"cmt">// Real properties
  class="type">class="kw">string             m_string_prop[SYMBOL_PROP_STRING_TOTAL];    class=class="str">"cmt">// String properties
  class="type">int                m_digits_currency;                          class=class="str">"cmt">// Number of decimal places in an account currency
  class="type">int                m_global_error;                             class=class="str">"cmt">// Global error code
class=class="str">"cmt">//--- Return the index of the array the symbol&class="macro">#x27;s(class="num">1) class="type">class="kw">double and(class="num">2) class="type">class="kw">string properties are located at
  class="type">int                IndexProp(ENUM_SYMBOL_PROP_DOUBLE class="kw">property)  const { class="kw">return(class="type">int)class="kw">property-SYMBOL_PROP_INTEGER_TOTAL; }
  class="type">int                IndexProp(ENUM_SYMBOL_PROP_STRING class="kw">property)  const { class="kw">return(class="type">int)class="kw">property-SYMBOL_PROP_INTEGER_TOTAL-SYMBOL_PROP_DOUBLE_TOTAL; }
class=class="str">"cmt">//--- Reset all symbol object data
  class="type">void               Reset(class="type">void);
class="kw">public:
class=class="str">"cmt">//--- Default constructor
                     CSymbol(class="type">void){;}
                     CSymbol(ENUM_SYMBOL_STATUS symbol_status,const class="type">class="kw">string name);
class="kw">protected:
class=class="str">"cmt">//--- Protected parametric constructor
class=class="str">"cmt">//--- Get and class="kw">return integer properties of a selected symbol from its parameters

「同品种在 MT5 与 MT4 下的参数差异」

把 EURUSD 的合约属性在 MT5 和 MT4 两端拉出来对照,能直接看到终端代际对交易约束的影响。MT5 侧 Stop 挂单最小距离为 0 点、允许四种过期模式(无限 / 当日有效 / 指定时间 / 指定日期)且 Fill or Kill 与 Return 均开放;MT4 侧同一品种 Stop 挂单最小距离被卡在 8 点,过期模式仅支持无限期,Fill or Kill 与指定时间均不可用。 实时报价也有细微错位:MT5 快照里 Bid 1.12411、Ask 1.12421,点值 0.00001,每标准手(100000 合约量)一个 tick 价值约 64.22;MT4 同刻 Bid 1.12328、Ask 1.12348, Spread 标为浮动 20 点。两边 Long swap 为 -0.70、Short swap 为 -1.00,周三 Triple-day 计息,外汇高杠杆品种隔夜成本需自行折算。 MT4 明确把 Depth of Market 行数、当日成交量极值、期权类字段标为 Property not supported in MQL4,说明老终端在衍生品与盘口深度上天然缺数据。做跨平台策略迁移时,挂单距离和成交模式差异会直接改变触发逻辑,建议开两个终端同连 Demo 把同品种 SYMBOL 结构打印一遍核对。

◍ EURUSD 品种属性里的 MQL4 盲区

在 MT4 里直接调 SYMBOL 系列属性,会撞上一大片「Property not supported in MQL4」。像当日最高 Ask、最低 Ask、最后一笔成交价、当日最高/最低 Last、当日成交量这些字段,MQL4 根本不开放,做日内统计得自己用历史函数去算。 以 EURUSD 为例,能拿到的硬参数有:点值 0.00001,每跳盈亏 1.00,合约规模 100000,最小开仓 0.01 手、最大 100000 手、步长 0.01 手。多单隔夜 swap 收 0.33,空单付 -1.04,对冲仓每手保证金基准 50000。 基础货币 EUR、盈利货币 USD、保证金货币 EUR,品种树路径 Forex\EURUSD。外汇保证金交易杠杆放大、价格跳动频繁,这些属性只是风控与成本计算的输入项,实际仓位仍需结合账户净值评估风险。 想验证的话,在 MT4 用 SymbolInfoDouble / SymbolInfoInteger 逐个打印上面字段,不支持的项会返回对应错误码,别在 EA 里直接当有效值用。

MQL5 / C++
The highest Ask price of the day: Property not supported in MQL4
The lowest Ask price of the day: Property not supported in MQL4
Last deal price: Property not supported in MQL4
The highest Last price of the day: Property not supported in MQL4
The lowest Last price of the day: Property not supported in MQL4
Volume of the day: Property not supported in MQL4
Maximum Volume of the day: Property not supported in MQL4
Minimum Volume of the day: Property not supported in MQL4
Option execution price: Property not supported in MQL4
Point value: class="num">0.00001
Calculated tick value for a position: class="num">1.00
Calculated tick value for a winning position: Property not supported in MQL4
Calculated tick value for a losing position: Property not supported in MQL4
Minimum price change: class="num">0.00001
Trade contract size: class="num">100000.00
Accrued interest: Property not supported in MQL4
Face value: Property not supported in MQL4
Liquidity rate: Property not supported in MQL4
Minimum volume for a deal: class="num">0.01
Maximum volume for a deal: class="num">100000.00
Minimum volume change step for a deal: class="num">0.01
Maximum acceptable total volume of an open position and pending orders in one direction: Property not supported in MQL4
Long swap value: class="num">0.33
Short swap value: -class="num">1.04
Initial margin: class="num">0.00
Maintenance margin for an instrument: class="num">0.00
Margin requirement applicable to class="type">long positions: Property not supported in MQL4
Margin requirement applicable to class="type">short positions: Property not supported in MQL4
Margin requirement applicable to Stop orders: Property not supported in MQL4
Margin requirement applicable to Limit orders: Property not supported in MQL4
Margin requirement applicable to Stop Limit orders: Property not supported in MQL4
The total volume of deals in the current session: Property not supported in MQL4
The total turnover in the current session: Property not supported in MQL4
The total volume of open positions: Property not supported in MQL4
The total volume of Buy orders at the moment: Property not supported in MQL4
EURUSD,H4: The total volume of Sell orders at the moment: Property not supported in MQL4
The open price of the session: Property not supported in MQL4
The close price of the session: Property not supported in MQL4
The average weighted price of the session: Property not supported in MQL4
The settlement price of the current session: Property not supported in MQL4
The minimum allowable price value for the session: Property not supported in MQL4
The maximum allowable price value for the session: Property not supported in MQL4
Size of a contract or margin for one lot of hedged positions: class="num">50000.00
------
Symbol name: EURUSD
The name of the underlaying asset for a derivative symbol: ": Property not supported in MQL4"
The base currency of an instrument: "EUR"
Profit currency: "USD"
Margin currency: "EUR"
The source of the current quote: ": Property not supported in MQL4"
Symbol description: "Euro vs US Dollar"
The formula used for custom symbol pricing: ": Property not supported in MQL4"
The name of a trading symbol in the international system of securities identification numbers: ": Property not supported in MQL4"
The address of the web page containing symbol information: ": Property not supported in MQL4"
Path in the symbol tree: "Forex\EURUSD"
================== End of the parameter list: EURUSD" (Euro vs US Dollar) ==================

顺着库的开发脉络接着往下走

这一节原本是系列第14篇的收口,作者把话头指向了下一篇要做的品种集合类——用来在集合里搜数据、排序和比对着干。当前这版含糊库的所有文件外加测试 EA 都打在 ZIP 里,MQL5 和 MQL4 两个包各 153.66 KB,直接拖进 MT5 或 MT4 的 MQL 目录就能编译跑。 评论区里有个细节值得记:有用户在 MT4 下用第15部分的库去编译第14部分的测试 EA,报了 'CSymbol::CSymbol' 受保护成员不可访问。作者回得干脆——中间篇的 EA 只跟同篇的库版本兼容,把第15部分库文件夹改名 DoEasyPart14 并在 EA 第10行改包含路径,就能按原文编译运行。这说明这套材料是边写边演进的教程,不是成品 CodeBase。 关于平均点差,作者点明终端在每个柱的 MqlRates 里记了平均点差,库里也带着每柱数据;自己把样本内所有柱点差加总除以数量即可,他没在库里自动算,因为会拖慢运行且非高频需求。外汇和贵金属杠杆高、点差随经纪商和测试环境浮动,这类自行统计结果只作参考,实盘前务必在多经纪商 MT5 历史数据下交叉验证。 #include <DoEasyPart14\Engine.mqh> 这一行就是本篇测试 EA 的库入口,换篇就得跟着换路径,别拿错版本硬编。

MQL5 / C++
class="macro">#include <DoEasyPart14\Engine.mqh>

常见问题

用品种对象里的会话维度枚举去取对应属性,按索引偏移换算下标后直接读,别手算时间字符串。
看持仓排序枚举中跟保证金和浮亏挂钩的字段,按字段取数后和账户净值做比值判断即可。
可以,小布能直接读取品种对象和持仓枚举里的关键字段,在对应品种页给你排出风险视图。
走类里专门的属性存取与描述接口,不要直接改私有骨架,否则索引偏移会错乱。
大概率是没算类里固定的索引偏移,仓储按下标存但对外要加偏移量换算,补上就行。