Работа с ценами в библиотеке DoEasy (Часть 61): Коллекция тиковых серий символов

Artyom Trishkin | 21 января, 2021

Содержание


Концепция

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

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


Класс-коллекция тиковых данных

В каталоге библиотеки \MQL5\Include\DoEasy\Collections\ создадим новый файл класса-коллекции тиковых данных с именем TickSeriesCollection.mqh.

Класс будет наследником класса базового объекта всех объектов библиотеки.

Рассмотрим тело класса, а затем разберём его переменные и методы:

//+------------------------------------------------------------------+
//|                                         TickSeriesCollection.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 "ListObj.mqh"
#include "..\Objects\Ticks\TickSeries.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
//+------------------------------------------------------------------+
//| Коллекция тиковых серий символов                                 |
//+------------------------------------------------------------------+
class CTickSeriesCollection : public CBaseObj
  {
private:
   CListObj                m_list;                                   // Список используемых тиковых серий символов
//--- Возвращает индекс тиковой серии по имени символа
   int                     IndexTickSeries(const string symbol);
public:
//--- Возвращает (1) себя, (2) список-коллекцию тиковых серий, (3) количество тиковых серий в списке
   CTickSeriesCollection  *GetObject(void)                              { return &this;               }
   CArrayObj              *GetList(void)                                { return &this.m_list;        }
   int                     DataTotal(void)                        const { return this.m_list.Total(); }
//--- Возвращает указатель на объект тиковых серий (1) по символу, (2) по индексу в списке
   CTickSeries            *GetTickseries(const string symbol);
   CTickSeries            *GetTickseries(const int index);
//--- Создаёт список-коллекцию тиковых серий символов
   bool                    CreateCollection(const CArrayObj *list_symbols,const uint required=0);
//--- Устанавливает флаг использования тиковой серии (1) указанного символа, (2) всех символов
   void                    SetAvailableTickSeries(const string symbol,const bool flag=true);
   void                    SetAvailableTickSeries(const bool flag=true);
//--- Возвращает флаг использования тиковой серии (1) указанного символа, (2) всех символов
   bool                    IsAvailableTickSeries(const string symbol);
   bool                    IsAvailableTickSeries(void);

//--- Устанавливает количество дней тиковой истории (1) указанного символа, (2) всех символов
   bool                    SetRequiredUsedDays(const string symbol,const uint required=0);
   bool                    SetRequiredUsedDays(const uint required=0);

//--- Возвращает последний объект-тик указанного символа (1) по индексу, (2) по времени, (4) по времени в миллисекундах
   CDataTick              *GetTick(const string symbol,const int index);
   CDataTick              *GetTick(const string symbol,const datetime tick_time);
   CDataTick              *GetTick(const string symbol,const long tick_time_msc);

//--- Возвращает флаг нового тика указанного символа
   bool                    IsNewTick(const string symbol);

//--- Создаёт тиковую серию (1) указанного символа, (2) всех символов
   bool                    CreateTickSeries(const string symbol,const uint required=0);
   bool                    CreateTickSeriesAll(const uint required=0);
//--- Обновляет тиковую серию (1) указанного символа, (2) всех символов
   void                    Refresh(const string symbol);
   void                    Refresh(void);

//--- Выводит в журнал (1) полное, (2) краткое описание коллекции
   void                    Print(void);
   void                    PrintShort(void);
   
//--- Конструктор
                           CTickSeriesCollection();
  };
//+------------------------------------------------------------------+

Переменная-член класса m_list имеет тип CListObj — класс, являющийся наследником класса CArrayObj стандартной библиотеки — как и многие списки, созданные в этой библиотеке. Всё, что делает класс CListObj, это реализует работу виртуального метода Type() класса CObject — базового класса объектов стандартной библиотеки. Метод должен возвращать идентификатор типа класса. Здесь им является идентификатор типа массива.
Именно реализация виртуального метода Type() и выполнена в классе CListObj, который уже давно был нами добавлен в библиотеку:

//+------------------------------------------------------------------+
//|                                                      ListObj.mqh |
//|                        Copyright 2019, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2019, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
//+------------------------------------------------------------------+
//| Класс списков коллекций                                          |
//+------------------------------------------------------------------+
class CListObj : public CArrayObj
  {
private:
   int               m_type;                    // Тип списка
public:
   void              Type(const int type)       { this.m_type=type;     }
   virtual int       Type(void)           const { return(this.m_type);  }
                     CListObj()                 { this.m_type=0x7778;   }
  };
//+------------------------------------------------------------------+

Здесь метод Type() устанавливает переменной m_type переданное значение, а виртуальный метод Type() возвращает значение, заданное этой переменной.

