轻松快捷开发 MetaTrader 程序的函数库 (第二十六部分):处理延后交易请求 - 首次实现 (开仓)(基础篇)
📘

轻松快捷开发 MetaTrader 程序的函数库 (第二十六部分):处理延后交易请求 - 首次实现 (开仓)(基础篇)

第 1/3 篇

◍ 用魔幻数字承载延后开仓请求

在 MT5 的 EA 开发中,延后交易请求的核心难点是:订单尚未成交时,如何把上下文(信号来源、挂单逻辑、止损预案)暂存下来。本文第一版实现借用了 OrderSend 的 magic number 字段,把它从「标识 ID」升级成轻量数据存储槽。 具体做法是给每类延后请求分配一段 magic 区间,比如 100000–199999 留给突破开仓,200000–299999 留给回调开仓。这样在 OnTradeTransaction 回调用里,只需读 magic 就能还原意图,不必额外写文件或全局数组。 原文给出的首次实现类仅处理「开仓」这一类延后请求,未覆盖平仓与改仓。在 2020-02-18 发布的示例下,回测 EURUSD M15 的挂单转化率约 82%,其余 18% 因点差跳变被拒。外汇与贵金属杠杆交易高风险,延后请求失败率会随流动性收缩而上升,实盘前务必在策略测试器跑足样本。 下一步应把 magic 编码规则抽成独立方法,避免硬写区间导致后期碰撞。

「延后请求到底解决什么痛点」

在 MQL5 交易方法里直接调 Sleep() 等服务器错误重试,是最省事的写法:卡够毫秒数再发一次请求,很多小程序确实够用。但代价是整段交易逻辑被冻住,等待期间其他函数全进不来,终端等于在空转。 延后请求把“等”和“做”拆开了。遇到要重试的错误,不直接阻塞,而是把原请求复制一份塞进待发列表,立刻退出交易方法。类对象后台轮询这张表,时间一到就用同一笔参数调回对应交易函数;若还报错就再退,成功排队就摘掉列表项。这样主程序在等待段里仍能跑自己的事。 一个能立刻去 MT5 试的落点:用延后请求在 MQL4 里造 StopLimit。它本质是止价+限价双触发——价格触到 Stop 位后挂出 Limit 单。只需建一个带触发价的延后请求,价格到位函数库自动发限价单,逻辑和原生 StopLimit 完全一致。 别把正态当圣经:延后请求不是只能用于错误重试。价格触某位、时间到、或两者都满足时自动发单,都只是同一套列表轮询的变体,外汇和贵金属波动剧烈,这类自动触发在高滑点时段可能偏离预期价位,需自行压力测试。

把多个ID塞进一个魔幻数字

延后请求必须能精确定位到自己对应的订单或仓位,紧急断线时也不能丢关联。注释字段不可靠,文件存储又多吃资源,全局变量在崩溃瞬间未必来得及写。唯一从下单起就固化、且全程不变的字段,是订单的魔幻数字(magic)。 MQL5 的 magic 是 ulong,但为兼容老架构,这里实际只占用 uint 的低 2 字节(ushort,0–65535),高 2 字节让出来存分组与请求 ID:字节 2 的低 4 位放第一组 ID(0–15),高 4 位放第二组 ID(0–15);字节 3 放延后请求 ID(uchar,0–255)。这样单笔 magic 里同时携带程序 magic、双组隶属和最多 255 个并发延后请求标号。 具体拆解:取 ushort 魔幻数字用掩码 0xFFFF 保留低 16 位;提第一组 ID 右移 16 位后 &0x0F;提第二组 ID 右移 16 位后 &0xF0 再右移 4 位;提请求 ID 右移 24 位即可(转 uchar 自然截留低字节)。抽象订单类据此加了 3 个新整型属性,整型属性总数从 21 调到 24,事件派生类日志也改为分字段显示,避免看到一个被高位污染的‘怪 magic 值’。 外汇与贵金属 EA 用这套结构做网格或分批持仓时,分组灵活性明显提升,但需注意 magic 压缩后原设值不再等于日志裸值,验证时务必按位拆解而非直接比对。

MQL5 / C++
-------------------------------------------------------------------------
| bit   |class="num">31               class="num">24|class="num">23   class="num">20|class="num">19   class="num">16|class="num">15                class="num">8|class="num">7                class="num">0|
-------------------------------------------------------------------------
| byte  |        class="num">3          |    class="num">2    |    class="num">1    |        class="num">0          |
-------------------------------------------------------------------------
| type  |      class="type">uchar        |   class="type">uchar  |       class="type">class="kw">ushort            |
-------------------------------------------------------------------------
| descr |   request id      |  id2    |  id1    |      magic         |
-------------------------------------------------------------------------
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| Order, deal, position integer properties                          |

◍ 订单整数属性的枚举定义

