轻松快捷开发 MetaTrader 程序的函数库(第十一部分)。 与 MQL4 的兼容性 - 平仓事件(基础篇)
📘

轻松快捷开发 MetaTrader 程序的函数库(第十一部分)。 与 MQL4 的兼容性 - 平仓事件(基础篇)

第 1/3 篇

◍ 给 MT5 接上 MQL4 的平仓事件

在 MQL5 里直接监听 MQL4 风格的平仓事件并不原生支持,但可以通过封装函数库把老 EA 的逻辑平移过来。2019 年 8 月发布的这套兼容层示例,核心就是补上 OrderClose 这类动作的回调钩子,让迁移项目不用重写事件结构。 实际做法是在库里删除 MT5 用不到的冗余属性,只保留与平仓相关的字段映射。这样 MQL4 的 OnOrderClose 类逻辑能在 MQL5 的 OnTradeTransaction 里被重新触发,回测时订单退出动作可被捕获。 打开 MT5 的 MetaEditor,新建一段兼容库并把下面代码塞进交易事务处理分支,就能验证平仓事件是否真的被接住。外汇与贵金属杠杆高,回测通过不代表实盘无滑点风险,平仓触发可能延迟。

「给订单属性做减法与加法」

MQL5 里所有时间参数都以毫秒计,而 MQL4 没有对应订单/持仓属性。原设计里同时存在以秒和 _MSC 结尾的毫秒属性,显示层对两者处理一致,毫秒只是时间格式尾部三位。既然冗余,就直接砍掉秒级订单属性,统一用毫秒覆盖,同时补一个自定义注释字符串属性,方便按条件打文本标签或后续图形外壳调用。 打开 Defines.mqh,Ctrl+F 搜以秒为单位及 _MSC 结尾的属性。删除毫秒订单属性、保留秒属性并把整数型属性总数从 24 改成 21;时间枚举只留两个常量,MQL5 走毫秒、MQL4 走秒。字符串型属性新增自定义注释,总数升到 4,排序枚举删掉毫秒引用并加入按自定义注释排序。 函数库里要清掉已删属性的所有引用:六个抽象订单继承类删毫秒属性;COrder 私有和公有里删毫秒取时方法,改用秒值方法,并加返回/设置自定义注释的方法。平仓构造里删第二个时间属性、毫秒替秒、注释存空串。MQL4 返回持仓 ID 时,有持仓返票证否则返零,并补返回逆向仓位 ID 方法——注释含 "close hedge by #" 就解析出对应票证。 DELib.mqh 给按类型返名称的函数加 MQL4 标志,默认作订单显示,市价单开仓日志会从 [Position Sell #123] 变成 [Market order Sell #123]。历史集合类选择逻辑由四个时间属性简化成两个,CEngine 里 SORT_BY_ORDER_TIME_OPEN_MSC 全换成 SORT_BY_ORDER_TIME_OPEN。改完即可在 MT5 里编译验证属性枚举是否只剩 21 个整数项。

MQL5 / C++
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="comment">class=class="str">"cmt">//| Order, deal, position integer properties&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;       |</span>
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span>
<span class="keyword">enum</span> ENUM_ORDER_PROP_INTEGER
&nbsp;&nbsp;{
&nbsp;&nbsp; ORDER_PROP_TICKET = <span class="number">class="num">0</span>,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Order ticket</span>
&nbsp;&nbsp; ORDER_PROP_MAGIC,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Order magic number</span>
&nbsp;&nbsp; <span style="background-class="type">color:rgb(class="num">216, class="num">232, class="num">194);">ORDER_PROP_TIME_OPEN,</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Open time(MQL5 Deal time)</span>
&nbsp;&nbsp; <span style="background-class="type">color:rgb(class="num">216, class="num">232, class="num">194);">ORDER_PROP_TIME_CLOSE</span>,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Close time(MQL5 Execution or removal time - ORDER_TIME_DONE)</span>
&nbsp;&nbsp; <span style="background-class="type">color:rgb(class="num">249, class="num">204, class="num">202);">ORDER_PROP_TIME_OPEN_MSC</span>,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<span class="comment">class=class="str">"cmt">// Open time in milliseconds(MQL5 Deal time in msc)</span>
&nbsp;&nbsp; <span style="background-class="type">color:rgb(class="num">249, class="num">204, class="num">202);">ORDER_PROP_TIME_CLOSE_MSC</span>,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Close time in milliseconds(MQL5 Execution or removal time - ORDER_TIME_DONE_MSC)</span>
&nbsp;&nbsp; ORDER_PROP_TIME_EXP,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Order expiration date(for pending orders)</span>
&nbsp;&nbsp; ORDER_PROP_STATUS,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">class=class="str">"cmt">// Order status(from the ENUM_ORDER_STATUS enumeration)</span>

订单整数属性的枚举清单与计数宏

在 MT5 的订单/成交结构体里,整数类属性被统一收进一个枚举,方便后续用数组下标直接读写。下面这段枚举列出了从类型、来源、状态到仓位ID、反向仓位ID、触发成交的母单 ticket 等共 24 个整型字段,覆盖了订单生命周期大部分元数据。 其中 ORDER_PROP_TIME_UPDATE 与 ORDER_PROP_TIME_UPDATE_MSC 成对出现,前者以秒记录仓位变动时间,后者精确到毫秒;ORDER_PROP_CLOSE_BY_SL / TP 则是用来标记该笔平仓是否由止损或止盈触发,做复盘过滤时很实用。 宏定义给出了硬数字:ORDER_PROP_INTEGER_TOTAL 为 24,ORDER_PROP_INTEGER_SKIP 为 0,意味着全部整型属性都参与排序,没有预留跳过位。开 MT5 在 MetaEditor 搜 ORDER_PROP_INTEGER_TOTAL 能直接定位这套定义,改结构体前先确认总数别错位。

MQL5 / C++
  ORDER_PROP_TYPE,                                                                       class=class="str">"cmt">// Order/deal type
  ORDER_PROP_REASON,                                                                     class=class="str">"cmt">// Deal/order/position reason or source
  ORDER_PROP_STATE,                                                                      class=class="str">"cmt">// Order status(from the ENUM_ORDER_STATE enumeration)
  ORDER_PROP_POSITION_ID,                                                                class=class="str">"cmt">// Position ID
  ORDER_PROP_POSITION_BY_ID,                                                             class=class="str">"cmt">// Opposite position ID
  ORDER_PROP_DEAL_ORDER_TICKET,                                                          class=class="str">"cmt">// Ticket of the order that triggered a deal
  ORDER_PROP_DEAL_ENTRY,                                                                 class=class="str">"cmt">// Deal direction – IN, OUT or IN/OUT
  ORDER_PROP_TIME_UPDATE,                                                                class=class="str">"cmt">// Position change time in seconds
  ORDER_PROP_TIME_UPDATE_MSC,                                                            class=class="str">"cmt">// Position change time in milliseconds
  ORDER_PROP_TICKET_FROM,                                                                class=class="str">"cmt">// Parent order ticket
  ORDER_PROP_TICKET_TO,                                                                  class=class="str">"cmt">// Derived order ticket
  ORDER_PROP_PROFIT_PT,                                                                  class=class="str">"cmt">// Profit in points
  ORDER_PROP_CLOSE_BY_SL,                                                                class=class="str">"cmt">// Flag of closing by StopLoss
  ORDER_PROP_CLOSE_BY_TP,                                                                class=class="str">"cmt">// Flag of closing by TakeProfit
  ORDER_PROP_GROUP_ID,                                                                   class=class="str">"cmt">// Order/position group ID
  ORDER_PROP_DIRECTION,                                                                  class=class="str">"cmt">// Direction type(Buy, Sell)
};
class="macro">#define ORDER_PROP_INTEGER_TOTAL(class="num">24)                                                  class=class="str">"cmt">// Total number of integer properties
class="macro">#define ORDER_PROP_INTEGER_SKIP(class="num">0)                                                   class=class="str">"cmt">// Number of order properties not used in sorting

◍ 订单与成交的整数属性枚举怎么读

在 MT5 的订单/成交/持仓统一结构里,整数类属性被收进一个枚举,名字叫 ENUM_ORDER_PROP_INTEGER。它从 0 开始连续编号,ORDER_PROP_TICKET 固定为 0,之后每个成员顺位移位,总共覆盖 19 个整数槽位(含 ORDER_PROP_CLOSE_BY_SL)。 这套枚举把挂单、市价单、成交、持仓的元数据拉平了:时间类给的是毫秒级整数,比如 ORDER_PROP_TIME_OPEN 是开仓毫秒戳,ORDER_PROP_TIME_CLOSE 对应的是执行或撤单时间(ORDER_TIME_DONE)。状态分两套——ORDER_PROP_STATUS 来自 ENUM_ORDER_STATUS,ORDER_PROP_STATE 来自 ENUM_ORDER_STATE,别在代码里混用。 实盘里最容易被忽略的是 ORDER_PROP_POSITION_BY_ID:它存的是对冲仓位的反向持仓 ID,做锁仓逻辑或一对多平仓时得靠它定位对手单。外汇和贵金属杠杆高,用这些属性写自动平仓前,先在策略测试器用历史数据跑一遍父子单(TICKET_FROM / TICKET_TO)链路。

MQL5 / C++
enum ENUM_ORDER_PROP_INTEGER
  {
   ORDER_PROP_TICKET = class="num">0,                     class=class="str">"cmt">// Order ticket
   ORDER_PROP_MAGIC,                           class=class="str">"cmt">// Order magic number
   ORDER_PROP_TIME_OPEN,                       class=class="str">"cmt">// Open time in milliseconds(MQL5 Deal time)
   ORDER_PROP_TIME_CLOSE,                      class=class="str">"cmt">// Close time in milliseconds(MQL5 Execution or removal time - ORDER_TIME_DONE)
   ORDER_PROP_TIME_EXP,                        class=class="str">"cmt">// Order expiration date(for pending orders)
   ORDER_PROP_STATUS,                          class=class="str">"cmt">// Order status(from the ENUM_ORDER_STATUS enumeration)
   ORDER_PROP_TYPE,                            class=class="str">"cmt">// Order/deal type
   ORDER_PROP_REASON,                          class=class="str">"cmt">// Deal/order/position reason or source
   ORDER_PROP_STATE,                           class=class="str">"cmt">// Order status(from the ENUM_ORDER_STATE enumeration)
   ORDER_PROP_POSITION_ID,                     class=class="str">"cmt">// Position ID
   ORDER_PROP_POSITION_BY_ID,                  class=class="str">"cmt">// Opposite position ID
   ORDER_PROP_DEAL_ORDER_TICKET,               class=class="str">"cmt">// Ticket of the order that triggered a deal
   ORDER_PROP_DEAL_ENTRY,                      class=class="str">"cmt">// Deal direction – IN, OUT or IN/OUT
   ORDER_PROP_TIME_UPDATE,                     class=class="str">"cmt">// Position change time in milliseconds
   ORDER_PROP_TICKET_FROM,                     class=class="str">"cmt">// Parent order ticket
   ORDER_PROP_TICKET_TO,                       class=class="str">"cmt">// Derived order ticket
   ORDER_PROP_PROFIT_PT,                       class=class="str">"cmt">// Profit in points
   ORDER_PROP_CLOSE_BY_SL,                     class=class="str">"cmt">// Flag of closing by StopLoss
  }

「订单整数属性与按时间筛选的枚举定义」

在 MT5 的订单属性体系里,整数属性收尾处定义了两个宏:ORDER_PROP_INTEGER_TOTAL 值为 21,代表整数类属性总数;ORDER_PROP_INTEGER_SKIP 值为 0,代表不参与排序的属性数量。这两个数字直接决定了后续字符串属性枚举的起始偏移量。 按时间筛选提供了 ENUM_SELECT_BY_TIME 枚举。早期版本含 4 个成员:SELECT_BY_TIME_OPEN、SELECT_BY_TIME_CLOSE 以及带毫秒精度的 SELECT_BY_TIME_OPEN_MSC、SELECT_BY_TIME_CLOSE_MSC;而后来的修订版仅保留 OPEN 与 CLOSE 两个成员,且注释明确其时间单位已是毫秒。写 EA 时若引用毫秒级筛选,需确认所用头文件版本,否则可能编译报找不到标识符。 字符串属性枚举 ENUM_ORDER_PROP_STRING 从 (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL) 开始计数,首个成员为 ORDER_PROP_SYMBOL 即订单交易品种,随后是 ORDER_PROP_COMMENT 订单注释。开 MT5 按 F4 进 stdlib 翻 Trade 相关头文件,搜 ORDER_PROP_COMMENT 能直接看到这套偏移逻辑。

MQL5 / C++
  ORDER_PROP_CLOSE_BY_TP,                                                                     class=class="str">"cmt">// Flag of closing by TakeProfit
  ORDER_PROP_GROUP_ID,                                                                          class=class="str">"cmt">// Order/position group ID
  ORDER_PROP_DIRECTION,                                                                         class=class="str">"cmt">// Direction type(Buy, Sell)
  };
