
Работа с ценами в библиотеке DoEasy (Часть 64): Стакан цен, классы объекта-снимка и объекта-серии снимков стакана цен
Содержание
- Концепция
- Доработка классов библиотеки
- Класс объекта-снимка стакана цен
- Класс объекта-серии снимков стакана цен
- Тестирование
- Что дальше
Концепция
В прошлой статье мы создали класс объекта абстрактной заявки стакана цен и классы-наследники этого объекта. Совокупность этих объектов составляет один снимок стакана цен, получаемый за один вызов функции MarketBookGet() в момент срабатывания обработчика OnBookEvent(). Данные, получаемые функцией MarketBookGet(), записываются в массив структур MqlBookInfo, и на основании полученных данных мы можем создать объект-снимок стакана цен, в котором будут храниться все заявки стакана цен, полученные в массив структур MqlBookInfo. Иными словами — данные в озвученном массиве структур и будут являться снимком стакана цен, в котором каждый член структуры будет представлен одним объектом-заявкой стакана цен. Каждое очередное срабатывание обработчика OnBookEvent() будет приводить к созданию очередного снимка стакана цен, которые в свою очередь мы будем заносить в объект класса серии снимков стакана цен.
Списком для хранения объектов-снимков стакана цен будет являться класс динамического массива указателей на экземпляры класса CObject и его наследников стандартной библиотеки. Размер этого списка будем ограничивать заданным количеством объектов. По умолчанию размер этого списка не будет превышать 200 000 объектов-снимков стакана цен, что должно охватить примерно один-два торговых дня. Такие списки-серии снимков стакана цен будут создаваться для каждого используемого в программе символа. В итоге, каждый такой список будет храниться в коллекции данных стакана цен.
Сегодня мы создадим два класса — класс объекта-снимка стакана цен одного символа и класс-серию снимков стакана цен одного символа. В следующей статье создадим и протестируем класс-коллекцию таких серий-снимков стакана цен.
Доработка классов библиотеки
В файл \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений библиотеки:
//--- 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, // Заявка на покупку по рыночной цене //--- CMarketBookSnapshot MSG_MBOOK_SNAP_TEXT_SNAPSHOT, // Снимок стакана цен //--- CMBookSeries MSG_MBOOK_SERIES_TEXT_MBOOKSERIES, // Серия снимков стакана цен }; //+------------------------------------------------------------------+
и текстовые сообщения, соответствующие вновь добавленным индексам:
//--- 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"}, //--- CMarketBookSnapshot {"Снимок стакана цен","Depth of Market Snapshot"}, //--- CMBookSeries {"Серия снимков стакана цен","Series of shots of the Depth of Market"}, }; //+---------------------------------------------------------------------+
Для того чтобы мы могли указывать критерий сортировки по времени при поиске нужных объектов-снимков стакана цен в списке-серии, нам необходимо будет добавить новое свойство объекту-заявки стакана цен — время получения снимка в миллисекундах. У самой заявки такого свойства нет, но время получения снимка стакана цен мы можем отследить, и чтобы не вводить новые перечисления для списка-серии снимков стакана цен, где будет лишь одно целочисленное свойство — время получения снимка в миллисекундах, мы добавим это свойство к свойствам объекта-заявки стакана цен. Каждой заявке в одном объекте-снимке будет присваиваться время получения этого снимка, а для поиска и сортировки в списке-серии мы будем использовать эту, вновь добавленную константу.
В файле \MQL5\Include\DoEasy\Defines.mqh пропишем параметры серий снимков стакана цен, нужные нам для указания количества дней данных и максимально-возможного количества снимков в списке:
//--- Параметры тиковых серий #define TICKSERIES_DEFAULT_DAYS_COUNT (1) // Требуемое количество дней для тиковых данных в сериях по умолчанию #define TICKSERIES_MAX_DATA_TOTAL (200000) // Максимальное количество хранимых тиковых данных одного символа //--- Параметры серий снимков стакана цен #define MBOOKSERIES_DEFAULT_DAYS_COUNT (1) // Требуемое количество дней для снимков стакана цен в сериях по умолчанию #define MBOOKSERIES_MAX_DATA_TOTAL (200000) // Максимальное количество хранимых снимков стакана цен одного символа //+------------------------------------------------------------------+
Первый параметр (количество дней) пока никак использовать не будем — в последующем попробуем привязать эти данные к количеству дней тиковых данных, а сейчас будем использовать лишь второй параметр — максимально-возможное количество данных снимков стакана цен.
В этом же файле впишем новое целочисленное свойство объекта-заявки стакана цен — время в миллисекундах,
и увеличим количество целочисленных свойств объекта до 4:
//+------------------------------------------------------------------+ //| Целочисленные свойства заявки стакана цен | //+------------------------------------------------------------------+ enum ENUM_MBOOK_ORD_PROP_INTEGER { MBOOK_ORD_PROP_STATUS = 0, // Статус заявки MBOOK_ORD_PROP_TYPE, // Тип заявки MBOOK_ORD_PROP_VOLUME, // Объём заявки MBOOK_ORD_PROP_TIME_MSC, // Время снятия снимка стакана цен в миллисекундах }; #define MBOOK_ORD_PROP_INTEGER_TOTAL (4) // Общее количество целочисленных свойств #define MBOOK_ORD_PROP_INTEGER_SKIP (0) // Количество неиспользуемых в сортировке целочисленных свойств стакана //+------------------------------------------------------------------+
Так как мы добавили новое целочисленное свойство, то нам нужно, соответственно, добавить и новый критерий сортировки по целочисленным свойствам:
//+------------------------------------------------------------------+ //| Возможные критерии сортировки заявок стакана цен | //+------------------------------------------------------------------+ #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_TIME_MSC, // Сортировать по времени снятия снимка стакана цен в миллисекундах //--- Сортировка по вещественным свойствам SORT_BY_MBOOK_ORD_PRICE = FIRST_MB_DBL_PROP, // Сортировать по цене заявки SORT_BY_MBOOK_ORD_VOLUME_REAL, // Сортировать по объёму заявки с повышенной точностью //--- Сортировка по строковым свойствам SORT_BY_MBOOK_ORD_SYMBOL = FIRST_MB_STR_PROP, // Сортировать по имени символа }; //+------------------------------------------------------------------+
Именно эту константу и будем указывать как параметр, по которому необходимо сортировать объекты-снимки стакана цен в создаваемом сейчас новом классе объекта-серии снимков стакана цен.
И здесь мы приходим к необходимости создания методов для поиска и сортировки объектов-заявок стакана цен в файле класса CSelect, хранящегося по адресу \MQL5\Include\DoEasy\Services\Select.mqh. Подробное описание которого было написано нами в третьей статье. Сейчас же мы просто опишем все необходимые доработки этого класса для организации поиска и сортировки по свойствам объектов-заявок стакана цен.
Подключим к файлу класс объекта-абстрактной заявки стакана цен:
//+------------------------------------------------------------------+ //| Select.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include <Arrays\ArrayObj.mqh> #include "..\Objects\Orders\Order.mqh" #include "..\Objects\Events\Event.mqh" #include "..\Objects\Accounts\Account.mqh" #include "..\Objects\Symbols\Symbol.mqh" #include "..\Objects\PendRequest\PendRequest.mqh" #include "..\Objects\Series\SeriesDE.mqh" #include "..\Objects\Indicators\Buffer.mqh" #include "..\Objects\Indicators\IndicatorDE.mqh" #include "..\Objects\Indicators\DataInd.mqh" #include "..\Objects\Ticks\DataTick.mqh" #include "..\Objects\Book\MarketBookOrd.mqh" //+------------------------------------------------------------------+
В конце тела класса объявим все необходимые методы:
//+------------------------------------------------------------------+ //| Методы работы с данными стакана цен | //+------------------------------------------------------------------+ //--- Возвращает список данных стакана цен, где одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс данных стакана цен в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства static int FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property); static int FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property); static int FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property); //--- Возвращает индекс данных стакана цен в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства static int FindMBookMin(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property); static int FindMBookMin(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property); static int FindMBookMin(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
А за пределами тела класса напишем их реализацию:
//+------------------------------------------------------------------+ //| Методы работы с данными стакана цен | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Возвращает список данных стакана цен, где одно из целочисленных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); int total=list_source.Total(); for(int i=0; i<total; i++) { CMarketBookOrd *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; long obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Возвращает список данных стакана цен, где одно из вещественных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CMarketBookOrd *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; double obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Возвращает список данных стакана цен, где одно из строковых | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByMBookProperty(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode) { if(list_source==NULL) return NULL; CArrayObj *list=new CArrayObj(); if(list==NULL) return NULL; list.FreeMode(false); ListStorage.Add(list); for(int i=0; i<list_source.Total(); i++) { CMarketBookOrd *obj=list_source.At(i); if(!obj.SupportProperty(property)) continue; string obj_prop=obj.GetProperty(property); if(CompareValues(obj_prop,value,mode)) list.Add(obj); } return list; } //+------------------------------------------------------------------+ //| Возвращает индекс данных стакана цен в списке | //| с максимальным значением целочисленного свойства | //+------------------------------------------------------------------+ int CSelect::FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CMarketBookOrd *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMarketBookOrd *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); long obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс данных стакана цен в списке | //| с максимальным значением вещественного свойства | //+------------------------------------------------------------------+ int CSelect::FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CMarketBookOrd *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMarketBookOrd *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); double obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс данных стакана цен в списке | //| с максимальным значением строкового свойства | //+------------------------------------------------------------------+ int CSelect::FindMBookMax(CArrayObj *list_source,ENUM_MBOOK_ORD_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CMarketBookOrd *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMarketBookOrd *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); max_obj=list_source.At(index); string obj2_prop=max_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс данных стакана цен в списке | //| с минимальным значением целочисленного свойства | //+------------------------------------------------------------------+ int CSelect::FindMBookMin(CArrayObj* list_source,ENUM_MBOOK_ORD_PROP_INTEGER property) { int index=0; CMarketBookOrd *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMarketBookOrd *obj=list_source.At(i); long obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); long obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс данных стакана цен в списке | //| с минимальным значением вещественного свойства | //+------------------------------------------------------------------+ int CSelect::FindMBookMin(CArrayObj* list_source,ENUM_MBOOK_ORD_PROP_DOUBLE property) { int index=0; CMarketBookOrd *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMarketBookOrd *obj=list_source.At(i); double obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); double obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+ //| Возвращает индекс данных стакана цен в списке | //| с минимальным значением строкового свойства | //+------------------------------------------------------------------+ int CSelect::FindMBookMin(CArrayObj* list_source,ENUM_MBOOK_ORD_PROP_STRING property) { int index=0; CMarketBookOrd *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CMarketBookOrd *obj=list_source.At(i); string obj1_prop=obj.GetProperty(property); min_obj=list_source.At(index); string obj2_prop=min_obj.GetProperty(property); if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i; } return index; } //+------------------------------------------------------------------+
Логика работы методов идентична логике работы других таких же методов этого класса, создаваемых нами ранее для других объектов библиотеки. Поэтому не будем останавливаться на описании работы методов, и лишь укажем, что всегда можно повторить уже пройденный неоднократно материал, обратившись к описанию работы этих методов.
Так как теперь мы ввели новый параметр — время получения снимка стакана цен, то это время будем прописывать в объекте абстрактной заявки стакана цен. В структуре MqlBookInfo, описывающей заявку стакана цен нет параметра времени, поэтому нам придётся самостоятельно вписывать время получения снимка к каждому объекту-заявке стакана цен.
Для этого впишем в файле класса абстрактной заявки стакана цен \MQL5\Include\DoEasy\Objects\Book\MarketBookOrd.mqh новый публичный метод:
public: //+------------------------------------------------------------------+ //|Методы упрощённого доступа к свойствам объекта-запроса стакана цен| //+------------------------------------------------------------------+ //--- Устанавливает время снимка - все заявки одного снимка стакана цен имеют одинаковое время void SetTime(const long time_msc) { this.SetProperty(MBOOK_ORD_PROP_TIME_MSC,time_msc); } //--- Возвращает (1) статус, (2) тип, (3) объём заявки
Метод просто устанавливает в новое свойство объекта переданное значение времени в миллисекундах.
В параметрическом конструкторе класса абстрактной заявки стакана цен инициализируем нулём время получения снимка:
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ 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)); //--- Время заявки отсутствует в параметрах, и оно учитывается в классе снимка стакана цен. Обнуляем время this.SetProperty(MBOOK_ORD_PROP_TIME_MSC,0); } //+------------------------------------------------------------------+
Время для каждой из заявок, присутствующих в одном снимке стакана цен, будем устанавливать в момент получения снимка стакана.
Также в файле класса абстрактной заявки стакана цен MarketBookOrd.mqh и в файлах классов его наследников MarketBookBuy.mqh, MarketBookBuyMarket.mqh, MarketBookSell.mqh и MarketBookSellMarket.mqh были внесены небольшие косметические изменения в виртуальные методы описания объектов:
//--- Выводит в журнал краткое описание объекта virtual void PrintShort(const bool symbol=false); //--- Возвращает краткое наименование объекта virtual string Header(const bool symbol=false);
В каждый из методов были добавлены флаги, указывающие на необходимость вывода наименования символа в описании объекта. По умолчанию символ не выводится в описании объекта-заявки. Причиной тому послужило то, что объект-заявка не является как бы самостоятельным объектом, а входит в состав снимка стакана цен, класс которого сегодня будем делать. И вот при выводе описания объекта-снимка стакана цен, где помимо описания объекта, выводятся и описания всех заявок, в него входящих, вывод символа для каждой заявки выглядит избыточным, так как символ уже выводится в заголовке описания объекта-снимка стакана цен.
Доработка этих методов выглядит одинаково для всех вышеперечисленных классов.
Для класса CMarketBookOrd:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование объекта | //+------------------------------------------------------------------+ string CMarketBookOrd::Header(const bool symbol=false) { return this.TypeDescription()+(symbol ? " \""+this.Symbol()+"\"" : ""); } //+------------------------------------------------------------------+ //| Выводит в журнал краткое описание объекта | //+------------------------------------------------------------------+ void CMarketBookOrd::PrintShort(const bool symbol=false) { ::Print(this.Header(symbol)); } //+------------------------------------------------------------------+
Для класса CMarketBookBuy, CMarketBookBuyMarket, CMarketBookSell и CMarketBookSellMarket:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование объекта | //+------------------------------------------------------------------+ string CMarketBookBuy::Header(const bool symbol=false) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY)+(symbol ? " \""+this.Symbol() : "")+ ": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]"; } //+------------------------------------------------------------------+
Для класса CMarketBookBuyMarket:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование объекта | //+------------------------------------------------------------------+ string CMarketBookBuyMarket::Header(const bool symbol=false) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_BUY_MARKET)+(symbol ? " \""+this.Symbol() : "")+ ": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]"; } //+------------------------------------------------------------------+
Для класса CMarketBookSell:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование объекта | //+------------------------------------------------------------------+ string CMarketBookSell::Header(const bool symbol=false) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL)+(symbol ? " \""+this.Symbol() : "")+ ": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]"; } //+------------------------------------------------------------------+
Для класса CMarketBookSellMarket:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование объекта | //+------------------------------------------------------------------+ string CMarketBookSellMarket::Header(const bool symbol=false) { return CMessage::Text(MSG_MBOOK_ORD_TYPE_SELL_MARKET)+(symbol ? " \""+this.Symbol() : "")+ ": "+::DoubleToString(this.Price(),this.Digits())+" ["+::DoubleToString(this.VolumeReal(),2)+"]"; } //+------------------------------------------------------------------+
Соответственно, в объявлении этих методов во всех классах-наследниках были добавлены флаги:
//--- Возвращает краткое наименование объекта virtual string Header(const bool symbol=false);
По умолчанию символ в описании объекта выводиться не будет.
Класс объекта-снимка стакана цен
Итак, у нас всё готово для создания класса объекта-снимка стакана цен. По сути — это список заявок стакана цен, полученных в массив структур MqlBookInfo при срабатывании обработчика OnBookEvent(). Только каждая из заявок, находящаяся в массиве, в данном классе будет представлена объектом класса CMarketBookOrd — его наследниками. Все они будут складываться в список CArrayObj, являющийся классом динамического массива указателей на экземпляры класса CObject и его наследников стандартной библиотеки. Помимо списка, в котором будут храниться объекты-заявки стакана цен, класс будет предоставлять стандартные для всех объектов библиотеки возможности работы с этими объектами и их списками — поиску и сортировке по различным их свойствам — для удобного сбора любых статистических данных в работе со стаканом цен в своих программах.
В папке библиотеки \MQL5\Include\DoEasy\Objects\Book\ создадим новый файл MarketBookSnapshot.mqh класса CMBookSnapshot.
Базовым классом должен быть класс базового объекта всех объектов библиотеки CBaseObj.
К файлу должны быть подключены файлы классов объектов-наследников объекта абстрактной заявки стакана цен.
Рассмотрим листинг класса и реализацию его методов:
//+------------------------------------------------------------------+ //| MarketBookSnapshot.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\Select.mqh" #include "MarketBookBuy.mqh" #include "MarketBookSell.mqh" #include "MarketBookBuyMarket.mqh" #include "MarketBookSellMarket.mqh" //+------------------------------------------------------------------+ //| Класс "Снимок стакана цен" | //+------------------------------------------------------------------+ class CMBookSnapshot : public CBaseObj { private: string m_symbol; // Символ long m_time; // Время снимка int m_digits; // Digits символа CArrayObj m_list; // Список объектов-заявок стакана цен public: //--- Возвращает (1) себя, (2) список объектов-заявок стакана цен CMBookSnapshot *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &m_list; } //--- Возвращает список объектов-заявок стакана цен по выбранному (1) double, (2) integer и (3) string свойству, удовлетворяющему сравниваемому условию CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } //--- (1) Возвращает объект-заявку стакана цен по индексу в списке, (2) размер списка заявок CMarketBookOrd *GetMBookByListIndex(const uint index) { return this.m_list.At(index); } int DataTotal(void) const { return this.m_list.Total(); } //--- Метод сравнения для поиска и сортировки по времени объектов снимков стакана цен virtual int Compare(const CObject *node,const int mode=0) const { const CMBookSnapshot *compared_obj=node; return(this.Time()<compared_obj.Time() ? -1 : this.Time()>compared_obj.Time() ? 1 : 0); } //--- Возвращает наименование снимка стакана цен string Header(void); //--- Выводит в журнал (1) описание, (2) краткое описание снимка стакана цен void Print(void); void PrintShort(void); //--- Конструкторы CMBookSnapshot(){;} CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]); //+------------------------------------------------------------------+ //|Методы упрощённого доступа к свойствам объекта-снимка стакана цен | //+------------------------------------------------------------------+ //--- Устанавливает (1) символ, (2) время снимка стакана цен, (3) указанное время всем заявкам в стакане void SetSymbol(const string symbol) { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); } void SetTime(const long time_msc) { this.m_time=time_msc; } void SetTimeToOrders(const long time_msc); //--- Возвращает (1) символ стакана цен, (2) Digits символа, (3) время снимка string Symbol(void) const { return this.m_symbol; } int Digits(void) const { return this.m_digits; } long Time(void) const { return this.m_time; } }; //+------------------------------------------------------------------+
Здесь видим обычную организацию класса как и у всех объектов библиотеки: в приватной секции объявлены переменные-члены класса, в публичной — стандартные для объектов библиотеки методы возврата списков по указанным свойствам объектов-заявок, метод сравнения двух объектов-снимков стакана цен для поиска и сортировки этих объектов в списке, в который впоследствии их будем размещать (в списке объекта-серии снимков стакана цен), методы для описания объекта-снимка стакана цен, два конструктора — конструктор по умолчанию и параметрический (параметрический конструктор будем использовать при создании новых объектов-снимков стакана цен, для которых известны все их свойства, а по умолчанию — будет служить для быстрого создания нового объекта и добавления к нему требуемого свойства в целях поиска объектов в списке с указанным значением этого свойства). Методы упрощённого доступа к свойствам объекта служат для установки и возврата некоторых свойств объекта, которые потребуются нам в дальнейшем.
Рассмотрим реализацию методов класса.
В параметрическом конструкторе класса просматриваем переданный в неё массив структур MqlBookInfo и, в соответствии с типом заявок, создаём соответствующие типы объектов-заявок стакана цен и добавляем их в список.
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ CMBookSnapshot::CMBookSnapshot(const string symbol,const long time,MqlBookInfo &book_array[]) : m_time(time) { //--- Устанавливаем символ this.SetSymbol(symbol); //--- Очищаем список this.m_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(this.m_symbol,book_array[i]); break; case BOOK_TYPE_SELL : mbook_ord=new CMarketBookSell(this.m_symbol,book_array[i]); break; case BOOK_TYPE_BUY_MARKET : mbook_ord=new CMarketBookBuyMarket(this.m_symbol,book_array[i]); break; case BOOK_TYPE_SELL_MARKET : mbook_ord=new CMarketBookSellMarket(this.m_symbol,book_array[i]); break; default: break; } if(mbook_ord==NULL) continue; //--- Устанавливаем заявке время снимка стакана цен mbook_ord.SetTime(this.m_time); //--- Ставим списку флаг сортированного списка (по значению цены) и добавляем в него текущий объект-заявку this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE); if(!this.m_list.InsertSort(mbook_ord)) delete mbook_ord; } } //+------------------------------------------------------------------+
Метод, возвращающий краткое наименование объекта-снимка стакана цен:
//+------------------------------------------------------------------+ //| Возвращает краткое наименование объекта | //+------------------------------------------------------------------+ string CMBookSnapshot::Header(void) { return CMessage::Text(MSG_MBOOK_SNAP_TEXT_SNAPSHOT)+" \""+this.Symbol(); } //+------------------------------------------------------------------+
Здесь просто создаётся строка из текстового сообщения с описанием объекта и символа, котрая выглядит примерно так:
Снимок стакана цен "EURUSD"
Метод, выводящий в журнал краткое описание объекта-снимка стакана цен:
//+------------------------------------------------------------------+ //| Выводит в журнал краткое описание объекта | //+------------------------------------------------------------------+ void CMBookSnapshot::PrintShort(void) { ::Print(this.Header()," ("+TimeMSCtoString(this.m_time),")"); } //+------------------------------------------------------------------+
Метод распечатывает в журнал строку, состоящую из наименования объекта плюс время снимка стакана цен в миллисекундах, пример:
Снимок стакана цен "EURUSD" (2021.02.09 22:16:24.557)
Метод, выводящий в журнал свойства объекта-снимка стакана цен:
//+------------------------------------------------------------------+ //| Выводит в журнал свойства объекта | //+------------------------------------------------------------------+ void CMBookSnapshot::Print(void) { ::Print(this.Header()," ("+TimeMSCtoString(this.m_time),"):"); this.m_list.Sort(SORT_BY_MBOOK_ORD_PRICE); for(int i=this.m_list.Total()-1;i>WRONG_VALUE;i--) { CMarketBookOrd *ord=this.m_list.At(i); if(ord==NULL) continue; ::Print(" - ",ord.Header()); } } //+------------------------------------------------------------------+
Сначала выводится заголовок с описанием снимка стакана цен, а затем в цикле по списку всех объектов-заявок стакана цен выводятся их описания.
Так как объекты-заявки, находящиеся в списке объекта-снимка стакана цен, не имеют собственной возможности получить их время, то в данном классе напишем метод, устанавливающий всем объектам-заявкам в списке время в миллисекундах, записанное в данном объекте:
//+------------------------------------------------------------------+ //| Устанавливает указанное время всем заявкам в стакане | //+------------------------------------------------------------------+ void CMBookSnapshot::SetTimeToOrders(const long time_msc) { int total=this.m_list.Total(); for(int i=0;i<total;i++) { CMarketBookOrd *ord=this.m_list.At(i); if(ord==NULL) continue; ord.SetTime(time_msc); } } //+------------------------------------------------------------------+
В цикле по списку всех объектов-заявок стакана цен получаем очередной объект-заявку и устанавливаем ему время, записанное в свойствах объекта-снимка стакана цен. Таким образом, все объекты-заявки, находящиеся в списке данного снимка стакана цен, имеют одинаковое время их получения, что логично — мы их получаем при срабатывании обработчика OnBookEvent(), и время срабатывания записываем объекту-снимку стакана цен и всем его заявкам.
Объект-снимок стакана цен готов. Теперь нам нужно размещать эти объекты в список — ведь при каждом срабатывании обработчика OnBookEvent() мы будем получать новый снимок стакана цен и создавать соответствующий объект.
Все эти объекты будем хранить в классе объекта-серии снимков стакана цен.
Класс объекта-серии снимков стакана цен
Класс серии снимков стакана цен по своей "идеологии" похож на классы таймсерий символов или тиковых данных. В отличии от этих классов, в которых данные можно получить из окружения, в классе-списке снимков стакана цен мы не можем получить исторические данные — их придётся накапливать в режиме реального времени. Поэтому в классе не будет метода создания списка, а будет только метод его обновления.
В папке библиотеки \MQL5\Include\DoEasy\Objects\Book\ создадим новый файл MBookSeries.mqh класса CMBookSeries.
Базовым классом должен быть класс базового объекта всех объектов библиотеки CBaseObj.
К файлу должен быть подключен файл класса объекта-снимка стакана цен.
Рассмотрим листинг класса, а затем разберём его методы:
//+------------------------------------------------------------------+ //| MBookSeries.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 "MarketBookSnapshot.mqh" //+------------------------------------------------------------------+ //| Класс "Серия снимков стакана цен" | //+------------------------------------------------------------------+ class CMBookSeries : public CBaseObj { private: string m_symbol; // Символ uint m_amount; // Количество используемых снимков стакана цен в серии uint m_required; // Требуемое количество дней для серии снимков стакана цен CArrayObj m_list; // Список-серия снимков стакана цен MqlBookInfo m_book_info[]; // Массив структур стакана цен public: //--- Возвращает (1) себя, (2) список-серию CMBookSeries *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &m_list; } //--- Возвращает список снимков стакана по выбранному (1) double, (2) integer и (3) string свойству, удовлетворяющему сравниваемому условию CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } CArrayObj *GetList(ENUM_MBOOK_ORD_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByMBookProperty(this.GetList(),property,value,mode); } //--- Возвращает объект-снимок стакана цен по (1) индексу в списке, (2) по времени, (3) реальный размер списка CMBookSnapshot *GetMBookByListIndex(const uint index) const { return this.m_list.At(index); } CMBookSnapshot *GetLastMBook(void) const { return this.m_list.At(this.DataTotal()-1); } CMBookSnapshot *GetMBook(const long time_msc); int DataTotal(void) const { return this.m_list.Total(); } //--- Устанавливает (1) символ, (2) количество дней для снимков стакана цен void SetSymbol(const string symbol); void SetRequiredUsedDays(const uint required=0); //--- Метод сравнения для поиска и сортировки по символу объектов серий снимков стакана цен virtual int Compare(const CObject *node,const int mode=0) const { const CMBookSeries *compared_obj=node; return(this.Symbol()==compared_obj.Symbol() ? 0 : this.Symbol()>compared_obj.Symbol() ? 1 : -1); } //--- Возвращает наименование серии снимков стакана цен string Header(void); //--- Выводит в журнал (1) описание, (2) краткое описание серии снимков стакана цен void Print(void); void PrintShort(void); //--- Конструкторы CMBookSeries(){;} CMBookSeries(const string symbol,const uint required=0); //+------------------------------------------------------------------+ //| Методы работы с объектами и доступа к их свойствам | //+------------------------------------------------------------------+ //--- Возвращает (1) символ, количество (2) используемых, (3) запрошенных снимков стакана в серии, //--- (4) время указанного по индексу снимка стакана цен в миллисекундах string Symbol(void) const { return this.m_symbol; } ulong AvailableUsedData(void) const { return this.m_amount; } ulong RequiredUsedDays(void) const { return this.m_required; } long MBookTime(const int index) const; //--- обновляет список-серию снимков стакана цен bool Refresh(const long time_msc); }; //+------------------------------------------------------------------+
Опять-таки, что мы здесь видим:
- стандартные для объектов библиотеки методы получения свойств объекта и списков объектов по указанным свойствам,
- методы установки некоторых свойств,
- метод сравнения двух объектов-списков для их сортировки только по наименованию символа,
- методы возврата наименований объекта и конструкторы класса.
Рассмотрим реализацию методов класса.
В параметрическом конструкторе в его списке инициализации устанавливается символ списка, а в теле метода очищается список, ему устанавливается флаг сортированного списка по времени в миллисекундах и устанавливается требуемое количество дней данных снимков стакана цен.
//+------------------------------------------------------------------+ //| Параметрический конструктор | //+------------------------------------------------------------------+ CMBookSeries::CMBookSeries(const string symbol,const uint required=0) : m_symbol(symbol) { this.m_list.Clear(); this.m_list.Sort(SORT_BY_MBOOK_ORD_TIME_MSC); this.SetRequiredUsedDays(required); } //+------------------------------------------------------------------+
Метод, обновляющий список снимков стакана цен:
//+------------------------------------------------------------------+ //| Обновляет список-серию снимков стакана цен | //+------------------------------------------------------------------+ bool CMBookSeries::Refresh(const long time_msc) { //--- Получаем записи стакана цен в массив структур if(!::MarketBookGet(this.m_symbol,this.m_book_info)) return false; //--- Создаём новый объект-снимок стакана цен CMBookSnapshot *book=new CMBookSnapshot(this.m_symbol,time_msc,this.m_book_info); if(book==NULL) return false; //--- Ставим списку флаг сортированного списка (по времени) и добавляем в него созданный снимок стакана цен this.m_list.Sort(SORT_BY_MBOOK_ORD_TIME_MSC); if(!this.m_list.InsertSort(book)) { delete book; return false; } //--- Устанавливаем время в миллисекундах всем объектам-заявкам снимка стакана цен book.SetTimeToOrders(time_msc); //--- Если количество снимков в списке превысило заданное по умолчанию максимальное их количество - //--- удаляем рассчитанное количество объектов-снимков с конца списка if(this.DataTotal()>MBOOKSERIES_MAX_DATA_TOTAL) { int total_del=this.m_list.Total()-MBOOKSERIES_MAX_DATA_TOTAL; for(int i=0;i<total_del;i++) this.m_list.Delete(i); } return true; } //+------------------------------------------------------------------+
Метод будет вызываться при срабатывании обработчика OnBookEvent(). Соответственно, в метод передаётся время срабатывания обработчика, при помощи MarketBookGet() получаем массив структур стакана цен, на основе этой структуры создаём новый объект-снимок стакана цен и добавляем его в список-серию снимков.
Вся логика подробно расписана в комментариях к коду метода и, надеюсь, она понятна.
Метод для установки наименования символа списку-серии снимков:
//+------------------------------------------------------------------+ //| Устанавливает символ | //+------------------------------------------------------------------+ void CMBookSeries::SetSymbol(const string symbol) { if(this.m_symbol==symbol) return; this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol); } //+------------------------------------------------------------------+
Здесь всё прозрачно — если передан NULL или пустая строка, то устанавливается текущий символ, иначе — переданный в метод.
Метод, устанавливающий количество дней для серии снимков стакана цен:
//+------------------------------------------------------------------+ //| Устанавливает количество дней для снимков стакана в серии | //+------------------------------------------------------------------+ void CMBookSeries::SetRequiredUsedDays(const uint required=0) { this.m_required=(required<1 ? MBOOKSERIES_DEFAULT_DAYS_COUNT : required); } //+------------------------------------------------------------------+
Если передано нулевое значение, то устанавливается количество дней, заданное по умолчанию, иначе — переданное в метод. Пока этот метод нигде не используется.
Метод, возвращающий объект-снимок стакана цен по указанному времени:
//+------------------------------------------------------------------+ //| Возвращает объект-снимок стакана цен по его времени | //+------------------------------------------------------------------+ CMBookSnapshot *CMBookSeries::GetMBook(const long time_msc) { CMBookSnapshot *book=new CMBookSnapshot(); if(book==NULL) return NULL; book.SetTime(time_msc); this.m_list.Sort(); int index=this.m_list.Search(book); delete book; return this.m_list.At(index); } //+------------------------------------------------------------------+
Здесь: создаём временный объект-снимок стакана цен, устанавливаем ему требуемое время, флаг сортированного списка, и при помощи метода Search() получаем индекс объекта в списке со временем, равным искомому. Обязательно удаляем временный объект и возвращаем указатель на найденный объект по индексу в списке.
Метод, возвращающий время в миллисекундах указанного по индексу снимка стакана цен:
//+------------------------------------------------------------------+ //| Возвращает время в миллисекундах | //| указанного по индексу снимка стакана цен | //+------------------------------------------------------------------+ long CMBookSeries::MBookTime(const int index) const { CMBookSnapshot *book=this.m_list.At(index); return(book!=NULL ? book.Time() : 0); } //+------------------------------------------------------------------+
Получаем указатель на объект-снимок стакана цен по указанному индексу и возвращаем его время в миллисекундах, либо NULL в случае неудачи.
Метод, возвращающий наименование серии снимков стакана цен:
//+------------------------------------------------------------------+ //| Возвращает наименование серии снимков стакана цен | //+------------------------------------------------------------------+ string CMBookSeries::Header(void) { return CMessage::Text(MSG_MBOOK_SERIES_TEXT_MBOOKSERIES)+" \""+this.m_symbol+"\""; } //+------------------------------------------------------------------+
Метод возвращает строку, состоящую из описания объекта и символа, например так:
Серия снимков стакана цен "EURUSD"
Метод, выводящий в журнал описание серии снимков стакана цен:
//+------------------------------------------------------------------+ //| Выводит в журнал описание серии снимков стакана цен | //+------------------------------------------------------------------+ void CMBookSeries::Print(void) { string txt= ( CMessage::Text(MSG_TICKSERIES_REQUIRED_HISTORY_DAYS)+(string)this.RequiredUsedDays()+", "+ CMessage::Text(MSG_LIB_TEXT_TS_ACTUAL_DEPTH)+(string)this.DataTotal() ); ::Print(this.Header(),": ",txt); } //+------------------------------------------------------------------+
Сначала создаётся заголовок с описанием серии снимков, запрошенном количестве дней данных и фактически собранным количеством снимков стакана цен, а затем в цикле выводятся описания всех заявок, входящих в состав объекта-снимка.
На этом создание классов объекта-снимка и объекта-серии снимков стакана цен завершено.
Тестирование
Для тестирования возьмём советник из прошлой статьи и сохраним его в новой папке \MQL5\Experts\TestDoEasy\Part64\ под новым именем TestDoEasyPart64.mq5.
В советнике создадим объект-серию снимков стакана цен для текущего символа и при каждом срабатывании обработчика OnBoolEvent() по текущему символу будем добавлять в список новый объект-снимок стакана цен. На график в комментарии выведем данные о количестве добавленных в список объектов-снимков и две крайние заявки текущего снимка — максимальную на продажу и минимальную на покупку. При первом получении данных стакана цен распечатаем их в журнале терминала.
Удалим из листинга советника полключение классов объектов-заявок — они теперь подключены к файлам новых классов, созданных нами сегодня:
//+------------------------------------------------------------------+ //| 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
И вместо них впишем подключение файла класса-серии снимков стакана цен:
//+------------------------------------------------------------------+ //| TestDoEasyPart64.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\MBookSeries.mqh> //--- enums
В списке глобальных переменных советника объявим объект класса серии снимков стакана цен:
//--- global variables CEngine engine; SDataButt butt_data[TOTAL_BUTT]; string prefix; double lot; double withdrawal=(InpWithdrawal<0.1 ? 0.1 : InpWithdrawal); ushort magic_number; uint stoploss; uint takeprofit; uint distance_pending; uint distance_stoplimit; uint distance_pending_request; uint bars_delay_pending_request; uint slippage; bool trailing_on; bool pressed_pending_buy; bool pressed_pending_buy_limit; bool pressed_pending_buy_stop; bool pressed_pending_buy_stoplimit; bool pressed_pending_close_buy; bool pressed_pending_close_buy2; bool pressed_pending_close_buy_by_sell; bool pressed_pending_sell; bool pressed_pending_sell_limit; bool pressed_pending_sell_stop; bool pressed_pending_sell_stoplimit; bool pressed_pending_close_sell; bool pressed_pending_close_sell2; bool pressed_pending_close_sell_by_buy; bool pressed_pending_delete_all; bool pressed_pending_close_all; bool pressed_pending_sl; bool pressed_pending_tp; double trailing_stop; double trailing_step; uint trailing_start; uint stoploss_to_modify; uint takeprofit_to_modify; int used_symbols_mode; string array_used_symbols[]; string array_used_periods[]; bool testing; uchar group1; uchar group2; double g_point; int g_digits; //--- CMBookSeries book_series; //+------------------------------------------------------------------+
Вся работа по созданию списка серии снимков стакана цен у нас выполняется в обработчике OnBoolEvent():
//+------------------------------------------------------------------+ //| OnBookEvent function | //+------------------------------------------------------------------+ void OnBookEvent(const string& symbol) { static bool first=true; //--- Получаем объект-символ CSymbol *sym=engine.GetSymbolCurrent(); //--- Если объект-символ не получили, или по нему нет подписки на стакан цен - уходим if(sym==NULL || !sym.BookdepthSubscription()) return; //--- Работаем по текущему символу if(symbol==sym.Name()) { //--- Устанавливаем для объекта-серии снимков стакана цен символ и требуемое количество дней данных book_series.SetSymbol(sym.Name()); book_series.SetRequiredUsedDays(); //--- Обновляем серию снимков стакана цен if(!book_series.Refresh(sym.Time())) return; //--- Получаем последний объект-снимок стакана цен из объекта-серии снимков стакана цен CMBookSnapshot *book=book_series.GetLastMBook(); if(book==NULL) return; //--- Получаем самый первый и самый последний объекты-заявки стакана цен из объекта-снимка стакана цен CMarketBookOrd *ord_0=book.GetMBookByListIndex(0); CMarketBookOrd *ord_N=book.GetMBookByListIndex(book.DataTotal()-1); if(ord_0==NULL || ord_N==NULL) return; //--- Выводим на график в комментарии время текущего снимка стакана цен, //--- максимальное количество показываемых заявок в стакане для символа, //--- полученное количество заявок в текущем снимке стакана цен, //--- общее количество снимков стакана цен, записанных в список-серию и //--- максимальную и минимальную по цене заявки текущего снимка стакана цен Comment ( DFUN,sym.Name(),": ",TimeMSCtoString(book.Time()), ", symbol book size=",sym.TicksBookdepth(), ", last book data total: ",book.DataTotal(), ", series books total: ",book_series.DataTotal(), "\nMax: ",ord_N.Header(),"\nMin: ",ord_0.Header() ); //--- Выведем в журнал первый снимок стакана цен if(first) { //--- описание серии book_series.Print(); //--- описание снимка book.Print(); first=false; } } } //+------------------------------------------------------------------+
Здесь в комментариях расписаны все строки кода и, надеюсь, в дополнительном пояснении они не нуждаются.
В любом случае, вы можете задать интересующие вас вопросы в обсуждении к статье.
Скомпилируем советник и запустим его на графике символа, предварительно задав в настройках работу по двум указанным символам и текущему таймфрейму:
В журнал будут выведены данные о созданной серии снимков стакана цен и самом первом снимке:
Счёт 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10428.13 USD, 1:100, Hedge, Демонстрационный счёт MetaTrader 5 --- Инициализация библиотеки "DoEasy" --- Работа с предопределённым списком символов. Количество используемых символов: 2 "AUDUSD" "EURUSD" Работа только с текущим таймфреймом: H1 Таймсерия символа AUDUSD: - Таймсерия "AUDUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 5121 Таймсерия символа EURUSD: - Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 6046 Тиковая серия "AUDUSD": Запрошенное количество дней: 1, Создано исторических данных: 176033 Тиковая серия "EURUSD": Запрошенное количество дней: 1, Создано исторических данных: 181969 Осуществлена подписка на стакан цен AUDUSD Осуществлена подписка на стакан цен EURUSD Время инициализации библиотеки: 00:00:12.516 Серия снимков стакана цен "EURUSD": Запрошенное количество дней: 1, Фактическая глубина истории: 1 Снимок стакана цен "EURUSD" (2021.02.09 22:16:24.557): - Заявка на продажу: 1.21198 [250.00] - Заявка на продажу: 1.21193 [100.00] - Заявка на продажу: 1.21192 [50.00] - Заявка на продажу: 1.21191 [30.00] - Заявка на продажу: 1.21190 [6.00] - Заявка на покупку: 1.21188 [36.00] - Заявка на покупку: 1.21186 [50.00] - Заявка на покупку: 1.21185 [100.00] - Заявка на покупку: 1.21180 [250.00]
На график символа будут выведены данные о времени последнего снимка стакана цен, количестве заявок для символа, количестве заявок в текущем снимке и общем количестве снимков стакана, добавленных в список снимков стакана цен:
На рисунке отображены данные по уже работающему некоторое время советнику (5019 снимков добавлено в список)
Что дальше
В следующей статье создадим коллекцию серий снимков стакана цен, что позволит полноценно работать со стаканами цен любых символов, для которых активирована подписка на стакан цен и разрешена его трансляция.
Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для MQL5. Их можно скачать и протестировать всё самостоятельно.
Хочу отметить, что классы для работы со стаканом цен находятся в процессе разработки, и поэтому использование этих классов в своих программах на данном этапе крайне нежелательно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
*Статьи этой серии:
Работа с ценами в библиотеке DoEasy (Часть 62): Реалтайм-обновление тиковых серий, подготовка к работе со стаканом цен
Работа с ценами в библиотеке DoEasy (Часть 63): Стакан цен, класс абстрактной заявки стакана цен





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования