轻松快捷开发 MetaTrader 程序的函数库 (第 二十七部分) :操控交易请求 - 下挂单(基础篇)
📘

轻松快捷开发 MetaTrader 程序的函数库 (第 二十七部分) :操控交易请求 - 下挂单(基础篇)

第 1/3 篇

◍ 用代码在 MT5 挂一笔限价单

在 MT5 里挂挂单,核心不是点界面,而是构造一个 MqlTradeRequest 结构再丢给 OrderSendAsync。这样能把下单逻辑固化进 EA,避免手动操作滑点。 下面这段演示了如何下一张 BUY_LIMIT:先填 symbol、action、type,再给 volume 和 price。价格必须落在当前市价下方,否则请求会被交易服务器拒掉。 [CODE] MqlTradeRequest request={0}; request.action=TRADE_ACTION_PENDING; request.symbol=Symbol(); request.volume=0.1; request.type=ORDER_TYPE_BUY_LIMIT; request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID)-100*_Point; request.deviation=10; request.magic=12345; OrderSendAsync(request,NULL); [/CODE] 代码逐行拆解:第1行清零结构体避免脏数据;action 设 TRADE_ACTION_PENDING 表示挂单;symbol 取当前品种;volume 0.1 手;type 为 BUY_LIMIT;price 取 BID 减 100 点,确保低于市价;deviation 容差 10 点;magic 编号 12345 用于后续持仓归类;最后异步发送不阻塞 EA。 外汇与贵金属杠杆高,挂单可能因跳空不成交或滑点,实盘前请在策略测试器用 2020 年 2 月至今数据回放验证。

MQL5 / C++
class="type">MqlTradeRequest request={class="num">0};
request.action=TRADE_ACTION_PENDING;
request.symbol=Symbol();
request.volume=class="num">0.1;
request.type=ORDER_TYPE_BUY_LIMIT;
request.price=SymbolInfoDouble(Symbol(),SYMBOL_BID)-class="num">100*_Point;
request.deviation=class="num">10;
request.magic=class="num">12345;
OrderSendAsync(request,NULL);

挂单延后请求前的缺陷清理

上篇实现了开仓延后请求,本篇接着处理挂单场景下的延后逻辑。但先得修掉交易类里的坑:构造函数初始化品种交易对象时写死了默认值,而部分品种规格根本不支持这些数值。 实测中这直接触发服务器‘不支持的订单到期类型’报错,且错误无法在别处纠正,结果就是挂单完全下不去。根因是请求里混进了不支持的默认数据。 改法不靠手填规格数值,而是在 EA 的 OnInit() 里自动挑正确参数初始化所有品种交易对象。交易方法默认收 -1,到期类型走预设正确值;传了别的就用传的,无效再由交易类纠错。这样挂单延后请求才有干净的运行前提。

「修集合 ID 撞号并铺好延后请求的消息与类型」

给延后请求对象加日志描述,先得把消息索引塞进函数库的消息数组。打开 Datas.mqh,在枚举里补上请求相关的文本索引,比如 MSG_LIB_TEXT_RE_TRY_N 代表“重复交易尝试 #”,后面日志打印才能直接引用。 标准库里集合 ID 和对象类型 ID 是重叠的。COLLECTION_HISTORY_ID 取值 0x7779,恰好等于 CList 的类型 ID(CObject 后代的动态列表)。对象 ID 和集合 ID 撞到同一个数值,在调试和类型判断时容易误判,这不合理。 下面是一部分标准库对象 ID 的十六进制对照:CObject 为 0,CArrayChar 为 0x77,CArrayObj 为 0x7778,CList 为 0x7779,CChartObject 为 0x8888,CChart 为 0x1111。可以看到 CList 的 0x7779 和历史订单成交集合的 ID 完全撞号。 修复办法是在 Defines.mqh 里把所有集合的 ID 值统一加 1,错开标准库对象类型区间。顺带引入“请求类型”概念,在文件末尾加枚举区分:一种基于交易服务器错误代码生成,一种由交易请求对应的程序创建(后者用于延后请求交易,后面再写)。 日志侧要在 DELib.mqh 加函数,依据 Datas.mqh 的文本集合生成 MqlTradeRequest 的说明,包括订单填充模式、到期类型等字段值。这样延后请求对象跑起来,MT5 日志里能直接看到参数全貌,排查更轻松。外汇和贵金属杠杆高,这类底层改动不影响仓位风险,但误用 ID 可能导致逻辑异常。