class="macro">#define ORDER_PROP_INTEGER_TOTAL(class="num">21)                                                          class=class="str">"cmt">// Total number of integer properties
class="macro">#define ORDER_PROP_INTEGER_SKIP(class="num">0)                                                           class=class="str">"cmt">// Number of order properties not used in sorting
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Possible selection options by time                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_SELECT_BY_TIME
  {
   SELECT_BY_TIME_OPEN,                                                                         class=class="str">"cmt">// By open time
   SELECT_BY_TIME_CLOSE,                                                                        class=class="str">"cmt">// By close time
   SELECT_BY_TIME_OPEN_MSC,                                                                     class=class="str">"cmt">// By open time in milliseconds
   SELECT_BY_TIME_CLOSE_MSC,                                                                    class=class="str">"cmt">// By close time in milliseconds
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Possible selection options by time                                                              |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_SELECT_BY_TIME
  {
   SELECT_BY_TIME_OPEN,                                                                         class=class="str">"cmt">// By open time(in milliseconds)
   SELECT_BY_TIME_CLOSE,                                                                        class=class="str">"cmt">// By close time(in milliseconds)
  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Order, deal, position class="type">class="kw">string properties                                                         |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_ORDER_PROP_STRING
  {
   ORDER_PROP_SYMBOL = (ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL), class=class="str">"cmt">// Order symbol
   ORDER_PROP_COMMENT,                                                                          class=class="str">"cmt">// Order comment

订单枚举的扩展字段与排序宏

在订单属性枚举收尾处,除了基础整型与双精度属性,还有两个字符串类扩展项:ORDER_PROP_COMMENT_EXT 用于挂自定义注释,ORDER_PROP_EXT_ID 记录外部交易系统中的订单 ID。 编译器用 #define ORDER_PROP_STRING_TOTAL (4) 显式标出字符串属性总数为 4,这个常量在写通用遍历函数时可以直接拿来当上界,避免硬凑数字。 排序宏把三类属性区段的偏移算好:FIRST_ORD_DBL_PROP 以整型总数减跳过项为基,FIRST_ORD_STR_PROP 再叠上双精度总数。据此 ENUM_SORT_ORDERS_MODE 从 0 到 11 定义了 12 种排序键,例如 SORT_BY_ORDER_TICKET=0 按订单号、SORT_BY_ORDER_TIME_OPEN=2 按开单毫秒、SORT_BY_ORDER_POSITION_ID=9 按持仓 ID。 在 MT5 里建一个订单池面板时,直接引用这些枚举值做 qsort 比较函数,就能按持仓 ID 或开单时间稳定排序列出,外汇与贵金属杠杆品种波动剧烈,排序后仍需人工核对风险。

MQL5 / C++
  ORDER_PROP_COMMENT_EXT,                                                                     class=class="str">"cmt">// Order custom comment
  ORDER_PROP_EXT_ID                                                                              class=class="str">"cmt">// Order ID in the external trading system
  };
class="macro">#define ORDER_PROP_STRING_TOTAL(class="num">4)                                                            class=class="str">"cmt">// Total number of class="type">class="kw">string properties
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Possible criteria of orders and deals sorting                    |
class=class="str">"cmt">//+------------------------------------------------------------------+
class="macro">#define FIRST_ORD_DBL_PROP(ORDER_PROP_INTEGER_TOTAL-ORDER_PROP_INTEGER_SKIP)
class="macro">#define FIRST_ORD_STR_PROP(ORDER_PROP_INTEGER_TOTAL+ORDER_PROP_DOUBLE_TOTAL-ORDER_PROP_INTEGER_SKIP)
enum ENUM_SORT_ORDERS_MODE
  {
  class=class="str">"cmt">//--- Sort by integer properties
  SORT_BY_ORDER_TICKET         = class="num">0,                                                             class=class="str">"cmt">// Sort by order ticket
  SORT_BY_ORDER_MAGIC          = class="num">1,                                                             class=class="str">"cmt">// Sort by order magic number
  SORT_BY_ORDER_TIME_OPEN      = class="num">2,                                                             class=class="str">"cmt">// Sort by order open time in milliseconds
  SORT_BY_ORDER_TIME_CLOSE     = class="num">3,                                                             class=class="str">"cmt">// Sort by order close time in milliseconds
  SORT_BY_ORDER_TIME_EXP       = class="num">4,                                                             class=class="str">"cmt">// Sort by order expiration date
  SORT_BY_ORDER_STATUS         = class="num">5,                                                             class=class="str">"cmt">// Sort by order status(market order/pending order/deal/balance, credit operation)
  SORT_BY_ORDER_TYPE           = class="num">6,                                                             class=class="str">"cmt">// Sort by order type
  SORT_BY_ORDER_REASON         = class="num">7,                                                             class=class="str">"cmt">// Sort by order/position reason/source
  SORT_BY_ORDER_STATE          = class="num">8,                                                             class=class="str">"cmt">// Sort by order status
  SORT_BY_ORDER_POSITION_ID    = class="num">9,                                                             class=class="str">"cmt">// Sort by position ID
  SORT_BY_ORDER_POSITION_BY_ID = class="num">10,                                                            class=class="str">"cmt">// Sort by opposite position ID
  SORT_BY_ORDER_DEAL_ORDER     = class="num">11                                                             class=class="str">"cmt">// Sort by order a deal is based on
  };

常见问题

在订单属性里补一个平仓事件标记,平掉时读该标记即可弹通知;先确认你的订单结构已做减法只留必要字段,避免误判。
用计数宏包住枚举清单,编译期直接得出总数;读的时候按时间筛选枚举去遍历,不用手数。
可以,小布能接管平仓事件监听,触发后直接推送到你的看盘页,你只需设定关注品种即可。
不一样,两者枚举分开定义;读的时候先判是订单还是成交再取对应枚举,否则拿到错值。
只要扩展字段插在排序宏覆盖范围外、并单独写排序宏,原按时间排序逻辑不受影响。