По умолчанию (в конструкторе класса) переменной устанавливается значение идентификатора типа массива как для CArrayObj — 0x7778.

Назначение всех методов класса описано в комментариях к коду, и далее рассмотрим реализацию этих методов.

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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CTickSeriesCollection::CTickSeriesCollection()
  {
   this.m_list.Clear();
   this.m_list.Sort();
   this.m_list.Type(COLLECTION_TICKSERIES_ID);
  }
//+------------------------------------------------------------------+

Приватный метод IndexTickSeries() — возвращает индекс тиковой серии по имени символа:

//+------------------------------------------------------------------+
//| Возвращает индекс тиковой серии по имени символа                 |
//+------------------------------------------------------------------+
int CTickSeriesCollection::IndexTickSeries(const string symbol)
  {
   const CTickSeries *obj=new CTickSeries(symbol==NULL || symbol=="" ? ::Symbol() : symbol);
   if(obj==NULL)
      return WRONG_VALUE;
   this.m_list.Sort();
   int index=this.m_list.Search(obj);
   delete obj;
   return index;
  }
//+------------------------------------------------------------------+

В метод передаётся наименование символа, индекс тиковой серии которого необходимо вернуть из списка.
Далее создаём временный пустой объект тиковой серии, которому при его создании устанавливаем имя переданного в метод символа,
устанавливаем списку флаг сортированного списка
и ищем индекс объекта в списке.
Затем удаляем временный объект и возвращаем полученный индекс. Если объект не найден или не удалось создать временный объект, то метод возвращает NULL.

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

//+------------------------------------------------------------------+
//| Возвращает объект тиковых серий указанного символа               |
//+------------------------------------------------------------------+
CTickSeries *CTickSeriesCollection::GetTickseries(const string symbol)
  {
   int index=this.IndexTickSeries(symbol);
   return this.m_list.At(index);
  }
//+------------------------------------------------------------------+

В метод передаётся наименование символа, объект тиковой серии которого необходимо вернуть из списка.
При помощи только что рассмотренного метода находим индекс объекта тиковой серии в списке, получаем указатель на этот объект по найденному индексу и возвращаем его. Если индекс не был найден, то его значение будет равно -1, и метод At() класса CArrayObj вернёт NULL.

Метод, устанавливающий флаг использования тиковой серии указанного символа:

//+------------------------------------------------------------------+
//| Устанавливает флаг использования тиковой серии указанного символа|
//+------------------------------------------------------------------+
void CTickSeriesCollection::SetAvailableTickSeries(const string symbol,const bool flag=true)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return;
   tickseries.SetAvailable(flag);
  }
//+------------------------------------------------------------------+

В метод передаётся наименование символа, объекту тиковой серии которого необходимо установить флаг использования.
При помощи рассмотренного  выше метода GetTickseries() получаем из списка указатель на объект тиковой серии и
устанавливаем ему переданный в метод флаг.

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

//+------------------------------------------------------------------+
//| Устанавливает флаг использования тиковых серий всех символов     |
//+------------------------------------------------------------------+
void CTickSeriesCollection::SetAvailableTickSeries(const bool flag=true)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.SetAvailable(flag);
     }
  }
//+------------------------------------------------------------------+

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

Метод, возвращающий флаг использования тиковой серии указанного символа:

//+------------------------------------------------------------------+
//| Возвращает флаг использования тиковой серии указанного символа   |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsAvailableTickSeries(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return tickseries.IsAvailable();
  }
//+------------------------------------------------------------------+

В метод передаётся наименование символа, флаг использования объекта тиковой серии которого необходимо вернуть.
При помощи метода GetTickseries() получаем указатель на объект тиковой серии нужного символа и
возвращаем флаг использования, установленный этому объекту. Если объект не удалось получить из списка, то метод возвращает false.

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

//+------------------------------------------------------------------+
//| Возвращает флаг использования тиковых серий всех символов        |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsAvailableTickSeries(void)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      res &=tickseries.IsAvailable();
     }
   return res;
  }
//+------------------------------------------------------------------+

Объявляем переменную res и инициализируем её значением true.
Затем в цикле по общему количеству объектов в списке
получаем указатель на очередной объект тиковой серии
и
добавляем к значению переменной res флаг использования, установленный для текущего объекта
.
По окончании цикла возвращаем полученное значение переменной res.

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

Метод, устанавливающий количество дней тиковой истории указанного символа:

//+------------------------------------------------------------------+
//| Устанавливает количество дней тиковой истории указанного символа |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::SetRequiredUsedDays(const string symbol,const uint required=0)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   tickseries.SetRequiredUsedDays(required);
   return true;
  }
//+------------------------------------------------------------------+