MQL5 / C++
class=class="str">"cmt">//+------------------------------------------------------------------+
class=class="str">"cmt">//| List of the library&class="macro">#x27;s text message indices                       |
class=class="str">"cmt">//+------------------------------------------------------------------+
enum ENUM_MESSAGES_LIB
  {
   MSG_LIB_PARAMS_LIST_BEG=ERR_USER_ERROR_FIRST,        class=class="str">"cmt">// Beginning of the parameter list
   MSG_LIB_PARAMS_LIST_END,                             class=class="str">"cmt">// End of the parameter list
   MSG_LIB_PROP_NOT_SUPPORTED,                          class=class="str">"cmt">// Property not supported
   MSG_LIB_PROP_NOT_SUPPORTED_MQL4,                     class=class="str">"cmt">// Property not supported in MQL4
   MSG_LIB_PROP_NOT_SUPPORTED_MT5_LESS_2155,            class=class="str">"cmt">// Property not supported in MetaTrader class="num">5 versions lower than class="num">2155
   MSG_LIB_PROP_NOT_SUPPORTED_POSITION,                 class=class="str">"cmt">// Property not supported for position
   MSG_LIB_PROP_NOT_SUPPORTED_PENDING,                  class=class="str">"cmt">// Property not supported for pending order
   MSG_LIB_PROP_NOT_SUPPORTED_MARKET,                   class=class="str">"cmt">// Property not supported for market order
   MSG_LIB_PROP_NOT_SUPPORTED_MARKET_HIST,              class=class="str">"cmt">// Property not supported for historical market order
   MSG_LIB_PROP_NOT_SET,                                class=class="str">"cmt">// Value not set
   MSG_LIB_PROP_EMPTY,                                  class=class="str">"cmt">// Not set
   MSG_LIB_PROP_AS_IN_ORDER,                            class=class="str">"cmt">// According to the order expiration mode

   MSG_LIB_SYS_ERROR,                                  class=class="str">"cmt">// Error
   MSG_LIB_TEXT_FAILING_CREATE_PENDING_REQ,             class=class="str">"cmt">// Failed to create a pending request
   MSG_LIB_TEXT_TRY_N,                                 class=class="str">"cmt">// Trading attempt #
   MSG_LIB_TEXT_RE_TRY_N,                              class=class="str">"cmt">// Repeated trading attempt #

   MSG_LIB_TEXT_REQUEST_ACTION,                        class=class="str">"cmt">// Type of a performed action
   MSG_LIB_TEXT_REQUEST_MAGIC,                         class=class="str">"cmt">// EA stamp(magic number)

◍ 交易请求结构里的字段与动作枚举

在 MT5 的 CTrade 封装逻辑里,一笔下单请求不是直接塞参数,而是靠一组文本常量来标记『这次请求到底要干啥』。下面这段枚举把订单核心字段、动作类型、成交方式与有效期全部列了出来,开 MT5 在 Include\Trade\Trade.mqh 附近能搜到同款定义。 字段侧覆盖了订单号、品种、手数、价格、StopLimit、SL/TP、最大滑点偏差、订单类型、成交类型、有效期类型、过期时间、注释,以及持仓单号和反向持仓单号——共 16 个请求维度。 动作侧分了 7 类:市价成交、挂单、改持仓 SLTP、改已挂单参数、删挂单、反向平仓、未知操作。外汇与贵金属波动剧烈,滑点偏差(Deviation)设太小可能在行情跳空时直接废单,属常见高风险现象。 成交方式三选一:FOK 全量或撤、IOK 部分成交后撤余量、RETURN 部分成交留余量。有效期则分 GTC 常驻直到手动撤、DAY 仅当日有效。复制下面代码进 MQ5 头文件,编译不报错即说明你的交易库版本兼容该套枚举。

MQL5 / C++
  MSG_LIB_TEXT_REQUEST_ORDER,                                           class=class="str">"cmt">// Order ticket
  MSG_LIB_TEXT_REQUEST_SYMBOL,                                           class=class="str">"cmt">// Name of a trading instrument
  MSG_LIB_TEXT_REQUEST_VOLUME,                                           class=class="str">"cmt">// Requested volume of a deal in lots
  MSG_LIB_TEXT_REQUEST_PRICE,                                            class=class="str">"cmt">// Price
  MSG_LIB_TEXT_REQUEST_STOPLIMIT,                                        class=class="str">"cmt">// StopLimit
  MSG_LIB_TEXT_REQUEST_SL,                                               class=class="str">"cmt">// Stop Loss
  MSG_LIB_TEXT_REQUEST_TP,                                               class=class="str">"cmt">// Take Profit
  MSG_LIB_TEXT_REQUEST_DEVIATION,                                        class=class="str">"cmt">// Maximum price deviation
  MSG_LIB_TEXT_REQUEST_TYPE,                                             class=class="str">"cmt">// Order type
  MSG_LIB_TEXT_REQUEST_TYPE_FILLING,                                     class=class="str">"cmt">// Order filling type
  MSG_LIB_TEXT_REQUEST_TYPE_TIME,                                        class=class="str">"cmt">// Order lifetime type
  MSG_LIB_TEXT_REQUEST_EXPIRATION,                                       class=class="str">"cmt">// Order expiration date
  MSG_LIB_TEXT_REQUEST_COMMENT,                                          class=class="str">"cmt">// Order comment
  MSG_LIB_TEXT_REQUEST_POSITION,                                         class=class="str">"cmt">// Position ticket
  MSG_LIB_TEXT_REQUEST_POSITION_BY,                                      class=class="str">"cmt">// Opposite position ticket

  MSG_LIB_TEXT_REQUEST_ACTION_DEAL,                                      class=class="str">"cmt">// Place a market order
  MSG_LIB_TEXT_REQUEST_ACTION_PENDING,                                   class=class="str">"cmt">// Place a pending order
  MSG_LIB_TEXT_REQUEST_ACTION_SLTP,                                      class=class="str">"cmt">// Change open position Stop Loss and Take Profit
  MSG_LIB_TEXT_REQUEST_ACTION_MODIFY,                                    class=class="str">"cmt">// Change parameters of the previously placed trading order
  MSG_LIB_TEXT_REQUEST_ACTION_REMOVE,                                    class=class="str">"cmt">// Remove previously placed pending order
  MSG_LIB_TEXT_REQUEST_ACTION_CLOSE_BY,                                  class=class="str">"cmt">// Close a position by an opposite one
  MSG_LIB_TEXT_REQUEST_ACTION_UNCNOWN,                                   class=class="str">"cmt">// Unknown trading operation type

  MSG_LIB_TEXT_REQUEST_ORDER_FILLING_FOK,                                class=class="str">"cmt">// Order is executed in the specified volume only, otherwise it is canceled
  MSG_LIB_TEXT_REQUEST_ORDER_FILLING_IOK,                                class=class="str">"cmt">// Order is filled within an available volume, class="kw">while the unfilled one is canceled
  MSG_LIB_TEXT_REQUEST_ORDER_FILLING_RETURN,                             class=class="str">"cmt">// Order is filled within an available volume, class="kw">while the unfilled one remains

  MSG_LIB_TEXT_REQUEST_ORDER_TIME_GTC,                                   class=class="str">"cmt">// Order is valid till explicitly canceled
  MSG_LIB_TEXT_REQUEST_ORDER_TIME_DAY,                                   class=class="str">"cmt">// Order is valid only during the current trading day

挂单请求的多语言文本枚举

在 MQL5 标准库的消息体系里,挂单(pending request)相关状态被拆成了一组独立枚举常量,方便在日志与界面里做双语输出。比如 MSG_LIB_TEXT_PEND_REQUEST_CREATED 对应「Pending request created」,MSG_LIB_TEXT_PEND_REQUEST_DELETED 说明挂单因到期被移除,这类常量直接决定你调试 EA 时看到的那行提示文本。 从代码片段能看到,messages_library 是二维数组 [][TOTAL_LANG],第一列俄语、第二列英语。其中有一条背景高亮项:"В соответствии с режимом истечения ордера" / "In accordance with the order expiration mode",说明订单过期逻辑是按交易服务器设定的过期模式走的,而不是简单写死时间。 如果你在写自己的交易面板,直接引用这些常量比手敲字符串稳。开 MT5 在 MetaEditor 里搜 MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET,能看到「Wait for the first trading attempt」——这就是首次发单前的等待标记,调 pending 重试节奏时它会频繁出现。外汇与贵金属杠杆高,这类底层文本仅用于诊断,不代表任何方向概率。

MQL5 / C++
  MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED,           class=class="str">"cmt">// Order is valid till the expiration date
  MSG_LIB_TEXT_REQUEST_ORDER_TIME_SPECIFIED_DAY,       class=class="str">"cmt">// Order is valid till class="num">23:class="num">59:class="num">59 of a specified day

  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">// Pending request is removed due to its expiration
  MSG_LIB_TEXT_PEND_REQUEST_PRICE_CREATE,              class=class="str">"cmt">// Price at the moment of request generation
  MSG_LIB_TEXT_PEND_REQUEST_TIME_CREATE,               class=class="str">"cmt">// Request creation time
  MSG_LIB_TEXT_PEND_REQUEST_TIME_ACTIVATE,             class=class="str">"cmt">// Request activation time
  MSG_LIB_TEXT_PEND_REQUEST_WAITING,                   class=class="str">"cmt">// Waiting time between trading attempts
  MSG_LIB_TEXT_PEND_REQUEST_CURRENT_ATTEMPT,           class=class="str">"cmt">// Current trading attempt
  MSG_LIB_TEXT_PEND_REQUEST_TOTAL_ATTEMPTS,            class=class="str">"cmt">// Total number of trading attempts
  MSG_LIB_TEXT_PEND_REQUEST_ID,                        class=class="str">"cmt">// Trading request ID
  MSG_LIB_TEXT_PEND_REQUEST_RETCODE,                   class=class="str">"cmt">// Return code a request is based on
  MSG_LIB_TEXT_PEND_REQUEST_TYPE,                      class=class="str">"cmt">// Pending request type

  MSG_LIB_TEXT_PEND_REQUEST_BY_ERROR,                  class=class="str">"cmt">// Pending request generated based on the server class="kw">return code
  MSG_LIB_TEXT_PEND_REQUEST_BY_REQUEST,                class=class="str">"cmt">// Pending request created by request
  MSG_LIB_TEXT_PEND_REQUEST_WAITING_ONSET,             class=class="str">"cmt">// Wait for the first trading attempt

  };
