Работа с ценами в библиотеке DoEasy (Часть 63): Стакан цен, класс абстрактной заявки стакана цен

5 февраля 2021, 09:09
Artyom Trishkin
2
742

Содержание


Концепция

С этой статьи мы начнём делать функционал для работы со стаканом цен. Концептуально классы для работы со стаканом цен не будут отличаться от всех ранее написанных классов библиотеки. Одномоментно мы будем иметь "слепок" стакана, в котором будут находиться данные о заказах в стакане цен, получаемые функцией MarketBookGet() при срабатывании обработчика OnBookEvent(), в котором для каждого из символов, для которых активизирована подписка на события стакана цен, срабатывает событие при изменении стакана.

Таким образом, структура классов стакана цен будет такой:

  1. Класс объекта-заявки стакана цен — объект, описывающий данные одной заявки из множества заявок, полученных из стакана цен при разовом срабатывании обработчика OnBookEvent() для одного символа;
  2. Класс объекта-слепка стакана цен — объект, описывающий данные всех заявок, полученных одномоментно из стакана цен на одном срабатывании обработчика OnBookEvent() для одного символа — совокупность объектов п1, вкупе составляющих текущий слепок стакана цен;
  3. Класс-таймсерия, состоящая из последовательности объектов п2, заносимых в список-таймсерию на каждом срабатывании OnBookEvent() для одного символа;
  4. Класс-коллекция таймсерий данных стаканов цен всех используемых в программе символов, для которых активизирована подписка на события стакана цен.

Сегодня сделаем класс объекта-заявки (1) и протестируем получение данных стакана цен при срабатывании OnBookEvent() по текущему символу.

Свойства каждой заявки прописаны в структуре MqlBookInfo, предоставляющей информацию в стакане цен:

  • тип заявки из перечисления ENUM_BOOK_TYPE
  • цена, по которой выставлена заявка
  • объём заявки
  • объём заявки с повышенной точностью

В стакане могут находиться четыре типа заявок (из перечисления ENUM_BOOK_TYPE):

  • Заявка на продажу
  • Заявка на продажу по рыночной цене
  • Заявка на покупку
  • Заявка на покупку по рыночной цене

Как видим, есть четыре типа заявки — две в сторону Buy, и две — в сторону Sell. Соответственно, для разделения всех типов заявок на две стороны, мы, помимо уже имеющихся свойств, добавим ещё одно свойство — статус заявки, указывающий на её направление — заявка в сторону Buy, или заявка в сторону Sell. Это позволит нам в дальнейшем быстро разделять все заявки на их стороны — спрос и предложение.