В метод передаётся наименование символа, количество дней тиковых данных которого необходимо установить.
Получаем указатель на объект тиковых серий при помощи ранее рассмотренного метода,
устанавливаем для него количество дней и возвращаем true.
Если указатель на объект тиковых серий получить не удалось из списка, то метод возвращает false,

Метод, устанавливающий количество дней тиковой истории всех символов:

//+------------------------------------------------------------------+
//| Устанавливает количество дней тиковой истории всех символов      |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::SetRequiredUsedDays(const uint required=0)
  {
   bool res=true;
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
        {
         res &=false;
         continue;
        }
      tickseries.SetRequiredUsedDays(required);
     }
   return res;
  }
//+------------------------------------------------------------------+

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

Если хотя бы для одного из объектов в списке не будет установлено количество дней тиковых данных, то в переменной res по окончанию цикла будет храниться значение false. Таким образом, метод позволяет установить для всех тиковых серий в коллекции количество дней и возвращает успешность выполнения только в случае установки количества дней для каждого из хранящихся в списке объектов тиковых данных.

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

//+------------------------------------------------------------------+
//| Возвращает объект-тик указанного символа по индексу              |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const int index)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTickByListIndex(index);
  }
//+------------------------------------------------------------------+

В метод передаётся символ тиковой серии класса CTickSeries и индекс нужного объекта-тика, хранящегося в списке тиковой серии.
Получаем указатель на объект тиковой серии из списка-коллекции по символу при помощи ранее рассмотренного метода GetTickseries() и
возвращаем указатель на объект-тик из списка тиковой серии при помощи метода GetTickByListIndex(), рассмотренного нами в прошлой статье.

Если объект тиковой серии не удалось получить из списка-коллекции, то метод возвращает NULL. Также, NULL может вернуть и метод GetTickByListIndex() класса CTickSeries.

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

//+------------------------------------------------------------------+
//| Возвращает последний объект-тик указанного символа по времени    |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const datetime tick_time)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTick(tick_time);
  }
//+------------------------------------------------------------------+

В метод передаётся символ тиковой серии класса CTickSeries и время нужного объекта-тика, хранящегося в списке тиковой серии.
Получаем указатель на объект тиковой серии из списка-коллекции по символу при помощи ранее рассмотренного метода GetTickseries() и
возвращаем указатель на объект-тик из списка тиковой серии при помощи метода GetTick(), рассмотренного нами в прошлой статье.

Если объект тиковой серии не удалось получить из списка-коллекции, то метод возвращает NULL. Также, NULL может вернуть и метод GetTick() класса CTickSeries.

Метод, возвращающий последний объект-тик указанного символа по времени в миллисекундах из списка тиковой серии:

//+------------------------------------------------------------------+
//| Возвращает последний объект-тик указанного символа               |
//| по времени в миллисекундах                                       |
//+------------------------------------------------------------------+
CDataTick *CTickSeriesCollection::GetTick(const string symbol,const long tick_time_msc)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return NULL;
   return tickseries.GetTick(tick_time_msc);
  }
//+------------------------------------------------------------------+

В метод передаётся символ тиковой серии класса CTickSeries и время в миллисекундах нужного объекта-тика, хранящегося в списке тиковой серии.
Получаем указатель на объект тиковой серии из списка-коллекции по символу при помощи ранее рассмотренного метода GetTickseries() и
возвращаем указатель на объект-тик из списка тиковой серии при помощи метода GetTick(), рассмотренного нами в прошлой статье.

Если объект тиковой серии не удалось получить из списка-коллекции, то метод возвращает NULL. Также, NULL может вернуть и метод GetTick() класса CTickSeries.

Стоит уточнить — для двух последних методов, возвращающих объекты-тики по времени, что тиков с одинаковым временем может быть несколько, поэтому метод GetTick() класса CTickSeries возвращает самый последний из них — с самым поздним временем как наиболее актуальный.

Метод, возвращающий флаг нового тика указанного символа:

//+------------------------------------------------------------------+
//| Возвращает флаг нового тика указанного символа                   |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::IsNewTick(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return tickseries.IsNewTick();
  }
//+------------------------------------------------------------------+

В метод передаётся наименование символа, флаг появления нового тика которого необходимо вернуть.
Получаем указатель на объект тиковой серии из списка-коллекции по символу при помощи ранее рассмотренного метода GetTickseries() и
возвращаем флаг нового тика тиковой серии при помощи метода IsNewTick() класса CTickSeries, рассмотренного нами в прошлой статье.
Если объект тиковой серии не удалось получить из списка-коллекции, то метод возвращает false.

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

Метод, создающий тиковую серию указанного символа:

