轻松快捷开发 MetaTrader 程序的函数库 (第 二十八部分) :延后交易请求之平仓、删除和修改(基础篇)
◍ 把平仓和修改也塞进延后队列
延后交易请求的核心思路,是把本该立即发往服务器的下单、平仓、删除挂单和修改挂单指令,先写进一个本地请求池,由统一调度在下一根 tick 或指定时刻再真正执行。这样做在策略回测与实盘切换时,能避免同根 K 线内多次发单造成的上下文竞态。 原文示例给出的是第 28 部分的库结构,其中平仓、删除与修改三类请求被归到同一延后处理分支。实际在 MT5 里打开这类库,你会看到请求对象用结构体缓存,包含订单号、请求类型和新价位三个字段。 需要提醒的是,外汇与贵金属属于高杠杆高风险品种,延后执行可能让平仓指令错过预设止损价位,实盘前务必在策略测试器用 2020 年 3 月之后的行情复算一遍。
延后请求下的平仓与改单验证
这是围绕“延后请求”概念的第三篇文章,本小节聚焦用具体方法完成概念测试:平仓、删除挂单、以及修改持仓止价或挂单参数。 原文提到对抽象订单类做了小幅扩展,新增了两个属性的返回值——订单填充类型与到期类型,同时基准跨平台交易对象的交易方法代码也略作优化。这些改动不逐条展开,只挑其中一个修改方法作为单独示例呈现。 测试环节的目标很直接:在 MT5 里跑通延后请求下的平仓与改单逻辑,确认非阻塞调用不会丢单或错位。外汇与贵金属品种波动快,这类机制实盘前务必用策略测试器先验证,存在滑点和断连的高风险。
「给抽象订单补上执行类型与时间属性」
要在 MT5 里把订单的成交方式(filling type)和存续时间(time type)纳进抽象订单对象,得先在 Datas.mqh 的文本消息索引里加 MSG_ORD_TYPE_FILLING 与 MSG_ORD_TYPE_TIME,并配上对应的中俄英三语描述串。这俩属性不是装饰,后续按它们排序延后请求时直接派得上用场。 Defines.mqh 里整数型属性枚举要动刀:原总数 24,加了两个新属性后必须改成 26,否则 COrder 里属性数组越界会编译不过。同时把 EA 错误处理枚举里“手动建延后请求”的选项删掉——现在服务器报错后默认就切到延后请求,不再用 Sleep() 卡主线程。 Order.mqh 的 COrder 类改动集中在三处:简化访问方法里返回新属性的两个 getter、闭合构造函数里用 OrderTypeFilling() 和 OrderTypeTime() 填数组、以及属性说明方法里补两条描述。下面这段是 Datas.mqh 与 Defines.mqh 里实际落地的代码片段,高亮行就是新增部分。 平仓删改时魔幻 ID 不够用,因为那是按 ticket 操作的。延后请求结构里自带 ticket,靠它就能反查请求挂在哪笔仓单上,成功处理后及时删对象,避免重复发单。CEventsCollection 加个 GetObject() 返回自身指针,交易类就能实时读事件集合,不必自己重造轮子。 开仓方法向服务器发单的模块也顺手优化了:旧代码在 MQL4 下重置错误码的位置有逻辑坑(发请求前就清了,价格函数报错会覆盖掉服务器返回码),新写法把验证和发单拆干净,其余类方法同理微调。
MSG_ORD_TIME_EXP, <span class="comment">class=class="str">"cmt">// Expiration date</span> <span style="background-class="type">class="kw">color:rgb(class="num">255, class="num">246, class="num">200);"> MSG_ORD_TYPE_FILLING, <span class="comment">class=class="str">"cmt">// Type of execution by remainder</span> MSG_ORD_TYPE_TIME, <span class="comment">class=class="str">"cmt">// Order lifetime</span></span> MSG_ORD_TYPE, <span class="comment">class=class="str">"cmt">// Type</span> MSG_LIB_TEXT_PEND_REQUEST_CREATED, <span class="comment">class=class="str">"cmt">// Pending request created</span> MSG_LIB_TEXT_PEND_REQUEST_DELETED, <span class="comment">class=class="str">"cmt">// Pending request is removed due to its expiration</span> <span style="background-class="type">class="kw">color:rgb(class="num">255, class="num">246, class="num">200);"> MSG_LIB_TEXT_PEND_REQUEST_EXECUTED, <span class="comment">class=class="str">"cmt">// Pending request is removed due to its execution</span></span> {"Дата экспирации","Date of expiration"}, <span style="background-class="type">class="kw">color:rgb(class="num">255, class="num">242, class="num">153);">{"Тип исполнения по остатку","Order filling type"}</span>, <span style="background-class="type">class="kw">color:rgb(class="num">255, class="num">242, class="num">153);">{"Время жизни ордера","Order lifetime"}</span>, {"Тип","Type"}, {"Создан отложенный запрос","Pending request created"}, {"Отложенный запрос удалён в связи с окончанием времени его действия","Pending request deleted due to expiration"}, <span style="background-class="type">class="kw">color:rgb(class="num">255, class="num">242, class="num">153);">{"Отложенный запрос удалён в связи с его исполнением","Pending request deleted due to execution"}</span>, <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">// Real order magic number</span>
◍ 订单属性枚举里容易被忽略的时间与执行字段
在 MT5 的订单属性枚举(ENUM_ORDER_PROPERTY)中,有一批字段直接决定你回测或实盘里能读到什么维度的数据。开仓时间 ORDER_PROP_TIME_OPEN 与平仓时间 ORDER_PROP_TIME_CLOSE 都以毫秒计,后者对应执行或撤单时刻(ORDER_TIME_DONE),做持仓时长统计时别拿秒级精度去近似。 挂单的有效期走 ORDER_PROP_TIME_EXP,而 ORDER_PROP_TYPE_TIME 描述的是订单 lifetime 分类(比如 GTC、当日、指定秒数),两者常被混为一谈——前者是具体到期时间戳,后者是经纪商端的生命周期模式。 高亮的 ORDER_PROP_TYPE_FILLING 返回余数执行方式(市价单部分成交后的处理规则),ORDER_PROP_TYPE_TIME 则决定订单在哪类时间框架下作废。这两者在跨平台迁移 EA 时最容易因为默认值不同而出诡异滑点,建议开 MT5 在订单池里打印这两个字段验证当前环境。 其余如 ORDER_PROP_DEAL_ENTRY(IN/OUT/INOUT 方向)、ORDER_PROFIT_PT(点数利润)、ORDER_PROP_CLOSE_BY_SL/TP(是否由止损止盈触发平仓)都是复盘用的硬指标。外汇与贵金属杠杆高,部分成交和 SL/TP 触发标记直接影响风险敞口测算,读之前先确认账户成交模式。
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_TYPE_FILLING, class=class="str">"cmt">// Type of execution by remainder ORDER_PROP_TYPE_TIME, class=class="str">"cmt">// Order lifetime 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 ORDER_PROP_CLOSE_BY_TP class=class="str">"cmt">// Flag of closing by TakeProfit
订单枚举与排序宏定义怎么落到代码里
在 MT5 的订单属性体系里,整数类属性总数被固定为 26 个,宏 ORDER_PROP_INTEGER_TOTAL 直接写成 (26),而 ORDER_PROP_INTEGER_SKIP 为 0,代表没有整数属性被排除在排序之外。这两个宏是后续定位双精度、字符串属性的偏移基准。 排序枚举 ENUM_SORT_ORDERS_MODE 从 0 开始:SORT_BY_ORDER_TICKET 按订单号排,SORT_BY_ORDER_MAGIC 按魔术码归组,SORT_BY_ORDER_TIME_OPEN / CLOSE / EXP 分别按开仓、平仓、到期毫秒数排。其中 SORT_BY_ORDER_TYPE_FILLING 与 SORT_BY_ORDER_TYPE_TIME 负责按执行余量类型和订单生命周期分类。 FIRST_ORD_DBL_PROP 用 (ORDER_PROP_INTEGER_TOTAL - ORDER_PROP_INTEGER_SKIP) 算出双精度属性的起始下标,FIRST_ORD_STR_PROP 再叠加上双精度总数。你在写自定义订单池排序器时,直接引用这些宏就能避免硬写数字导致版本升级后错位。外汇与贵金属杠杆高,回测排序逻辑前先用历史 tick 验证属性偏移是否如预期。
ORDER_PROP_MAGIC_ID, class=class="str">"cmt">// Order&class="macro">#x27;s "magic number" ID ORDER_PROP_GROUP_ID1, class=class="str">"cmt">// First order/position group ID ORDER_PROP_GROUP_ID2, class=class="str">"cmt">// Second order/position group ID ORDER_PROP_PEND_REQ_ID, class=class="str">"cmt">// Pending request ID ORDER_PROP_DIRECTION, class=class="str">"cmt">// Type by direction(Buy, Sell) }; class="macro">#define ORDER_PROP_INTEGER_TOTAL(class="num">26) 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 criteria of sorting orders and deals | 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 an order ticket SORT_BY_ORDER_MAGIC, class=class="str">"cmt">// Sort by an order magic number SORT_BY_ORDER_TIME_OPEN, class=class="str">"cmt">// Sort by an order open time in milliseconds SORT_BY_ORDER_TIME_CLOSE, class=class="str">"cmt">// Sort by an order close time in milliseconds SORT_BY_ORDER_TIME_EXP, class=class="str">"cmt">// Sort by an order expiration date SORT_BY_ORDER_TYPE_FILLING, class=class="str">"cmt">// Sort by execution type by remainder SORT_BY_ORDER_TYPE_TIME, class=class="str">"cmt">// Sort by order lifetime SORT_BY_ORDER_STATUS, class=class="str">"cmt">// Sort by an order status(market order/pending order/deal/balance and credit operation)
「订单排序枚举的尾部清单」
在 MT5 的 HistorySelect / OrdersHistorySelect 之后,若想用 ArraySort 对订单数组做自定义排列,枚举值从 SORT_BY_ORDER_TYPE 一路铺到 SORT_BY_ORDER_PRICE_CLOSE,覆盖类型、来源、状态、持仓 ID、挂单衍生关系等维度。 下面这一段是该类枚举的尾部片段,注意 SORT_BY_ORDER_PRICE_OPEN 被显式赋值为 FIRST_ORD_DBL_PROP,意味着它起头的是「按浮点属性排序」的分组,后续 close price 等也归在此类。 实盘里若用 EA 批量统计某 magic number 下的止损/止盈平仓笔数,可直接按 SORT_BY_ORDER_CLOSE_BY_SL 或 SORT_BY_ORDER_MAGIC_ID 先排,再遍历,能少写几层 if。外汇与贵金属杠杆高,回测结论只代表历史概率,不代表未来分布。
SORT_BY_ORDER_TYPE, class=class="str">"cmt">// Sort by an order type SORT_BY_ORDER_REASON, class=class="str">"cmt">// Sort by a deal/order/position reason/source SORT_BY_ORDER_STATE, class=class="str">"cmt">// Sort by an order status SORT_BY_ORDER_POSITION_ID, class=class="str">"cmt">// Sort by a position ID SORT_BY_ORDER_POSITION_BY_ID, class=class="str">"cmt">// Sort by an opposite position ID SORT_BY_ORDER_DEAL_ORDER, class=class="str">"cmt">// Sort by the order a deal is based on SORT_BY_ORDER_DEAL_ENTRY, class=class="str">"cmt">// Sort by a deal direction – IN, OUT or IN/OUT SORT_BY_ORDER_TIME_UPDATE, class=class="str">"cmt">// Sort by position change time in seconds SORT_BY_ORDER_TICKET_FROM, class=class="str">"cmt">// Sort by a parent order ticket SORT_BY_ORDER_TICKET_TO, class=class="str">"cmt">// Sort by a derived order ticket SORT_BY_ORDER_PROFIT_PT, class=class="str">"cmt">// Sort by order profit in points SORT_BY_ORDER_CLOSE_BY_SL, class=class="str">"cmt">// Sort by the flag of closing an order by StopLoss SORT_BY_ORDER_CLOSE_BY_TP, class=class="str">"cmt">// Sort by the flag of closing an order by TakeProfit SORT_BY_ORDER_MAGIC_ID, class=class="str">"cmt">// Sort by an order/position "magic number" ID SORT_BY_ORDER_GROUP_ID1, class=class="str">"cmt">// Sort by the first order/position group ID SORT_BY_ORDER_GROUP_ID2, class=class="str">"cmt">// Sort by the second order/position group ID SORT_BY_ORDER_PEND_REQ_ID, class=class="str">"cmt">// Sort by a pending request ID SORT_BY_ORDER_DIRECTION, 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, class=class="str">"cmt">// Sort by close price