轻松快捷开发 MetaTrader 程序的函数库 (第 三十一部分) :延后交易请求 - 在特定条件下开仓(基础篇)
📘

轻松快捷开发 MetaTrader 程序的函数库 (第 三十一部分) :延后交易请求 - 在特定条件下开仓(基础篇)

第 1/3 篇

「把开仓请求挂起来等条件」

在 MT5 里直接市价开仓,常常错过你真正想要的入场环境。把交易请求做成「延后对象」,让程序先记着这笔单,等价格或指标满足条件再真正发往服务器,是更稳的做法。 本文示例基于 2020 年 4 月 13 日发布的思路,原文示例阅读量 3038,说明这类延迟执行结构在 EA 开发里需求不小。 核心流程分四步:先理清概念,再准备所需数据,然后按需创建延后请求对象,最后做测试验证。下一步才是接入具体交易逻辑。 外汇和贵金属杠杆高,延后请求只是控制触发时机,不消除滑点与熔断风险,条件命中也可能不盈利。

◍ 用延后请求把条件交易写进库里

做函数库时我提过一个思路:交易不用立刻发,而是先挂成“延后请求”,等条件满足再送交易服务器。之前二十几篇一直在填“服务器报错后重发”的那一半,现在反过来补齐另一半——由程序自己判定条件,条件到了才下单。 具体能玩出什么组合?举几个轻量例子:价格跌破某阈值且过了指定时间就买;账户浮利超过某值就部分平仓;止损单被扫后自动反手。条件源只有三类——品种属性、账户属性、账户事件,三者可任意拼。本期先落地品种和账户属性的跟踪,事件响应留到后面。 延后请求对象里要塞进一张二维数组当条件表:第一维是条件序号,第二维装该条件的全部字段——源类型(品种/账户/事件)、具体条件枚举、比较符(>/</==/!=/>=/<=)、引用值、当前值。管理类开个定时器扫这张表,全部命中就在同一类里把请求推出去。 本篇只做最小验证:测试 EA 里只跟两个条件——价格和时间。它们可单独生效,也可“价格且时间”联合触发,用来确认延后请求在条件交易里的通路。外汇与贵金属杠杆高,条件单也只降低手动延迟,不消除滑点与黑天鹅风险。

延后请求的数据源与消息索引搭建

做延后请求功能前,先在 Datas.mqh 里补一批消息索引和对应文本。账户、品种、事件这几类激活条件的来源完全不同,如果只靠已有的属性索引,很容易把不同对象的同名属性搞混,所以必须显式声明数据源再跟踪数值。 在 Defines.mqh 中写两个枚举:一个是延后请求激活来源(账户 / 品种 / 事件),另一个是各来源下的具体激活准则。枚举常量的值直接等于对应属性文本消息的常量值,日志输出时只需用常量索引就能带出归属,不用再额外判断它属于哪一类。 三个枚举可任意组合,编译出所需的激活条件集合。DELib.mqh 里补一个返回比较类型描述的函数;原库里 STOP_LOSS / TAKE_PROFIT 字符串常量改名为 SL / TP,涉及文件较多,全局替换时要注意别漏。 下面这段是 Datas.mqh 中新增的索引与文本片断,高亮行为本次延后请求相关的新增项,可对照抄进你的库里验证编译。

MQL5 / C++
class=class="str">"cmt">//--- CEvent
   MSG_EVN_EVENT,                                              class=class="str">"cmt">// Event
   MSG_EVN_TYPE,                                               class=class="str">"cmt">// Event type