//+------------------------------------------------------------------+
//| Создаёт тиковую серию указанного символа                         |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateTickSeries(const string symbol,const uint required=0)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return false;
   return(tickseries.Create(required)>0);
  }
//+------------------------------------------------------------------+

В метод передаётся наименование символа, тиковую серию которого необходимо создать, и количество дней тиковых данных.
Получаем указатель на объект тиковой серии из списка-коллекции по символу при помощи ранее рассмотренного метода GetTickseries() и
возвращаем флаг того, метод Create() класса CTickSeries вернул значение больше нуля (количество созданных объектов-тиков не нулевое).

Метод, создающий тиковые серии всех используемых символов:

//+------------------------------------------------------------------+
//| Создаёт тиковые серии всех символов                              |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateTickSeriesAll(const uint required=0)
  {
   bool res=true;
   int total=this.m_list.Total();
   for(int i=0;i<total;i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      res &=(tickseries.Create(required)>0);
     }
   return res;
  }
//+------------------------------------------------------------------+

В метод передаётся количество дней тиковых данных.
Объявляем переменную res и инициализируем её значением true.
Затем в цикле по общему количеству объектов в списке
получаем указатель на очередной объект тиковой серии
и
добавляем к значению переменной res флаг того, что значение, возвращаемое методом Create() класса CTickSeries, больше нуля (тиковая серия создана)

По окончании цикла возвращаем полученное значение переменной res.

Если хотя бы для одного из объектов в списке не будет создана тиковая серия, то в переменной res по окончанию цикла будет храниться значение false. Таким образом, метод позволяет создать для всех символов коллекции тиковые серии, и возвращает успешность выполнения только в случае создания тиковых серий для каждого из хранящихся в списке объектов тиковых данных.

Метод, обновляющий тиковую серию указанного символа:

//+------------------------------------------------------------------+
//| Обновляет тиковую серию указанного символа                       |
//+------------------------------------------------------------------+
void CTickSeriesCollection::Refresh(const string symbol)
  {
   CTickSeries *tickseries=this.GetTickseries(symbol);
   if(tickseries==NULL)
      return;
   tickseries.Refresh();
  }
//+------------------------------------------------------------------+

В метод передаётся наименование символа, тиковую серию которого необходимо обновить.
Получаем указатель на объект тиковой серии из списка-коллекции по символу при помощи ранее рассмотренного метода GetTickseries() и
обновляем её при помощи метода Refresh() класса CTickSeries.

Метод, обновляющий тиковые серии всех символов:

//+------------------------------------------------------------------+
//| Обновляет тиковые серии всех символов                            |
//+------------------------------------------------------------------+
void CTickSeriesCollection::Refresh(void)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.Refresh();
     }
  }
//+------------------------------------------------------------------+

В цикле по общему количеству объектов в списке
получаем указатель на очередной объект тиковой серии
по индексу цикла и
обновляем серию при помощи метода Refresh() класса CTickSeries.

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

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

//+------------------------------------------------------------------+
//| Выводит в журнал полное описание коллекции                       |
//+------------------------------------------------------------------+
void CTickSeriesCollection::Print(void)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.Print();
     }
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание коллекции                      |
//+------------------------------------------------------------------+
void CTickSeriesCollection::PrintShort(void)
  {
   for(int i=0;i<this.m_list.Total();i++)
     {
      CTickSeries *tickseries=this.m_list.At(i);
      if(tickseries==NULL)
         continue;
      tickseries.PrintShort();
     }
  }
//+------------------------------------------------------------------+

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

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

Метод, создающий список-коллекцию тиковых серий символов:

//+------------------------------------------------------------------+
//| Создаёт список-коллекцию тиковых серий символов                  |
//+------------------------------------------------------------------+
bool CTickSeriesCollection::CreateCollection(const CArrayObj *list_symbols,const uint required=0)
  {
//--- Если передан пустой список объектов-символов - выходим
   if(list_symbols==NULL)
      return false;
//--- Получаем количество объектов-символов в переданном списке
   int total=list_symbols.Total();
//--- Очищаем список-коллекцию тиковых серий
   this.m_list.Clear();
//--- В цикле по всем объектам-символам
   for(int i=0;i<total;i++)
     {
      //--- получаем очередной объект-символ
      CSymbol *symbol_obj=list_symbols.At(i);
      //--- если объект-символ получить не удалось - переходим к следующему в списке
      if(symbol_obj==NULL)
         continue;
      //--- Создаём новый пустой объект-тиковую серию
      CTickSeries *tickseries=new CTickSeries();
      //--- Если объект-тиковую серию создать не удалось - переходим к следующему символу в списке
      if(tickseries==NULL)
         continue;
      //--- Устанавливаем объекту тиковой серии наименование символа
      tickseries.SetSymbol(symbol_obj.Name());
      //--- Списку-коллекции тиковых серий устанавливаем флаг сортированного списка
      this.m_list.Sort();
      //--- Если объект с таким именем символа уже есть в списке-коллекции тиковых серий - удаляем объект-тиковой серии
      if(this.m_list.Search(tickseries)>WRONG_VALUE)
         delete tickseries;
      //--- иначе - объекта с таким именем символа ещё нет в коллекции
      else
        {
         //--- Устанавливаем объекту тиковой серии количество дней тиковых данных
         tickseries.SetRequiredUsedDays(required);
         //--- если объект-тиковую серию не удалось добавить в список-коллекцию - удаляем объект-тиковую серию
         if(!this.m_list.Add(tickseries))
            delete tickseries;
        } 
     }
//--- Возвращаем флаг того, что созданный список-коллекция имеет размер больше нуля
   return this.m_list.Total()>0;
  }
