Рецепты MQL5 - обработка события BookEvent

Denis Kirichenko | 17 октября, 2014

Введение

Как известно, торговый терминал MetaTrader 5 — это мультирыночная платформа. С ее помощью можно торговать на рынках: Форекс (Forex), фондовые рынки (биржи), Фьючерсы и Контракты на разницу. Судя по заказам в разделе Фриланс, доля трейдеров, которые торгуют не только на Форексе, растет.

В своей статье я хотел бы познакомить начинающего MQL5-программиста с обработчиком события BookEvent. Это событие связано с биржевым стаканом, который является инструментом трейдера, торгующего фондовыми активами и производными на них. Однако для Forex-трейдеров стакан тоже может оказаться полезным. Сейчас все большую популярность набирают так называемые ECN-счета, где поставщики ликвидности предоставляют информацию по заявкам, пускай и в рамках только своей модели агрегатора.


1. Событие BookEvent

Согласно Документации данное событие появляется при изменении состояния стакана цен. Условимся, что событие BookEvent — это "событие стакана".

Сам стакан — это массив заявок, которые отличаются направлением (sell и buy), ценой и объемом. Цены в стакане распределены возле рыночных, поэтому считаются лучшими.

Рис.1 Стакан цен в MetaTrader 5

Рис.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

Таблица 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 Панель глубины рынка

Рис. 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 члена-данных.

Что касается методов, то их набор (кроме стандартных конструктора и деструктора) составляют:

Для каждой строки панели (уровня стакана), где указаны цена, горизонтальный столбик и объем, создадим свой класс 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.