轻松快捷开发 MetaTrader 程序的函数库 (第 二十九部分) :延后交易请求 - 请求对象类·进阶篇
🧩

轻松快捷开发 MetaTrader 程序的函数库 (第 二十九部分) :延后交易请求 - 请求对象类·进阶篇

(2/3)· 把散落在交易类里的延后请求代码收拢成可扩展对象,是函数库从验证概念走向工程化的关键一步

含代码示例偏理论 第 2/3 篇
把延后请求的处理直接写进交易类里,初期能跑通,后期加一种新请求类型就要改一堆判断。很多人卡在“能下单就行”,等到要支持 stoplimit 组合条件时才发现结构根本扩不动。

「挂单请求队列的排序维度枚举」

在 MT5 的 pending request 管理结构里,排队中的交易请求需要按不同属性做排序,才能决定下一次激活的优先级。下面这段枚举把可排序的维度一次性列清:从请求自身状态、类型、ID,到重试次数、等待间隔,再到 MqlTradeRequest 结构体内的 action、magic、deviation 等字段,共覆盖 20 个以上的整数与双精度属性。 宏 FIRST_PREQ_STR_PROP 用整数与双精度属性的总数减去跳过项来计算字符串属性的起始偏移,说明排序键是分块连续编号的:整数类在前,双精度类接后,字符串类另起。实际写 EA 时,若想按‘距离下次激活时间’排队列,直接传 SORT_BY_PEND_REQ_TIME_ACTIVATE 即可,不用自己写比较函数。 外汇与贵金属品种点差跳变频繁,这类队列排序逻辑只解决‘先发哪个请求’,不保证成交;实盘前务必在策略测试器用真实点差回放验证。