Объект одной заявки стакана будет сделан по образу объектов-ордеров (да и многих других объектов библиотеки) — у нас будет базовый объект абстрактной заявки стакана цен, и четыре наследуемых объекта с уточнением типа заявки. Концепция построения таких объектов была нами рассмотрена в самом начале создания библиотеки в первой и второй статьях.

    Прежде, чем начнём делать классы для работы со стаканом цен, добавим новые сообщения библиотеки и немного доработаем классы объектов тиковых данных. В файле \MQL5\Include\DoEasy\Data.mqh допишем индексы новых сообщений:

       MSG_SYM_EVENT_SYMBOL_ADD,                          // В окно "Обзор рынка" добавлен символ
       MSG_SYM_EVENT_SYMBOL_DEL,                          // Из окна "Обзор рынка" удалён символ
       MSG_SYM_EVENT_SYMBOL_SORT,                         // Изменено расположение символов в окне "Обзор рынка"
       MSG_SYM_SYMBOLS_MODE_CURRENT,                      // Работа только с текущим символом
       MSG_SYM_SYMBOLS_MODE_DEFINES,                      // Работа с предопределённым списком символов
       MSG_SYM_SYMBOLS_MODE_MARKET_WATCH,                 // Работа с символами из окна "Обзор рынка"
       MSG_SYM_SYMBOLS_MODE_ALL,                          // Работа с полным списком всех доступных символов
       MSG_SYM_SYMBOLS_BOOK_ADD,                          // Осуществлена подписка на стакан цен 
       MSG_SYM_SYMBOLS_BOOK_DEL,                          // Осуществлена отписка от стакан цен 
       MSG_SYM_SYMBOLS_MODE_BOOK,                         // Подписка на стакан цен
       MSG_SYM_SYMBOLS_ERR_BOOK_ADD,                      // Ошибка при подписке на стакан цен
       MSG_SYM_SYMBOLS_ERR_BOOK_DEL,                      // Ошибка при отписке от стакан цен 
       
    //--- CAccount
    

    ...

    //--- CTickSeries
       MSG_TICKSERIES_TEXT_TICKSERIES,                    // Тиковая серия
       MSG_TICKSERIES_ERR_GET_TICK_DATA,                  // Ошибка получения тиковых данных
       MSG_TICKSERIES_FAILED_CREATE_TICK_DATA_OBJ,        // Не удалось создать объект тиковых данных
       MSG_TICKSERIES_FAILED_ADD_TO_LIST,                 // Не удалось добавить объект тиковых данных в список
       MSG_TICKSERIES_TEXT_IS_NOT_USE,                    // Тиковая серия не используется. Нужно установить флаг использования при помощи SetAvailable()
       MSG_TICKSERIES_REQUIRED_HISTORY_DAYS,              // Запрошенное количество дней
       
    //--- CMarketBookOrd
       MSG_MBOOK_ORD_TEXT_MBOOK_ORD,                      // Заявка в стакане цен
       MSG_MBOOK_ORD_VOLUME,                              // Объем
       MSG_MBOOK_ORD_VOLUME_REAL,                         // Объем c повышенной точностью
       MSG_MBOOK_ORD_STATUS_BUY,                          // Сторона Buy
       MSG_MBOOK_ORD_STATUS_SELL,                         // Сторона Sell
       MSG_MBOOK_ORD_TYPE_SELL,                           // Заявка на продажу
       MSG_MBOOK_ORD_TYPE_BUY,                            // Заявка на покупку 
       MSG_MBOOK_ORD_TYPE_SELL_MARKET,                    // Заявка на продажу по рыночной цене
       MSG_MBOOK_ORD_TYPE_BUY_MARKET,                     // Заявка на покупку по рыночной цене
    
      };
    //+------------------------------------------------------------------+
    

    и тексты сообщений, соответствующие вновь добавленным индексам:

       {"В окно \"Обзор рынка\" добавлен символ","Added a symbol to the \"Market Watch\" window"},
       {"Из окна \"Обзор рынка\" удалён символ","From the \"Market Watch\" window was removed"},
       {"Изменено расположение символов в окне \"Обзор рынка\"","Changed the arrangement of symbols in the \"Market Watch\" window"},
       {"Работа только с текущим символом","Work only with the current symbol"},
       {"Работа с предопределённым списком символов","Work with a predefined list of symbols"},
       {"Работа с символами из окна \"Обзор рынка\"","Working with symbols from the \"Market Watch\" window"},
       {"Работа с полным списком всех доступных символов","Work with the full list of all available symbols"},
       {"Осуществлена подписка на стакан цен ","Subscribed to Depth of Market"},
       {"Осуществлена отписка от стакан цен ","Unsubscribed from Depth of Market"},
       {"Подписка на стакан цен","Subscription to Depth of Market"},
       {"Ошибка при подписке на стакан цен",""},
       {"Ошибка при отписке от стакан цен",""},
       
    //--- CAccount
    

    ...

    //--- CMarketBookOrd
       {"Заявка в стакане цен","Order in Depth of Market"},
       {"Объем","Volume"},
       {"Объем c повышенной точностью","Volume Real"},
       {"Сторона Buy","Buy side"},
       {"Сторона Sell","Sell side"},
       {"Заявка на продажу","Sell order"},
       {"Заявка на покупку","Buy order"},
       {"Заявка на продажу по рыночной цене","Sell order at market price"},
       {"Заявка на покупку по рыночной цене","Buy order at market price"},
       
      };
    //+---------------------------------------------------------------------+
    

    В файле класса объекта-символа \MQL5\Include\DoEasy\Objects\Symbols\Symbol.mqh допишем вывод сообщений об ошибке при осуществлении подписки на стакан цен:

    //+------------------------------------------------------------------+
    //| Осуществляет подписку на стакан цен                              |
    //+------------------------------------------------------------------+
    bool CSymbol::BookAdd(void)
      {
       this.m_book_subscribed=(#ifdef __MQL5__ ::MarketBookAdd(this.m_name) #else false #endif);
       this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]=this.m_book_subscribed;
       if(this.m_book_subscribed)
          ::Print(CMessage::Text(MSG_SYM_SYMBOLS_BOOK_ADD)+" "+this.m_name);
       else
          ::Print(CMessage::Text(MSG_SYM_SYMBOLS_ERR_BOOK_ADD)+": "+CMessage::Text(::GetLastError()));
       return this.m_book_subscribed;
      }
    //+------------------------------------------------------------------+
    

    и точно так же — при отписке от него:

    //+------------------------------------------------------------------+
    //| Осуществляет закрытие стакана цен                                |
    //+------------------------------------------------------------------+
    bool CSymbol::BookClose(void)
      {
    //--- Если флаг подписки на стакан снят - значит уже (или ещё) нет подписки - возвращаем true
       if(!this.m_book_subscribed)
          return true;
    //--- Сохраняем результат отписки от стакана цен
       bool res=( #ifdef __MQL5__ ::MarketBookRelease(this.m_name) #else true #endif );
    //--- Если успешно отписались - сбрасываем флаг подписки на стакан и записываем состояние в свойство объекта
       if(res)
         {
          this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]=this.m_book_subscribed=false;
          ::Print(CMessage::Text(MSG_SYM_SYMBOLS_BOOK_DEL)+" "+this.m_name);
         }
       else
         {
          this.m_long_prop[SYMBOL_PROP_BOOKDEPTH_STATE]=this.m_book_subscribed=true;
          ::Print(CMessage::Text(MSG_SYM_SYMBOLS_ERR_BOOK_DEL)+": "+CMessage::Text(::GetLastError()));
         }
    //--- Возвращаем результат отписки от стакана цен
       return res;
      }
    //+------------------------------------------------------------------+
    

    Из метода обновления тиковой серии класса тиковой серии в файле \MQL5\Include\DoEasy\Objects\Ticks\TickSeries.mqh удалим вывод отладочных комментариев на график символа, который мы оставили для тестов в прошлой статье:

    //+------------------------------------------------------------------+
    //| Обновляет список-тиковую серию                                   |
    //+------------------------------------------------------------------+
    void CTickSeries::Refresh(void)
      {
       MqlTick ticks_array[];
       if(IsNewTick())
         {
          //--- Копируем тики от времени m_last_time+1 мсек до конца истории
          int err=ERR_SUCCESS;
          int total=::CopyTicksRange(this.Symbol(),ticks_array,COPY_TICKS_ALL,this.m_last_time+1,0);
          //--- Если тики скопированы, в цикле по их количеству создаём новые объекты тиковых данных и добавляем их в список
          if(total>0)
            {
             for(int i=0;i<total;i++)
               {
                //--- Создаём объект-тик и добавляем его в список
                CDataTick *tick_obj=this.CreateNewTickObj(ticks_array[i]);
                if(tick_obj==NULL)
                   break;
                //--- Записываем время последнего тика для последующего копирования вновь поступивших тиков
                long end_time=ticks_array[::ArraySize(ticks_array)-1].time_msc;
                if(this.Symbol()=="AUDUSD")
                   Comment(DFUN,this.Symbol(),", copied=",total,", m_last_time=",TimeMSCtoString(m_last_time),", end_time=",TimeMSCtoString(end_time),", total=",DataTotal());
                this.m_last_time=end_time;
               }
             //--- Если количество тиков в списке превысило заданное по умолчанию максимальное их количество -
             //--- удаляем рассчитанное количество объектов-тиков с конца списка
             if(this.DataTotal()>TICKSERIES_MAX_DATA_TOTAL)
               {
                int total_del=m_list_ticks.Total()-TICKSERIES_MAX_DATA_TOTAL;
                for(int j=0;j<total_del;j++)
                   this.m_list_ticks.Delete(j);
               }
            }
         }
      }
    //+------------------------------------------------------------------+
    

    Время последнего тика теперь будем записывать сразу в переменную m_last_time, для этого предназначенную по причине, что в прошлой статье нам необходимо было выводить проверочные данные комментарием на график символа, где требовалось видеть время прошлого и текущего тиков. Теперь же нам это не нужно, и время сразу сохраняем в переменную:

    //+------------------------------------------------------------------+
    //| Обновляет список-тиковую серию                                   |
    //+------------------------------------------------------------------+
    void CTickSeries::Refresh(void)
      {
       MqlTick ticks_array[];
       if(IsNewTick())
         {
          //--- Копируем тики от времени m_last_time+1 мсек до конца истории
          int err=ERR_SUCCESS;
          int total=::CopyTicksRange(this.Symbol(),ticks_array,COPY_TICKS_ALL,this.m_last_time+1,0);
          //--- Если тики скопированы, в цикле по их количеству создаём новые объекты тиковых данных и добавляем их в список
          if(total>0)
            {
             for(int i=0;i<total;i++)
               {
                //--- Создаём объект-тик и добавляем его в список
                CDataTick *tick_obj=this.CreateNewTickObj(ticks_array[i]);
                if(tick_obj==NULL)
                   break;
                //--- Записываем время последнего тика для последующего копирования вновь поступивших тиков
                this.m_last_time=ticks_array[::ArraySize(ticks_array)-1].time_msc;
               }
             //--- Если количество тиков в списке превысило заданное по умолчанию максимальное их количество -
             //--- удаляем рассчитанное количество объектов-тиков с конца списка
             if(this.DataTotal()>TICKSERIES_MAX_DATA_TOTAL)
               {
                int total_del=m_list_ticks.Total()-TICKSERIES_MAX_DATA_TOTAL;
                for(int j=0;j<total_del;j++)
                   this.m_list_ticks.Delete(j);
               }
            }
         }
      }
    //+------------------------------------------------------------------+
    


    Класс объекта абстрактной заявки в стакане цен

    Как и для всех объектов библиотеки, имеющих свои наборы перечислений для определения констант свойств объекта, так же и для новых объектов классов объектов-заявок стакана цен, нам необходимо создать перечисления целочисленных, вещественных и строковых свойств объекта.

    В файле \MQL5\Include\DoEasy\Defines.mqh впишем перечисления свойств и параметров объекта-заявки стакана цен. Так как событийной модели работы с каждой из заявок в стакане мы делать не будем (в один момент времени стакан отображает текущее состояние всех заявок, а их изменение приводит к следующему его состоянию и обрабатывается на следующем срабатывании OnBookEvent()), то просто добавим константу, указывающую код следующего события после последнего кода события стакана цен — просто чтобы соблюсти идентичность констант всех объектов — привести их к одному виду:

    //+------------------------------------------------------------------+
    //| Данные для работы со стаканом цен                                |
    //+------------------------------------------------------------------+
    //+------------------------------------------------------------------+
    //| Список возможных событий стакана цен                             |
    //+------------------------------------------------------------------+
    #define MBOOK_ORD_EVENTS_NEXT_CODE  (SERIES_EVENTS_NEXT_CODE+1)   // Код следующего события после последнего кода события стакана цен
    //+------------------------------------------------------------------+
    

    Определим перечисление, в котором пропишем два возможных статуса одной заявки стакана цен — сторона Buy или Sell:

    //+------------------------------------------------------------------+
    //| Тип (статус) абстрактного стакана цен                            |
    //+------------------------------------------------------------------+
    enum ENUM_MBOOK_ORD_STATUS
      {
       MBOOK_ORD_STATUS_BUY,                              // Сторона Buy
       MBOOK_ORD_STATUS_SELL,                             // Сторона Sell
      };
    //+------------------------------------------------------------------+
    

    Фильтрация всех заявок в стакане цен по данным свойствам позволит быстро выбрать в список все заявки в стакане цен, принадлежащие либо спросу, либо предожению.

    Далее впишем перечисления целочисленных, вещественных и строковых свойств объекта-заявки стакана цен:

    //+------------------------------------------------------------------+
    //| Целочисленные свойства заявки стакана цен                        |
    //+------------------------------------------------------------------+
    enum ENUM_MBOOK_ORD_PROP_INTEGER
      {
       MBOOK_ORD_PROP_STATUS = 0,                         // Статус заявки
       MBOOK_ORD_PROP_TYPE,                               // Тип заявки
       MBOOK_ORD_PROP_VOLUME,                             // Объём заявки
      }; 
    #define MBOOK_ORD_PROP_INTEGER_TOTAL (3)              // Общее количество целочисленных свойств
    #define MBOOK_ORD_PROP_INTEGER_SKIP  (0)              // Количество неиспользуемых в сортировке целочисленных свойств стакана
    //+------------------------------------------------------------------+
    //| Вещественные свойства заявки стакана цен                         |
    //+------------------------------------------------------------------+
    enum ENUM_MBOOK_ORD_PROP_DOUBLE
      {
       MBOOK_ORD_PROP_PRICE = MBOOK_ORD_PROP_INTEGER_TOTAL, // Цена заявки
       MBOOK_ORD_PROP_VOLUME_REAL,                        // Объём заявки с повышенной точностью
      };
    #define MBOOK_ORD_PROP_DOUBLE_TOTAL  (2)              // Общее количество вещественных свойств
    #define MBOOK_ORD_PROP_DOUBLE_SKIP   (0)              // Количество неиспользуемых в сортировке вещественных свойств
    //+------------------------------------------------------------------+
    //| Строковые свойства заявки стакана цен                            |
    //+------------------------------------------------------------------+
    enum ENUM_MBOOK_ORD_PROP_STRING
      {
       MBOOK_ORD_PROP_SYMBOL = (MBOOK_ORD_PROP_INTEGER_TOTAL+MBOOK_ORD_PROP_DOUBLE_TOTAL), // Имя символа заявки
      };
    #define MBOOK_ORD_PROP_STRING_TOTAL  (1)              // Общее количество строковых свойств
    //+------------------------------------------------------------------+
    

    И соответственно созданным свойствам, напишем перечисление возможных критериев сортировки заявок в стакане цен:

    //+------------------------------------------------------------------+
    //| Возможные критерии сортировки заявок стакана цен                 |
    //+------------------------------------------------------------------+
    #define FIRST_MB_DBL_PROP  (MBOOK_ORD_PROP_INTEGER_TOTAL-MBOOK_ORD_PROP_INTEGER_SKIP)
    #define FIRST_MB_STR_PROP  (MBOOK_ORD_PROP_INTEGER_TOTAL-MBOOK_ORD_PROP_INTEGER_SKIP+MBOOK_ORD_PROP_DOUBLE_TOTAL-MBOOK_ORD_PROP_DOUBLE_SKIP)
    enum ENUM_SORT_MBOOK_ORD_MODE
      {
    //--- Сортировка по целочисленным свойствам
       SORT_BY_MBOOK_ORD_STATUS = 0,                      // Сортировать по статусу заявки
       SORT_BY_MBOOK_ORD_TYPE,                            // Сортировать по типу заявки
       SORT_BY_MBOOK_ORD_VOLUME,                          // Сортировать по объёму заявки
    //--- Сортировка по вещественным свойствам
       SORT_BY_MBOOK_ORD_PRICE = FIRST_MB_DBL_PROP,       // Сортировать по цене заявки
       SORT_BY_MBOOK_ORD_VOLUME_REAL,                     // Сортировать по объёму заявки с повышенной точностью
    //--- Сортировка по строковым свойствам
       SORT_BY_MBOOK_ORD_SYMBOL = FIRST_MB_STR_PROP,      // Сортировать по имени символа
      };
    //+------------------------------------------------------------------+
    

    Теперь можно создать класс объекта абстрактной заявки в стакане цен.

    В каталоге библиотеки \MQL5\Include\DoEasy\Objects\ создадим новую папку Book\, а в ней — новый файл MarketBookOrd.mqh класса CMarketBookOrd, унаследованного от базового объекта всех объектов библиотеки CBaseObj:

    //+------------------------------------------------------------------+
    //|                                                MarketBookOrd.mqh |
    //|                        Copyright 2021, MetaQuotes Software Corp. |
    //|                             https://mql5.com/ru/users/artmedia70 |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2021, MetaQuotes Software Corp."
    #property link      "https://mql5.com/ru/users/artmedia70"
    #property version   "1.00"
    #property strict    // Нужно для mql4
    //+------------------------------------------------------------------+
    //| Включаемые файлы                                                 |
    //+------------------------------------------------------------------+
    #include "..\..\Services\DELib.mqh"
    #include "..\..\Objects\BaseObj.mqh"
    //+------------------------------------------------------------------+
    //| Класс абстрактной заявки стакана цен                             |
    //+------------------------------------------------------------------+
    class CMarketBookOrd : public CBaseObj
      {
    private:
       int               m_digits;                                       // Количество знаков после запятой
       long              m_long_prop[MBOOK_ORD_PROP_INTEGER_TOTAL];      // Целочисленные свойства
       double            m_double_prop[MBOOK_ORD_PROP_DOUBLE_TOTAL];     // Вещественные свойства
       string            m_string_prop[MBOOK_ORD_PROP_STRING_TOTAL];     // Строковые свойства
    
    //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство
       int               IndexProp(ENUM_MBOOK_ORD_PROP_DOUBLE property)  const { return(int)property-MBOOK_ORD_PROP_INTEGER_TOTAL;                              }
       int               IndexProp(ENUM_MBOOK_ORD_PROP_STRING property)  const { return(int)property-MBOOK_ORD_PROP_INTEGER_TOTAL-MBOOK_ORD_PROP_DOUBLE_TOTAL;  }
    
    public:
    //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
       void              SetProperty(ENUM_MBOOK_ORD_PROP_INTEGER property,long value)   { this.m_long_prop[property]=value;                      }
       void              SetProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property,double value)  { this.m_double_prop[this.IndexProp(property)]=value;    }
       void              SetProperty(ENUM_MBOOK_ORD_PROP_STRING property,string value)  { this.m_string_prop[this.IndexProp(property)]=value;    }
    //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство объекта
       long              GetProperty(ENUM_MBOOK_ORD_PROP_INTEGER property)        const { return this.m_long_prop[property];                     }
       double            GetProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property)         const { return this.m_double_prop[this.IndexProp(property)];   }
       string            GetProperty(ENUM_MBOOK_ORD_PROP_STRING property)         const { return this.m_string_prop[this.IndexProp(property)];   }
    //--- Возвращает себя
       CMarketBookOrd   *GetObject(void)                                                { return &this;}
    
    //--- Возвращает флаг поддержания объектом данного свойства
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property)          { return true; }
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property)           { return true; }
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_STRING property)           { return true; }
    
    //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства
       string            GetPropertyDescription(ENUM_MBOOK_ORD_PROP_INTEGER property);
       string            GetPropertyDescription(ENUM_MBOOK_ORD_PROP_DOUBLE property);
       string            GetPropertyDescription(ENUM_MBOOK_ORD_PROP_STRING property);
    
    //--- Выводит в журнал описание свойств объекта (full_prop=true - все свойства, false - только поддерживаемые)
       void              Print(const bool full_prop=false);
    //--- Выводит в журнал краткое описание объекта
       virtual void      PrintShort(void);
    //--- Возвращает краткое наименование объекта
       virtual string    Header(void);
       
    //--- Сравнивает объекты CMarketBookOrd между собой по всем возможным свойствам (для сортировки списков по указанному свойству объекта-ордера)
       virtual int       Compare(const CObject *node,const int mode=0) const;
    //--- Сравнивает объекты CMarketBookOrd между собой по всем свойствам (для поиска равных объектов-запросов)
       bool              IsEqual(CMarketBookOrd* compared_req) const;
       
    //--- Конструктор по умолчанию
                         CMarketBookOrd(){;}
    protected:
    //--- Защищённый параметрический конструктор
                         CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol);
                         
    public:
    //+------------------------------------------------------------------+ 
    //|Методы упрощённого доступа к свойствам объекта-запроса стакана цен|
    //+------------------------------------------------------------------+
    //--- Возвращает (1) статус, (2) тип, (3) объём заявки
       ENUM_MBOOK_ORD_STATUS Status(void)        const { return (ENUM_MBOOK_ORD_STATUS)this.GetProperty(MBOOK_ORD_PROP_STATUS);   }
       ENUM_BOOK_TYPE    TypeOrd(void)           const { return (ENUM_BOOK_TYPE)this.GetProperty(MBOOK_ORD_PROP_TYPE);            }
       long              Volume(void)            const { return this.GetProperty(MBOOK_ORD_PROP_VOLUME);                          }
    //--- Возвращает (1) цену, (2) объём заявки с повышенной точностью
       double            Price(void)             const { return this.GetProperty(MBOOK_ORD_PROP_PRICE);                           }
       double            VolumeReal(void)        const { return this.GetProperty(MBOOK_ORD_PROP_VOLUME_REAL);                     }
    //--- Возвращает (1) символ заявки, (2) Digits символа
       string            Symbol(void)            const { return this.GetProperty(MBOOK_ORD_PROP_SYMBOL);                          }
       int               Digits()                const { return this.m_digits;                                                    }
       
    //--- Возвращает описание (1) типа (ENUM_BOOK_TYPE), (2) статуса (ENUM_MBOOK_ORD_STATUS) заявки
       virtual string    TypeDescription(void)   const { return this.StatusDescription();                                         }
       string            StatusDescription(void) const;
    
      };
    //+------------------------------------------------------------------+
    

    Состав класса абсолютно идентичен другим классам объектов библиотеки. Обсуждали мы их не один раз, и можно освежить память, почитав первые и последующие статьи, где всё подробно расписано.

    Рассмотрим реализацию методов класса.

    В закрытом параметрическом конструкторе класса устанавливаем все свойства объекта из переданной в конструктор структуры заявки из стакана цен:

    //+------------------------------------------------------------------+
    //| Параметрический конструктор                                      |
    //+------------------------------------------------------------------+
    CMarketBookOrd::CMarketBookOrd(const ENUM_MBOOK_ORD_STATUS status,const MqlBookInfo &book_info,const string symbol)
      {
    //--- Сохраняем Digits символа
       this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS);
    //--- Сохраняем целочисленные свойства объекта
       this.SetProperty(MBOOK_ORD_PROP_STATUS,status);
       this.SetProperty(MBOOK_ORD_PROP_TYPE,book_info.type);
       this.SetProperty(MBOOK_ORD_PROP_VOLUME,book_info.volume);
    //--- Сохраняем вещественные свойства объекта
       this.SetProperty(MBOOK_ORD_PROP_PRICE,book_info.price);
       this.SetProperty(MBOOK_ORD_PROP_VOLUME_REAL,book_info.volume_real);
    //--- Сохраняем дополнительные свойства объекта
       this.SetProperty(MBOOK_ORD_PROP_SYMBOL,(symbol==NULL || symbol=="" ? ::Symbol() : symbol));
      }
    //+------------------------------------------------------------------+
    

    Так же в конструктор передаётся статус заявки, который будем указывать в объектах-наследниках этого класса при создании нового объекта-заявки стакана цен.

    Метод для сравнения двух объектов CMarketBookOrd между собой по указанному свойству для определения равенства указанных свойств двух объектов:

    //+------------------------------------------------------------------+
    //| Сравнивает объекты CMarketBookOrd                                |
    //| между собой по указанному свойству                               |
    //+------------------------------------------------------------------+
    int CMarketBookOrd::Compare(const CObject *node,const int mode=0) const
      {
       const CMarketBookOrd *obj_compared=node;
    //--- сравнение целочисленных свойств двух объектов
       if(mode<MBOOK_ORD_PROP_INTEGER_TOTAL)
         {
          long value_compared=obj_compared.GetProperty((ENUM_MBOOK_ORD_PROP_INTEGER)mode);
          long value_current=this.GetProperty((ENUM_MBOOK_ORD_PROP_INTEGER)mode);
          return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
         }
    //--- сравнение вещественных свойств двух объектов
       else if(mode<MBOOK_ORD_PROP_DOUBLE_TOTAL+MBOOK_ORD_PROP_INTEGER_TOTAL)
         {
          double value_compared=obj_compared.GetProperty((ENUM_MBOOK_ORD_PROP_DOUBLE)mode);
          double value_current=this.GetProperty((ENUM_MBOOK_ORD_PROP_DOUBLE)mode);
          return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
         }
    //--- сравнение строковых свойств двух объектов
       else if(mode<MBOOK_ORD_PROP_DOUBLE_TOTAL+MBOOK_ORD_PROP_INTEGER_TOTAL+MBOOK_ORD_PROP_STRING_TOTAL)
         {
          string value_compared=obj_compared.GetProperty((ENUM_MBOOK_ORD_PROP_STRING)mode);
          string value_current=this.GetProperty((ENUM_MBOOK_ORD_PROP_STRING)mode);
          return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0);
         }
       return 0;
      }
    //+------------------------------------------------------------------+
    

    В метод передаётся объект, свойство которого необходимо сравнить с тем же свойством текущего объекта. Если указанное свойство сравниваемого объекта имеет значение меньше, чем у текущего, то возвращается -1, если больше — то +1, если свойства равны, то возвращается 0.

    Метод для сравнения двух объектов CMarketBookOrd между собой по всем свойствам. Позволяет определить полную идентичность двух сравниваемых объектов:

    //+------------------------------------------------------------------+
    //| Сравнивает объекты CMarketBookOrd между собой по всем свойствам  |
    //+------------------------------------------------------------------+
    bool CMarketBookOrd::IsEqual(CMarketBookOrd *compared_obj) const
      {
       int beg=0, end=MBOOK_ORD_PROP_INTEGER_TOTAL;
       for(int i=beg; i<end; i++)
         {
          ENUM_MBOOK_ORD_PROP_INTEGER prop=(ENUM_MBOOK_ORD_PROP_INTEGER)i;
          if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
         }
       beg=end; end+=MBOOK_ORD_PROP_DOUBLE_TOTAL;
       for(int i=beg; i<end; i++)
         {
          ENUM_MBOOK_ORD_PROP_DOUBLE prop=(ENUM_MBOOK_ORD_PROP_DOUBLE)i;
          if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
         }
       beg=end; end+=MBOOK_ORD_PROP_STRING_TOTAL;
       for(int i=beg; i<end; i++)
         {
          ENUM_MBOOK_ORD_PROP_STRING prop=(ENUM_MBOOK_ORD_PROP_STRING)i;
          if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
         }
       return true;
      }
    //+------------------------------------------------------------------+
    

    Здесь поочерёдно сравниваются каждое очередное свойство двух объектов, и если свойства не равны, то возвращается false. После проверки абсолютно всех свойств двух объектов на равенство, и если не было возвращено false, то возвращается true — оба объекта полностью идентичны.

    Метод, выводящий в журнал все свойства объекта:

    //+------------------------------------------------------------------+
    //| Выводит в журнал свойства объекта                                |
    //+------------------------------------------------------------------+
    void CMarketBookOrd::Print(const bool full_prop=false)
      {
       ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.Header(),") =============");
       int beg=0, end=MBOOK_ORD_PROP_INTEGER_TOTAL;
       for(int i=beg; i<end; i++)
         {
          ENUM_MBOOK_ORD_PROP_INTEGER prop=(ENUM_MBOOK_ORD_PROP_INTEGER)i;
          if(!full_prop && !this.SupportProperty(prop)) continue;
          ::Print(this.GetPropertyDescription(prop));
         }
       ::Print("------");
       beg=end; end+=MBOOK_ORD_PROP_DOUBLE_TOTAL;
       for(int i=beg; i<end; i++)
         {
          ENUM_MBOOK_ORD_PROP_DOUBLE prop=(ENUM_MBOOK_ORD_PROP_DOUBLE)i;
          if(!full_prop && !this.SupportProperty(prop)) continue;
          ::Print(this.GetPropertyDescription(prop));
         }
       ::Print("------");
       beg=end; end+=MBOOK_ORD_PROP_STRING_TOTAL;
       for(int i=beg; i<end; i++)
         {
          ENUM_MBOOK_ORD_PROP_STRING prop=(ENUM_MBOOK_ORD_PROP_STRING)i;
          if(!full_prop && !this.SupportProperty(prop)) continue;
          ::Print(this.GetPropertyDescription(prop));
         }
       ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.Header(),") =============\n");
      }
    //+------------------------------------------------------------------+
    

    В трёх циклах по целочисленным, вещественным и строковым свойствам объекта выводятся строковые описания каждого последующего свойства в журнал.

    Методы, возвращающие описания указанного целочисленного, вещественного и строкового свойств объекта:

    //+------------------------------------------------------------------+
    //| Возвращает описание целочисленного свойства объекта              |
    //+------------------------------------------------------------------+
    string CMarketBookOrd::GetPropertyDescription(ENUM_MBOOK_ORD_PROP_INTEGER property)
      {
       return
         (
          property==MBOOK_ORD_PROP_STATUS        ?  CMessage::Text(MSG_ORD_STATUS)+
             (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
              ": "+this.StatusDescription()
             )  :
          property==MBOOK_ORD_PROP_TYPE          ?  CMessage::Text(MSG_ORD_TYPE)+
             (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
              ": "+this.TypeDescription()
             )  :
          property==MBOOK_ORD_PROP_VOLUME        ?  CMessage::Text(MSG_MBOOK_ORD_VOLUME)+
             (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
              ": "+(string)this.GetProperty(property)
             )  :
          ""
         );
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание вещественного свойства объекта               |
    //+------------------------------------------------------------------+
    string CMarketBookOrd::GetPropertyDescription(ENUM_MBOOK_ORD_PROP_DOUBLE property)
      {
       int dg=(this.m_digits>0 ? this.m_digits : 1);
       return
         (
          property==MBOOK_ORD_PROP_PRICE         ?  CMessage::Text(MSG_LIB_TEXT_REQUEST_PRICE)+
             (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
              ": "+::DoubleToString(this.GetProperty(property),dg)
             )  :
          property==MBOOK_ORD_PROP_VOLUME_REAL   ?  CMessage::Text(MSG_MBOOK_ORD_VOLUME_REAL)+
             (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
              ": "+::DoubleToString(this.GetProperty(property),dg)
             )  :
          ""
         );
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание строкового свойства объекта                  |
    //+------------------------------------------------------------------+
    string CMarketBookOrd::GetPropertyDescription(ENUM_MBOOK_ORD_PROP_STRING property)
      {
       return(property==MBOOK_ORD_PROP_SYMBOL ? CMessage::Text(MSG_LIB_PROP_SYMBOL)+": \""+this.GetProperty(property)+"\"" : "");
      }
    //+------------------------------------------------------------------+
    

    В каждый из методов передаётся свойство, описание которого необходимо вернуть, и в зависимости от переданного в метод свойства, создаётся строка, которая в итоге и возвращается из метода.

    Метод, возвращающий краткое наименование объекта:

    //+------------------------------------------------------------------+
    //| Возвращает краткое наименование объекта                          |
    //+------------------------------------------------------------------+
    string CMarketBookOrd::Header(void)
      {
       return this.TypeDescription()+" \""+this.Symbol()+"\"";
      }
    //+------------------------------------------------------------------+
    

    Метод возвращает строку, состоящую из описания типа заявки и её символа.

    Метод, выводящий в журнал краткое описание объекта:

    //+------------------------------------------------------------------+
    //| Выводит в журнал краткое описание объекта                        |
    //+------------------------------------------------------------------+
    void CMarketBookOrd::PrintShort(void)
      {
       ::Print(this.Header());
      }
    //+------------------------------------------------------------------+
    

    Метод просто распечатывает в журнал строку, созданную предыдущим методом.

    Метод, возвращающий описание статуса заявки в стакане цен:

    //+------------------------------------------------------------------+
    //| Возвращает описание статуса заявки в стакане цен                 |
    //+------------------------------------------------------------------+
    string CMarketBookOrd::StatusDescription(void) const
      {
       return
         (
          Status()==MBOOK_ORD_STATUS_SELL  ?  CMessage::Text(MSG_MBOOK_ORD_STATUS_SELL) :
          Status()==MBOOK_ORD_STATUS_BUY   ?  CMessage::Text(MSG_MBOOK_ORD_STATUS_BUY)  :
          ""
         );
      }
    //+------------------------------------------------------------------+
    

    В зависимости от свойства "статус" заявки, возвращается строка с описанием этого статуса.

    Это весь класс объекта-заявки в стакане цен.

    Теперь нам необходимо создать четыре класса-наследника этого объекта абстрактной заявки. Именно классы-наследники и будут использоваться для создания новых объектов-заявок из стакана цен. Просто в конструкторе класса-наследника, в его списке инициализации будет указываться статус создаваемого объекта-заявки в зависимости от типа этой заявки.


    Классы-наследники объекта абстрактной заявки

    В папке \MQL5\Include\DoEasy\Objects\Book\ создадим новый файл MarketBookBuy.mqh класса CMarketBookBuy. Родительским классом должен быть только что созданный нами класс абстрактной заявки CMarketBookOrd:

    //+------------------------------------------------------------------+
    //|                                                MarketBookBuy.mqh |
    //|                        Copyright 2021, MetaQuotes Software Corp. |
    //|                             https://mql5.com/ru/users/artmedia70 |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2021, MetaQuotes Software Corp."
    #property link      "https://mql5.com/ru/users/artmedia70"
    #property version   "1.00"
    //+------------------------------------------------------------------+
    //| Включаемые файлы                                                 |
    //+------------------------------------------------------------------+
    #include "MarketBookOrd.mqh"
    //+------------------------------------------------------------------+
    //| Заявка на покупку в стакане                                      |
    //+------------------------------------------------------------------+
    class CMarketBookBuy : public CMarketBookOrd
      {
    private:
    
    public:
       //--- Конструктор
                         CMarketBookBuy(const string symbol,const MqlBookInfo &book_info) :
                            CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) {}
       //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
    //--- Возвращает краткое наименование объекта
       virtual string    Header(void);
    //--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
       virtual string    TypeDescription(void);
      };
    //+------------------------------------------------------------------+
    //| Возвращает истину, если ордер поддерживает переданное            |
    //| целочисленное свойство, возвращает ложь в противном случае       |
    //+------------------------------------------------------------------+
    bool CMarketBookBuy::SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property)
      {
       return true;
      }
    //+------------------------------------------------------------------+
    //| Возвращает истину, если ордер поддерживает переданное            |
    //| вещественное свойство, возвращает ложь в противном случае        |
    //+------------------------------------------------------------------+
    bool CMarketBookBuy::SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property)
      {
       return true;
      }
    //+------------------------------------------------------------------+
    //| Возвращает краткое наименование объекта                          |
    //+------------------------------------------------------------------+
    string CMarketBookBuy::Header(void)
      {
       return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY)+" \""+this.Symbol()+
              "\": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]";
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание типа заявки                                  |
    //+------------------------------------------------------------------+
    string CMarketBookBuy::TypeDescription(void)
      {
       return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY);
      }
    //+------------------------------------------------------------------+
    

    При создании нового объекта-заявки стакана цен, указываем статус "Сторона Buy" в конструкторе класса-родителя.

    В виртуальных методах, возвращающих флаг поддержания целочисленного и вещественного свойства возвращаем true — каждое свойство поддерживается объектом.

    В виртуальном методе, возвращающем краткое наименование объекта-заявки стакана цен, возвращаем строку в формате

    Type "Symbol": Price [VolumeReal]

    Например, так:

    Заявка на покупку "EURUSD": 1.20123 [10.00]

    В виртуальном методе, возвращающем описание типа объекта-заявки стакана цен, возвращаем строку "Заявка на покупку"

    Остальные три класса-наследника базового класса абстрактной заявки стакана цен идентичны рассмотренному, за исключением статуса заявки — в каждом конструкторе класса указывается свой статус, соответствующий описываемому объекту-заявке, и их виртуальных методов, возвращающих строки, соответствующие типу заявки стакана цен, описываемой каждым из объектов. Все эти классы расположены в той же папке, что и вышерассмотренный класс. Просто приведём их листинги для самостоятельного разбора и сравнения их виртуальных методов.

    MarketBookBuyMarket.mqh:

    //+------------------------------------------------------------------+
    //|                                          MarketBookBuyMarket.mqh |
    //|                        Copyright 2021, MetaQuotes Software Corp. |
    //|                             https://mql5.com/ru/users/artmedia70 |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2021, MetaQuotes Software Corp."
    #property link      "https://mql5.com/ru/users/artmedia70"
    #property version   "1.00"
    //+------------------------------------------------------------------+
    //| Включаемые файлы                                                 |
    //+------------------------------------------------------------------+
    #include "MarketBookOrd.mqh"
    //+------------------------------------------------------------------+
    //| Заявка на покупку по рыночной цене в стакане                     |
    //+------------------------------------------------------------------+
    class CMarketBookBuyMarket : public CMarketBookOrd
      {
    private:
    
    public:
       //--- Конструктор
                         CMarketBookBuyMarket(const string symbol,const MqlBookInfo &book_info) :
                            CMarketBookOrd(MBOOK_ORD_STATUS_BUY,book_info,symbol) {}
       //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
    //--- Возвращает краткое наименование объекта
       virtual string    Header(void);
    //--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
       virtual string    TypeDescription(void);
      };
    //+------------------------------------------------------------------+
    //| Возвращает истину, если ордер поддерживает переданное            |
    //| целочисленное свойство, возвращает ложь в противном случае       |
    //+------------------------------------------------------------------+
    bool CMarketBookBuyMarket::SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property)
      {
       return true;
      }
    //+------------------------------------------------------------------+
    //| Возвращает истину, если ордер поддерживает переданное            |
    //| вещественное свойство, возвращает ложь в противном случае        |
    //+------------------------------------------------------------------+
    bool CMarketBookBuyMarket::SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property)
      {
       return true;
      }
    //+------------------------------------------------------------------+
    //| Возвращает краткое наименование объекта                          |
    //+------------------------------------------------------------------+
    string CMarketBookBuyMarket::Header(void)
      {
       return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY_MARKET)+" \""+this.Symbol()+
              "\": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]";
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание типа заявки                                  |
    //+------------------------------------------------------------------+
    string CMarketBookBuyMarket::TypeDescription(void)
      {
       return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY_MARKET);
      }
    //+------------------------------------------------------------------+
    

    MarketBookSell.mqh:

    //+------------------------------------------------------------------+
    //|                                               MarketBookSell.mqh |
    //|                        Copyright 2021, MetaQuotes Software Corp. |
    //|                             https://mql5.com/ru/users/artmedia70 |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2021, MetaQuotes Software Corp."
    #property link      "https://mql5.com/ru/users/artmedia70"
    #property version   "1.00"
    //+------------------------------------------------------------------+
    //| Включаемые файлы                                                 |
    //+------------------------------------------------------------------+
    #include "MarketBookOrd.mqh"
    //+------------------------------------------------------------------+
    //| Заявка на продажу в стакане                                      |
    //+------------------------------------------------------------------+
    class CMarketBookSell : public CMarketBookOrd
      {
    private:
    
    public:
       //--- Конструктор
                         CMarketBookSell(const string symbol,const MqlBookInfo &book_info) :
                            CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) {}
       //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
    //--- Возвращает краткое наименование объекта
       virtual string    Header(void);
    //--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
       virtual string    TypeDescription(void);
      };
    //+------------------------------------------------------------------+
    //| Возвращает истину, если ордер поддерживает переданное            |
    //| целочисленное свойство, возвращает ложь в противном случае       |
    //+------------------------------------------------------------------+
    bool CMarketBookSell::SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property)
      {
       return true;
      }
    //+------------------------------------------------------------------+
    //| Возвращает истину, если ордер поддерживает переданное            |
    //| вещественное свойство, возвращает ложь в противном случае        |
    //+------------------------------------------------------------------+
    bool CMarketBookSell::SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property)
      {
       return true;
      }
    //+------------------------------------------------------------------+
    //| Возвращает краткое наименование объекта                          |
    //+------------------------------------------------------------------+
    string CMarketBookSell::Header(void)
      {
       return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL)+" \""+this.Symbol()+
              "\": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]";
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание типа заявки                                  |
    //+------------------------------------------------------------------+
    string CMarketBookSell::TypeDescription(void)
      {
       return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL);
      }
    //+------------------------------------------------------------------+
    

    MarketBookSellMarket.mqh:

    //+------------------------------------------------------------------+
    //|                                         MarketBookSellMarket.mqh |
    //|                        Copyright 2021, MetaQuotes Software Corp. |
    //|                             https://mql5.com/ru/users/artmedia70 |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2021, MetaQuotes Software Corp."
    #property link      "https://mql5.com/ru/users/artmedia70"
    #property version   "1.00"
    //+------------------------------------------------------------------+
    //| Включаемые файлы                                                 |
    //+------------------------------------------------------------------+
    #include "MarketBookOrd.mqh"
    //+------------------------------------------------------------------+
    //| Заявка на продажу по рыночной цене в стакане                     |
    //+------------------------------------------------------------------+
    class CMarketBookSellMarket : public CMarketBookOrd
      {
    private:
    
    public:
       //--- Конструктор
                         CMarketBookSellMarket(const string symbol,const MqlBookInfo &book_info) :
                            CMarketBookOrd(MBOOK_ORD_STATUS_SELL,book_info,symbol) {}
       //--- Поддерживаемые свойства ордера (1) вещественные, (2) целочисленные
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property);
       virtual bool      SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property);
    //--- Возвращает краткое наименование объекта
       virtual string    Header(void);
    //--- Возвращает описание типа (ENUM_BOOK_TYPE) заявки
       virtual string    TypeDescription(void);
      };
    //+------------------------------------------------------------------+
    //| Возвращает истину, если ордер поддерживает переданное            |
    //| целочисленное свойство, возвращает ложь в противном случае       |
    //+------------------------------------------------------------------+
    bool CMarketBookSellMarket::SupportProperty(ENUM_MBOOK_ORD_PROP_INTEGER property)
      {
       return true;
      }
    //+------------------------------------------------------------------+
    //| Возвращает истину, если ордер поддерживает переданное            |
    //| вещественное свойство, возвращает ложь в противном случае        |
    //+------------------------------------------------------------------+
    bool CMarketBookSellMarket::SupportProperty(ENUM_MBOOK_ORD_PROP_DOUBLE property)
      {
       return true;
      }
    //+------------------------------------------------------------------+
    //| Возвращает краткое наименование объекта                          |
    //+------------------------------------------------------------------+
    string CMarketBookSellMarket::Header(void)
      {
       return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL_MARKET)+" \""+this.Symbol()+
              "\": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]";
      }
    //+------------------------------------------------------------------+
    //| Возвращает описание типа заявки                                  |
    //+------------------------------------------------------------------+
    string CMarketBookSellMarket::TypeDescription(void)
      {
       return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL_MARKET);
      }
    //+------------------------------------------------------------------+
    

    Это всё, что мы сегодня хотели сделать.


    Тестирование

    Для тестирования возьмём советник из прошлой статьи и
    сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part63\ под новым именем TestDoEasyPart63.mq5.

    Что мы сделаем: при запуске советника у нас будет осуществлена подписка на стаканы цен символов, установленных в настройках для работы. Все события со стаканом цен регистрируются в обработчике OnBookEvent(). Соответственно, в этом обработчике проверим, что событие произошло на текущем символе, получим снимок стакана цен и сохраним все имеющиеся заявки в сортированный по значению цены список. Затем выведем в комментарии на графике самую первую и самую последнюю заявки из этого списка. Таким образом, мы отобразим две крайние заявки стакана цен — одну на продажу, и одну на покупку. В журнал выведем список всех полученных заявок стакана цен на самом первом срабатывании OnBookEvent().

    Чтобы советник мог видеть вновь созданные классы, подключим их к файлу советника (пока к ним нет доступа из основного объекта библиотеки CEngine):

    //+------------------------------------------------------------------+
    //|                                             TestDoEasyPart63.mq5 |
    //|                        Copyright 2021, MetaQuotes Software Corp. |
    //|                             https://mql5.com/ru/users/artmedia70 |
    //+------------------------------------------------------------------+
    #property copyright "Copyright 2021, MetaQuotes Software Corp."
    #property link      "https://mql5.com/ru/users/artmedia70"
    #property version   "1.00"
    //--- includes
    #include <DoEasy\Engine.mqh>
    #include <DoEasy\Objects\Book\MarketBookBuy.mqh>
    #include <DoEasy\Objects\Book\MarketBookSell.mqh>
    #include <DoEasy\Objects\Book\MarketBookBuyMarket.mqh>
    #include <DoEasy\Objects\Book\MarketBookSellMarket.mqh>
    //--- enums
    

    Теперь нам нужно создать в советнике обработчик OnBookEvent() и прописать в нём обработку события стакана цен:

    //+------------------------------------------------------------------+
    //| OnBookEvent function                                             |
    //+------------------------------------------------------------------+
    void OnBookEvent(const string& symbol)
      {
       static bool first=true;
       //--- Получаем объект-символ
       CSymbol *sym=engine.GetSymbolCurrent();
       //--- Если объект-символ не получили, или по нему нет подписки на стакан цен - уходим
       if(sym==NULL || !sym.BookdepthSubscription()) return;
       //--- создаём список для хранения объектов-заявок стакана
       CArrayObj *list=new CArrayObj();
       if(list==NULL) return;
       //--- Работаем по текущему символу
       if(symbol==sym.Name())
         {
          //--- Объявляем массив структур стакана цен
          MqlBookInfo book_array[];
          //--- Получаем записи стакана цен в массив структур
          if(!MarketBookGet(sym.Name(),book_array))
             return;
          //--- очищаем список
          list.Clear();
          //--- В цикле по массиву структур
          int total=ArraySize(book_array);
          for(int i=0;i<total;i++)
            {
             //--- Создаём объекты-заявки текущего снимка стакана цен в зависимости от типа заявки
             CMarketBookOrd *mbook_ord=NULL;
             switch(book_array[i].type)
               {
                case BOOK_TYPE_BUY         : mbook_ord=new CMarketBookBuy(sym.Name(),book_array[i]);         break;
                case BOOK_TYPE_SELL        : mbook_ord=new CMarketBookSell(sym.Name(),book_array[i]);        break;
                case BOOK_TYPE_BUY_MARKET  : mbook_ord=new CMarketBookBuyMarket(sym.Name(),book_array[i]);   break;
                case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(sym.Name(),book_array[i]);  break;
                default: break;
               }
             if(mbook_ord==NULL)
                continue;
             //--- Ставим списку флаг сортированного списка (по значению цены) и добавляем в него текущий объект-заявку
             list.Sort(SORT_BY_MBOOK_ORD_PRICE);
             if(!list.InsertSort(mbook_ord))
                delete mbook_ord;
            }
          //--- Получаем самый первый и самый последний объекты-заявки стакана цен из списка
          CMarketBookOrd *ord_0=list.At(0);
          CMarketBookOrd *ord_N=list.At(list.Total()-1);
          if(ord_0==NULL || ord_N==NULL) return;
          //--- Выводим на график в комментарии размер текущего снимка стакана цен, 
          //--- максимальное количество показываемых заявок в стакане для символа и
          //--- максимальную и минимальную по цене заявки текущего снимка стакана цен
          Comment
            (
             DFUN,sym.Name(),": ",TimeMSCtoString(sym.Time()),", array total=",total,", book size=",sym.TicksBookdepth(),", list.Total: ",list.Total(),"\n",
             "Max: ",ord_N.Header(),"\nMin: ",ord_0.Header()
            );
          //--- Выведем в журнал первый снимок стакана цен
          if(first)
            {
             for(int i=list.Total()-1;i>WRONG_VALUE;i--)
               {
                CMarketBookOrd *ord=list.At(i);
                ord.PrintShort();
               }
             first=false;
            }
         }
       //--- Удаляем созданный список
       delete list;
      }
    //+------------------------------------------------------------------+
    

    Здесь всё подробно расписано в комментариях к коду и, надеюсь, всё понятно. В любом случае можно задать уточняющие вопросы в обсуждении к статье.

    Скомпилируем советник и запустим его на графике символа, предварительно выбрав в настройках использование двух указанных символов и текущего таймфрейма:


    После запуска советника и прихода первого события изменения стакана цен, на график будут выведены в комментариях параметры списка текущего снимка стакана цен и две заявки — максимальная на продажу и минимальная на покупку:


    В журнал будет выведен список всех заявок текущего снимка стакана цен:

    Осуществлена подписка на стакан цен  AUDUSD
    Осуществлена подписка на стакан цен  EURUSD
    Время инициализации библиотеки: 00:00:11.391
    Заявка на продажу "EURUSD": 1.20250 [250.00]
    Заявка на продажу "EURUSD": 1.20245 [100.00]
    Заявка на продажу "EURUSD": 1.20244 [50.00]
    Заявка на продажу "EURUSD": 1.20242 [36.00]
    Заявка на покупку "EURUSD": 1.20240 [16.00]
    Заявка на покупку "EURUSD": 1.20239 [20.00]
    Заявка на покупку "EURUSD": 1.20238 [50.00]
    Заявка на покупку "EURUSD": 1.20236 [100.00]
    Заявка на покупку "EURUSD": 1.20232 [250.00]
    


    Что дальше

    В следующей статье продолжим создание функционала для работы со стаканом цен.

    Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для MQL5. Их можно скачать и протестировать всё самостоятельно.
    Хочу отметить, что классы для работы со стаканом цен находятся в процессе разработки, и поэтому использование этих классов в своих программах на данном этапе крайне нежелательно.
    При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.

    К содержанию

    *Статьи этой серии:

    Работа с ценами в библиотеке DoEasy (Часть 59): Объект для хранения данных одного тика
    Работа с ценами в библиотеке DoEasy (Часть 60): Список-серия тиковых данных символа
    Работа с ценами в библиотеке DoEasy (Часть 61): Коллекция тиковых серий символов
    Работа с ценами в библиотеке DoEasy (Часть 62): Реалтайм-обновление тиковых серий, подготовка к работе со стаканом цен

    Прикрепленные файлы |
    MQL5.zip (3926.52 KB)
    Последние комментарии | Перейти к обсуждению на форуме трейдеров (2)
    Denis Kirichenko
    Denis Kirichenko | 5 фев 2021 в 11:18

    Не судите строго. Эпиграмма. 


    Писали все мы понемногу, 

    О чём-нибудь и как-нибудь.

    Лишь он один ушёл в дорогу,

    Успев нам хитро подмигнуть.


    На той дороге кода много,

    А строчек просто всех не счесть.

    И если бы не мать-природа, 

    То через них не перелезть!


    Моё перо само просилось,

    Сказать ещё о чём-нибудь.

    Но он один ушёл в дорогу,

    С которой всем нам не свернуть.

                                                          Февраль, 2021

    Artyom Trishkin
    Artyom Trishkin | 5 фев 2021 в 14:12
    ???
    Нейросети — это просто (Часть 11): Вариации на тему GPT Нейросети — это просто (Часть 11): Вариации на тему GPT
    Сегодня, наверное, одной из самых передовых языковых моделей нейросетей является GPT-3, которая в максимальном своем варианте содержит 175 млрд. параметров. Конечно, мы не будем создавать подобного монстра в домашних условиях. Но давайте посмотрим, какие архитектурные решения мы можем использовать в своей работе и какие это нам даст преимущества.
    Работа с ценами в библиотеке DoEasy (Часть 62): Реалтайм-обновление тиковых серий, подготовка к работе со стаканом цен Работа с ценами в библиотеке DoEasy (Часть 62): Реалтайм-обновление тиковых серий, подготовка к работе со стаканом цен
    В статье сделаем реалтайм-обновление коллекции тиковых данных и подготовим класс объекта-символа для работы со стаканом цен, работу над которым начнём со следующей статьи.
    Работа с ценами в библиотеке DoEasy (Часть 64): Стакан цен, классы объекта-снимка и объекта-серии снимков стакана цен Работа с ценами в библиотеке DoEasy (Часть 64): Стакан цен, классы объекта-снимка и объекта-серии снимков стакана цен
    В статье создадим два класса - класс объекта-снимка стакана цен и класс объекта-серии снимков стакана цен и протестируем создание серии данных стакана цен.
    Практическое применение нейросетей в трейдинге (Часть 2). Компьютерное зрение Практическое применение нейросетей в трейдинге (Часть 2). Компьютерное зрение
    Применение компьютерного зрения позволит обучать нейронные сети на визуальном представлении ценового графика и индикаторов. Данный метод позволит нам более свободно оперировать всем комплексом технических индикаторов, так как не требует их цифровой подачи в нейронную сеть.