class=class="str">"cmt">//--- CAccount
   MSG_ACC_ACCOUNT,                                            class=class="str">"cmt">// Account
   MSG_ACC_PROP_LOGIN,                                         class=class="str">"cmt">// Account number
   MSG_LIB_TEXT_REQUEST,                                       class=class="str">"cmt">// Pending request #
   MSG_LIB_TEXT_REQUEST_ACTIVATED,                             class=class="str">"cmt">// Pending request activated: #
   MSG_LIB_TEXT_REQUEST_DATAS,                                 class=class="str">"cmt">// Trading request parameters
   MSG_LIB_TEXT_PEND_REQUEST_DATAS,                            class=class="str">"cmt">// Pending trading request parameters
   MSG_LIB_TEXT_PEND_REQUEST_CREATED,                          class=class="str">"cmt">// Pending request created
   MSG_LIB_TEXT_PEND_REQUEST_DELETED,                          class=class="str">"cmt">// Removed due to expiration
   MSG_LIB_TEXT_PEND_REQUEST_EXECUTED,                         class=class="str">"cmt">// Removed due to execution
   MSG_LIB_TEXT_PEND_REQUEST_GETTING_FAILED,                   class=class="str">"cmt">// Failed to obtain a pending request object from the list
   MSG_LIB_TEXT_PEND_REQUEST_FAILED_ADD_PARAMS,                class=class="str">"cmt">// Failed to add request activation parameters. Error: 
   MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE,                     class=class="str">"cmt">// Price at the moment of request generation
   MSG_LIB_TEXT_PEND_REQUEST_ACTUAL_EXPIRATION,                class=class="str">"cmt">// Actual order lifetime

   MSG_LIB_TEXT_PEND_REQUEST_NO_FREE_IDS,                      class=class="str">"cmt">// No free IDs to create a pending request
   MSG_LIB_TEXT_PEND_REQUEST_ACTIVATION_TERMS,                 class=class="str">"cmt">// Activation conditions
   MSG_LIB_TEXT_PEND_REQUEST_CRITERION,                        class=class="str">"cmt">// Criterion
   MSG_LIB_TEXT_PEND_REQUEST_ADD_CRITERIONS,                   class=class="str">"cmt">// Added pending request activation conditions

   };
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//--- CEvent
   {"Событие","Event"},
   {"Тип события","Event&class="macro">#x27;s type"},
class=class="str">"cmt">//--- CAccount
   {"Аккаунт","Account"},

「挂单请求的多语言标签与激活枚举」

在 MT5 的 EA 工程里,挂单请求(pending request)的诊断信息常以俄英双字符串数组形式硬编码,方便日志同时输出母语与英文。上面这段数组里,背景高亮的几行才是运行时真正容易踩坑的节点:'Pending request activated: #' 表示挂单已被触发,'Failed to get pending request object from list' 则说明内部链表索引失效,这类错误在账户切换或历史清理时概率明显上升。 激活来源被拆成三个枚举值:PEND_REQ_ACTIVATION_SOURCE_ACCOUNT 代表由账户权益、保证金等数据驱动;PEND_REQ_ACTIVATION_SOURCE_SYMBOL 由品种报价或点差变动驱动;PEND_REQ_ACTIVATION_SOURCE_EVENT 则来自成交、删单等交易事件。实盘中外汇与贵金属波动剧烈,若把激活源误配成 SYMBOL 而品种流动性断裂,挂单可能长时间不触发。 请求类型只有两种:PEND_REQ_TYPE_ERROR 是基于错误码生成的挂单(通常不该手动建),PEND_REQ_TYPE_REQUEST 才是正常由交易请求派生的挂单。写代码时建议先判断类型再处理,避免把系统纠错单当成用户策略单。

MQL5 / C++
enum ENUM_PEND_REQ_TYPE
  {
   PEND_REQ_TYPE_ERROR=PENDING_REQUEST_ID_TYPE_ERR,       class=class="str">"cmt">// Pending request created based on the class="kw">return code or error
   PEND_REQ_TYPE_REQUEST=PENDING_REQUEST_ID_TYPE_REQ,     class=class="str">"cmt">// Pending request created by request
  };
enum ENUM_PEND_REQ_ACTIVATION_SOURCE
  {
   PEND_REQ_ACTIVATION_SOURCE_ACCOUNT,                    class=class="str">"cmt">// Pending request activated by account data
   PEND_REQ_ACTIVATION_SOURCE_SYMBOL,                     class=class="str">"cmt">// Pending request activated by symbol data
   PEND_REQ_ACTIVATION_SOURCE_EVENT,                      class=class="str">"cmt">// Pending request activated by trading event data
  };

◍ 挂单按账户状态触发的枚举映射