//+------------------------------------------------------------------+

Метод несложный — в него передаётся список используемых в программе символов (такой список у нас уже давно есть, и используется при создании коллекции таймсерий символов), затем в цикле по общему количеству символов создаём очередной новый объект-тиковую серию и устанавливаем ему наименование символа из списка символов в текущей позиции цикла. Если объекта-тиковой серии с таким символом ещё нет в списке, то устанавливаем для него переданное в метод значение количества дней тиковых данных и добавляем объект в список-коллекцию. И так делаем для каждого символа в списке. Это вкратце. На самом деле чуть иначе — с проверками на успешность создания и добавления объектов-тиковых серий в список, и удаления ненужных объектов при необходимости. Вся логика метода подробно расписана в его листинге, оставим его для самостоятельного разбора.

Для связи созданной коллекции с "внешним миром" используем  основной класс библиотеки CEngine,
хранящийся по адресу\MQL5\Include\DoEasy\Engine.mqh.

Подключим к нему файл вновь созданного класса:

//+------------------------------------------------------------------+
//|                                                       Engine.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 "Services\TimerCounter.mqh"
#include "Collections\HistoryCollection.mqh"
#include "Collections\MarketCollection.mqh"
#include "Collections\EventsCollection.mqh"
#include "Collections\AccountsCollection.mqh"
#include "Collections\SymbolsCollection.mqh"
#include "Collections\ResourceCollection.mqh"
#include "Collections\TimeSeriesCollection.mqh"
#include "Collections\BuffersCollection.mqh"
#include "Collections\IndicatorsCollection.mqh"
#include "Collections\TickSeriesCollection.mqh"
#include "TradingControl.mqh"
//+------------------------------------------------------------------+

В приватной секции класса объявим объект класса-коллекции тиковых серий:

//+------------------------------------------------------------------+
//| Класс-основа библиотеки                                          |
//+------------------------------------------------------------------+
class CEngine
  {
private:
   CHistoryCollection   m_history;                       // Коллекция исторических ордеров и сделок
   CMarketCollection    m_market;                        // Коллекция рыночных ордеров и сделок
   CEventsCollection    m_events;                        // Коллекция событий
   CAccountsCollection  m_accounts;                      // Коллекция аккаунтов
   CSymbolsCollection   m_symbols;                       // Коллекция символов
   CTimeSeriesCollection m_time_series;                  // Коллекция таймсерий
   CBuffersCollection   m_buffers;                       // Коллекция индикаторных буферов
   CIndicatorsCollection m_indicators;                   // Коллекция индикаторов
   CTickSeriesCollection m_tick_series;                  // Коллекция тиковых серий
   CResourceCollection  m_resource;                      // Список ресурсов
   CTradingControl      m_trading;                       // Объект управления торговлей
   CPause               m_pause;                         // Объект "Пауза"
   CArrayObj            m_list_counters;                 // Список счётчиков таймера

В классе есть метод SetUsedSymbols(), который позволяет установить в библиотеке список заданных для использования в программе символов.
Добавим в него передачу количества дней, для которых необходимо иметь тиковые данные в библиотеке:

//--- Устанавливает список используемых символов в коллекции символов и создаёт коллекцию таймсерий символов
   bool                 SetUsedSymbols(const string &array_symbols[],const uint required=0);

По умолчанию передаётся ноль, что означает один день, и задаётся в файле \MQL5\Include\DoEasy\Defines.mqh константой TICKSERIES_DEFAULT_DAYS_COUNT.

В реализации метода добавим создание коллекции тиковых серий.

//+------------------------------------------------------------------+
//| Устанавливает список используемых символов в коллекции символов  |
//| и создаёт коллекцию таймсерий символов                           |
//+------------------------------------------------------------------+
bool CEngine::SetUsedSymbols(const string &array_symbols[],const uint required=0)
  {
   bool res=this.m_symbols.SetUsedSymbols(array_symbols);
   CArrayObj *list=this.GetListAllUsedSymbols();
   if(list==NULL)
      return false;
   res&=this.m_time_series.CreateCollection(list);
   res&=this.m_tick_series.CreateCollection(list,required);
   return res;
  }
//+------------------------------------------------------------------+

Теперь при вызове этого метода из программы будут создаваться две коллекции — коллекция таймсерий и коллекция тиковых серий.

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

//--- Копирует в массив указанное double-свойство указанной таймсерии указанного символа
//--- Независимо от направления индексации массива, копирование производится как в массив-таймсерию
   bool                 SeriesCopyToBufferAsSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_BAR_PROP_DOUBLE property,
                                                   double &array[],const double empty=EMPTY_VALUE)
                          { return this.m_time_series.CopyToBufferAsSeries(symbol,timeframe,property,array,empty);}


