Рецепты MQL5 - обработка события BookEvent
Denis Kirichenko | 17 октября, 2014
Введение
Как известно, торговый терминал MetaTrader 5 — это мультирыночная платформа. С ее помощью можно торговать на рынках: Форекс (Forex), фондовые рынки (биржи), Фьючерсы и Контракты на разницу. Судя по заказам в разделе Фриланс, доля трейдеров, которые торгуют не только на Форексе, растет.
В своей статье я хотел бы познакомить начинающего MQL5-программиста с обработчиком события BookEvent. Это событие связано с биржевым стаканом, который является инструментом трейдера, торгующего фондовыми активами и производными на них. Однако для Forex-трейдеров стакан тоже может оказаться полезным. Сейчас все большую популярность набирают так называемые ECN-счета, где поставщики ликвидности предоставляют информацию по заявкам, пускай и в рамках только своей модели агрегатора.
1. Событие BookEvent
Согласно Документации данное событие появляется при изменении состояния стакана цен. Условимся, что событие BookEvent — это "событие стакана".
Сам стакан — это массив заявок, которые отличаются направлением (sell и buy), ценой и объемом. Цены в стакане распределены возле рыночных, поэтому считаются лучшими.
Рис.1 Стакан цен в MetaTrader 5
В MetaTrader 5 биржевой стакан называется "Стаканом цен" (рис.1). Более подробно о стакане цен можно почитать в Руководстве пользователя клиентского терминала.
Пару слов стоит сказать об информационной структуре в стакане цен.
struct MqlBookInfo { ENUM_BOOK_TYPE type; // тип заявки из перечисления ENUM_BOOK_TYPE double price; // цена long volume; // объем };
Она содержит 3 поля. Обработав структуру заявки, можно получить данные по типу заявки, цене заявки и ее объему.
2. Обработчик события BookEvent
Обработчик—функция OnBookEvent() принимает в качестве параметров одну константу — ссылку на строковый параметр.
void OnBookEvent (const string& symbol)
Строковый параметр содержит имя символа, для которого произошло событие стакана.
Сам обработчик требует предварительной подготовки. Для того чтобы советник мог обрабатывать событие стакана, нужно подписаться на это событие. Это делается с помощью встроенной функции MarketBookAdd(). Обычно она находится в блоке инициализации работы советника. Если на событие стакана не подписаться, то советник никак не будет реагировать на него.
Хорошим тоном является наличие отписки от события. При деинициализации нужно отписаться от получения этих данных, вызвав функцию MarketBookRelease().
Механизм подписки-отписки напоминает создание и обработку таймера — прежде чем обрабатывать таймер, нужно его активизировать.
3. Шаблон обработки события BookEvent
Создадим простой шаблон советника, который вызывает функцию OnBookEvent(). Назовем его BookEventProcessor1.mq5.
В шаблоне есть минимальный набор: обработчики инициализации и деинициализации советника, а также обработчик события стакана.
Сам обработчик события стакана выглядит предельно просто:
//+------------------------------------------------------------------+ //| BookEvent function | //+------------------------------------------------------------------+ void OnBookEvent(const string &symbol) { Print("Book event for: "+symbol); //--- select the symbol if(symbol==_Symbol) { //--- array of the DOM structures MqlBookInfo last_bookArray[]; //--- get the book if(MarketBookGet(_Symbol,last_bookArray)) { //--- process book data for(int idx=0;idx<ArraySize(last_bookArray);idx++) { MqlBookInfo curr_info=last_bookArray[idx]; //--- print PrintFormat("Type: %s",EnumToString(curr_info.type)); PrintFormat("Price: %0."+IntegerToString(_Digits)+"f",curr_info.price); PrintFormat("Volume: %d",curr_info.volume); } } } }
Так как событие стакана является широковещательным (после подписки оно будет появляться для всех инструментов), нужно выбрать только свой инструмент.
Однако отмечу, что в последних билдах были изменения в работе стакана. И в текущей версии Build 975 я не нашел присутствие широковещательности — обработчик вызывался только для "своего" инструмента. Для подтверждения этого факта просто добавил вывод информации в журнал "Эксперты".
После этого обращаемся к встроенной функции MarketBookGet(). Она вернет весь "расклад" по стакану: массив структур MqlBookInfo, содержащий записи стакана цен указанного символа. Отмечу, что у разных брокеров этот массив будет иметь разные размеры.
Шаблон позволяет распечатать в журнале значения структур из массива.
Советник в режиме отладки запустил на фьючерсе SBRF-12.14. Получится такая последовательность записей в журнал:
EL 0 11:24:32.250 BookEventProcessor1 (SBRF-12.14,M1) Book event for: SBRF-12.14 MF 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL KP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7708 LJ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 6 MP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL HF 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7705 LP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 6 MJ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL GL 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7704 ON 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 3 MD 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL FR 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7703 PD 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 2 MN 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL DH 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7701 QQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 10 GH 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL QM 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7700 OK 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 1011 ER 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL KS 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7698 EE 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 50 OM 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL KI 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7696 IO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 21 QG 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL LO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7695 MK 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 1 QQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL KE 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7694 QQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 5 QK 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL PK 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7691 LO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 2 QE 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL HQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7688 OE 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 106 MO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL RG 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7686 IP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 18 GI 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL FL 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7684 QI 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 3 GS 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL IR 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7681 LG 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 4 GM 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL JH 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7680 RM 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 2 GG 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL DN 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7679 HH 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 19 IQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL ED 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7678 EQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 1 IK 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL OJ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7676 DO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 2 IE 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_SELL RP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7675 EE 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 1 QR 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY LF 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7671 LP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 40 QD 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY KL 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7670 QJ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 21 QN 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY CR 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7669 RD 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 20 QP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY DH 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7668 NN 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 17 QJ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY QN 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7667 RK 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 2 OL 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY DE 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7666 MQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 151 QF 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY OJ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7665 RO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 2 OH 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY FQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7664 EF 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 49 OR 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY GG 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7663 OS 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 3 ED 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY JM 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7662 PI 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 4 CN 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY ES 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7661 LD 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 13 CP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY FI 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7660 LM 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 2 IJ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY NO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7659 II 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 12 IL 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY ME 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7658 IP 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 3 GF 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY NK 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7657 FM 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 15 GH 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY MQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7656 DD 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 6 MS 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY NG 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7655 KR 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 9 KE 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY OM 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7654 IK 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 14 KO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY NS 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7653 DF 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 534 MQ 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Type: BOOK_TYPE_BUY OI 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Price: 7652 IO 0 11:24:33.812 BookEventProcessor1 (SBRF-12.14,M1) Volume: 25
Это слепок стакана в моменте. Первый элемент массива структур — это заявка на продажу по самой высокой цене (7708 руб.). Последний элемент — это заявка на покупку по самой низкой цене (7652 руб.). Таким образом, данные стакана считываются в массив сверху вниз.
Для целей визуализации соберу эти данные в таблицу 1.
Таблица 1. Стакан цен для SBRF-12.14
Верхний красный блок — это заявки на продажу (sell-limits). Нижний зеленый блок — заявки на покупку (buy-limits).
Можно заметить, что среди заявок на продажу самой большой по объему была за номером 6 — цена 7700 руб., объем 1011 лотов. Самая большая по объему заявка на покупку была за номером 39 — цена 7653 руб., 534 лота.
Исходные данные в стакане могут служить той пищей для размышления, которую трейдер "жует" для формирования своей стратегии. Самая простая торговая идея заключается в том, что скопления и кластеризация заявок с большим объемом на соседних ценовых уровнях создают зоны сопротивления и поддержки. В следующем разделе попробуем создать индикатор, который будет следить за изменениями в стакане.
4. Глубина рынка
Хочу заметить, что есть масса разнообразных индикаторов, работающих по данным стакана. Например, в Маркете для MetaTrader 5 есть тоже несколько интересных программ. Особенно мне понравилась панель IShift. Ее автор, Yury Kulikov, сумел создать достаточно компактный и информативный инструмент.
Все программы, работающие по данным стакана, по форме будут являться советниками либо индикаторами, ведь обработчик события BookEvent есть только в этих MQL5-программах.
Давайте попробуем создать небольшую программу, которая в режиме реального времени станет визуализировать стакан цен. Что нужно отобразить на экране? Это будет панель, где горизонтальные столбики покажут величину объема заявки, причем их размер будет носить относительный характер. За 100% берется максимальный объем по всем заявкам на текущий момент. На рис. 2 самый большой объем есть у заявки на цене 7507 руб. (519 лотов).
Для корректной инициализации панели нужно точно указать глубину стакана — число уровней (параметр "DOM depth"). У разных брокеров это число отличается.
Рис. 2 Панель глубины рынка
Программный код для панели написан с использованием ООП. Класс, отвечающий за работу панели, называется CBookBarsPanel.
//+------------------------------------------------------------------+ //| Book bars class | //+------------------------------------------------------------------+ class CBookBarsPanel { private: //--- Data members CArrayObj m_obj_arr; uint m_arr_size; //--- uint m_width; uint m_height; //--- Methods public: void CBookBarsPanel(const uint _arr_size); void ~CBookBarsPanel(void){}; //--- bool Init(const uint _width,const uint _height); void Deinit(void){this.m_obj_arr.Clear();}; void Refresh(const MqlBookInfo &_bookArray[]); };
В классе есть 4 члена-данных.
- Атрибут m_obj_arr — это контейнер для указателей на объекты типа CObject. Он нужен для того, чтобы хранить записи стакана. Для них будет создан отдельный класс.
- Атрибут m_arr_size отвечает за число уровней стакана.
- Атрибуты m_width и m_height сохраняют размеры панели (ширина и высота в пикселях).
Что касается методов, то их набор (кроме стандартных конструктора и деструктора) составляют:
- метод инициализации;
- метод деинициализации;
- метод обновления.
Для каждой строки панели (уровня стакана), где указаны цена, горизонтальный столбик и объем, создадим свой класс CBookRecord.
В нем будут 3 указателя: один на объект типа CChartObjectRectLabel (прямоугольная метка для работы со столбиком) и два указателя для типа CChartObjectLabel (текстовые метки для цены и объема).
//+------------------------------------------------------------------+ //| Book record class | //+------------------------------------------------------------------+ class CBookRecord : public CObject { private: //--- Data members CChartObjectRectLabel *m_rect; CChartObjectLabel *m_price; CChartObjectLabel *m_vol; //--- color m_color; //--- Methods public: void CBookRecord(void); void ~CBookRecord(void); bool Create(const string _name,const color _color,const int _X, const int _Y,const int _X_size,const int _Y_size); //--- data bool DataSet(const long _vol,const double _pr,const uint _len); bool DataGet(long &_vol,double &_pr) const; private: string StringVolumeFormat(const long _vol); };
Среди методов есть:
- метод создания записи;
- метод установки данных;
- метод получения данных;
- метод форматирования больших чисел.
Тогда обработчик BookEvent для самого советника, а по сути индикатора, будет иметь такой код:
//+------------------------------------------------------------------+ //| BookEvent function | //+------------------------------------------------------------------+ void OnBookEvent(const string &symbol) { //--- select the symbol if(symbol==_Symbol) { //--- array of the DOM structures MqlBookInfo last_bookArray[]; //--- get the book if(MarketBookGet(_Symbol,last_bookArray)) //--- refresh panel myPanel.Refresh(last_bookArray); } }
Таким образом, при каждом срабатывании BookEvent панель будет обновлять свои данные. Назовем новую версию программы BookEventProcessor2.mq5.
Работа советника в динамике представлена на видео.
Заключение
Данная статья была посвящена очередному событию терминала — событию стакана. К нему часто обращаются торговые алгоритмы, природа которых, скорее, относится к высокочастотной торговле (HFT), а она все больше и больше пользуется популярностью у трейдеров.
Надеюсь, что начинающим программировать на языке MQL5 будет полезен материал с примерами обработки события стакана. Исходные файлы из архива удобно расположить в папке проектов. В моем случае это папка \MQL5\Projects\BookEvent.