在 MQL5 的挂单管理封装里,可以用一组枚举把「挂单激活条件」绑定到账户属性上。下面这段枚举把空值、杠杆、挂单上限、交易许可等映射到对应的账户属性消息常量。 整数类条件覆盖账户开关与限额:未设置值映射到 MSG_LIB_PROP_NOT_SET;杠杆对应 MSG_ACC_PROP_LEVERAGE;最大活动挂单数对应 MSG_ACC_PROP_LIMIT_ORDERS;服务器侧账户交易权限与 EA 交易权限分别映射到 MSG_ACC_PROP_TRADE_ALLOWED 和 MSG_ACC_PROP_TRADE_EXPERT。 double 类条件则绑定资金与风险指标:余额、信用、利润、净值、已用保证金、自由保证金、保证金水平百分比、初始保证金、维持保证金、账户资产,各自映射到 MSG_ACC_PROP_BALANCE 到 MSG_ACC_PROP_ASSETS 这一系列常量。 开 MT5 在 Include 里搜 PEND_REQ_ACTIVATE_BY_ACCOUNT_ 前缀,能直接看到这组映射;写 EA 时若想让挂单在「保证金水平低于某值」才激活,就走 PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL 这条枚举。外汇与贵金属杠杆波动大,这类触发逻辑仅作风控辅助,实际触发概率受经纪商规则影响。

MQL5 / C++
  PEND_REQ_ACTIVATE_BY_ACCOUNT_EMPTY                  = MSG_LIB_PROP_NOT_SET,                class=class="str">"cmt">// Value not set
  PEND_REQ_ACTIVATE_BY_ACCOUNT_LEVERAGE               = MSG_ACC_PROP_LEVERAGE,               class=class="str">"cmt">// Activate by a provided leverage
  PEND_REQ_ACTIVATE_BY_ACCOUNT_LIMIT_ORDERS           = MSG_ACC_PROP_LIMIT_ORDERS,           class=class="str">"cmt">// Activate by a maximum allowed number of active pending orders
  PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_ALLOWED          = MSG_ACC_PROP_TRADE_ALLOWED,          class=class="str">"cmt">// Activate by the permission to trade for the current account from the server side
  PEND_REQ_ACTIVATE_BY_ACCOUNT_TRADE_EXPERT           = MSG_ACC_PROP_TRADE_EXPERT,           class=class="str">"cmt">// Activate by the permission to trade for an EA from the server side
  class=class="str">"cmt">//--- class="type">class="kw">double
  PEND_REQ_ACTIVATE_BY_ACCOUNT_BALANCE                = MSG_ACC_PROP_BALANCE,                class=class="str">"cmt">// Activate by an account balance in the deposit currency
  PEND_REQ_ACTIVATE_BY_ACCOUNT_CREDIT                = MSG_ACC_PROP_CREDIT,                 class=class="str">"cmt">// Activate by credit in a deposit currency
  PEND_REQ_ACTIVATE_BY_ACCOUNT_PROFIT                = MSG_ACC_PROP_PROFIT,                 class=class="str">"cmt">// Activate by the current profit on the account in the deposit currency
  PEND_REQ_ACTIVATE_BY_ACCOUNT_EQUITY                = MSG_ACC_PROP_EQUITY,                 class=class="str">"cmt">// Sort by an account equity in the deposit currency
  PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN                = MSG_ACC_PROP_MARGIN,                 class=class="str">"cmt">// Activate by an account reserved margin in the deposit currency
  PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_FREE           = MSG_ACC_PROP_MARGIN_FREE,            class=class="str">"cmt">// Activate by account free funds available for opening a position in the deposit currency
  PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_LEVEL          = MSG_ACC_PROP_MARGIN_LEVEL,           class=class="str">"cmt">// Activate by account margin level in %
  PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_INITIAL        = MSG_ACC_PROP_MARGIN_INITIAL,         class=class="str">"cmt">// Activate by funds reserved on an account to ensure a guarantee amount for all pending orders
  PEND_REQ_ACTIVATE_BY_ACCOUNT_MARGIN_MAINTENANCE    = MSG_ACC_PROP_MARGIN_MAINTENANCE,     class=class="str">"cmt">// Activate by funds reserved on an account to ensure a minimum amount for all open positions
  PEND_REQ_ACTIVATE_BY_ACCOUNT_ASSETS                = MSG_ACC_PROP_ASSETS                 class=class="str">"cmt">// Activate by the current assets on the account

按品种属性触发挂单请求的条件枚举

