轻松快捷开发 MetaTrader 程序的函数库(第十一部分)。 与 MQL4 的兼容性 - 平仓事件(基础篇)
◍ 给 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 个整数项。
<span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="comment">class=class="str">"cmt">//| Order, deal, position integer properties |</span> <span class="comment">class=class="str">"cmt">//+------------------------------------------------------------------+</span> <span class="keyword">enum</span> ENUM_ORDER_PROP_INTEGER { ORDER_PROP_TICKET = <span class="number">class="num">0</span>, <span class="comment">class=class="str">"cmt">// Order ticket</span> ORDER_PROP_MAGIC, <span class="comment">class=class="str">"cmt">// Order magic number</span> <span style="background-class="type">color:rgb(class="num">216, class="num">232, class="num">194);">ORDER_PROP_TIME_OPEN,</span> <span class="comment">class=class="str">"cmt">// Open time(MQL5 Deal time)</span> <span style="background-class="type">color:rgb(class="num">216, class="num">232, class="num">194);">ORDER_PROP_TIME_CLOSE</span>, <span class="comment">class=class="str">"cmt">// Close time(MQL5 Execution or removal time - ORDER_TIME_DONE)</span> <span style="background-class="type">color:rgb(class="num">249, class="num">204, class="num">202);">ORDER_PROP_TIME_OPEN_MSC</span>, <span class="comment">class=class="str">"cmt">// Open time in milliseconds(MQL5 Deal time in msc)</span> <span style="background-class="type">color:rgb(class="num">249, class="num">204, class="num">202);">ORDER_PROP_TIME_CLOSE_MSC</span>, <span class="comment">class=class="str">"cmt">// Close time in milliseconds(MQL5 Execution or removal time - ORDER_TIME_DONE_MSC)</span> ORDER_PROP_TIME_EXP, <span class="comment">class=class="str">"cmt">// Order expiration date(for pending orders)</span> ORDER_PROP_STATUS, <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 能直接定位这套定义,改结构体前先确认总数别错位。
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)链路。
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 能直接看到这套偏移逻辑。
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 或开单时间稳定排序列出,外汇与贵金属杠杆品种波动剧烈,排序后仍需人工核对风险。
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 };