//--- Возвращает (1) коллекцию тиковых серий, (2) список тиковых серий из коллекции тиковых серий
   CTickSeriesCollection *GetTickSeriesCollection(void)                       { return &this.m_tick_series;                                     }
   CArrayObj           *GetListTickSeries(void)                               { return this.m_tick_series.GetList();                            }


//--- Возвращает (1) коллекцию буферов, (2) список буферов из коллекции 

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

На сегодня это всё, что нам необходимо для создания коллекции тиковых серий.


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

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

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

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart60.mq5 |
//|                        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"
//--- includes
#include <DoEasy\Engine.mqh>
#include <DoEasy\Objects\Ticks\TickSeries.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;

//--- Объект "Новый тик"
CNewTickObj    check_tick;
//--- Объект данных тиковой серии текущего символа
CTickSeries    tick_series;
//+------------------------------------------------------------------+

В конце обработчика OnInit() удалим установку текущего символа для объекта "Новый тик":

//--- Ждём 600 милисекунд
   engine.Pause(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","The sound of a falling coin 2"));

//--- Установим текущий символ объекту "Новый тик"
   check_tick.SetSymbol(Symbol());
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Удалим из функции OnInitDoEasy() блок кода, служащий для проверки создания тиковой серии текущего символа:

//--- Проверка созданных таймсерий - выводим в журнал описания всех созданных таймсерий
//--- (true - только созданные, false - созданные и объявленные)
   engine.GetTimeSeriesCollection().PrintShort(false); // Краткие описания
   //engine.GetTimeSeriesCollection().Print(true);      // Полные описания

//--- Блок кода для проверки создания списка тиков и работы с ним
   Print("");
//--- Так как объект тиковой серии создан с конструктором по умолчанию, то
//--- установим символ, флаг использования и количество дней (1 по умолчанию) для копирования тиков
//--- Создадим тиковую серию и распечатаемеё данные в журнал
   tick_series.SetSymbol(Symbol());
   tick_series.SetAvailable(true);
   tick_series.SetRequiredUsedDays();
   tick_series.Create();
   tick_series.Print();
   
   Print("");
//--- Получим и выведем в журнал данные объекта с максимальной ценой Ask в дневном диапазоне цен
   int index_max=CSelect::FindTickDataMax(tick_series.GetList(),TICK_PROP_ASK);
   CDataTick *tick_max=tick_series.GetList().At(index_max);
   if(tick_max!=NULL)
      tick_max.Print();
//--- Получим и выведем в журнал данные объекта с минимальной ценой Bid в дневном диапазоне цен
   int index_min=CSelect::FindTickDataMin(tick_series.GetList(),TICK_PROP_BID);
   CDataTick *tick_min=tick_series.GetList().At(index_min);
   if(tick_min!=NULL)
      tick_min.Print();

//--- Создание тестовых файлов ресурсов

Теперь на этом месте нам нужно расположить создание тиковых серий для всех символов созданной коллекции тиковых данных:

//--- Проверка созданных таймсерий - выводим в журнал описания всех созданных таймсерий
//--- (true - только созданные, false - созданные и объявленные)
   engine.GetTimeSeriesCollection().PrintShort(false); // Краткие описания
   //engine.GetTimeSeriesCollection().Print(true);      // Полные описания

//--- Создание тиковых серий всех используемых символов
   engine.GetTickSeriesCollection().CreateTickSeriesAll();
//--- Проверка созданных тиковых серий - выводим в журнал описания всех созданных тиковых серий
   engine.GetTickSeriesCollection().Print();

//--- Создание тестовых файлов ресурсов

В обработчике OnTick() при поступлении нового тика попробуем найти в коллекции тиковых серий для каждого символа по одному объекту-тику с максимальной ценой Ask и минимальной ценой Bid в списках тиковых данных, и вывести параметры каждого найденного объекта-тика в журнал:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Обработка события NewTick в библиотеке
   engine.OnTick(rates_data);

//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Работа в таймере
      PressButtonsControl();        // Контроль нажатия кнопок
      engine.EventsHandling();      // Работа с событиями
     }