延着账户负债与冻结佣金触发逻辑往下,品种维度的激活条件被收进 ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP 枚举,把报价、成交量、挂单分布等实时属性直接映射成触发开关。 double 类里最常用的是 BID、ASK 和 LAST:分别对应可卖出最优价、可买入最优价与最近成交价,盯盘时若想在某品种报价刺穿关键位才激活请求,这三个值就是底层钩子。 long 类覆盖的是 session 与日度量级数据,例如 SESSION_DEALS 取当前会话成交笔数,SESSION_BUY_ORDERS / SESSION_SELL_ORDERS 取当下多空挂单数量,VOLUMEHIGH / VOLUMELOW 取当日最大与最小成交量;SPREAD 则以点数为单位暴露点差宽度。 这套枚举没有默认生效项,EMPTY 对应 MSG_LIB_PROP_NOT_SET 表示未设置。实盘接贵金属或外汇品种前,先在 MT5 用 SymbolInfoInteger/SymbolInfoDouble 把上面字段打印一遍,确认你的券商确实推送了对应属性,否则触发可能永远不响。

MQL5 / C++
enum ENUM_PEND_REQ_ACTIVATE_BY_SYMBOL_PROP
  {
   PEND_REQ_ACTIVATE_BY_SYMBOL_EMPTY                                           = MSG_LIB_PROP_NOT_SET,                                                    class=class="str">"cmt">// Value not set
   class=class="str">"cmt">//--- class="type">class="kw">double
   PEND_REQ_ACTIVATE_BY_SYMBOL_BID                                             = MSG_LIB_PROP_BID,                                                        class=class="str">"cmt">// Activate by Bid - the best price at which a symbol can be sold
   PEND_REQ_ACTIVATE_BY_SYMBOL_ASK                                             = MSG_LIB_PROP_ASK,                                                        class=class="str">"cmt">// Activate by Ask - best price, at which an instrument can be bought
   PEND_REQ_ACTIVATE_BY_SYMBOL_LAST                                            = MSG_LIB_PROP_LAST,                                                       class=class="str">"cmt">// Activate by the last deal price
   class=class="str">"cmt">//--- class="type">long
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_DEALS                                   = MSG_SYM_PROP_SESSION_DEALS,                                              class=class="str">"cmt">// Activate by number of deals in the current session
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_BUY_ORDERS                              = MSG_SYM_PROP_SESSION_BUY_ORDERS,                                         class=class="str">"cmt">// Activate by number of Buy orders at the moment
   PEND_REQ_ACTIVATE_BY_SYMBOL_SESSION_SELL_ORDERS                             = MSG_SYM_PROP_SESSION_SELL_ORDERS,                                        class=class="str">"cmt">// Activate by number of Sell orders at the moment
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUME                                          = MSG_SYM_PROP_VOLUME,                                                     class=class="str">"cmt">// Activate by the last deal volume
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMEHIGH                                      = MSG_SYM_PROP_VOLUMEHIGH,                                                 class=class="str">"cmt">// Activate by maximum Volume per day
   PEND_REQ_ACTIVATE_BY_SYMBOL_VOLUMELOW                                       = MSG_SYM_PROP_VOLUMELOW,                                                  class=class="str">"cmt">// Activate by minimum Volume per day
   PEND_REQ_ACTIVATE_BY_SYMBOL_TIME                                            = MSG_SYM_PROP_TIME,                                                       class=class="str">"cmt">// Activate by the last quote time
   PEND_REQ_ACTIVATE_BY_SYMBOL_SPREAD                                          = MSG_SYM_PROP_SPREAD                                                      class=class="str">"cmt">// Activate by spread in points
  };

常见问题

用延后交易请求把开仓指令暂存,设定触发条件(如指标交叉),条件满足后由程序自动提交,避免手动盯盘漏单。
通过激活枚举和触发条件枚举做映射,给每种挂单标好来源与触发类型,方便后续按账户状态或品种属性分类处理。
小布可读取你的条件交易设置,提示枚举映射冲突或触发逻辑遗漏,并给出账户与品种维度的诊断建议。
在库中预置多语言标签枚举,按客户端语言加载对应文本,激活与挂单类型均走统一消息索引,避免硬编码。
按账户状态触发枚举映射,可设为占用比低于阈值才激活;不满足时请求保留,达标后自动尝试开仓。