MQL5 / C++
class="macro">#define FIRST_PREQ_STR_PROP(PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_INTEGER_SKIP+PEND_REQ_PROP_DOUBLE_TOTAL-PEND_REQ_PROP_DOUBLE_SKIP)
enum ENUM_SORT_PEND_REQ_MODE
  {
class=class="str">"cmt">//--- Sort by integer properties
   SORT_BY_PEND_REQ_STATUS = class="num">0,                                                              class=class="str">"cmt">// Sort by a trading request status(from the ENUM_PEND_REQ_STATUS enumeration)
   SORT_BY_PEND_REQ_TYPE,                                                                    class=class="str">"cmt">// Sort by a trading request type(from the ENUM_PEND_REQ_TYPE enumeration)
   SORT_BY_PEND_REQ_ID,                                                                      class=class="str">"cmt">// Sort by a trading request ID
   SORT_BY_PEND_REQ_RETCODE,                                                                 class=class="str">"cmt">// Sort by a result a request is based on
   SORT_BY_PEND_REQ_TIME_CREATE,                                                             class=class="str">"cmt">// Sort by a request generation time
   SORT_BY_PEND_REQ_TIME_ACTIVATE,                                                           class=class="str">"cmt">// Sort by next attempt activation time
   SORT_BY_PEND_REQ_WAITING,                                                                 class=class="str">"cmt">// Sort by a waiting time between requests
   SORT_BY_PEND_REQ_CURENT,                                                                  class=class="str">"cmt">// Sort by the current attempt index
   SORT_BY_PEND_REQ_TOTAL,                                                                   class=class="str">"cmt">// Sort by a number of attempts
   class=class="str">"cmt">//--- class="type">MqlTradeRequest
   SORT_BY_PEND_REQ_MQL_REQ_ACTION,                                                          class=class="str">"cmt">// Sort by a type of a performed action in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_TYPE,                                                            class=class="str">"cmt">// Sort by an order type in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_MAGIC,                                                           class=class="str">"cmt">// Sort by an EA stamp(magic number ID) in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_ORDER,                                                           class=class="str">"cmt">// Sort by an order ticket in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_POSITION,                                                        class=class="str">"cmt">// Sort by a position ticket in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_POSITION_BY,                                                     class=class="str">"cmt">// Sort by an opposite position ticket in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_DEVIATION,                                                       class=class="str">"cmt">// Sort by a maximum acceptable deviation from a requested price in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_EXPIRATION,                                                      class=class="str">"cmt">// Sort by an order expiration time(for ORDER_TIME_SPECIFIED type orders) in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_TYPE_FILLING,                                                    class=class="str">"cmt">// Sort by an order filling type in the request structure
   SORT_BY_PEND_REQ_MQL_REQ_TYPE_TIME,                                                       class=class="str">"cmt">// Sort by order lifetime type in the request structure
class=class="str">"cmt">//--- Sort by real properties

正文

&nbsp;&nbsp; SORT_BY_PEND_REQ_PRICE_CREATE = FIRST_PREQ_DBL_PROP,&nbsp;&nbsp;&nbsp;&nbsp; <span class="comment">// Sort by a price at the moment of a request generation</span> &nbsp;&nbsp; <span class="comment">//--- MqlTradeRequest</span> &nbsp;&nbsp; SORT_BY_PEND_REQ_MQL_REQ_VOLUME,&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">// Sort by a requested volume of a deal in lots in the request structure</span> &nbsp;&nbsp; SORT_BY_PEND_REQ_MQL_REQ_PRICE,&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">// Sort by a price in the request structure</span> &nbsp;&nbsp; SORT_BY_PEND_REQ_MQL_REQ_STOPLIMIT,&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">// Sort by StopLimit order level in the request structure</span> &nbsp;&nbsp; SORT_BY_PEND_REQ_MQL_REQ_SL,&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">// Sort by StopLoss order level in the request structure</span> &nbsp;&nbsp; SORT_BY_PEND_REQ_MQL_REQ_TP,&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&

◍ 挂单请求对象的属性索引与账户参数

在封装挂单请求的类里,double 和 string 类型的属性并不从 0 开始编号,而是用枚举值减去前面所有整数、双精度属性的总数来定位数组下标。比如 IndexProp(ENUM_PEND_REQ_PROP_STRING) 的返回值等于 property 减去 PEND_REQ_PROP_INTEGER_TOTAL 再减去 PEND_REQ_PROP_DOUBLE_TOTAL,这样三类属性各自落在独立的数组里,互不越界。 protected 段暴露了几个关键账户字段:m_digits 报价格小数位、m_digits_lot 是手数小数位、m_is_hedge 标记是否为对冲账户,外加三块定长数组 m_long_prop / m_double_prop / m_string_prop 分别存整数、实数和字符串请求属性。外汇与贵金属杠杆高,对冲账户和非对冲账户的订单管理逻辑差异大,m_is_hedge 直接决定后续平仓方式。 带参构造函数接收状态、ID、价格、时间、原始 MqlTradeRequest 引用和 retcode,把一次挂单动作完整快照下来。GetMagicID 则从设置里取魔术码低 16 位(& 0xFFFF),用于区分 EA 自有的挂单。打开 MT5 新建 EA,复制下面这段类片段,把 PEND_REQ_PROP_INTEGER_TOTAL 等宏换成你实际枚举值,就能在 OnInit 里打印 IndexProp 验证偏移是否正确。

MQL5 / C++
class=class="str">"cmt">//--- Return the index of the array the request(class="num">1) class="type">class="kw">double and(class="num">2) class="type">class="kw">string properties are actually located at
  class="type">int                IndexProp(ENUM_PEND_REQ_PROP_DOUBLE class="kw">property)class="kw">const { class="kw">return(class="type">int)class="kw">property-PEND_REQ_PROP_INTEGER_TOTAL;               }
  class="type">int                IndexProp(ENUM_PEND_REQ_PROP_STRING class="kw">property)class="kw">const { class="kw">return(class="type">int)class="kw">property-PEND_REQ_PROP_INTEGER_TOTAL-PEND_REQ_PROP_DOUBLE_TOTAL;  }
class="kw">protected:
  class="type">int                m_digits;                                                                         class=class="str">"cmt">// Number of decimal places in a quote
  class="type">int                m_digits_lot;                                                                     class=class="str">"cmt">// Number of decimal places in the symbol lot value
  class="type">bool               m_is_hedge;                                                                       class=class="str">"cmt">// Hedging account flag
  class="type">long               m_long_prop[PEND_REQ_PROP_INTEGER_TOTAL];                                          class=class="str">"cmt">// Request integer properties
  class="type">class="kw">double             m_double_prop[PEND_REQ_PROP_DOUBLE_TOTAL];                                         class=class="str">"cmt">// Request real properties
  class="type">class="kw">string             m_string_prop[PEND_REQ_PROP_STRING_TOTAL];                                         class=class="str">"cmt">// Request class="type">class="kw">string properties
class=class="str">"cmt">//--- Protected parametric constructor
                     CPendRequest(class="kw">const ENUM_PEND_REQ_STATUS status,
                                  class="kw">const class="type">uchar id,
                                  class="kw">const class="type">class="kw">double price,
                                  class="kw">const class="type">class="kw">ulong time,
                                  class="kw">const class="type">MqlTradeRequest &request,
                                  class="kw">const class="type">int retcode);
class=class="str">"cmt">//--- Return(class="num">1) the magic number specified in the settings, (class="num">2) hedging account flag
  class="type">class="kw">ushort             GetMagicID(class="type">void)                                                                 class="kw">const { class="kw">return class="type">class="kw">ushort(this.GetProperty(PEND_REQ_PROP_MQL_REQ_MAGIC) & 0xFFFF);}

「挂单请求对象的属性读写与比对」

在MT5里封装挂单请求时,用 CPendRequest 类把整数字段、双精度字段和字符串字段分开存,能避免手动拼结构体的低级错误。 下面这段代码给出了核心方法的骨架: [CODE] bool IsHedge(void) const { return this.m_is_hedge; } public: //--- Default constructor CPendRequest(){;} //--- Set request (1) integer, (2) real and (3) string properties void SetProperty(ENUM_PEND_REQ_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_PEND_REQ_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_PEND_REQ_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value; } //--- Return (1) integer, (2) real and (3) string request properties from the properties array long GetProperty(ENUM_PEND_REQ_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_PEND_REQ_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_PEND_REQ_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Return the flag of the request supporting the property virtual bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_PEND_REQ_PROP_STRING property) { return true; } //--- Compare CPendRequest objects by a specified property (to sort the lists by a specified request object property) virtual int Compare(const CObject *node,const int mode=0) const; //--- Compare CPendRequest objects by all properties (to search for equal request objects) bool IsEqual(CPendRequest* compared_obj); [/CODE] 逐行看:IsHedge 只读成员 m_is_hedge,用来判断当前请求是否走对冲账户逻辑。三个 SetProperty 重载分别落库到 m_long_prop、m_double_prop、m_string_prop,双精度和字符串版本靠 IndexProp 把枚举映射成数组下标,整数版本则直接用枚举当下标。 GetProperty 对称地取值,SupportProperty 默认全返回 true,意味着派生类若不支持某属性,需要自己重写返回 false。Compare 留作列表排序用,IsEqual 则按全部属性匹配来查重。 开MT5新建EA,把这段贴进自定义类,先只改 m_is_hedge 的赋值,观察对冲账户下挂单是否被跳过——外汇和贵金属杠杆高,误单可能造成快速浮亏,验证时务必用策略测试器跑历史数据。

MQL5 / C++
class="type">bool IsHedge(class="type">void) class="kw">const { class="kw">return this.m_is_hedge; }
class="kw">public:
class=class="str">"cmt">//--- Default constructor
CPendRequest(){;}
class=class="str">"cmt">//--- Set request(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string properties
class="type">void SetProperty(ENUM_PEND_REQ_PROP_INTEGER class="kw">property,class="type">long value) { this.m_long_prop[class="kw">property]=value; }
class="type">void SetProperty(ENUM_PEND_REQ_PROP_DOUBLE class="kw">property,class="type">class="kw">double value){ this.m_double_prop[this.IndexProp(class="kw">property)]=value; }
class="type">void SetProperty(ENUM_PEND_REQ_PROP_STRING class="kw">property,class="type">class="kw">string value){ this.m_string_prop[this.IndexProp(class="kw">property)]=value; }
class=class="str">"cmt">//--- Return(class="num">1) integer, (class="num">2) real and(class="num">3) class="type">class="kw">string request properties from the properties array
class="type">long GetProperty(ENUM_PEND_REQ_PROP_INTEGER class="kw">property) class="kw">const { class="kw">return this.m_long_prop[class="kw">property]; }
class="type">class="kw">double GetProperty(ENUM_PEND_REQ_PROP_DOUBLE class="kw">property) class="kw">const { class="kw">return this.m_double_prop[this.IndexProp(class="kw">property)]; }
class="type">class="kw">string GetProperty(ENUM_PEND_REQ_PROP_STRING class="kw">property) class="kw">const { class="kw">return this.m_string_prop[this.IndexProp(class="kw">property)]; }
class=class="str">"cmt">//--- Return the flag of the request supporting the class="kw">property
class="kw">virtual class="type">bool SupportProperty(ENUM_PEND_REQ_PROP_INTEGER class="kw">property) { class="kw">return true; }
class="kw">virtual class="type">bool SupportProperty(ENUM_PEND_REQ_PROP_DOUBLE class="kw">property) { class="kw">return true; }
class="kw">virtual class="type">bool SupportProperty(ENUM_PEND_REQ_PROP_STRING class="kw">property) { class="kw">return true; }
class=class="str">"cmt">//--- Compare CPendRequest objects by a specified class="kw">property (to sort the lists by a specified request object class="kw">property)
class="kw">virtual class="type">int Compare(class="kw">const CObject *node,class="kw">const class="type">int mode=class="num">0) class="kw">const;
class=class="str">"cmt">//--- Compare CPendRequest objects by all properties(to search for equal request objects)
class="type">bool IsEqual(CPendRequest* compared_obj);

挂单请求对象的属性读取接口

在 MT5 的 pending request 封装类里,一组 const 方法把请求对象的内部状态暴露出来,方便在 EA 里随时核查某笔挂单请求走到哪一步。 MqlRequest() 直接返回结构体本身,其余方法都走 GetProperty() 去取对应枚举属性,比如 Status() 读 PEND_REQ_PROP_STATUS,TypeRequest() 读 PEND_REQ_PROP_TYPE。 PriceCreate() 给出请求生成瞬间的报价,TimeCreate() 是生成时刻(ulong 时间戳),TimeActivate() 是下一次重试激活时间,WaitingMSC() 返回两次请求间的等待毫秒数——这几个值在排查「为什么我的挂单没发出去」时最常用。 CurrentAttempt() 返回 uchar 类型的当前重试序号,配合类里的总尝试次数,就能判断是不是已经撞了重试上限。外汇和贵金属杠杆交易高风险,这类状态读取只用于诊断,不预示成交结果。

MQL5 / C++
class="type">MqlTradeRequest      MqlRequest(class="type">void)                                                  class="kw">const { class="kw">return this.m_request;                                                                      }
ENUM_PEND_REQ_STATUS Status(class="type">void)                                                          class="kw">const { class="kw">return (ENUM_PEND_REQ_STATUS)this.GetProperty(PEND_REQ_PROP_STATUS);   }
ENUM_PEND_REQ_TYPE   TypeRequest(class="type">void)                                                    class="kw">const { class="kw">return (ENUM_PEND_REQ_TYPE)this.GetProperty(PEND_REQ_PROP_TYPE);        }
class="type">class="kw">double                PriceCreate(class="type">void)                                                   class="kw">const { class="kw">return this.GetProperty(PEND_REQ_PROP_PRICE_CREATE);                    }
class="type">class="kw">ulong                 TimeCreate(class="type">void)                                                    class="kw">const { class="kw">return this.GetProperty(PEND_REQ_PROP_TIME_CREATE);                     }
class="type">class="kw">ulong                 TimeActivate(class="type">void)                                                  class="kw">const { class="kw">return this.GetProperty(PEND_REQ_PROP_TIME_ACTIVATE);                   }
class="type">class="kw">ulong                 WaitingMSC(class="type">void)                                                    class="kw">const { class="kw">return this.GetProperty(PEND_REQ_PROP_WAITING);                         }
class="type">uchar                 CurrentAttempt(class="type">void)                                                class="kw">const { class="kw">return (class="type">uchar)this.GetProperty(PEND_REQ_PROP_CURENT);                   }

◍ 挂单请求对象的属性读取与写入

在 MT5 的 pending request 封装类里,只读访问器把底层属性映射成具体类型。TotalAttempts 返回 uchar 类型的已尝试次数,ID 同样用 uchar 承载请求编号,Retcode 以 int 形式给出服务器回执码,Order 与 Position 则用 ulong 拿对应票据号,Action 直接转型为 ENUM_TRADE_REQUEST_ACTIONS 枚举。 写入口集中在 Set 系列方法。注释列了九项可设字段:建请价格、建请时间、本次尝试时间、尝试间隔、当前尝试序号、总次数、ID、订单票、持仓票。下面两段代码只露了前两个——SetPriceCreate 把 double 价格写进 PEND_REQ_PROP_PRICE_CREATE,SetTimeCreate 把 ulong 时间戳写进 PEND_REQ_PROP_TIME_CREATE。 开 MT5 自建一个 CPendingRequest 之类封装类时,照这组 getter/setter 对齐属性常量,能直接复用结构体避免每次手填交易请求。外汇与贵金属杠杆品种跳空频繁,这类重试上下文字段对诊断拒单原因有实际价值,但品种波动高风险依旧存在。

MQL5 / C++
  class="type">uchar              TotalAttempts(class="type">void)                class="kw">const { class="kw">return (class="type">uchar)this.GetProperty(PEND_REQ_PROP_TOTAL);               }
  class="type">uchar              ID(class="type">void)                            class="kw">const { class="kw">return (class="type">uchar)this.GetProperty(PEND_REQ_PROP_ID);                 }
  class="type">int                Retcode(class="type">void)                       class="kw">const { class="kw">return (class="type">int)this.GetProperty(PEND_REQ_PROP_RETCODE);              }
  class="type">class="kw">ulong              Order(class="type">void)                         class="kw">const { class="kw">return this.GetProperty(PEND_REQ_PROP_MQL_REQ_ORDER);             }
  class="type">class="kw">ulong              Position(class="type">void)                      class="kw">const { class="kw">return this.GetProperty(PEND_REQ_PROP_MQL_REQ_POSITION);           }
  ENUM_TRADE_REQUEST_ACTIONS Action(class="type">void)                class="kw">const { class="kw">return (ENUM_TRADE_REQUEST_ACTIONS)this.GetProperty(PEND_REQ_PROP_MQL_REQ_ACTION);}
class=class="str">"cmt">//--- Set(class="num">1) the price when creating a request, (class="num">2) request creation time,
class=class="str">"cmt">//--- (class="num">3) current attempt time, (class="num">4) waiting time between requests,
class=class="str">"cmt">//--- (class="num">5) current attempt index, (class="num">6) number of attempts, (class="num">7) ID,
class=class="str">"cmt">//--- (class="num">8) order ticket, (class="num">9) position ticket
  class="type">void               SetPriceCreate(class="kw">const class="type">class="kw">double price)         { this.SetProperty(PEND_REQ_PROP_PRICE_CREATE,price);              }
  class="type">void               SetTimeCreate(class="kw">const class="type">class="kw">ulong time)             { this.SetProperty(PEND_REQ_PROP_TIME_CREATE,time);                }
把对象结构检查交给小布
这些延后请求对象类的字段与继承关系,小布盯盘的 AIGC 已内置结构视图,打开对应品种页即可对照你自己的库是否缺了某类衍生对象。

常见问题

抽象基类只承载所有请求共有的属性与接口,具体厘清交给衍生类,这样新增一种请求类型只需加衍生类,不必动基类与调用方。
在对象分类上开仓衍生对象会按账户类型区分行为:对冲是新仓,净持是加仓或冲抵,但都继承自同一基准请求对象。
算。价格触及止价位后下一笔限价挂单,这属于基准对象下的挂单衍生对象,组合条件由衍生对象原生属性描述。
目前小布提供结构对照与缺失项提示,代码骨架仍建议在 MQL5 函数库里按本文基类衍生方式手写,避免脱离你的交易逻辑。
消息索引用于对象运行时的状态与错误反馈,是对象可观测性的基础,需在定义枚举前先写入,否则调试时无法定位请求状态。