轻松快捷开发 MetaTrader 程序的函数库 (第 二十七部分) :操控交易请求 - 下挂单(基础篇)
◍ 用代码在 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 月至今数据回放验证。
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 可能导致逻辑异常。
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 头文件,编译不报错即说明你的交易库版本兼容该套枚举。
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 重试节奏时它会频繁出现。外汇与贵金属杠杆高,这类底层文本仅用于诊断,不代表任何方向概率。
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 毫秒跳到经纪商限制的几百毫秒)。这类品种杠杆高、滑点大,参数需自行在策略测试器验证。
{"Повторная торговая попытка #","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: "}