//--- Если установлен флаг трейлинга
   if(trailing_on)
     {
      TrailingPositions();          // Трейлинг позиций
      TrailingOrders();             // Трейлинг отложенных ордеров
     }
     
//--- Проверка созданных тиковых данных на первом тике
//--- Получим и выведем в журнал данные объекта с максимальной ценой Ask и с минимальной ценой Bid в дневном диапазоне цен
   static bool check=false;
   if(!check)
     {
      Print("");
      //--- Получаем указатель на список тиковых данных всех символов из коллекции тиков
      CArrayObj* list=engine.GetTickSeriesCollection().GetList();
      int total=engine.GetTickSeriesCollection().DataTotal();
      //--- В цикле по количеству тиковых серий в коллекции
      for(int i=0;i<list.Type();i++)
        {
         //--- Получаем очередную тиковую серию из коллекции по индексу
         CTickSeries *tick_series=engine.GetTickSeriesCollection().GetTickseries(i);
         if(tick_series!=NULL)
           {
            //--- В полученной тиковой серии находим индексы объектов-тиков с макимальным Ask и минимальным Bid
            int index_max=CSelect::FindTickDataMax(tick_series.GetList(),TICK_PROP_ASK);
            int index_min=CSelect::FindTickDataMin(tick_series.GetList(),TICK_PROP_BID);
            //--- Распечатываем в журнал данные полученных из тиковой серии объектов-тиков
            engine.GetTickSeriesCollection().GetTick(tick_series.Symbol(),index_max).Print();
            engine.GetTickSeriesCollection().GetTick(tick_series.Symbol(),index_min).Print();
           }
        }
      check=true;
     }
  }
//+------------------------------------------------------------------+

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


После непродолжительного времени, требуемого на создание тиковых данных для двух используемых символов в обработчике OnInit(), в журнал будут выведены данные о параметрах программы, созданных таймсериях и созданных тиковых данных, а по приходу первого тика в журнал будут выведены данные четырёх найденных тиков с максимальним Ask и минимальным Bid для каждого из двух символов:

Счёт 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10426.13 USD, 1:100, Hedge, Демонстрационный счёт MetaTrader 5
--- Инициализация библиотеки "DoEasy" ---
Работа с предопределённым списком символов. Количество используемых символов: 2
"AUDUSD" "EURUSD"
Работа только с текущим таймфреймом: H1
Таймсерия символа AUDUSD: 
- Таймсерия "AUDUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 6194
Таймсерия символа EURUSD: 
- Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 5675
Тиковая серия "AUDUSD": Запрошенное количество дней: 1, Создано исторических данных: 142712
Тиковая серия "EURUSD": Запрошенное количество дней: 1, Создано исторических данных: 113985
Время инициализации библиотеки: 00:00:06.156
 
============= Начало списка параметров (Тик "AUDUSD" 2021.01.19 10:06:53.387) =============
Время последнего обновления цен в миллисекундах: 2021.01.19 10:06:53.387
Время последнего обновления цен: 2021.01.19 10:06:53
Объем для текущей цены Last: 0
Флаги: 6
Изменённые данные на тике:
 - Изменение цены Ask
 - Изменение цены Bid
------
Цена Bid: 0.77252
Цена Ask: 0.77256
Цена Last: 0.00000
Объем для текущей цены Last c повышенной точностью: 0.00
Спред: 0.00004
------
Символ: "AUDUSD"
============= Конец списка параметров (Тик "AUDUSD" 2021.01.19 10:06:53.387) =============
 
============= Начало списка параметров (Тик "AUDUSD" 2021.01.18 11:51:48.662) =============
Время последнего обновления цен в миллисекундах: 2021.01.18 11:51:48.662
Время последнего обновления цен: 2021.01.18 11:51:48
Объем для текущей цены Last: 0
Флаги: 130
Изменённые данные на тике:
 - Изменение цены Bid
------
Цена Bid: 0.76589
Цена Ask: 0.76593
Цена Last: 0.00000
Объем для текущей цены Last c повышенной точностью: 0.00
Спред: 0.00004
------
Символ: "AUDUSD"
============= Конец списка параметров (Тик "AUDUSD" 2021.01.18 11:51:48.662) =============
 
