- Главное событие экспертов: OnTick
- Основные принципы и понятия: ордер, сделка, позиция
- Типы торговых операций
- Типы ордеров
- Режимы исполнения ордеров по цене и объемам
- Сроки действия отложенных ордеров
- Расчет залога для будущего ордера: OrderCalcMargin
- Оценка прибыли торговой операции: OrderCalcProfit
- Структура торгового запроса MqlTradeRequest
- Структура проверки запроса MqlTradeCheckResult
- Проверка корректности запроса: OrderCheck
- Результат отправки запроса: структура MqlTradeResult
- Отправка торгового запроса: OrderSend и OrderSendAsync
- Совершение покупки или продажи
- Модификация уровней Stop Loss и/или Take Profit позиции
- Трейлинг стоп
- Полное и частичное закрытие позиции
- Полное и частичное закрытие встречных позиций (хедж)
- Установка отложенного ордера
- Модификация отложенного ордера
- Удаление отложенного ордера
- Получение списка действующих ордеров
- Свойства ордеров (действующих и в истории)
- Функции для чтения свойств действующих ордеров
- Отбор ордеров по свойствам
- Получение списка позиций
- Свойства позиций
- Функции для чтения свойств позиций
- Свойства сделок
- Выборка ордеров и сделок из истории
- Функции для чтения свойств ордеров из истории
- Функции для чтения свойств сделок из истории
- Типы торговых транзакций
- Событие OnTradeTransaction
- Синхронные и асинхронные запросы
- Событие OnTrade
- Контроль за изменениями торгового окружения
- Особенности создания мультисимвольных экспертов
- Ограничения и преимущества экспертов
- Создание заготовки эксперта в Мастере MQL
Модификация уровней Stop Loss и/или Take Profit позиции
У открытой позиции MQL-программа может менять защитные ценовые уровни Stop Loss и Take Profit. Для этой цели предназначен элемент TRADE_ACTION_SLTP в перечислении ENUM_TRADE_REQUEST_ACTIONS, то есть при заполнении структуры MqlTradeRequest в поле action следует записывать TRADE_ACTION_SLTP.
Это единственное обязательное поле. Необходимость заполнения других полей обуславливается режимом работы счета ENUM_ACCOUNT_MARGIN_MODE. Так на счетах с неттингом обязательно заполнять поле symbol, но можно опустить тикет позиции. На счетах с хеджированием, наоборот, обязательно указывать тикет позиции position, но можно опустить символ. Это объясняется особенностями идентификации позиции на счетах разных типов. Напомним, при неттинге по каждому символу может существовать только одна позиция.
В целях унификации кода рекомендуется заполнять оба поля при наличии информации.
Непосредственно защитные ценовые уровни устанавливаются в полях sl и tp. Допускается задать только sl или только tp. Для удаления защитных уровней следует присвоить им нулевые значения.
В следующей таблице в обобщенном виде представлены требования по заполнению полей в зависимости от режимов счета. Обязательные поля помечены звездочкой, опциональные — плюсом.
Поле |
Неттинг |
Хедж |
---|---|---|
action |
* |
* |
symbol |
* |
+ |
position |
+ |
* |
sl |
+ |
+ |
tp |
+ |
+ |
В структуре MqlTradeRequestSync для выполнения операции модификации защитных уровней введем несколько перегрузок метода adjust.
struct MqlTradeRequestSync: public MqlTradeRequest
|
Как мы видели выше, в зависимости от окружения, модификацию можно делать только по тикету или только по символу позиции — эти варианты учтены в первых двух прототипах.
Кроме того, поскольку структура может уже быть использована для предыдущих запросов, в ней могут быть заполнены поля position и symbol. Тогда можно вызывать метод с последним прототипом.
Мы пока не показываем реализацию этих трех методов, потому что ясно, что она должна иметь общую основную часть с отправкой запроса. Эта часть оформлена, как вспомогательный закрытый метод _adjust с полным набором параметров. Здесь его код приводится с некоторыми сокращениями, не влияющими на логику работы.
private:
|
Суть проста — заполняем все поля структуры по вышеприведенным правилам, вызывая ранее описанные методы setSymbol и setSLTP, а затем отправляем запрос на сервер. Результатом является статус успеха (true) или ошибки (false).
Исходные параметры для запроса каждый из перегруженных методов adjust подготавливает по своему. Вот, например, как это сделано при наличии тикета позиции.
public:
|
Здесь с помощью встроенной функции PositionSelectByTicket делается проверка наличия позиции и её выделение в торговом окружении терминала, что необходимо для последующего чтения её свойств, в данном случае — символа (PositionGetString(POSITION_SYMBOL)). Затем происходит вызов универсального варианта adjust.
При модификации позиции по имени символа (что доступно только на неттинг-счете) можно использовать другой вариант adjust.
bool adjust(const string name, const double stop = 0, const double take = 0)
|
Здесь выбор позиции происходит с помощью встроенной функции PositionSelect, а из её свойств получается номер тикета (PositionGetInteger(POSITION_TICKET)).
Все эти функции будут рассмотрены подробно в соответствующих разделах о работе с позициями и свойствах позиций.
Вариант метода adjust с самым минималистским набором параметров — только уровнями stop и take — выглядит следующим образом.
bool adjust(const double stop = 0, const double take = 0)
|
Этот код обеспечивает корректное заполнение полей position и symbol в различных режимах или досрочный выход с сообщением об ошибке в журнал. В конце вызывается приватная версия _adjust, отправляющая запрос через OrderSend.
Так же как и в случае методов buy/sell, представленный набор методов adjust работает "асинхронно" в том смысле, что по их завершении известен только статус отправки запроса, но нет подтверждения модификации уровней. Напомним, что в связке с биржей уровень Take Profit может выводиться на неё как лимитный ордер. Поэтому в структуре MqlTradeResultSync следует обеспечить "синхронное" ожидание, пока изменения не вступят в силу.
Общий механизм ожидания в виде метода MqlTradeResultSync::wait уже готов и был использован для ожидания открытия позиции. Метод wait получает в качестве первого параметра указатель на другой метод с предопределенным прототипом condition — для опроса в цикле, пока не выполнится требуемое условие или не случится таймаут. Этот condition-совместимый метод должен, в данном случае, выполнять прикладную проверку стоп-уровней в позиции.
Добавим такой новый метод под именем adjusted.
struct MqlTradeResultSync: public MqlTradeResult
|
В первую очередь, разумеется, проверяем статус в поле retcode. Если он — один из штатных, продолжаем проверку самих уровней, передавая в wait вспомогательный метод checkSLTP.
struct MqlTradeResultSync: public MqlTradeResult
|
Данный код гарантирует, что позиция выбрана по тикету в торговом окружении терминала с помощью PositionSelectByTicket и читает свойства позиции POSITION_SL и POSITION_TP, которые надо сравнить с тем, что было в запросе. Проблема в том, что здесь мы не имеем доступа к объекту запроса и должны каким-то образом передать сюда пару значений для мест, помеченных '.?.'.
В принципе, поскольку структура MqlTradeResultSync проектируется нами, мы можем добавить в неё поля sl и tp, и заполнять их значениями из MqlTradeRequestSync перед отправкой запроса (ядро не "знает" о наших добавленных полях и оставит их нетронутыми в процессе вызова OrderSend). Но для простоты мы воспользуемся тем, что уже имеется. Поля bid и ask в структуре MqlTradeResultSync используются только для сообщения цен реквот (статус TRADE_RETCODE_REQUOTE), что не относится к запросу TRADE_ACTION_SLTP, поэтому мы можем сохранить в них значение sl и tp из заполненной MqlTradeRequestSync.
Сделать это логично в методе completed структуры MqlTradeRequestSync, который запускает блокирующее ожидание результатов торговой операции с предопределенным таймаутом. До сих пор в его коде была только одна ветвь для действия TRADE_ACTION_DEAL. В продолжение добавим ветвь для TRADE_ACTION_SLTP.
struct MqlTradeRequestSync: public MqlTradeRequest
|
Как видно, после установки тикета позиции и ценовых уровней из запроса, мы вызываем метод adjusted, рассмотренный выше, в котором происходит проверка: wait(checkSLTP). Теперь мы можем вернуться к вспомогательному методу checkSLTP в структуре MqlTradeResultSync и привести его к окончательному виду.
struct MqlTradeResultSync: public MqlTradeResult
|
На этом расширение функционала структур MqlTradeRequestSync и MqlTradeResultSync для операции модификации Stop Loss и Take Profit завершено.
С учетом этого продолжим пример эксперта MarketOrderSend.mq5, начатый в предыдущем разделе. Добавим в него входной параметр Distance2SLTP, позволяющий указать расстояние в пунктах до уровней Stop Loss и Take Profit.
input int Distance2SLTP = 0; // Distance to SL/TP in points (0 = no) |
Когда он равен нулю, защитные уровни не будут ставиться.
В рабочем коде, после получения подтверждения об открытии позиции вычисляем значения уровней в переменных SL и TP, и выполняем синхронную модификацию: request.adjust(SL, TP) && request.completed().
...
|
В первом вызове completed после успешной покупки или продажи тикет позиции сохраняется в поле position структуры запроса. Поэтому для модификации стопов достаточно только ценовых уровней, а символ и тикет позиции уже присутствуют в request.
Попробуем выполнить с помощью эксперта покупку с настройками по умолчанию, но установив Distance2SLTP в 500 пунктов.
OK Order: #=1273913958
|
Две последние строки соответствуют отладочному выводу в журнал содержимого структур request и request.result, инициированному в конце функции. В этих строках интересно, что поля хранят в себе симбиоз значений из двух запросов: сначала была открыта позиция, а потом произведена её модификация. В частности, поля с объемом (0.01) и ценой (1.10889) в запросе остались после TRADE_ACTION_DEAL, но не помешали выполнению TRADE_ACTION_SLTP. В принципе, от этого легко избавиться, выполнив обнуление структуры между двумя запросами, однако мы предпочли оставить их как есть, потому что среди заполненных полей есть и полезные: поле position получило тикет, который нам нужен для запроса модификации. Если бы мы обнулили структуру, то нужно было бы вводить переменную для промежуточного хранения тикета.
В общем случае, конечно, желательно придерживаться строгой политики инициализации данных, но знание особенностей их использования в конкретных сценариях (таких как два или несколько взаимосвязанных запросов предопределенного типа) позволяет оптимизировать код.
Также не следует удивляться тому, что в структуре с результатом мы видим запрошенные уровни sl и tp в полях под цены Bid и Ask: записал их туда метод MqlTradeRequestSync::completed с целью сравнения с фактическими изменениями позиции. При выполнении запроса ядро системы заполнило в структуре result только retcode (DONE), comment ("Request executed") и request_id (26).
Далее мы рассмотрим другой пример модификации уровней, реализующий "трейлинг стоп".