class=class="str">"cmt">//+------------------------------------------------------------------+
class="type">class="kw">string messages_library[][TOTAL_LANG]=
  {
  {"Начало списка параметров","Beginning of event parameter list"},
  {"Конец списка параметров","End of parameter list"},
  {"Свойство не поддерживается","Property not supported"},
  {"Свойство не поддерживается в MQL4","Property not supported in MQL4"},
  {"Свойство не поддерживается в MetaTrader5 версии ниже class="num">2155","Property not supported in MetaTrader class="num">5, build lower than class="num">2155"},
  {"Свойство не поддерживается у позиции","Property not supported for position"},
  {"Свойство не поддерживается у отложенного ордера","Property not supported for pending order"},
  {"Свойство не поддерживается у маркет-ордера","Property not supported for market order"},
  {"Свойство не поддерживается у исторического маркет-ордера","Property not supported for historical market order"},
  {"Значение не задано","Value not set"},
  {"Отсутствует","Not set"},
  {"В соответствии с режимом истечения ордера","In accordance with the order expiration mode"},

  {"Ошибка ","Error"},
  {"Не удалось создать отложенный запрос","Failed to create pending request"},
  {"Торговая попытка #","Trading attempt #"},

「把交易动作字典塞进多语言数组」

EA 在重发订单或记录重试日志时,常需要把交易动作、执行方式、有效期等枚举转成可读字符串。下面这段结构体数组就是直接给 MQL5 的 string 双语言表用的初始化清单,俄文在前、英文在后,实际项目里你可以只留英文或中文。 数组覆盖了从「Retry trading attempt #」重试计数,到「Trade operation type」动作类型,再到「Expert Advisor ID (magic number)」「Order ticket」「Trade symbol」「Requested volume for a deal in lots」等 17 个字段的标签。注意「Close a position by an opposite one」对应锁仓平仓动作,「Unknown trade action type」是兜底分支,漏处理会走到这里。 执行类型里 FOK 要求完全成交否则取消,IOK 只吃可用量其余撤掉,Return 则是未成交部分留在市场。有效期有「Good till cancel」一直挂到撤单、「Good till current trade day」仅当日有效、「Good till expired」到指定日期止,以及精确到 23:59:59 的日内变种。 把这些字符串直接贴进 OnTradeTransaction 的日志输出,能在 MT5 Experts 标签页里看到「Request creation time:」「Waiting time between trading attempts:」等现场数据,方便排查外汇与贵金属品种在高波动时的重试间隔(可能从 0 毫秒跳到经纪商限制的几百毫秒)。这类品种杠杆高、滑点大,参数需自行在策略测试器验证。

MQL5 / C++
  {"Повторная торговая попытка #","Retry trading attempt #"},
  {"Тип выполняемого действия","Trade operation type"},
  {"Штамп эксперта (magic number)","Expert Advisor ID(magic number)"},
  {"Тикет ордера","Order ticket"},
  {"Имя торгового инструмента","Trade symbol"},
  {"Запрашиваемый объем сделки в лотах","Requested volume for a deal in lots"},
  {"Цена","Price"},
  {"Уровень StopLimit ордера","StopLimit level of the order"},
  {"Уровень Stop Loss ордера","Stop Loss level of the order"},
  {"Уровень Take Profit ордера","Take Profit level of the order"},
  {"Максимальное отклонение от цены","Maximal deviation from the price"},
  {"Тип ордера","Order type"},
  {"Тип ордера по исполнению","Order execution type"},
  {"Тип ордера по времени действия","Order expiration type"},
  {"Срок истечения ордера","Order expiration time"},
  {"Комментарий к ордеру","Order comment"},
  {"Тикет позиции","Position ticket"},
  {"Тикет встречной позиции","Opposite position ticket"},
  {"Поставить рыночный ордер","Place market order"},
  {"Установить отложенный ордер","Place pending order"},
  {"Изменить значения Stop Loss и Take Profit у открытой позиции","Modify Stop Loss and Take Profit values of an opened position"},
  {"Изменить параметры ранее установленного торгового ордера","Modify the parameters of the order placed previously"},
  {"Удалить ранее выставленный отложенный ордер","Delete the pending order placed previously"},
  {"Закрыть позицию встречной","Close a position by an opposite one"},
  {"Неизвестный тип торговой операции","Unknown trade action type"},
  {"Ордер исполняется исключительно в указанном объеме, иначе отменяется (FOK)","The order is executed exclusively in the specified volume, otherwise it is canceled(FOK)"},
  {"Ордер исполняется на доступный объем, неисполненный отменяется (IOK)","The order is executed on the available volume, the unfulfilled is canceled(IOK)"},
  {"Ордер исполняется на доступный объем, неисполненный остаётся (Return)","The order is executed at an available volume, unfulfilled remains in the market(Return)"},
  {"Ордер действителен до явной отмены","Good till cancel order"},
  {"Ордер действителен только в течение текущего торгового дня","Good till current trade day order"},
  {"Ордер действителен до даты истечения","Good till expired order"},
  {"Ордер действителен до class="num">23:class="num">59:class="num">59 указанного дня","The order will be effective till class="num">23:class="num">59:class="num">59 of the specified day"},
  {"Параметры торгового запроса","Trade request&class="macro">#x27;s parameters"},
  {"Параметры отложенного торгового запроса","Pending trade request&class="macro">#x27;s parameters"},
  {"Создан отложенный запрос","Pending request created"},
  {"Отложенный запрос удалён в связи с окончанием времени его действия","Pending request deleted due to expiration"},
  {"Цена в момент создания запроса: ","Price at time of request create: "},
  {"Время создания запроса: ","Request creation time: "},
  {"Время активации запроса: ","Request activation time: "},
  {"Время ожидания между торговыми попытками: ","Waiting time between trading attempts: "},
  {"Текущая торговая попытка: ","Current trading attempt: "}

常见问题

交易请求结构里至少要给 action 设挂单类型、symbol 写品种名、volume 写手数、price 写挂单价、type 填对应限价枚举,填完直接发请求即可。
延后请求前必须清理上一次请求的结构缺陷和返回码,否则旧错误会带进新请求;先 reset 请求体再赋值最稳妥。
可以,小布能读取你的请求结构,标出 ID 撞号风险并补全延后请求的消息与类型字段,你照提示改就行。
把交易动作字典塞进多语言数组,用枚举值做索引取对应语种文本,这样界面显示和代码逻辑就不会错位。
单独列一个挂单类型枚举清单,赋值前用注释写明每类单子对应的 type 常量,编译时核对一遍即可降错。