============= Начало списка параметров (Тик "EURUSD" 2021.01.19 10:05:07.246) =============
Время последнего обновления цен в миллисекундах: 2021.01.19 10:05:07.246
Время последнего обновления цен: 2021.01.19 10:05:07
Объем для текущей цены Last: 0
Флаги: 6
Изменённые данные на тике:
 - Изменение цены Ask
 - Изменение цены Bid
------
Цена Bid: 1.21189
Цена Ask: 1.21189
Цена Last: 0.00000
Объем для текущей цены Last c повышенной точностью: 0.00
Спред: 0.00000
------
Символ: "EURUSD"
============= Конец списка параметров (Тик "EURUSD" 2021.01.19 10:05:07.246) =============

============= Начало списка параметров (Тик "EURUSD" 2021.01.18 14:57:53.847) =============
Время последнего обновления цен в миллисекундах: 2021.01.18 14:57:53.847
Время последнего обновления цен: 2021.01.18 14:57:53
Объем для текущей цены Last: 0
Флаги: 134
Изменённые данные на тике:
 - Изменение цены Ask
 - Изменение цены Bid
------
Цена Bid: 1.20536
Цена Ask: 1.20536
Цена Last: 0.00000
Объем для текущей цены Last c повышенной точностью: 0.00
Спред: 0.00000
------
Символ: "EURUSD"
============= Конец списка параметров (Тик "EURUSD" 2021.01.18 14:57:53.847) =============

По журналу видно, что на инициализацию библиотеки с созданием списков тиковых данных двух символов ушло 16 секунд, и далее при поступлении нового тика мы нашли по два тика для каждого из используемых символов — с максимальной ценой Ask и минимальной ценой Bid за текущий день.

Что дальше

В следующей статье начнём создание реалтайм-обновления и контроль событий данных в созданной сегодня коллекции тиков.

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

К содержанию

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

Работа с таймсериями в библиотеке DoEasy (Часть 35): Объект "Бар" и список-таймсерия символа
Работа с таймсериями в библиотеке DoEasy (Часть 36): Объект таймсерий всех используемых периодов символа
Работа с таймсериями в библиотеке DoEasy (Часть 37): Коллекция таймсерий - база данных таймсерий по символам и периодам
Работа с таймсериями в библиотеке DoEasy (Часть 38): Коллекция таймсерий - реалтайм обновление и доступ к данным из программы
Работа с таймсериями в библиотеке DoEasy (Часть 39): Индикаторы на основе библиотеки - подготовка данных и события таймсерий
Работа с таймсериями в библиотеке DoEasy (Часть 40): Индикаторы на основе библиотеки - реалтайм обновление данных
Работа с таймсериями в библиотеке DoEasy (Часть 41): Пример мультисимвольного мультипериодного индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 42): Класс объекта абстрактного индикаторного буфера
Работа с таймсериями в библиотеке DoEasy (Часть 43): Классы объектов индикаторных буферов
Работа с таймсериями в библиотеке DoEasy (Часть 44): Класс-коллекция объектов индикаторных буферов
Работа с таймсериями в библиотеке DoEasy (Часть 45): Мультипериодные индикаторные буферы
Работа с таймсериями в библиотеке DoEasy (Часть 46): Мультипериодные, мультисимвольные индикаторные буферы
Работа с таймсериями в библиотеке DoEasy (Часть 47): Мультипериодные мультисимвольные стандартные индикаторы
Работа с таймсериями в библиотеке DoEasy (Часть 48): Мультипериодные мультисимвольные индикаторы на одном буфере в подокне
Работа с таймсериями в библиотеке DoEasy (Часть 49): Мультипериодные мультисимвольные многобуферные стандартные индикаторы
Работа с таймсериями в библиотеке DoEasy (Часть 50): Мультипериодные мультисимвольные стандартные индикаторы со смещением
Работа с таймсериями в библиотеке DoEasy (Часть 51): Составные мультипериодные мультисимвольные стандартные индикаторы
Работа с таймсериями в библиотеке DoEasy (Часть 52): Кроссплатформенность мультипериодных мультисимвольных однобуферных стандартных индикаторов
Работа с таймсериями в библиотеке DoEasy (Часть 53): Класс абстрактного базового индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 54): Классы-наследники абстрактного базового индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 55): Класс-коллекция индикаторов
Работа с таймсериями в библиотеке DoEasy (Часть 56): Объект пользовательского индикатора, получение данных от объектов-индикаторов в коллекции
Работа с таймсериями в библиотеке DoEasy (Часть 57): Объект данных буфера индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 58): Таймсерии данных буферов индикаторов
Работа с ценами в библиотеке DoEasy (Часть 59): Объект для хранения данных одного тика
Работа с ценами в библиотеке DoEasy (Часть 60): Список-серия тиковых данных символа