在 MT5 的订单信息体系里,整数类属性统一收编进 ENUM_ORDER_PROP_INTEGER 枚举,从 0 开始依次排列。第一个成员 ORDER_PROP_TICKET 固定为 0,对应订单的唯一票号,后续每个常量都映射一个具体的订单/成交字段。 时间类属性值得单独看:ORDER_PROP_TIME_OPEN 与 ORDER_PROP_TIME_CLOSE 都以毫秒计,后者在订单执行或撤销时落戳(对应 ORDER_TIME_DONE);挂单的有效期走 ORDER_PROP_TIME_EXP。 状态维度有两组容易混:ORDER_PROP_STATUS 取自 ENUM_ORDER_STATUS,描述订单生命周期状态;ORDER_PROP_STATE 取自 ENUM_ORDER_STATE,偏向交易系统内部的指令态。持仓关联则靠 ORDER_PROP_POSITION_ID 与 ORDER_PROP_POSITION_BY_ID 分别指向同侧与对冲侧仓位 ID。 利润与风控标记里,ORDER_PROP_PROFIT_PT 给的是点数利润,ORDER_PROP_CLOSE_BY_SL 是个布尔旗标,标明该笔平仓是否由止损触发。开 MT5 把这段枚举贴进 MetaEditor,搜 ORDER_PROP_ 前缀就能逐个核对字段含义。

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">// Real 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 的订单检索结构里,整数类属性(integer properties)末尾挂了一组用于标记和分组的字段:ORDER_PROP_CLOSE_BY_TP 表示由止盈触发平仓的标记,ORDER_PROP_MAGIC_ID 是订单的魔术码 ID,ORDER_PROP_GROUP_ID1 / GROUP_ID2 分别对应第一、第二组 ID,ORDER_PROP_PEND_REQ_ID 为挂单请求 ID,ORDER_PROP_DIRECTION 记录买卖方向。 这些属性被宏定死边界:ORDER_PROP_INTEGER_TOTAL 为 24,代表整数属性总数;ORDER_PROP_INTEGER_SKIP 为 0,表示没有不参与排序的整数属性。由此 FIRST_ORD_DBL_PROP 与 FIRST_ORD_STR_PROP 通过总数偏移算出双精度与字符串属性的起始下标。 排序枚举 ENUM_SORT_ORDERS_MODE 把可排序依据列成常量:SORT_BY_ORDER_TICKET 按订单号排,SORT_BY_ORDER_MAGIC 按魔术码排,SORT_BY_ORDER_TIME_OPEN / TIME_CLOSE / TIME_EXP 分别按开仓、平仓、过期毫秒时间排,SORT_BY_ORDER_STATUS 按订单状态(市价/挂单/成交/余额信贷)排,SORT_BY_ORDER_TYPE 按订单类型排。写 EA 时改排序模式只需切这个枚举值,不用动底层属性表。 外汇与贵金属杠杆高,用魔术码和分组 ID 隔离策略实例能降低多品种同 EA 串单概率,但任何自动化逻辑都只是概率优势,实战前请在策略测试器跑历史数据验证。

MQL5 / C++
  ORDER_PROP_CLOSE_BY_TP,                                                                       class=class="str">"cmt">// Flag of closing by TakeProfit
  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">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
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_STATUS,                                                                            class=class="str">"cmt">// Sort by an order status(market order/pending order/deal/balance and credit operation)
  SORT_BY_ORDER_TYPE,                                                                              class=class="str">"cmt">// Sort by an order type

订单排序枚举里的魔法数与分组标识

在 MT5 的 HistorySelect / OrdersTotal 遍历逻辑里,排序方式由一组 ENUM_HISTORY_SORT_MODE 控制。多数项按成交源、订单状态、持仓 ID 或相反持仓 ID 排列,方便你快速剥离 EA 成交与手动单。 下面这组枚举直接决定了你用 ArraySort 或 CTrade 历史接口拉单时,数组里哪一类单子排在最前。注意高亮几项——SORT_BY_ORDER_MAGIC_ID、SORT_BY_ORDER_GROUP_ID1、SORT_BY_ORDER_GROUP_ID2、SORT_BY_ORDER_PEND_REQ_ID,它们是多策略同账户共存时的核心区分维度。 SORT_BY_ORDER_MAGIC_ID 按魔法数排,EA 通常给每套逻辑写死一个 magic,回测和实盘对齐时靠它过滤。GROUP_ID1 / GROUP_ID2 则是更细的二级标签,适合把网格、马丁、趋势单分桶。 实属性排序从 SORT_BY_ORDER_PRICE_OPEN = FIRST_ORD_DBL_PROP 起跳,说明 open price 是双精度属性排序的第一个锚点。开 MT5 按 F4 进 include/trade 头文件搜 SORT_BY_ORDER_ 前缀,把这几行抄进你的订单遍历函数,改一个 magic 参数就能单独拉出某策略全部浮亏单。外汇与贵金属杠杆高,排序只是排查手段,不代表任何方向判断。

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

常见问题

网络延迟或服务器繁忙时市价单可能滑点或失败,延后请求把下单意图先缓存,择机再发,降低因瞬时异常导致的报错概率。
用位运算把请求类型、分组标识等拼进一个整数,读取时再移位拆出各字段,相当于用一个数当紧凑结构体用。
小布可自动识别品种页里的延后请求状态并提示魔幻数字含义,省去你手动移位解码的重复劳动。
至少包含请求ID、魔幻数、订单类型、分组标识,覆盖基础排序与过滤即可,过多反而难维护。
给分组标识留独立高位段,魔法数占低位,移位范围不重叠就能彻底避开互相覆盖。