- Главное событие экспертов: OnTick
- Основные принципы и понятия: ордер, сделка, позиция
- Типы торговых операций
- Типы ордеров
- Режимы исполнения ордеров по цене и объемам
- Сроки действия отложенных ордеров
- Расчет залога для будущего ордера: OrderCalcMargin
- Оценка прибыли торговой операции: OrderCalcProfit
- Структура торгового запроса MqlTradeRequest
- Структура проверки запроса MqlTradeCheckResult
- Проверка корректности запроса: OrderCheck
- Результат отправки запроса: структура MqlTradeResult
- Отправка торгового запроса: OrderSend и OrderSendAsync
- Совершение покупки или продажи
- Модификация уровней Stop Loss и/или Take Profit позиции
- Трейлинг стоп
- Полное и частичное закрытие позиции
- Полное и частичное закрытие встречных позиций (хедж)
- Установка отложенного ордера
- Модификация отложенного ордера
- Удаление отложенного ордера
- Получение списка действующих ордеров
- Свойства ордеров (действующих и в истории)
- Функции для чтения свойств действующих ордеров
- Отбор ордеров по свойствам
- Получение списка позиций
- Свойства позиций
- Функции для чтения свойств позиций
- Свойства сделок
- Выборка ордеров и сделок из истории
- Функции для чтения свойств ордеров из истории
- Функции для чтения свойств сделок из истории
- Типы торговых транзакций
- Событие OnTradeTransaction
- Синхронные и асинхронные запросы
- Событие OnTrade
- Контроль за изменениями торгового окружения
- Особенности создания мультисимвольных экспертов
- Ограничения и преимущества экспертов
- Создание заготовки эксперта в Мастере MQL
Отправка торгового запроса: OrderSend и OrderSendAsync
Для выполнения торговых операций MQL5 API предоставляет две функции: OrderSend и OrderSendAsync. Они, также как и OrderCheck, выполняют в терминале формальную проверку параметров запроса, переданных в виде структуры MqlTradeRequest, а затем, в случае успеха, отправляют запрос на сервер.
Различие между двумя функциями заключается в следующем. OrderSend дожидается постановки приказа в очередь на обработку на сервере и получает оттуда значащие данные в полях структуры MqlTradeResult, передаваемой вторым параметром функции. OrderSendAsync сразу же возвращает управление вызывающему коду, не заботясь о том, что ответит сервер. При этом из всех полей структуры MqlTradeResult, помимо retcode, важной информацией заполняется только request_id. Используя этот идентификатор запроса, MQL-программа может получать последующую информацию о ходе обработки этого запроса в событии OnTradeTransaction. Альтернативный подход — периодически анализировать списки ордеров, сделок и позиций, причем это можно делать и в цикле, задавшись некоторым таймаутом на случай проблем со связью.
Важно отметить, что, несмотря на суффикс "Async" в названии второй функции, первая функция без этого суффикса также не является в полном смысле синхронной. Дело в том, что результат обработки приказа сервером, в частности, совершение сделки (или, вероятно, нескольких сделок на основании одного приказа) и открытие позиции, в общем случае происходит асинхронно — во внешней торговой системе. Таким образом, функция OrderSend также требует отложенного сбора и анализа последствий выполнения запроса, который MQL-программ должна, при необходимости, реализовать сама. Мы рассмотрим пример действительно синхронной отправки запроса и приема всех его результатов позднее (см. MqlTradeSync.mqh).
bool OrderSend(const MqlTradeRequest &request, MqlTradeResult &result)
Функция возвращает true в случае успешной базовой проверки структуры request в терминале и нескольких дополнительных проверок на сервере. Однако это лишь свидетельствует о принятии ордера сервером и не гарантирует успешного выполнения торговой операции.
Торговый сервер может заполнить в возвращаемой структуре result значения полей deal или order, если эти данные будут ему известны в момент формирования ответа на вызов OrderSend. Однако в общем случае события исполнения сделок или выставления лимитных ордеров, соответствующих ордеру, могут произойти уже после того, как ответ будет отправлен MQL-программе, в терминал. Поэтому для любого типа торгового запроса при получении результата выполнения OrderSend необходимо проверять код возврата торгового сервера retcode и код ответа внешней торговой системы retcode_external (при необходимости), которые доступны в возвращаемой структуре result. На их основе следует принять решение об ожидании незавершенных действий на сервере или предпринять собственные действия.
Каждый принятый ордер хранится на торговом сервере в ожидании обработки, пока не наступит какое-либо из событий, влияющих на его жизненный цикл:
- исполнение при появлении встречного запроса,
- срабатывание при поступлении цены исполнения,
- истечение срока действия,
- отмена пользователем или MQL-программой,
- удаление брокером (например, при клиринге или нехватке средств, по Stop Out)
Прототип OrderSendAsync полностью повторяет OrderSend.
bool OrderSendAsync(const MqlTradeRequest &request, MqlTradeResult &result)
Функция предназначена для высокочастотной торговли, когда по условиям алгоритма недопустимо терять время на ожидание ответа от сервера. Скорость обработки запросов сервером и их выведение во внешнюю торговую систему не увеличивается от использования OrderSendAsync.
Внимание! В тестере функция OrderSendAsync работает как OrderSend. Это затрудняет отладку отложенной обработки асинхронных запросов.
Функция возвращает true по факту успешной отсылки запроса на сервер MetaTrader 5, однако это не означает, что запрос дошел до сервера и был принят для обработки. При этом в приёмной структуре result код ответа содержит значение TRADE_RETCODE_PLACED (10008) — "ордер размещен".
Сервер при обработке полученного запроса отправит терминалу ответное сообщение об изменении текущего состояния позиций, ордеров и сделок, которое приводит к генерации события OnTrade в MQL-программе. Там она может проанализировать новое торговое окружение и историю счета — далее мы рассмотрим соответствующие примеры.
Также подробности исполнения торгового запроса на сервере можно отслеживать при помощи обработчика OnTradeTransaction. При этом следует учитывать, что в результате исполнения одного торгового запроса обработчик OnTradeTransaction будет вызван несколько раз. Например, при отсылке запроса на покупку по рынку, он принимается на обработку сервером, для счета создается соответствующий ордер на покупку, происходит исполнение ордера и заключение сделки, в результате чего он удаляется из списка открытых и добавляется в историю ордеров, далее сделка добавляется в историю и создается новая позиция. Для каждого из этих событий будет вызвана функция OnTradeTransaction.
Для начала рассмотрим простой пример эксперта CustomOrderSend.mq5. Он позволяет задать во входных параметрах все поля запроса, что аналогично CustomOrderCheck.mq5, но далее отличается тем, что отправляет запрос на сервер вместо простой проверки в терминале. Запускайте эксперт на демо-счете. После завершения экспериментов не забудьте удалить эксперт с графика или закрыть график, чтобы не отправлять тестовый запрос при каждом следующем запуске терминала.
В новом примере есть и несколько других усовершенствований. Прежде всего добавлен входной параметр Async.
input bool Async = false; |
Эта опция предназначена для выбора того, какой функцией запрос будет отсылаться на сервер. По умолчанию параметр равен false, и используется функция OrderSend. Если задать его равным true, будет вызываться OrderSendAsync.
Кроме того, с этого примера мы начнем описывать и пополнять специальный набор функций в заголовочном файле TradeUtils.mqh, который пригодится для упрощения кодирования роботов. Все функции помещены в пространство имен TU (от "Trade Utilities"), и первыми из них представим функции для удобного вывода в журнал структур MqlTradeRequest и MqlTradeResult.
namespace TU
|
Суть функций — предоставить в кратком, но удобном виде все значащие (непустые) поля: они выводятся в одну строку с уникальным обозначением каждого.
Как можно заметить, в функции для MqlTradeRequest используется класс SymbolMetrics. Он упрощает нормализацию нескольких цен или объемов по одному и тому же инструменту. Напомним, что нормализация цен и объемов — обязательное условие подготовки корректного торгового запроса.
class SymbolMetrics
|
Непосредственно нормализация величин поручена вспомогательным функциям NormalizePrice и NormalizeLot (принцип работы последней идентичен тому, что мы видели в файле LotMarginExposure.mqh).
double NormalizePrice(const double price, const string symbol = NULL)
|
С подключенным файлом TradeUtils.mqh пример CustomOrderSend.mq5 приобретает следующий вид (опущенные фрагменты кода '...' остались без изменений с CustomOrderCheck.mq5).
void OnTimer()
|
Из-за того, что цены и объем теперь нормализуются, вы можете попробовать вводить в соответствующие входные параметры "неровные" значения — они часто получаются в программах в ходе вычислений, а наш код их преобразует согласно спецификации инструмента.
С настройками по умолчанию эксперт создает запрос на покупку минимального лота текущего инструмента по рынку, причем делает это функцией OrderSend.
OrderSend(request,result)=true / ok TRADE_ACTION_DEAL, EURUSD, ORDER_TYPE_BUY, V=0.01, ORDER_FILLING_FOK, @ 1.12462 DONE, D=1250236209, #=1267684253, V=0.01, @ 1.12462, Bid=1.12456, Ask=1.12462, Request executed, Req=1 |
Как правило, при разрешенных торгах эта операция должна завершиться успешно (статус DONE, комментарий "Request executed"). В структуре result мы сразу получили номер сделки D.
Если открыть настройки эксперта и заменить значение параметра Async на true, мы отправим аналогичный запрос, но уже функцией OrderSendAsync.
OrderSendAsync(request,result)=true / ok TRADE_ACTION_DEAL, EURUSD, ORDER_TYPE_BUY, V=0.01, ORDER_FILLING_FOK, @ 1.12449 PLACED, Order placed, Req=2 |
В этом случае статус равен PLACED, и номер сделки на момент возврата функции не известен. Мы знаем только уникальный идентификатор запроса Req=2. Чтобы получить номер сделки и позиции, необходимо перехватить сообщение TRADE_TRANSACTION_REQUEST с таким же идентификатором запроса в обработчике OnTradeTransaction, куда в качестве параметра поступит заполненная структура MqlTradeResult.
С точки зрения пользователя оба запроса должны выполниться одинаково быстро.
Сравнить скорость работы этих двух функций непосредственно в коде MQL-программы можно будет с помощью другого примера эксперта (см. раздел о синхронных и асинхронных запросах), который мы рассмотрим после изучения модели торговых событий.
Следует отметить, что торговые события посылаются в обработчик OnTradeTransaction (при его наличии в коде), независимо от того, какая функция используется для отправки запросов — OrderSend или OrderSendAsync. Просто в случае применения OrderSend некоторая или вся информация о выполнении приказа сразу доступна в приемной структуре MqlTradeResult. Однако в общем случае результат распределен по времени и по объемам, например, при "заливке" одного ордера в несколько сделок. Тогда полную информацию можно получить из торговых событий или анализируя историю сделок и ордеров.
Если попробовать отправить заведомо некорректный запрос, например, изменить тип ордера на отложенный ORDER_TYPE_BUY_STOP, получим сообщение об ошибке, потому что для таких ордеров следует использовать действие TRADE_ACTION_PENDING, а кроме того они должны располагаться на удалении от текущей цены (у нас же по умолчанию подставляется рыночная). Перед этим тестом важно не забыть вернуть режим запросов на синхронный (Async = false), чтобы сразу увидеть ошибку в структуре MqlTradeResult по завершению вызова OrderSend — в противном случае OrderSendAsync вернула бы true, но ордер всё равно не был бы установлен, причем информацию об этом программа могла бы получить только в OnTradeTransaction, которого у нас пока нет.
OrderSend(request,result)=false / TRADE_SEND_FAILED(4756) TRADE_ACTION_DEAL, EURUSD, ORDER_TYPE_BUY_STOP, V=0.01, ORDER_FILLING_FOK, @ 1.12452, ORDER_TIME_GTC REQUOTE, Bid=1.12449, Ask=1.12452, Requote, Req=5 |
В данном случае ошибка сообщает о неверной цене "Requote".
Примеры использования функций для выполнения конкретных торговых действий будут представлены в следующих разделах.