![Рецепты MQL5 - обработка события TradeTransaction](https://c.mql5.com/i/articles/overlay.png)
Рецепты MQL5 - обработка события TradeTransaction
Введение
В своей статье я хотел бы познакомить читателя с одним из способов контролировать торговые события средствами MQL5. Сразу хочу сказать, что данной теме были посвящены статьи, например «Обработка торговых событий в эксперте при помощи функции OnTrade()». Но в отличие от упомянутого материала я буду использовать другой обработчик – OnTradeTransaction().
Хотелось бы отметить вот какой момент. В текущей версии языка MQL5 формально есть 14 обработчиков событий клиентского терминала. Причём, у программиста есть возможность создавать свои пользовательские события посредством EventChartCustom() и обрабатывать их с помощью OnChartEvent(). Но нигде в Документации нет упоминания такого термина, как «Событийно-ориентированное программирование» (СОП). Ведь именно с учётом принципов СОП создаётся любая программа в MQL5. Возьмите хотя бы любой шаблон будущего советника, когда на шаге «Обработчики событий для советника» пользователю предлагается сделать свой выбор.
Очевидно, что в MQL5 так или иначе используется механизм событийно-ориентированного программирования. В языке могут присутствовать блоки программы, состоящие из двух частей: выборки события и обработки события. Причём, если речь идёт о событиях клиентского терминала, то программисту доступна только вторая часть - обработчик события. Правда, для некоторых событий есть исключения. К ним относятся таймер и пользовательское событие. Контроль данных событий полностью отдан в руки программиста.
1. Событие TradeTransaction
Итак, давайте сузим предмет нашего внимания до заявленной темы и обратимся к официальной информации.
Согласно Документации событие TradeTransaction есть результат выполнения определенных действий с торговым счетом. Само действие состоит из нескольких этапов, которые и определяются транзакциями. Например, одно из самых популярных действий с торговым счётом – открытие позиции посредством рыночного ордера – реализуется по следующим этапам:
- Создать торговый приказ;
- Проверить торговый приказ;
- Отправить торговый приказ на сервер;
- Получить ответ о выполнении торгового приказа на сервере.
Но такая последовательность отражает скорее логику работы связки терминал-сервер, что выражается в строчках кода советника. С точки зрения торгового события TradeTransaction открытие позиции по рынку происходит следующим образом:
- MQL5-программа получает уведомление от сервера о результате выполненной заявки;
- Заявка в форме ордера с уникальным тикетом попадает в список открытых ордеров;
- После исполнения ордер удаляется из списка открытых ордеров;
- Затем ордер уходит в историю;
- Также в истории появляется сделка, являющаяся результатом исполнения ордера.
В итоге на открытие позиции понадобилось 5 вызовов обработчика OnTradeTransaction().
Чуть позже разберёмся с программным кодом, а пока предлагаю обратиться к заголовку функции-обработчика. Он имеет 3 входных параметра.
void OnTradeTransaction( const MqlTradeTransaction& trans, // структура торговой транзакции const MqlTradeRequest& request, // структура запроса const MqlTradeResult& result // структура ответа );
Детально о параметрах написано в Документации. Здесь хотел бы заметить, что параметр структуры торговой транзакции – это своего рода слепок той информации, которую обработчик транзакции смог получить на текущем вызове.
Думаю, что ещё нужно несколько слов сказать о типе торговой транзакции, так как он будет нам часто встречаться.
В языке MQL5 есть специальное перечисление, которое отвечает за тип торговой транзакции - ENUM_TRADE_TRANSACTION_TYPE. Чтобы узнать, к какому типу относится та или иная торговая транзакция, нужно обратиться к параметру-константе типа MqlTradeTransaction.
struct MqlTradeTransaction { ulong deal; // Тикет сделки ulong order; // Тикет ордера string symbol; // Имя торгового инструмента ENUM_TRADE_TRANSACTION_TYPE type; // Тип торговой транзакции ENUM_ORDER_TYPE order_type; // Тип ордера ENUM_ORDER_STATE order_state; // Состояние ордера ENUM_DEAL_TYPE deal_type; // Тип сделки ENUM_ORDER_TYPE_TIME time_type; // Тип ордера по времени действия datetime time_expiration; // Срок истечения ордера double price; // Цена double price_trigger; // Цена срабатывания стоп-лимитного ордера double price_sl; // Уровень Stop Loss double price_tp; // Уровень Take Profit double volume; // Объем в лотах };
Четвёртое поле структуры как раз и является искомым перечислением.
2. Обработка позиций
Практически все торговые операции, связанные с обработкой позиций, влекут 5 вызовов обработчика OnTradeTransaction(). К таким операциям относятся:
- открытие позиции;
- позиции;
- переворот позиции;
- добавление к позиции;
- сокращение позиции.
И лишь одна операция - модификация позиции – вызовет обработчик TradeTransaction два раза.
Раз в документации нет информации о том, какие типы транзакций отвечают за какие-то конкретные торговые действия, то методом проб и ошибок проверим их самостоятельно.
Предварительно нужно будет создать шаблон будущего советника, в котором обязательно должен присутствовать обработчик события TradeTransaction. Свою версию такого шаблона я назвал TradeProcessor.mq5. В него ещё добавил возможность вывода информации в лог о значениях полей структур, являющихся параметрами функции-обработчика. С этими записями придётся, что называется, повозиться, когда будем их анализировать. Зато потом затраты окупятся возможностью видеть всю картину происходящего.
На любом графике терминала MetaTrader 5 нужно запустить советник в режиме отладки.
Откроем вручную позицию и посмотрим на код. Первый вызов обработчика будет таким (рис. 1).
Рис.1. Поле type равно TRADE_TRANSACTION_REQUEST
В логе появятся следующие записи:
IO 0 17:37:53.233 TradeProcessor (EURUSD,H1) ---===Транзакция===--- NK 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тикет сделки: 0 RR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тип сделки: DEAL_TYPE_BUY DE 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тикет ордера: 0 JS 0 17:37:53.233 TradeProcessor (EURUSD,H1) Состояние ордера: ORDER_STATE_STARTED JN 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY FD 0 17:37:53.233 TradeProcessor (EURUSD,H1) Цена: 0.0000 FN 0 17:37:53.233 TradeProcessor (EURUSD,H1) Уровень Stop Loss: 0.0000 HF 0 17:37:53.233 TradeProcessor (EURUSD,H1) Уровень Take Profit: 0.0000 FQ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Цена срабатывания стоп-лимитного ордера: 0.0000 RR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Торговый инструмент: HD 0 17:37:53.233 TradeProcessor (EURUSD,H1) Срок истечения отложенного ордера: 1970.01.01 00:00 GS 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC DN 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тип торговой транзакции: TRADE_TRANSACTION_REQUEST FK 0 17:37:53.233 TradeProcessor (EURUSD,H1) Объём в лотах: 0.00
В этом блоке интересна только запись, касающаяся типа транзакции. Видим, что тип относился к запросу (TRADE_TRANSACTION_REQUEST).
В блоке «Запрос» можно получить информацию о деталях заявки.
QG 0 17:37:53.233 TradeProcessor (EURUSD,H1) ---===Запрос===--- HL 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тип выполняемого действия: TRADE_ACTION_DEAL EE 0 17:37:53.233 TradeProcessor (EURUSD,H1) Комментарий к ордеру: JP 0 17:37:53.233 TradeProcessor (EURUSD,H1) Отклонение от запрашиваемой цены: 0 GS 0 17:37:53.233 TradeProcessor (EURUSD,H1) Срок истечения ордера: 1970.01.01 00:00 LF 0 17:37:53.233 TradeProcessor (EURUSD,H1) Магик советника: 0 FM 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тикет ордера: 22535869 EJ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Цена: 1.3137 QR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Уровень Stop Loss ордера: 0.0000 IJ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Уровень Take Profit ордера: 0.0000 KK 0 17:37:53.233 TradeProcessor (EURUSD,H1) Уровень StopLimit ордера: 0.0000 FS 0 17:37:53.233 TradeProcessor (EURUSD,H1) Торговый инструмент: EURUSD RD 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY
В блок «Ответ» попадут данные о результате исполнения заявки.
KG 0 17:37:53.233 TradeProcessor (EURUSD,H1) ---===Ответ===--- JR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Код результата операции: 10009 GD 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тикет сделки: 15258202 NR 0 17:37:53.233 TradeProcessor (EURUSD,H1) Тикет ордера: 22535869 EF 0 17:37:53.233 TradeProcessor (EURUSD,H1) Объём сделки: 0.11 MN 0 17:37:53.233 TradeProcessor (EURUSD,H1) Цена в сделке: 1.3137 HJ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Бид: 1.3135 PM 0 17:37:53.233 TradeProcessor (EURUSD,H1) Аск: 1.3137 OG 0 17:37:53.233 TradeProcessor (EURUSD,H1) Комментарий к операции: RQ 0 17:37:53.233 TradeProcessor (EURUSD,H1) Идентификатор запроса: 1
Именно на первом вызове можно узнать дополнительную информацию о запросе, проанализировав другие параметры обработчика - структуры запроса и ответа.
Второй вызов обработчика касается того, что ордер добавляется в список открытых ордеров (рис. 2).
Рис.2. Поле type равно TRADE_TRANSACTION_ORDER_ADD
В логе интерес представляет только блок «Транзакция».
MJ 0 17:41:12.280 TradeProcessor (EURUSD,H1) ---===Транзакция===--- JN 0 17:41:12.280 TradeProcessor (EURUSD,H1) Тикет сделки: 0 FG 0 17:41:12.280 TradeProcessor (EURUSD,H1) Тип сделки: DEAL_TYPE_BUY LM 0 17:41:12.280 TradeProcessor (EURUSD,H1) Тикет ордера: 22535869 LI 0 17:41:12.280 TradeProcessor (EURUSD,H1) Состояние ордера: ORDER_STATE_STARTED LP 0 17:41:12.280 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY QN 0 17:41:12.280 TradeProcessor (EURUSD,H1) Цена: 1.3137 PD 0 17:41:12.280 TradeProcessor (EURUSD,H1) Уровень Stop Loss: 0.0000 NL 0 17:41:12.280 TradeProcessor (EURUSD,H1) Уровень Take Profit: 0.0000 PG 0 17:41:12.280 TradeProcessor (EURUSD,H1) Цена срабатывания стоп-лимитного ордера: 0.0000 DL 0 17:41:12.280 TradeProcessor (EURUSD,H1) Торговый инструмент: EURUSD JK 0 17:41:12.280 TradeProcessor (EURUSD,H1) Срок истечения отложенного ордера: 1970.01.01 00:00 QD 0 17:41:12.280 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC IQ 0 17:41:12.280 TradeProcessor (EURUSD,H1) Тип торговой транзакции: TRADE_TRANSACTION_ORDER_ADD PL 0 17:41:12.280 TradeProcessor (EURUSD,H1) Объём в лотах: 0.11
Здесь видим, что ордер уже получил свой тикет и прочие параметры (символ, цена, объём) и находится в списке открытых ордеров.
Третий вызов обработчика связан с удалением ордера из списка открытых (рис. 3).
Рис.3. Поле type равно TRADE_TRANSACTION_ORDER_DELETE
В логе интерес представляет только блок «Транзакция».
PF 0 17:52:36.722 TradeProcessor (EURUSD,H1) ---===Транзакция===--- OE 0 17:52:36.722 TradeProcessor (EURUSD,H1) Тикет сделки: 0 KL 0 17:52:36.722 TradeProcessor (EURUSD,H1) Тип сделки: DEAL_TYPE_BUY EH 0 17:52:36.722 TradeProcessor (EURUSD,H1) Тикет ордера: 22535869 QM 0 17:52:36.722 TradeProcessor (EURUSD,H1) Состояние ордера: ORDER_STATE_STARTED QK 0 17:52:36.722 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY HS 0 17:52:36.722 TradeProcessor (EURUSD,H1) Цена: 1.3137 MH 0 17:52:36.722 TradeProcessor (EURUSD,H1) Уровень Stop Loss: 0.0000 OP 0 17:52:36.722 TradeProcessor (EURUSD,H1) Уровень Take Profit: 0.0000 EJ 0 17:52:36.722 TradeProcessor (EURUSD,H1) Цена срабатывания стоп-лимитного ордера: 0.0000 IH 0 17:52:36.722 TradeProcessor (EURUSD,H1) Торговый инструмент: EURUSD KP 0 17:52:36.722 TradeProcessor (EURUSD,H1) Срок истечения отложенного ордера: 1970.01.01 00:00 LO 0 17:52:36.722 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC HG 0 17:52:36.722 TradeProcessor (EURUSD,H1) Тип торговой транзакции: TRADE_TRANSACTION_ORDER_DELETE CG 0 17:52:36.722 TradeProcessor (EURUSD,H1) Объём в лотах: 0.11
Кроме типа транзакции ничего нового тут нет.
Четвёртый раз обработчик вызывается тогда, когда в истории появляется новый «исторический» ордер (рис. 4).
Рис.4. Поле type равно TRADE_TRANSACTION_HISTORY_ADD
Смотрим в лог на блок «Транзакция».
QO 0 17:57:32.234 TradeProcessor (EURUSD,H1) ---===Транзакция===--- RJ 0 17:57:32.234 TradeProcessor (EURUSD,H1) Тикет сделки: 0 NS 0 17:57:32.234 TradeProcessor (EURUSD,H1) Тип сделки: DEAL_TYPE_BUY DQ 0 17:57:32.234 TradeProcessor (EURUSD,H1) Тикет ордера: 22535869 EH 0 17:57:32.234 TradeProcessor (EURUSD,H1) Состояние ордера: ORDER_STATE_FILLED RL 0 17:57:32.234 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY KJ 0 17:57:32.234 TradeProcessor (EURUSD,H1) Цена: 1.3137 NO 0 17:57:32.234 TradeProcessor (EURUSD,H1) Уровень Stop Loss: 0.0000 PI 0 17:57:32.234 TradeProcessor (EURUSD,H1) Уровень Take Profit: 0.0000 FS 0 17:57:32.234 TradeProcessor (EURUSD,H1) Цена срабатывания стоп-лимитного ордера: 0.0000 JS 0 17:57:32.234 TradeProcessor (EURUSD,H1) Торговый инструмент: EURUSD LG 0 17:57:32.234 TradeProcessor (EURUSD,H1) Срок истечения отложенного ордера: 1970.01.01 00:00 KP 0 17:57:32.234 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC OL 0 17:57:32.234 TradeProcessor (EURUSD,H1) Тип торговой транзакции: TRADE_TRANSACTION_HISTORY_ADD JH 0 17:57:32.234 TradeProcessor (EURUSD,H1) Объём в лотах: 0.00
На этом этапе видим, что ордер был исполнен.
И последний (пятый) вызов имеет место при добавлении сделки в историю (рис. 5).
Рис.5. Поле type равно TRADE_TRANSACTION_DEAL_ADD
В логе снова смотрим только на блок «Транзакция».
OE 0 17:59:40.718 TradeProcessor (EURUSD,H1) ---===Транзакция===--- MS 0 17:59:40.718 TradeProcessor (EURUSD,H1) Тикет сделки: 15258202 RJ 0 17:59:40.718 TradeProcessor (EURUSD,H1) Тип сделки: DEAL_TYPE_BUY HN 0 17:59:40.718 TradeProcessor (EURUSD,H1) Тикет ордера: 22535869 LK 0 17:59:40.718 TradeProcessor (EURUSD,H1) Состояние ордера: ORDER_STATE_STARTED LE 0 17:59:40.718 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY MM 0 17:59:40.718 TradeProcessor (EURUSD,H1) Цена: 1.3137 PF 0 17:59:40.718 TradeProcessor (EURUSD,H1) Уровень Stop Loss: 0.0000 NN 0 17:59:40.718 TradeProcessor (EURUSD,H1) Уровень Take Profit: 0.0000 PI 0 17:59:40.718 TradeProcessor (EURUSD,H1) Цена срабатывания стоп-лимитного ордера: 0.0000 DJ 0 17:59:40.718 TradeProcessor (EURUSD,H1) Торговый инструмент: EURUSD JM 0 17:59:40.718 TradeProcessor (EURUSD,H1) Срок истечения отложенного ордера: 1970.01.01 00:00 QI 0 17:59:40.718 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC CK 0 17:59:40.718 TradeProcessor (EURUSD,H1) Тип торговой транзакции: TRADE_TRANSACTION_DEAL_ADD RQ 0 17:59:40.718 TradeProcessor (EURUSD,H1) Объём в лотах: 0.11
Тут важным пунктом является тикет сделки.
Представлю шаблоны связок транзакционных типов. Для позиций их будет всего два. Первый выглядит так (рис. 6).
Рис.6. Первый шаблон связок транзакционных типов
Этому шаблону подчиняются все торговые операции, связанные с обработкой позиций, кроме единственной – модификации позиции. Последняя операция включает в свой состав обработку следующих двух транзакций (рис. 7).
Рис.7. Второй шаблон связок транзакционных типов
Таким образом, модификацию позиции нельзя отследить в истории сделок и ордеров.
Вот, в общем, и всё, что касается позиций.
3. Обработка отложенных ордеров
Что касается отложенных ордеров, то нужно заметить, что на операции с ними уходит меньшее число транзакций. Но с другой стороны при работе с ордерами больше комбинаций типов транзакций.
При модификации ордера, как и в случае с модификацией позиции, обработчик будет вызван 2 раза. Три вызова появится при установке и отмене ордера. Четыре раза событие TradeTransaction наступит при удалении ордера или его срабатывании.
Попробуем выставить отложенный ордер. На любом графике терминала MetaTrader 5 нужно снова запустить советник в режиме отладки.
Первый вызов обработчика будет связан с запросом (рис. 8).
Рис.8. Поле type равно TRADE_TRANSACTION_REQUEST
В логе увидим записи:
IO 0 18:13:33.195 TradeProcessor (EURUSD,H1) ---===Транзакция===--- NK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тикет сделки: 0 RR 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тип сделки: DEAL_TYPE_BUY DE 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тикет ордера: 0 JS 0 18:13:33.195 TradeProcessor (EURUSD,H1) Состояние ордера: ORDER_STATE_STARTED JN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY FD 0 18:13:33.195 TradeProcessor (EURUSD,H1) Цена: 0.0000 FN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Уровень Stop Loss: 0.0000 HF 0 18:13:33.195 TradeProcessor (EURUSD,H1) Уровень Take Profit: 0.0000 FQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Цена срабатывания стоп-лимитного ордера: 0.0000 RR 0 18:13:33.195 TradeProcessor (EURUSD,H1) Торговый инструмент: HD 0 18:13:33.195 TradeProcessor (EURUSD,H1) Срок истечения отложенного ордера: 1970.01.01 00:00 GS 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC DN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тип торговой транзакции: TRADE_TRANSACTION_REQUEST FK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Объём в лотах: 0.00 NS 0 18:13:33.195 TradeProcessor (EURUSD,H1) QG 0 18:13:33.195 TradeProcessor (EURUSD,H1) ---===Запрос===--- IQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тип выполняемого действия: TRADE_ACTION_PENDING OE 0 18:13:33.195 TradeProcessor (EURUSD,H1) Комментарий к ордеру: PQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Отклонение от запрашиваемой цены: 0 QS 0 18:13:33.195 TradeProcessor (EURUSD,H1) Срок истечения ордера: 1970.01.01 00:00 FI 0 18:13:33.195 TradeProcessor (EURUSD,H1) Магик советника: 0 CM 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тикет ордера: 22535983 PK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Цена: 1.6500 KR 0 18:13:33.195 TradeProcessor (EURUSD,H1) Уровень Stop Loss ордера: 0.0000 OI 0 18:13:33.195 TradeProcessor (EURUSD,H1) Уровень Take Profit ордера: 0.0000 QK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Уровень StopLimit ордера: 0.0000 QQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Торговый инструмент: GBPUSD RD 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY_LIMIT LS 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тип ордера по исполнению: ORDER_FILLING_RETURN MN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC IK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Объём в лотах: 0.14 NS 0 18:13:33.195 TradeProcessor (EURUSD,H1) CD 0 18:13:33.195 TradeProcessor (EURUSD,H1) ---===Ответ===--- RQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Код результата операции: 10009 JI 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тикет сделки: 0 GM 0 18:13:33.195 TradeProcessor (EURUSD,H1) Тикет ордера: 22535983 LF 0 18:13:33.195 TradeProcessor (EURUSD,H1) Объём сделки: 0.14 JN 0 18:13:33.195 TradeProcessor (EURUSD,H1) Цена в сделке: 0.0000 MK 0 18:13:33.195 TradeProcessor (EURUSD,H1) Бид: 0.0000 CM 0 18:13:33.195 TradeProcessor (EURUSD,H1) Аск: 0.0000 IG 0 18:13:33.195 TradeProcessor (EURUSD,H1) Комментарий к операции: DQ 0 18:13:33.195 TradeProcessor (EURUSD,H1) Идентификатор запроса: 1
Второй вызов обработчика добавит ордер в список открытых (рис. 9).
Рис.9. Поле type равно TRADE_TRANSACTION_ORDER_ADDED
В логе нас интересует только блок «Транзакция».
HJ 0 18:17:02.886 TradeProcessor (EURUSD,H1) ---===Транзакция===--- GQ 0 18:17:02.886 TradeProcessor (EURUSD,H1) Тикет сделки: 0 CH 0 18:17:02.886 TradeProcessor (EURUSD,H1) Тип сделки: DEAL_TYPE_BUY RL 0 18:17:02.886 TradeProcessor (EURUSD,H1) Тикет ордера: 22535983 II 0 18:17:02.886 TradeProcessor (EURUSD,H1) Состояние ордера: ORDER_STATE_STARTED OG 0 18:17:02.886 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY_LIMIT GL 0 18:17:02.886 TradeProcessor (EURUSD,H1) Цена: 1.6500 IE 0 18:17:02.886 TradeProcessor (EURUSD,H1) Уровень Stop Loss: 0.0000 CO 0 18:17:02.886 TradeProcessor (EURUSD,H1) Уровень Take Profit: 0.0000 IF 0 18:17:02.886 TradeProcessor (EURUSD,H1) Цена срабатывания стоп-лимитного ордера: 0.0000 PL 0 18:17:02.886 TradeProcessor (EURUSD,H1) Торговый инструмент: GBPUSD OL 0 18:17:02.886 TradeProcessor (EURUSD,H1) Срок истечения отложенного ордера: 1970.01.01 00:00 HJ 0 18:17:02.886 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC LF 0 18:17:02.886 TradeProcessor (EURUSD,H1) Тип торговой транзакции: TRADE_TRANSACTION_ORDER_ADD FR 0 18:17:02.886 TradeProcessor (EURUSD,H1) Объём в лотах: 0.14
Третий вызов обработчика обновит данные по выставленному ордеру (рис. 10).
В частности, состояние ордера получит значение ORDER_STATE_PLACED.
Рис.10. Поле type равно TRADE_TRANSACTION_ORDER_UPDATE
В журнале увидим записи по блоку «Транзакция».
HS 0 18:21:27.004 TradeProcessor (EURUSD,H1) ---===Транзакция===--- GF 0 18:21:27.004 TradeProcessor (EURUSD,H1) Тикет сделки: 0 CO 0 18:21:27.004 TradeProcessor (EURUSD,H1) Тип сделки: DEAL_TYPE_BUY RE 0 18:21:27.004 TradeProcessor (EURUSD,H1) Тикет ордера: 22535983 KM 0 18:21:27.004 TradeProcessor (EURUSD,H1) Состояние ордера: ORDER_STATE_PLACED QH 0 18:21:27.004 TradeProcessor (EURUSD,H1) Тип ордера: ORDER_TYPE_BUY_LIMIT EG 0 18:21:27.004 TradeProcessor (EURUSD,H1) Цена: 1.6500 GL 0 18:21:27.004 TradeProcessor (EURUSD,H1) Уровень Stop Loss: 0.0000 ED 0 18:21:27.004 TradeProcessor (EURUSD,H1) Уровень Take Profit: 0.0000 GO 0 18:21:27.004 TradeProcessor (EURUSD,H1) Цена срабатывания стоп-лимитного ордера: 0.0000 RE 0 18:21:27.004 TradeProcessor (EURUSD,H1) Торговый инструмент: GBPUSD QS 0 18:21:27.004 TradeProcessor (EURUSD,H1) Срок истечения отложенного ордера: 1970.01.01 00:00 JS 0 18:21:27.004 TradeProcessor (EURUSD,H1) Тип ордера по времени действия: ORDER_TIME_GTC RD 0 18:21:27.004 TradeProcessor (EURUSD,H1) Тип торговой транзакции: TRADE_TRANSACTION_ORDER_UPDATE JK 0 18:21:27.004 TradeProcessor (EURUSD,H1) Объём в лотах: 0.14
Тут важной строкой является состояние ордера.
В отличие от позиций отложенные ордера, вернее их обработку, под шаблон подвести не получится. Каждая торговое действие, связанное с отложенным ордером, будет уникальным с точки зрения набора типов торговых транзакций.
Установка отложенного ордера потребует провести 3 транзакции (рис. 11).
Рис.11. Транзакции, обрабатывающие создание отложенного ордера
Модификация отложенного ордера сгенерирует 2 транзакции (рис. 12).
Рис.12. Транзакции, обрабатывающие изменение отложенного ордера
Если нужно будет удалить отложенный ордер, то обработчик OnTradeTransaction() будет вызван 4 раза (рис. 13).
Рис.13. Транзакции, обрабатывающие удаление отложенного ордера
Отмена отложенного ордера проходит по следующей схеме (рис. 14).
Рис.14. Транзакции, обрабатывающие отмену отложенного ордера
И последняя торговая операция - срабатывание отложенного ордера - породит 4 различных транзакции (рис. 15).
Рис.15. Транзакции, обрабатывающие активацию отложенного ордера
Не буду приводить записи из журнала по каждой комбинации транзакций. При желании читатель может самостоятельно их изучить, запустив код на исполнение.
4. Универсальный обработчик
Давайте посмотрим глазами конечного пользователя на программу, которая умеет работать с событием TradeTransaction. Наверное, пользователю понадобится именно та, которая сможет успешно работать как с ордерами, так и с позициями. Тогда программист должен написать код для обработчика OnTradeTransaction() таким образом, чтобы последний умел идентифицировать все транзакции и их комбинации, независимо от того, что обрабатывалось – позиция или ордер. Ну и желательно, чтобы по завершению обработки серии транзакций программа смогла бы сказать, какая торговая операция была выполнена.
В моём примере используется последовательная обработка транзакций. Однако разработчик языка MQL5 утверждает следующее:
Поэтому если возникает необходимость создать что-то работающее близко к идеалу, нужно совершенствовать предлагаемый мною пример и сделать обработку транзакций независимой от очерёдности поступления транзакций.
Если говорить в общем, то позиции и ордера могут иметь общие типы транзакций. Всего имеется 11 типов транзакций. Из них к непосредственной торговле из терминала мало относятся 4 типа:
- TRADE_TRANSACTION_DEAL_UPDATE;
- TRADE_TRANSACTION_DEAL_DELETE;
- TRADE_TRANSACTION_HISTORY_UPDATE;
- TRADE_TRANSACTION_HISTORY_DELETE.
В статье об этих типах речь идти не будет. Эти типы, как заявляет разработчик, предусмотрены для расширения функциональности на стороне торгового сервера. Автор статьи честно признаётся, что с данными типами не встречался.
Тогда остаётся 7 полноценных типов, которые чаще всего и обрабатываются в блоке OnTradeTransaction().
В теле обработчика важное место будет занимать раздел, определяющий текущий тип транзакции.
//--- ========== Типы транзакций [START] switch(trans_type) { //--- 1) если это запрос case TRADE_TRANSACTION_REQUEST: { //--- break; } //--- 2) если это добавление нового открытого ордера case TRADE_TRANSACTION_ORDER_ADD: { //--- break; } //--- 3) если это удаление ордера из списка открытых case TRADE_TRANSACTION_ORDER_DELETE: { //--- break; } //--- 4) если это добавление ордера в историю case TRADE_TRANSACTION_HISTORY_ADD: { //--- break; } //--- 5) если это добавление сделки в историю case TRADE_TRANSACTION_DEAL_ADD: { //--- break; } //--- 6) если это модификация позиции case TRADE_TRANSACTION_POSITION: { //--- break; } //--- 7) если это изменение открытого ордера case TRADE_TRANSACTION_ORDER_UPDATE: { //--- break; } } //--- ========== Типы транзакций [END]
В зависимости от текущего типа транзакции будем пробовать поставить диагноз обрабатываемой торговой операции. Чтобы определить, с чем мы работаем – с позицией или ордером, поручим case-модулю обработки запроса запоминать тип торговой операции.
Тогда сам этот модуль будет выглядеть так:
//--- 1) если это запрос case TRADE_TRANSACTION_REQUEST: { //--- last_action=request.action; string action_str; //--- запрос на что? switch(last_action) { //--- а) по рынку case TRADE_ACTION_DEAL: { action_str="поставить рыночный ордер"; trade_obj=TRADE_OBJ_POSITION; break; } //--- б) выставить отложенный ордер case TRADE_ACTION_PENDING: { action_str="выставить отложенный ордер"; trade_obj=TRADE_OBJ_ORDER; break; } //--- в) изменить позицию case TRADE_ACTION_SLTP: { trade_obj=TRADE_OBJ_POSITION; //--- StringConcatenate(action_str,request.symbol,": изменить значения Stop Loss", " и Take Profit"); //--- break; } //--- г) изменить ордер case TRADE_ACTION_MODIFY: { action_str="изменить параметры отложенного ордера"; trade_obj=TRADE_OBJ_ORDER; break; } //--- д) удалить ордер case TRADE_ACTION_REMOVE: { action_str="удалить отложенный ордер"; trade_obj=TRADE_OBJ_ORDER; break; } } //--- if(InpIsLogging) Print("Поступил запрос: "+action_str); //--- break; }
Несложно заметить несколько переменных.
static ENUM_TRADE_REQUEST_ACTIONS last_action; // рыночное действие на 1-м проходе
Переменная last_action будет запоминать, зачем вообще была инициирована работа обработчика.
static ENUM_TRADE_OBJ trade_obj; // на 1-м проходе задаёт торговый объект
Переменная trade_obj будет запоминать, что обрабатывалось – позиция или ордер. Для этого нужно будет создать перечисление ENUM_TRADE_OBJ.
Далее, перемещаемся в модуль, который займётся обработкой транзакции типа TRADE_TRANSACTION_ORDER_ADD:
//--- 2) если это добавление нового открытого ордера case TRADE_TRANSACTION_ORDER_ADD: { if(InpIsLogging) { if(trade_obj==TRADE_OBJ_POSITION) Print("Открыть новый рыночный ордер: "+ EnumToString(trans.order_type)); //--- else if(trade_obj==TRADE_OBJ_ORDER) Print("Установить новый отложенный ордер: "+ EnumToString(trans.order_type)); } //--- break; }
Здесь всё достаточно просто. Если на первом шаге обрабатывалась позиция, то на текущем появится запись в журнале "Открыть новый рыночный ордер: ", иначе - "Установить новый отложенный ордер:". Кроме информационных, никаких других действий в этом блоке нет.
Теперь смотрим на третий модуль – обработка типа TRADE_TRANSACTION_ORDER_DELETE:
//--- 3) если это удаление ордера из списка открытых case TRADE_TRANSACTION_ORDER_DELETE: { if(InpIsLogging) PrintFormat("Удалён из списка открытых ордер: #%d, "+ EnumToString(trans.order_type),trans.order); //--- break; }
Модуль также несёт только информационную нагрузку.
Четвёртый case-модуль обрабатывает тип TRADE_TRANSACTION_HISTORY_ADD:
//--- 4) если это добавление ордера в историю case TRADE_TRANSACTION_HISTORY_ADD: { if(InpIsLogging) PrintFormat("Добавлен в историю ордер: #%d, "+ EnumToString(trans.order_type),trans.order); //--- если обрабатывается отложенный ордер if(trade_obj==TRADE_OBJ_ORDER) { //--- если 3-й проход if(gTransCnt==2) { //--- если ордер был отменён, проверить сделки datetime now=TimeCurrent(); //--- запросить история сделок и ордеров HistorySelect(now-PeriodSeconds(PERIOD_H1),now); //--- попытка найти сделку для ордера CDealInfo myDealInfo; int all_deals=HistoryDealsTotal(); //--- bool is_found=false; for(int deal_idx=all_deals;deal_idx>=0;deal_idx--) if(myDealInfo.SelectByIndex(deal_idx)) if(myDealInfo.Order()==trans.order) is_found=true; //--- если сделка не найдена if(!is_found) { is_to_reset_cnt=true; //--- PrintFormat("Ордер был отменён: #%d",trans.order); } } //--- если 4-й проход if(gTransCnt==3) { is_to_reset_cnt=true; PrintFormat("Ордер был удалён: #%d",trans.order); } } //--- break; }
В нём кроме записи о том, что ордер добавлен в историю есть проверка ещё того, не работаем ли мы изначально с отложенным ордером. Если это так, то проверяем, каким по счёту проходом обработчика является текущий. Дело в том, что этот тип транзакции может появиться при работе с ордером на третьем проходе, если отложенный ордер был отменён или сработал. И на четвёртом проходе этот тип может появиться тогда, когда отложенный ордер был принудительно удалён.
В строках проверки 3-го прохода нужно ещё обратиться к истории сделок. Если для текущего ордера сделка не будет найдена, то такой ордер будем считать отменённым.
Пятый case-модуль обрабатывает тип TRADE_TRANSACTION_DEAL_ADD. Это самый большой по размеру строк кода блок программы.
В нём проверяем сделку. Важно выбрать сделку по тикету, чтобы получить доступ к её свойствам. По типу сделки можно узнать была позиция открыта, закрыта и т.д. Также, можно получить информацию о том, что сработал отложенный ордер. Это единственный случай, когда отложенный ордер может породить сделку в контексте работы обработчика события TradeTransaction.
//--- 5) если это добавление сделки в историю case TRADE_TRANSACTION_DEAL_ADD: { is_to_reset_cnt=true; //--- ulong deal_ticket=trans.deal; ENUM_DEAL_TYPE deal_type=trans.deal_type; //--- if(InpIsLogging) PrintFormat("Добавлена в историю сделка: #%d, "+EnumToString(deal_type),deal_ticket); if(deal_ticket>0) { datetime now=TimeCurrent(); //--- запросить история сделок и ордеров HistorySelect(now-PeriodSeconds(PERIOD_H1),now); //--- выбрать сделку по тикету if(HistoryDealSelect(deal_ticket)) { //--- проверить сделку CDealInfo myDealInfo; myDealInfo.Ticket(deal_ticket); long order=myDealInfo.Order(); //--- параметры сделки ENUM_DEAL_ENTRY deal_entry=myDealInfo.Entry(); double deal_vol=0.; //--- if(myDealInfo.InfoDouble(DEAL_VOLUME,deal_vol)) if(myDealInfo.InfoString(DEAL_SYMBOL,deal_symbol)) { //--- позиция CPositionInfo myPos; double pos_vol=WRONG_VALUE; //--- if(myPos.Select(deal_symbol)) pos_vol=myPos.Volume(); //--- если был вход в рынок if(deal_entry==DEAL_ENTRY_IN) { //--- 1) открытие позиции if(deal_vol==pos_vol) PrintFormat("\n%s: открыта новая позиция",deal_symbol); //--- 2) добавление к открытой позиции else if(deal_vol<pos_vol) PrintFormat("\n%s: добавление к текущей позиции",deal_symbol); } //--- если был выход с рынка else if(deal_entry==DEAL_ENTRY_OUT) { if(deal_vol>0.0) { //--- 1) закрытие позиции if(pos_vol==WRONG_VALUE) PrintFormat("\n%s: закрыта позиция",deal_symbol); //--- 2) уменьшение открытой позиции else if(pos_vol>0.0) PrintFormat("\n%s: уменьшение текущей позиции",deal_symbol); } } //--- если был переворот else if(deal_entry==DEAL_ENTRY_INOUT) { if(deal_vol>0.0) if(pos_vol>0.0) PrintFormat("\n%s: переворот позиции",deal_symbol); } } //--- активация ордера if(trade_obj==TRADE_OBJ_ORDER) PrintFormat("Активация отложенного ордера: %d",order); } } //--- break; }
Тип транзакции TRADE_TRANSACTION_POSITION является уникальным и обрабатывается только тогда, когда происходит модификация позиции:
//--- 6) если это модификация позиции case TRADE_TRANSACTION_POSITION: { is_to_reset_cnt=true; //--- PrintFormat("Модификация позиции: %s",deal_symbol); //--- if(InpIsLogging) { PrintFormat("Новая цена slop loss: %0."+ IntegerToString(_Digits)+"f",trans.price_sl); PrintFormat("Новая цена take profit: %0."+ IntegerToString(_Digits)+"f",trans.price_tp); } //--- break; }
Последний case-модуль включается при обработке типа TRADE_TRANSACTION_ORDER_UPDATE.
Появление этого типа может быть обусловлено только работой с отложенным ордером. Причём он появляется при срабатывании любой торговой операции, имеющей отношение к отложенным ордерам, но на разных этапах.
//--- 7) если это изменение открытого ордера case TRADE_TRANSACTION_ORDER_UPDATE: { //--- если был 1-ый проход if(gTransCnt==0) { trade_obj=TRADE_OBJ_ORDER; PrintFormat("Снятие ордера: #%d",trans.order); } //--- если был 2-ой проход if(gTransCnt==1) { //--- если модификация ордера if(last_action==TRADE_ACTION_MODIFY) { PrintFormat("Изменён отложенный ордер: #%d",trans.order); //--- сбросить счётчик is_to_reset_cnt=true; } //--- если удаление ордера if(last_action==TRADE_ACTION_REMOVE) { PrintFormat("Удалить отложенный ордер: #%d",trans.order); } } //--- если был 3-ий проход if(gTransCnt==2) { PrintFormat("Установлен новый отложенный ордер: #%d, "+ EnumToString(trans.order_type),trans.order); //--- сбросить счётчик is_to_reset_cnt=true; } //--- break; }
Итак, если этот тип появился при первом срабатывании OnTradeTransaction(), то ордер либо был отменён, либо сработал.
Если тип возник на втором срабатывании обработчика, то ордер либо был удалён, либо модифицирован. Чтобы узнать, что именно произошло, нужно обратиться к статической переменной last_action, которая запомнит последнее торговое действие.
И последний случай, когда может возникнуть данный тип, касается третьего срабатывания обработчика. Оно завершает процедуру выставления отложенного ордера.
Легко заметить, что в коде используется булевая переменная is_to_reset_cnt. Она выступает в качестве флага для сброса счётчика проходов обработчика OnTradeTransaction().
Вот в принципе и все, что касается обработки события TradeTransaction. Да, ещё добавил бы паузу в начале вызова обработчика. Тогда вероятность того, что в историю ещё не попала сделка или ордер будет минимизирована.
Заключение
В своей статье я попытался продемонстрировать, как можно работать с различными торговыми операциями и получать информацию о происходящем в терминале.
Преимущество данного подхода состоит в том, что программа может получать информацию о поэтапном выполнении торговой операции. Думаю, что такой подход можно смело применять для целей копирования торговых сделок с терминала на терминал.
![Рецепты MQL5 - обработка типичных событий графика](https://c.mql5.com/2/11/OnChartEvent_MetaTrader5.png)
![Регрессионный анализ влияния макроэкономических данных на изменение цен валют](https://c.mql5.com/2/11/fundamental_analysis_statistica_MQL5_MetaTrader5.png)
![Случайные леса предсказывают тренды](https://c.mql5.com/2/11/Random_Forest_MetaTrader5.png)
![Разработка социального технологического стартапа, часть II: Программируем клиент MQL5 REST](https://c.mql5.com/2/10/tecla-twitter.png)
![MQL5 - Язык торговых стратегий для клиентского терминала MetaTrader 5](https://c.mql5.com/i/registerlandings/logo-2.png)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Денис, спасибо за статью! С интересом прочитал, но ещё пока до конца не осознал прочитанное. Так же скачал Ваш советник TradeProcessor и запустил на своем терминале, где у меня сейчас имеется открытая позиция. Посмотрел распечатки и сразу же появились вопросы, на которые хочу самостоятельно найти ответы. Ну, уж если не сдюжу, то тогда, не обессудьте - придётся Вас побеспокоить. ))
С уважением, Владимир.
Денис, спасибо за статью! С интересом прочитал, но ещё пока до конца не осознал прочитанное. Так же скачал Ваш советник TradeProcessor и запустил на своем терминале, где у меня сейчас имеется открытая позиция. Посмотрел распечатки и сразу же появились вопросы, на которые хочу самостоятельно найти ответы. Ну, уж если не сдюжу, то тогда, не обессудьте - придётся Вас побеспокоить. ))
С уважением, Владимир.
Спасибо за мнение. You are welcome, MrBrooklin! ))
Спасибо за мнение. You are welcome, MrBrooklin! ))
Денис, извините, но без Вашей помощи моё сознание "взрывается". Вот результат одного из проходов Вашего советника (кстати, очень классный советник!!!):
В чём моё непонимание:
ENUM_DEAL_TYPE
Идентификатор
Описание
DEAL_TYPE_BUY
Покупка
Вопрос - как при модификации отложенного ордера SELL_STOP тип сделки определяется на ПОКУПКУ??? ))
С уважением, Владимир.
А сделки никакой нет. Выставление отложенного ордера и его обработка не влечёт за собой никакой сделки. Тикет сделки = 0, тип = 0, где 0 - это для перечисления ENUM_DEAL_TYPE эквивалентно DEAL_TYPE_BUY. Т.е. в структуре MqlTradeTransaction trans некоторые поля заполняются, а некоторые нет. Незаполненные поля обычно обнулены.
Подробнее: в Документации про отложенные ордера.
Поле deal будет заполнено для транзакции такого типа:
TRADE_TRANSACTION_DEAL_*
Для торговых транзакций, касающихся обработки сделок (TRADE_TRANSACTION_DEAL_ADD, TRADE_TRANSACTION_DEAL_UPDATE и TRADE_TRANSACTION_DEAL_DELETE), в структуре MqlTradeTransaction заполняются следующие поля:
К "сделочным" типам транзакций относятся всего 3 типа: TRADE_TRANSACTION_DEAL_ADD, TRADE_TRANSACTION_DEAL_UPDATE, TRADE_TRANSACTION_DEAL_DELETE.
А сделки никакой нет. Выставление отложенного ордера не влечёт за собой никакой сделки. Тикет сделки = 0, тип = 0, где 0 - это для перечисления ENUM_DEAL_TYPE эквивалентно DEAL_TYPE_BUY. Т.е. в структуре MqlTradeTransaction trans некоторые поля заполняются, а некоторые нет. Незаполненные поля обычно обнулены.
Подробнее: в Документации про отложенные ордера.
А-а-а, вот оно что!!! Блин, всю голову сломал! ))
Спасибо!!!
С уважением, Владимир.