轻松快捷开发 MetaTrader 程序的函数库(第十一部分)。 与 MQL4 的兼容性 - 平仓事件·进阶篇
(2/3)· 还在用秒级时间硬套 MQL5 订单属性?本篇拆解平仓事件兼容里被忽略的毫秒坑与注释扩展
订单排序枚举的尾段分布
在 MT5 的订单历史与持仓查询接口里,排序方式靠一组枚举值驱动。从 12 到 20 覆盖的是整数类属性:成交方向(IN/OUT/IN_OUT)、仓位变更时间、父子订单 ticket、点值利润、SL/TP 触发标记、分组 ID 与买卖方向。 实数属性的起点由 FIRST_ORD_DBL_PROP 接管,其后顺延:开平价格占 +0/+1,止损止盈占 +2/+3,利润与费用从 +4 排到 +6,成交量与未成交量分别是 +7 与 +8,全成本利润(含佣息)在 +9,StopLimit 激活后的挂单价在 +10。 字符串类从 FIRST_ORD_STR_PROP 起算,品种名占基准位,注释占 +1,自定义注释 SORT_BY_ORDER_COMMENT_EXT 落在 +2。开 MT5 头文件 OrderInfo.mqh 搜这些宏,能直接核对你的 EA 排序逻辑有没有越界。
SORT_BY_ORDER_DEAL_ENTRY = class="num">12, class=class="str">"cmt">// Sort by deal direction – IN, OUT or IN/OUT SORT_BY_ORDER_TIME_UPDATE = class="num">13, class=class="str">"cmt">// Sort by position change time in seconds SORT_BY_ORDER_TICKET_FROM = class="num">14, class=class="str">"cmt">// Sort by parent order ticket SORT_BY_ORDER_TICKET_TO = class="num">15, class=class="str">"cmt">// Sort by derived order ticket SORT_BY_ORDER_PROFIT_PT = class="num">16, class=class="str">"cmt">// Sort by order profit in points SORT_BY_ORDER_CLOSE_BY_SL = class="num">17, class=class="str">"cmt">// Sort by order closing by StopLoss flag SORT_BY_ORDER_CLOSE_BY_TP = class="num">18, class=class="str">"cmt">// Sort by order closing by TakeProfit flag SORT_BY_ORDER_GROUP_ID = class="num">19, class=class="str">"cmt">// Sort by order/position group ID SORT_BY_ORDER_DIRECTION = class="num">20, class=class="str">"cmt">// Sort by direction(Buy, Sell) class=class="str">"cmt">//--- Sort by real properties SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP, class=class="str">"cmt">// Sort by open price SORT_BY_ORDER_PRICE_CLOSE = FIRST_ORD_DBL_PROP+class="num">1, class=class="str">"cmt">// Sort by close price SORT_BY_ORDER_SL = FIRST_ORD_DBL_PROP+class="num">2, class=class="str">"cmt">// Sort by StopLoss price SORT_BY_ORDER_TP = FIRST_ORD_DBL_PROP+class="num">3, class=class="str">"cmt">// Sort by TakeProfit price SORT_BY_ORDER_PROFIT = FIRST_ORD_DBL_PROP+class="num">4, class=class="str">"cmt">// Sort by profit SORT_BY_ORDER_COMMISSION = FIRST_ORD_DBL_PROP+class="num">5, class=class="str">"cmt">// Sort by commission SORT_BY_ORDER_SWAP = FIRST_ORD_DBL_PROP+class="num">6, class=class="str">"cmt">// Sort by swap SORT_BY_ORDER_VOLUME = FIRST_ORD_DBL_PROP+class="num">7, class=class="str">"cmt">// Sort by volume SORT_BY_ORDER_VOLUME_CURRENT= FIRST_ORD_DBL_PROP+class="num">8, class=class="str">"cmt">// Sort by unexecuted volume SORT_BY_ORDER_PROFIT_FULL = FIRST_ORD_DBL_PROP+class="num">9, class=class="str">"cmt">// Sort by profit+commission+swap criterion SORT_BY_ORDER_PRICE_STOP_LIMIT= FIRST_ORD_DBL_PROP+class="num">10, class=class="str">"cmt">// Sort by Limit order when StopLimit order is activated class=class="str">"cmt">//--- Sort by class="type">class="kw">string properties SORT_BY_ORDER_SYMBOL = FIRST_ORD_STR_PROP, class=class="str">"cmt">// Sort by symbol SORT_BY_ORDER_COMMENT = FIRST_ORD_STR_PROP+class="num">1, class=class="str">"cmt">// Sort by comment SORT_BY_ORDER_COMMENT_EXT = FIRST_ORD_STR_PROP+class="num">2, class=class="str">"cmt">// Sort by custom comment
「订单对象的毫秒级时间与属性取数接口」
在 MT5 的订单封装类里,时间维度被拆成了两套:普通 datetime(秒精度)与 MSC 后缀的毫秒精度。高亮的那几行 SORT_BY_ORDER_TIME_OPEN_MSC、ORDER_PROP_TIME_CLOSE_MSC 等,就是给外部排序和属性读取用的毫秒字段,做高频撤单分析或跨系统对账时,秒级时间可能漏掉同一秒内多笔成交的顺序。 OrderOpenTime() 与 OrderCloseTime() 返回的是秒级 datetime,而 PositionTimeUpdateMSC() 拿的是持仓最后更新时间的毫秒值。若你的 EA 要比较两笔订单谁先被外部系统处理,直接用 Ticket() 配合 SORT_BY_ORDER_EXT_ID(枚举值 = FIRST_ORD_STR_PROP+3)更稳,避免本地秒级时间撞车。 下面这段是类里简化取属性的内联实现,全部走 GetProperty 宏,调用成本极低: Ticket() 取订单唯一号,TicketFrom() / TicketTo() 分别取父单与衍生单的 ticket,Magic() 取策略魔数。十九个基础属性(ticket、magic、reason、state 等)都按此模式暴露,回测里批量扫历史订单时可直接读,不用再调 OrderGetInteger。 外汇与贵金属杠杆高,用毫秒时间做微观结构统计时,样本偏差可能被放大,结论仅作概率参考。
SORT_BY_ORDER_EXT_ID = FIRST_ORD_STR_PROP+class="num">3 class=class="str">"cmt">// Sort by order ID in an external trading system }; class=class="str">"cmt">//+------------------------------------------------------------------+ SORT_BY_ORDER_TIME_OPEN_MSC SORT_BY_ORDER_TIME_CLOSE_MSC SORT_BY_ORDER_TIME_OPEN SORT_BY_ORDER_TIME_CLOSE ORDER_PROP_TIME_CLOSE_MSC ORDER_PROP_TIME_UPDATE_MSC class="type">class="kw">datetime OrderOpenTime(class="type">void) const; class="type">class="kw">datetime OrderCloseTime(class="type">void) const; class="type">class="kw">datetime OrderExpiration(class="type">void) const; class="type">class="kw">datetime PositionTimeUpdate(class="type">void) const; class="type">class="kw">datetime PositionTimeUpdateMSC(class="type">void) const; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Methods of a simplified access to the order object properties | class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//--- Return(class="num">1) ticket, (class="num">2) parent order ticket, (class="num">3) derived order ticket, (class="num">4) magic number, (class="num">5) order reason, class=class="str">"cmt">//--- (class="num">6) position ID, (class="num">7) opposite position ID, (class="num">8) group ID, (class="num">9) type, (class="num">10) flag of closing by StopLoss, class=class="str">"cmt">//--- (class="num">11) flag of closing by TakeProfit(class="num">12) open time, (class="num">13) close time, (class="num">14) open time in milliseconds, class=class="str">"cmt">//--- (class="num">15) close time in milliseconds, (class="num">16) expiration date, (class="num">17) state, (class="num">18) status, (class="num">19) order type by direction class="type">long Ticket(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_TICKET); } class="type">long TicketFrom(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_TICKET_FROM); } class="type">long TicketTo(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_TICKET_TO); } class="type">long Magic(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_MAGIC); }
◍ 订单对象的属性读取接口
在 MT5 的订单封装类里,一组 const 成员函数负责把底层属性暴露给策略逻辑。它们全部走 this.GetProperty(),传入不同的 ORDER_PROP_* 枚举来定位字段,返回类型依属性而定。 比如 Reason() 返回 long 类型的成交原因码,PositionID() 取关联持仓 ID,TypeOrder() 取订单类型枚举值。外汇与贵金属交易里,这些字段常用于复盘某笔 XAUUSD 订单是被 SL 还是 TP 关闭的。 IsCloseByStopLoss() 与 IsCloseByTakeProfit() 把 ORDER_PROP_CLOSE_BY_SL / TP 强转为 bool,直接回答「这单怎么死的」。注意贵金属杠杆高,滑点可能让实际关闭原因与预期有偏差。 下方代码里 TimeOpen / TimeClose 标灰的两行给出开平时间,返回 datetime。复制进 MT5 的 EA 头文件,就能在 OnTradeTransaction 里打印每笔订单的生命周期。
class="type">long Reason(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_REASON); } class="type">long PositionID(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_POSITION_ID); } class="type">long PositionByID(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_POSITION_BY_ID); } class="type">long GroupID(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_GROUP_ID); } class="type">long TypeOrder(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_TYPE); } class="type">bool IsCloseByStopLoss(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(ORDER_PROP_CLOSE_BY_SL); } class="type">bool IsCloseByTakeProfit(class="type">void) const { class="kw">return (class="type">bool)this.GetProperty(ORDER_PROP_CLOSE_BY_TP); } class="type">class="kw">datetime TimeOpen(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(ORDER_PROP_TIME_OPEN); } class="type">class="kw">datetime TimeClose(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE); }
订单对象的毫秒级时间与属性读取
在 MT5 的订单封装类里,TimeOpenMSC() 和 TimeCloseMSC() 返回的是带毫秒的 datetime,精度比普通 TimeOpen() 高三个数量级,对剥头皮或 Tick 级回测有意义。 State() 返回 ENUM_ORDER_STATE,区分订单是挂单、部分成交还是已关闭;Status() 返回 ENUM_ORDER_STATUS,更偏经纪商侧的生命周期标记,两者不要混用。 CommentExt() 读取 ORDER_PROP_COMMENT_EXT,这是扩展备注字段,普通 Comment() 读不到,EA 间传递自定义标签时可借它避开冲突。 下面这段是类方法原样,可直接贴进 MT5 编辑器核对:
class="type">class="kw">datetime TimeOpenMSC(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(ORDER_PROP_TIME_OPEN_MSC); } class="type">class="kw">datetime TimeCloseMSC(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(ORDER_PROP_TIME_CLOSE_MSC); } class="type">class="kw">datetime TimeExpiration(class="type">void) const { class="kw">return (class="type">class="kw">datetime)this.GetProperty(ORDER_PROP_TIME_EXP); } ENUM_ORDER_STATE State(class="type">void) const { class="kw">return (ENUM_ORDER_STATE)this.GetProperty(ORDER_PROP_STATE); } ENUM_ORDER_STATUS Status(class="type">void) const { class="kw">return (ENUM_ORDER_STATUS)this.GetProperty(ORDER_PROP_STATUS); } ENUM_ORDER_TYPE TypeByDirection(class="type">void) const { class="kw">return (ENUM_ORDER_TYPE)this.GetProperty(ORDER_PROP_DIRECTION); } class=class="str">"cmt">//--- Return(class="num">1) open price, (class="num">2) close price, (class="num">3) profit, (class="num">4) commission, (class="num">5) swap, (class="num">6) volume, class=class="str">"cmt">//--- Return(class="num">1) symbol, (class="num">2) comment, (class="num">3) ID at an exchange class="type">class="kw">string Symbol(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_SYMBOL); } class="type">class="kw">string Comment(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_COMMENT); } class="type">class="kw">string CommentExt(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_COMMENT_EXT); }
「订单对象的属性读取与构造落地」
在自建订单类里,把 MT5 原生订单字段映射成类内属性,能省掉反复调用 CTrade 接口的开销。下面这段把外部 ID、总利润、点值利润都做成了只读方法,调用时直接取,不走二次查询。 ExternalID() 返回 ORDER_PROP_EXT_ID 里存的外部标识;ProfitFull() 把 Profit()、Comission()、Swap() 三者相加,得到含手续费与库存费的真实盈亏;ProfitInPoints() 单独给出点数利润,方便做按点统计的回测。 SetGroupID() 和 SetCommentExt() 是写入口:前者把自定义分组 ID 写进 ORDER_PROP_GROUP_ID,后者把扩展注释写进 ORDER_PROP_COMMENT_EXT,用于多策略同账号隔离。 构造函数 COrder::COrder 用 ticket 和状态枚举初始化,把 magic、ticket、过期时间、订单类型、状态、方向、关联持仓 ID 一次性灌进 m_long_prop 数组。实盘外汇与贵金属波动剧烈、杠杆风险高,这种预存属性在高频扫描中可能降低延迟,但是否稳定仍取决于你的品种流动性和点差。
class="type">class="kw">string ExternalID(class="type">void) const { class="kw">return this.GetProperty(ORDER_PROP_EXT_ID); } class=class="str">"cmt">//--- Get the full order profit class="type">class="kw">double ProfitFull(class="type">void) const { class="kw">return this.Profit()+this.Comission()+this.Swap(); } class=class="str">"cmt">//--- Get order profit in points class="type">int ProfitInPoints(class="type">void) const; class=class="str">"cmt">//--- Set(class="num">1) group ID and(class="num">2) custom comment class="type">void SetGroupID(const class="type">long group_id) { this.SetProperty(ORDER_PROP_GROUP_ID,group_id); } class="type">void SetCommentExt(const class="type">class="kw">string comment_ext) { this.SetProperty(ORDER_PROP_COMMENT_EXT,comment_ext); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Closed parametric constructor | class=class="str">"cmt">//+------------------------------------------------------------------+ COrder::COrder(ENUM_ORDER_STATUS order_status,const class="type">ulong ticket) { class=class="str">"cmt">//--- Save integer properties this.m_ticket=ticket; this.m_long_prop[ORDER_PROP_STATUS] = order_status; this.m_long_prop[ORDER_PROP_MAGIC] = this.OrderMagicNumber(); this.m_long_prop[ORDER_PROP_TICKET] = this.OrderTicket(); this.m_long_prop[ORDER_PROP_TIME_EXP] = this.OrderExpiration(); this.m_long_prop[ORDER_PROP_TYPE] = this.OrderType(); this.m_long_prop[ORDER_PROP_STATE] = this.OrderState(); this.m_long_prop[ORDER_PROP_DIRECTION] = this.OrderTypeByDirection(); this.m_long_prop[ORDER_PROP_POSITION_ID] = this.OrderPositionID();
◍ 把订单历史塞进属性数组的写法
在封装历史订单对象时,把 MT5 返回的各类字段分别写进 long / double / string 三类属性容器,是后续做统计和回放的前提。下面这段赋值直接对应订单的成交原因、关联单据、开平时间等元数据。 注意高亮的三行:ORDER_PROP_TIME_OPEN / CLOSE / UPDATE 取的是带毫秒的 MSC 版本(OrderOpenTimeMSC 等),精度到毫秒级,比你用 TimeCurrent() 秒级比对更能还原真实滑点区间。外汇与贵金属点差跳动快,毫秒时间戳对复盘成交质量有实际意义,但这类品种杠杆高、跳空频繁,仍属高风险。 double 组覆盖了开平仓价、利润、佣金、库存费、成交量与挂单的 SL/TP,其中 ORDER_PROP_VOLUME_CURRENT 抓的是当前未平部分,和初始 ORDER_PROP_VOLUME 不同,用来算部分平仓很关键。string 组只存品种名、注释、外部 ID,方便和小布知识库的标签体系做映射。 跑通后你可以在 MT5 策略测试器里用 Print(m_long_prop[ORDER_PROP_TIME_OPEN]) 验证毫秒值是否落进数组,再决定要不要接自己的分析脚本。
this.m_long_prop[ORDER_PROP_REASON] = this.OrderReason(); this.m_long_prop[ORDER_PROP_DEAL_ORDER_TICKET] = this.DealOrderTicket(); this.m_long_prop[ORDER_PROP_DEAL_ENTRY] = this.DealEntry(); this.m_long_prop[ORDER_PROP_POSITION_BY_ID] = this.OrderPositionByID(); this.m_long_prop[ORDER_PROP_TIME_OPEN] = this.OrderOpenTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_CLOSE] = this.OrderCloseTimeMSC(); this.m_long_prop[ORDER_PROP_TIME_UPDATE] = this.PositionTimeUpdateMSC(); class=class="str">"cmt">//--- Save real properties this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_OPEN)] = this.OrderOpenPrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_CLOSE)] = this.OrderClosePrice(); this.m_double_prop[this.IndexProp(ORDER_PROP_PROFIT)] = this.OrderProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_COMMISSION)] = this.OrderCommission(); this.m_double_prop[this.IndexProp(ORDER_PROP_SWAP)] = this.OrderSwap(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME)] = this.OrderVolume(); this.m_double_prop[this.IndexProp(ORDER_PROP_SL)] = this.OrderStopLoss(); this.m_double_prop[this.IndexProp(ORDER_PROP_TP)] = this.OrderTakeProfit(); this.m_double_prop[this.IndexProp(ORDER_PROP_VOLUME_CURRENT)] = this.OrderVolumeCurrent(); this.m_double_prop[this.IndexProp(ORDER_PROP_PRICE_STOP_LIMIT)]= this.OrderPriceStopLimit(); class=class="str">"cmt">//--- Save class="type">class="kw">string properties this.m_string_prop[this.IndexProp(ORDER_PROP_SYMBOL)] = this.OrderSymbol(); this.m_string_prop[this.IndexProp(ORDER_PROP_COMMENT)] = this.OrderComment(); this.m_string_prop[this.IndexProp(ORDER_PROP_EXT_ID)] = this.OrderExternalID(); class=class="str">"cmt">//--- Save additional integer properties