Работа с таймсериями в библиотеке DoEasy (Часть 36): Объект таймсерий всех используемых периодов символа

26 февраля 2020, 13:12
Artyom Trishkin
23
1 284

Содержание


Концепция

В прошлой статье мы начали новую серию описания создания библиотеки DoEasy и рассмотрели создание объекта "Бар" и список объектов-баров. Применительно к терминологии платформы MetaTrader мы создали одну таймсерию для одного символа по одному таймфрейму, и наполнили её данными по каждому бару этой таймсерии.
Применительно к терминологии библиотеки DoEasy мы создали объект-коллекцию баров для одного символа по одному таймфрейму. И теперь имеем возможность любого поиска и сортировки в пределах созданной коллекции (в пределах заданной глубины истории коллекции) по каждому из свойств имеющихся в коллекции объектов-баров. Говоря простыми словами, мы имеем возможность поиска различных параметров баров таймсерии и различных их комбинаций (различных комбинаций баров — в последующих реализациях), а также определять факт рождения нового бара в созданной коллекции и обновлять текущие данные в ней.

Это хорошо, но этого мало — ведь мы можем использовать для своих программ не один таймфрейм и не один символ. А, значит, нам нужно иметь столько коллекций таймсерий по символу, сколько его таймфреймов нам необходимо использовать в своих программах.
Созданная коллекция-таймсерия это позволяет сделать — она создаётся применительно к символу и таймфрейму, а, значит, мы можем создать столько таких коллекций по одному символу, сколько нам требуется.

И было бы удобно все коллекции по одному символу, но по разным таймфреймам хранить в одном объекте — объекте таймсерий символа. Далее уже из этих объектов будет создана одна общая коллекция таймсерий по разным символам и их таймфреймам.


Объект таймсерий символа

В обозримом будущем для многих классов библиотеки потребуется знание типа программы, в которой они запущены. Для этого нужно использовать функцию MQLInfoInteger() со спецификатором MQL_PROGRAM_TYPE. В данном случае функция возвратит тип выполняющейся mql5-программы.
Чтобы не прописывать в каждом классе переменные, хранящие тип программы, мы эту переменную объявим в базовом классе всех объектов программы, и все классы, унаследованные от базового класса, будут иметь переменную, хранящую тип выполняемой программы.

В защищённой секции класса CBaseObj, расположенного по пути \MQL5\Include\DoEasy\Objects\BaseObj.mqh,
объявим переменную-член класса, хранящую тип выполняемой программы:

//+------------------------------------------------------------------+
//| Класс базового объекта для всех объектов библиотеки              |
//+------------------------------------------------------------------+
#define  CONTROLS_TOTAL    (10)
class CBaseObj : public CObject
  {
private:
   int               m_long_prop_total;
   int               m_double_prop_total;
   //--- Заполняет массив свойств объекта
   template<typename T> bool  FillPropertySettings(const int index,T &array[][CONTROLS_TOTAL],T &array_prev[][CONTROLS_TOTAL],int &event_id);
protected:
   CArrayObj         m_list_events_base;                       // Список базовых событий объекта
   CArrayObj         m_list_events;                            // Список событий объекта
   ENUM_LOG_LEVEL    m_log_level;                              // Уровень логирования
   ENUM_PROGRAM_TYPE m_program;                                // Тип программы
   MqlTick           m_tick;                                   // Структура тика для получения котировочных данных

а в конструкторе класса установим в неё значение:

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CBaseObj::CBaseObj() : m_global_error(ERR_SUCCESS),
                       m_hash_sum(0),m_hash_sum_prev(0),
                       m_is_event(false),m_event_code(WRONG_VALUE),
                       m_chart_id_main(::ChartID()),
                       m_chart_id(::ChartID()),
                       m_folder_name(DIRECTORY),
                       m_name(__FUNCTION__),
                       m_long_prop_total(0),
                       m_double_prop_total(0),
                       m_first_start(true)
  {
   ::ArrayResize(this.m_long_prop_event,0,100);
   ::ArrayResize(this.m_double_prop_event,0,100);
   ::ArrayResize(this.m_long_prop_event_prev,0,100);
   ::ArrayResize(this.m_double_prop_event_prev,0,100);
   ::ZeroMemory(this.m_tick);
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_digits_currency=(#ifdef __MQL5__ (int)::AccountInfoInteger(ACCOUNT_CURRENCY_DIGITS) #else 2 #endif);
   this.m_list_events.Clear();
   this.m_list_events.Sort();
   this.m_list_events_base.Clear();
   this.m_list_events_base.Sort();
  }
//+------------------------------------------------------------------+

Теперь все объекты, унаследованные от базового класса всех объектов библиотеки, будут "иметь знание" о типе программы, в которой они запущены.

К листингу класса CNewBarObj, расположенного в файле \MQL5\Include\DoEasy\Objects\Series\NewBarObj.mqh, подключим файл класса базового объекта:

//+------------------------------------------------------------------+
//|                                                    NewBarObj.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"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "..\..\Objects\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Класс объекта "Новый бар"                                        |
//+------------------------------------------------------------------+

унаследуем объект "Новый бар" от базового объекта:

//+------------------------------------------------------------------+
//| Класс объекта "Новый бар"                                        |
//+------------------------------------------------------------------+
class CNewBarObj : public CBaseObj
  {
private:

и удалим из листинга "все упоминания" переменной типа программы — теперь тип программы устанавливается в CBaseObj:

//+------------------------------------------------------------------+
//| Класс объекта "Новый бар"                                        |
//+------------------------------------------------------------------+
class CNewBarObj
  {
private:
   ENUM_PROGRAM_TYPE m_program;                                   // Тип программы
   string            m_symbol;                                    // Символ
   ENUM_TIMEFRAMES   m_timeframe;                                 // Таймфрейм
   datetime          m_new_bar_time;                              // Время нового бара для автоматического управления временем
   datetime          m_prev_time;                                 // Прошлое время для автоматического управления временем
   datetime          m_new_bar_time_manual;                       // Время нового бара для ручного управления временем
   datetime          m_prev_time_manual;                          // Прошлое время для ручного управления временем
//--- Возвращает дату текущего бара
   datetime          GetLastBarDate(const datetime time);
public:
//--- Устанавливает (1) символ, (2) таймфрейм
   void              SetSymbol(const string symbol)               { this.m_symbol=(symbol==NULL || symbol==""   ? ::Symbol() : symbol);                     }
   void              SetPeriod(const ENUM_TIMEFRAMES timeframe)   { this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe); }
//--- Сохраняет время нового бара при ручном управлении временем
   void              SaveNewBarTime(const datetime time)          { this.m_prev_time_manual=this.GetLastBarDate(time);                                      }
//--- Возвращает (1) символ, (2) таймфрейм
   string            Symbol(void)                           const { return this.m_symbol;       }
   ENUM_TIMEFRAMES   Period(void)                           const { return this.m_timeframe;    }
//--- Возвращает (1) время нового бара
   datetime          TimeNewBar(void)                       const { return this.m_new_bar_time; }
//--- Возвращает флаг открытия нового бара при (1) автоматическом, (2) ручном управлении временем
   bool              IsNewBar(const datetime time);
   bool              IsNewBarManual(const datetime time);
//--- Конструкторы
                     CNewBarObj(void) : m_program((ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE)),
                                        m_symbol(::Symbol()),
                                        m_timeframe((ENUM_TIMEFRAMES)::Period()),
                                        m_prev_time(0),m_new_bar_time(0),
                                        m_prev_time_manual(0),m_new_bar_time_manual(0) {}
                     CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe);
  };
//+------------------------------------------------------------------+
//| Параметрический конструктор                                      |
//+------------------------------------------------------------------+
CNewBarObj::CNewBarObj(const string symbol,const ENUM_TIMEFRAMES timeframe) : m_symbol(symbol),m_timeframe(timeframe)
  {
   this.m_program=(ENUM_PROGRAM_TYPE)::MQLInfoInteger(MQL_PROGRAM_TYPE);
   this.m_prev_time=this.m_prev_time_manual=this.m_new_bar_time=this.m_new_bar_time_manual=0;
  }
//+------------------------------------------------------------------+

Полный листинг класса с исправлениями можно посмотреть в прикреплённых в конце статьи файлах.

Немного доработаем класс CSeries, созданный в прошлой статье.

Унаследуем класс от базового объекта CBaseObj вместо CObject и удалим из него переменную, хранящую тип программы:
//+------------------------------------------------------------------+
//| Класс "Таймсерия"                                                |
//+------------------------------------------------------------------+
class CSeries : public CBaseObj
  {
private:
   ENUM_PROGRAM_TYPE m_program;                                         // Тип программы
   ENUM_TIMEFRAMES   m_timeframe;                                       // Таймфрейм
   string            m_symbol;                                          // Символ
   uint              m_amount;                                          // Количество используемых данных таймсерии
   uint              m_bars;                                            // Количество баров в истории по символу и таймфрейму
   bool              m_sync;                                            // Флаг синхронизированности данных
   CArrayObj         m_list_series;                                     // Список-таймсерия
   CNewBarObj        m_new_bar_obj;                                     // Объект "Новый бар"
public:

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

//+------------------------------------------------------------------+
//| Класс "Таймсерия"                                                |
//+------------------------------------------------------------------+
class CSeries : public CBaseObj
  {
private:
   ENUM_TIMEFRAMES   m_timeframe;                                       // Таймфрейм
   string            m_symbol;                                          // Символ
   uint              m_amount;                                          // Количество используемых данных таймсерии
   uint              m_bars;                                            // Количество баров в истории по символу и таймфрейму
   bool              m_sync;                                            // Флаг синхронизированности данных
   CArrayObj         m_list_series;                                     // Список-таймсерия
   CNewBarObj        m_new_bar_obj;                                     // Объект "Новый бар"
public:
//--- Возвращает список-таймсерию
   CArrayObj        *GetList(void)                                      { return &m_list_series;}
//--- Возвращает список баров по выбранному (1) double, (2) integer и (3) string свойству, удовлетворяющему сравниваемому условию
   CArrayObj        *GetList(ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode=EQUAL) { return CSelect::ByBarProperty(this.GetList(),property,value,mode); }
   CArrayObj        *GetList(ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode=EQUAL){ return CSelect::ByBarProperty(this.GetList(),property,value,mode); }

//--- Устанавливает (1) символ, (2) таймфрейм, (3) символ и таймфрейм, (4) количество используемых данных таймсерии
   void              SetSymbol(const string symbol);
   void              SetTimeframe(const ENUM_TIMEFRAMES timeframe);
   void              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe);
   bool              SetAmountUsedData(const uint amount,const uint rates_total);

//--- Возвращает (1) символ, (2) таймфрейм, (3) количество используемых данных таймсерии,
//--- (4) количество баров в таймсерии, флаг нового бара с (5) автоматическим, (6) ручным управлением временем
   string            Symbol(void)                                          const { return this.m_symbol;                            }
   ENUM_TIMEFRAMES   Timeframe(void)                                       const { return this.m_timeframe;                         }
   uint              AmountUsedData(void)                                  const { return this.m_amount;                            }
   uint              Bars(void)                                            const { return this.m_bars;                              }
   bool              IsNewBar(const datetime time)                               { return this.m_new_bar_obj.IsNewBar(time);        }
   bool              IsNewBarManual(const datetime time)                         { return this.m_new_bar_obj.IsNewBarManual(time);  }
//--- Возвращает объект-бар по индексу (1) в списке, (2) в таймсерии, (3) реальный размер списка
   CBar             *GetBarByListIndex(const uint index);
   CBar             *GetBarBySeriesIndex(const uint index);
   int               DataTotal(void)                                       const { return this.m_list_series.Total();               }
//--- Возвращает (1) Open, (2) High, (3) Low, (4) Close, (5) время, (6) тиковый объём, (7) реальный объём, (8) спред бара по индексу
   double            Open(const uint index,const bool from_series=true);
   double            High(const uint index,const bool from_series=true);
   double            Low(const uint index,const bool from_series=true);
   double            Close(const uint index,const bool from_series=true);
   datetime          Time(const uint index,const bool from_series=true);
   long              TickVolume(const uint index,const bool from_series=true);
   long              RealVolume(const uint index,const bool from_series=true);
   int               Spread(const uint index,const bool from_series=true);

//--- Сохраняет время нового бара при ручном управлении временем
   void              SaveNewBarTime(const datetime time)                         { this.m_new_bar_obj.SaveNewBarTime(time);         }
//--- Синхронизирует данные по символу и таймфрейму с данными на сервере
   bool              SyncData(const uint amount,const uint rates_total);
//--- (1) Создаёт, (2) обновляет список-таймсерию
   int               Create(const uint amount=0);
   void              Refresh(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
                             
//--- Конструкторы
                     CSeries(void);
                     CSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const uint amount=0);
  };
//+------------------------------------------------------------------+

За пределами тела класса напишем реализацию методов установки символа и таймфрейма:

//+------------------------------------------------------------------+
//| Устанавливает символ                                             |
//+------------------------------------------------------------------+
void CSeries::SetSymbol(const string symbol)
  {
   this.m_symbol=(symbol==NULL || symbol==""   ? ::Symbol() : symbol);
   this.m_new_bar_obj.SetSymbol(this.m_symbol);
  }
//+------------------------------------------------------------------+
//| Устанавливает таймфрейм                                          |
//+------------------------------------------------------------------+
void CSeries::SetTimeframe(const ENUM_TIMEFRAMES timeframe)
  {
   this.m_timeframe=(timeframe==PERIOD_CURRENT ? (ENUM_TIMEFRAMES)::Period() : timeframe);
   this.m_new_bar_obj.SetPeriod(this.m_timeframe);
  }
//+------------------------------------------------------------------+

Сначала проверяются и корректируются при необходимости, и записываются в переменные переданные в методы значения,
затем это значение записывается в объект класса "Новый бар".

К листингу класса CSelect, расположенного по адресу \MQL5\Include\DoEasy\Services\Select.mqh,
вместо файла Bar.mqh класса CBar подключим файл класса CSeries:

//+------------------------------------------------------------------+
//|                                                       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\Series.mqh"
//+------------------------------------------------------------------+
//| Список-хранилище                                                 |
//+------------------------------------------------------------------+
CArrayObj   ListStorage; // Объект-хранилище для хранения сортированных списков коллекций
//+------------------------------------------------------------------+
//| Класс для выборки объектов, удовлетворяющих критерию             |
//+------------------------------------------------------------------+
class CSelect
  {

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

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

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

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

  • задать вручную использование в программе:
    • указанных периодов графика одного символа
    • всех возможных периодов графика одного символа
  • создавать:
    • указанные объекты-таймсерии одного символа
    • все возможные объекты-таймсерии одного символа
  • обновлять данные:
    • указанных объектов-таймсерий одного символа
    • всех созданных объектов-таймсерий одного символа

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

Как уже стало обычным, сначала допишем необходимые для нового класса сообщения — индексы сообщений и тексты, соответствующие индексам.

В файле Datas.mqh, расположенном по адресу \MQL5\Include\DoEasy\Datas.mqh, добавим индексы новых сообщений:

   MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA,        // Сначала нужно установить требуемое количество данных при помощи SetAmountUsedData()
  
//--- CTimeSeries
   MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL,              // Сначала нужно установить символ при помощи SetSymbol()
   MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME,            // Неизвестный таймфрейм
   MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ,             // Не удалось получить объект-таймсерию
  };
//+------------------------------------------------------------------+

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

   {"Сначала нужно установить требуемое количество данных при помощи SetAmountUsedData()","First you need to set the required amount of data using SetAmountUsedData()"},
   
   {"Сначала нужно установить символ при помощи SetSymbol()","First you need to set the Symbol using SetSymbol()"},
   {"Неизвестный таймфрейм","Unknown timeframe"},
   {"Не удалось получить объект-таймсерию ","Failed to get timeseries object "},
   
  };
//+---------------------------------------------------------------------+

В каталоге библиотеки \MQL5\Include\DoEasy\Objects\Series\ создадим новый файл TimeSeries.mqh класса CTimeSeries с подключенным к нему файлом объекта-таймсерии Series.mqh, и унаследованным от базового объекта стандартной библиотеки CObject.
Сразу заполним тело класса всем необходимым содержимым, а далее рассмотрим все переменные и методы индивидуально:

//+------------------------------------------------------------------+
//|                                                   TimeSeries.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"
#property strict    // Нужно для mql4
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include "Series.mqh"
//+------------------------------------------------------------------+
//| Класс "Таймсерии"                                                |
//+------------------------------------------------------------------+
class CTimeSeries : public CObject
  {
private:
   string            m_symbol;                                             // Символ таймсерий
   CArrayObj         m_list_series;                                        // Список таймсерий по таймфреймам
//--- Возвращает (1) индекс таймфрейма в списке, (2) таймфрейм по индексу
   char              IndexTimeframe(const ENUM_TIMEFRAMES timeframe) const;
   ENUM_TIMEFRAMES   TimeframeByIndex(const uchar index)             const;
public:
//--- Возвращает (1) полный список таймсерий, (2) указанный объект-таймсерию, (3) объект-таймсерию по индексу
   CArrayObj        *GetList(void)                                         { return &this.m_list_series;                                        }
   CSeries          *GetSeries(const ENUM_TIMEFRAMES timeframe)            { return this.m_list_series.At(this.IndexTimeframe(timeframe));      }
   CSeries          *GetSeriesByIndex(const uchar index)                   { return this.m_list_series.At(index);                               }
//--- Устанавливает/возвращает символ таймсерии
   void              SetSymbol(const string symbol)                        { this.m_symbol=(symbol==NULL || symbol=="" ? ::Symbol() : symbol);  }
   string            Symbol(void)                                    const { return this.m_symbol;                                              }
//--- Устанавливает глубину истории (1) указанной таймсерии, (2) всех используемых таймсерий символа
   bool              SetAmountUsedData(const ENUM_TIMEFRAMES timeframe,const uint amount=0,const int rates_total=0);
   bool              SetAmountAllUsedData(const uint amount=0,const int rates_total=0);
//--- Возвращает флаг синхронизации данных с данными на сервере (1) указанной таймсери, (2) всех таймсерий
   bool              SyncData(const ENUM_TIMEFRAMES timeframe,const uint amount=0,const uint rates_total=0);
   bool              SyncAllData(const uint amount=0,const uint rates_total=0);
   
//--- Создаёт (1) указанный список-таймсерию, (2) все списки-таймсерии
   bool              SeriesCreate(const ENUM_TIMEFRAMES timeframe,const uint amount=0);
   bool              SeriesCreateAll(const uint amount=0);
//--- Обновляет (1) указанный список-таймсерию, (2) все списки-таймсерии
   void              Refresh(const ENUM_TIMEFRAMES timeframe,
                             const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
   void              RefreshAll(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0);
//--- Конструктор
                     CTimeSeries(void);
  };
//+------------------------------------------------------------------+

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

Объект массива указателей на экземпляры классов CObject m_list_series предназначен для хранения в нём объектов-таймсерий, созданных в прошлой статье. Таких объектов может быть в этом списке ровно по количеству всех доступных в платформе таймфреймов, и располагаться в списке они все будут в порядке их следования в перечислении ENUM_TIMEFRAMES, что в свою очередь даёт нам возможность точно знать индекс каждого объекта-таймсерии в этом списке. Для возврата индекса в списке объекта-таймсерии созданы два метода:

Метод IndexTimeframe() — возвращает индекс объекта-таймсерии в списке по значению таймфрейма.
Его реализация за пределами тела класса:

//+------------------------------------------------------------------+
//| Возвращает индекс таймфрейма в списке                            |
//+------------------------------------------------------------------+
char CTimeSeries::IndexTimeframe(ENUM_TIMEFRAMES timeframe) const
  {
   int statement=(timeframe==PERIOD_CURRENT ? ::Period() : timeframe);
   switch(statement)
     {
      case PERIOD_M1    :  return 0;
      case PERIOD_M2    :  return 1;
      case PERIOD_M3    :  return 2;
      case PERIOD_M4    :  return 3;
      case PERIOD_M5    :  return 4;
      case PERIOD_M6    :  return 5;
      case PERIOD_M10   :  return 6;
      case PERIOD_M12   :  return 7;
      case PERIOD_M15   :  return 8;
      case PERIOD_M20   :  return 9;
      case PERIOD_M30   :  return 10;
      case PERIOD_H1    :  return 11;
      case PERIOD_H2    :  return 12;
      case PERIOD_H3    :  return 13;
      case PERIOD_H4    :  return 14;
      case PERIOD_H6    :  return 15;
      case PERIOD_H8    :  return 16;
      case PERIOD_H12   :  return 17;
      case PERIOD_D1    :  return 18;
      case PERIOD_W1    :  return 19;
      case PERIOD_MN1   :  return 20;
      default           :  ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_UNKNOWN_TIMEFRAME)); return WRONG_VALUE;
     }
  }
//+------------------------------------------------------------------+

Тут всё наглядно — в зависимости от переданного в метод таймфрейма возвращается его порядковый номер расположения в перечислении ENUM_TIMEFRAMES, и соответственно — его индекса расположения в списке m_list_series.  

Метод TimeframeByIndex() — возвращает таймфрейм по индексу объекта-таймсерии в списке.
Его реализация за пределами тела класса:

//+------------------------------------------------------------------+
//| Возвращает таймфрейм по индексу                                  |
//+------------------------------------------------------------------+
ENUM_TIMEFRAMES CTimeSeries::TimeframeByIndex(const uchar index) const
  {
   switch(index)
     {
      case 0   :  return PERIOD_M1;
      case 1   :  return PERIOD_M2;
      case 2   :  return PERIOD_M3;
      case 3   :  return PERIOD_M4;
      case 4   :  return PERIOD_M5;
      case 5   :  return PERIOD_M6;
      case 6   :  return PERIOD_M10;
      case 7   :  return PERIOD_M12;
      case 8   :  return PERIOD_M15;
      case 9   :  return PERIOD_M20;
      case 10  :  return PERIOD_M30;
      case 11  :  return PERIOD_H1;
      case 12  :  return PERIOD_H2;
      case 13  :  return PERIOD_H3;
      case 14  :  return PERIOD_H4;
      case 15  :  return PERIOD_H6;
      case 16  :  return PERIOD_H8;
      case 17  :  return PERIOD_H12;
      case 18  :  return PERIOD_D1;
      case 19  :  return PERIOD_W1;
      case 20  :  return PERIOD_MN1;
      default  :  ::Print(DFUN,CMessage::Text(MSG_LIB_SYS_NOT_GET_DATAS),"... ",CMessage::Text(MSG_SYM_STATUS_INDEX),": ",(string)index); return WRONG_VALUE;
     }
  }
//+------------------------------------------------------------------+

Данный метод является обратным методу IndexTimeframe(). В зависимости от переданного в метод индекса возвращается соответствующий индексу таймфрейм в порядке расположения в перечислении ENUM_TIMEFRAMES.

Метод GetList() возвращает полный список всех таймсерий "как есть" в управляющую программу. И уже в программе можно выбирать из полученного списка нужную таймсерию для работы с ней.

Метод GetSeries() возвращает указанный объект-таймсерию из списка m_list_series по наименованию требуемой таймсерии из перечисления ENUM_TIMEFRAMES. Индекс таймсерии в списке получаем ранее рассмотренным методом IndexTimeframe()

Метод GetSeriesByIndex() возвращает объект-таймсерию по его индексу в списке m_list_series.

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

//+------------------------------------------------------------------+
//| Устанавливает глубину истории указанной таймсерии                |
//+------------------------------------------------------------------+
bool CTimeSeries::SetAmountUsedData(const ENUM_TIMEFRAMES timeframe,const uint amount=0,const int rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL));
      return false;
     }
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   return series_obj.SetAmountUsedData(amount,rates_total);
  }
//+------------------------------------------------------------------+

В метод передаётся таймфрейм таймсерии, глубину истории которой необходимо установить, требуемый размер исторических данных таймсерии (глубина истории; при значении 0 используется глубина в 1000 баров) и количество баров текущей таймсерии (только для индикаторов при установке глубины истории для текущего символа на текущем таймфрейме — передаём параметр rates_total в OnCalculate(), в остальных случаях параметр не имеет значения).

Если символ объекту класса ещё не установлен — выводим об этом сообщение и возвращаем false.
Получаем запрошенный объект-таймсерию из списка по его индексу, полученному по наименованию таймфрейма, и возвращаем результат установки глубины истории при помощи одноимённого метода класса объекта-таймсерии, рассмотренного нами в прошлой статье.

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

//+------------------------------------------------------------------+
//| Устанавливает глубину истории всех используемых таймсерий символа|
//+------------------------------------------------------------------+
bool CTimeSeries::SetAmountAllUsedData(const uint amount=0,const int rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL));
      return false;
     }
   bool res=true;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL)
         continue;
      res &=series_obj.SetAmountUsedData(amount,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+

В метод передаётся  требуемый размер исторических данных таймсерии (глубина истории; при значении 0 используется глубина в 1000 баров) и количество баров текущей таймсерии (только для индикаторов при установке глубины истории для текущего символа на текущем таймфрейме — передаём параметр rates_total в OnCalculate(), в остальных случаях параметр не имеет значения).

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

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

//+------------------------------------------------------------------+
//| Возвращает флаг синхронизации данных                             |
//| указанной таймсерии с данными на сервере                         |
//+------------------------------------------------------------------+
bool CTimeSeries::SyncData(const ENUM_TIMEFRAMES timeframe,const uint amount=0,const uint rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL));
      return false;
     }
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ),this.m_symbol," ",TimeframeDescription(timeframe));
      return false;
     }
   return series_obj.SyncData(amount,rates_total);
  }
//+------------------------------------------------------------------+

В метод передаётся таймфрейм таймсерии, флаг синхронизации которой необходимо вернуть, требуемый размер исторических данных таймсерии (глубина истории; при значении 0 используется глубина в 1000 баров) и количество баров текущей таймсерии (только для индикаторов при установке глубины истории для текущего символа на текущем таймфрейме — передаём параметр rates_total в OnCalculate(), в остальных случаях параметр не имеет значения).

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

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

//+------------------------------------------------------------------+
//| Возвращает флаг синхронизации данных                             |
//| всех таймсерй с данными на сервере                               |
//+------------------------------------------------------------------+
bool CTimeSeries::SyncAllData(const uint amount=0,const uint rates_total=0)
  {
   if(this.m_symbol==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_TEXT_FIRS_SET_SYMBOL));
      return false;
     }
   bool res=true;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL)
         continue;
      res &=series_obj.SyncData(amount,rates_total);
     }
   return res;
  }
//+------------------------------------------------------------------+

В метод передаётся  требуемый размер исторических данных таймсерии (глубина истории; при значении 0 используется глубина в 1000 баров) и количество баров текущей таймсерии (только для индикаторов при установке глубины истории для текущего символа на текущем таймфрейме — передаём параметр rates_total в OnCalculate(), в остальных случаях параметр не имеет значения).

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

Реализация метода, создающего указанный список-таймсерию:

//+------------------------------------------------------------------+
//| Создаёт указанный список-таймсерию                               |
//+------------------------------------------------------------------+
bool CTimeSeries::Create(const ENUM_TIMEFRAMES timeframe,const uint amount=0)
  {
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_TS_FAILED_GET_SERIES_OBJ),this.m_symbol," ",TimeframeDescription(timeframe));
      return false;
     }
   if(series_obj.AmountUsedData()==0)
     {
      ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BAR_TEXT_FIRS_SET_AMOUNT_DATA));
      return false;
     }
   return(series_obj.Create(amount)>0);
  }
//+------------------------------------------------------------------+

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

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

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

Реализация метода, создающего все списки-таймсерии:

//+------------------------------------------------------------------+
//| Создаёт все списки-таймсерии                                     |
//+------------------------------------------------------------------+
bool CTimeSeries::CreateAll(const uint amount=0)
  {
   bool res=true;
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL || series_obj.AmountUsedData()==0)
         continue;
      res &=(series_obj.Create(amount)>0);
     }
   return res;
  }
//+------------------------------------------------------------------+

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

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

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

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

//+------------------------------------------------------------------+
//| Обновляет указанный список-таймсерию                             |
//+------------------------------------------------------------------+
void CTimeSeries::Refresh(const ENUM_TIMEFRAMES timeframe,
                          const datetime time=0,
                          const double open=0,
                          const double high=0,
                          const double low=0,
                          const double close=0,
                          const long tick_volume=0,
                          const long volume=0,
                          const int spread=0)
  {
   CSeries *series_obj=this.m_list_series.At(this.IndexTimeframe(timeframe));
   if(series_obj==NULL || series_obj.DataTotal()==0)
      return;
   series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread);
  }
//+------------------------------------------------------------------+

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

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

Реализация метода, обновляющего все списки-таймсерии:

//+------------------------------------------------------------------+
//| Обновляет все списки-таймсерии                                   |
//+------------------------------------------------------------------+
void CTimeSeries::RefreshAll(const datetime time=0,
                             const double open=0,
                             const double high=0,
                             const double low=0,
                             const double close=0,
                             const long tick_volume=0,
                             const long volume=0,
                             const int spread=0)
  {
   for(int i=0;i<21;i++)
     {
      CSeries *series_obj=this.m_list_series.At(i);
      if(series_obj==NULL || series_obj.DataTotal()==0)
         continue;
      series_obj.Refresh(time,open,high,low,close,tick_volume,volume,spread);
     }
  }
//+------------------------------------------------------------------+

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

В цикле по списку всех таймфреймов получаем очередной объект-таймсерию по индексу циклаЕсли объект получить не удалось, или размер созданной истории для таймфрейма равен нулю (таймфрейм не используется или не создавался при помощи метода Create()) — переходим к следующему таймфрейму.

Далее вызываем одноимённый метод обновления объекта-таймсерии, рассмотренный нами в прошлой статье.

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

В файле класса CEngine, расположенного по адресу \MQL5\Include\DoEasy\Engine.mqh, заменим строку подключения файла класса объекта-таймсерии:

//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#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 "TradingControl.mqh"
#include "Objects\Series\Series.mqh"
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#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 "TradingControl.mqh"
#include "Objects\Series\TimeSeries.mqh"
//+------------------------------------------------------------------+

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

Тест

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

Для тестирования работы класса создадим две версии тестового советника при помощи директив условной компиляции.

  • Первая версия советника будет создавать две таймсерии текущего символа:
    одна для периода M15, состоящая всего из двух баров,
    другая, состоящая из десяти баров — для текущего периода графика, на котором запущен советник.
  • Вторая версия советника будет создавать все таймсерии текущего символа со значениями по умолчанию:
    будет создано либо 1000 баров для каждой таймсерии, либо максимально-возможное — при условии, что доступное количество баров для таймсерии меньше 1000.

В блоке глобальных переменных советника оставим только один объект класса таймсерий — вместо класса CSeries определим переменную класса CTimeSeries так как объекты класса CSeries теперь являются составной частью объекта класса CTimeSeries.
Удалим один объект CSeries:

//--- global variables
CEngine        engine;
CSeries        series;
CSeries        series_m1;
SDataButt      butt_data[TOTAL_BUTT];

а второй объект переименуем и определим его тип как CTimeSeries:

//--- global variables
CEngine        engine;
CTimeSeries    timeseries;
SDataButt      butt_data[TOTAL_BUTT];

В самом конце обработчика OnInit() советника напишем блок кода для создания таймсерий в зависимости от существования/отсутствия заданного идентификатора TIMESERIES_ALL:

//--- Проверка воспроизведения стандартного звука по макроподстановке и пользовательского звука по описанию
   engine.PlaySoundByDescription(SND_OK);
   Sleep(600);
   engine.PlaySoundByDescription(TextByLanguage("Звук упавшей монетки 2","The sound of a falling coin 2"));

//--- Установка символа создаваемым таймсериям
   timeseries.SetSymbol(Symbol());
//#define TIMESERIES_ALL
//--- Создание двух таймсерий
   #ifndef TIMESERIES_ALL
      timeseries.SyncData(PERIOD_CURRENT,10);
      timeseries.Create(PERIOD_CURRENT);
      timeseries.SyncData(PERIOD_M15,2);
      timeseries.Create(PERIOD_M15);
//--- Создание всех таймсерий
   #else 
      timeseries.SyncAllData();
      timeseries.CreateAll();
   #endif 
//--- Проверка созданных таймсерий
   CArrayObj *list=timeseries.GetList();
   Print(TextByLanguage("Данные созданных таймсерий:","Data of created timeseries:"));
   for(int i=0;i<list.Total();i++)
     {
      CSeries *series_obj=timeseries.GetSeriesByIndex((uchar)i);
      if(series_obj==NULL || series_obj.AmountUsedData()==0 || series_obj.DataTotal()==0)
         continue;
      Print(
            DFUN,i,": ",series_obj.Symbol()," ",TimeframeDescription(series_obj.Timeframe()),
            ": AmountUsedData=",series_obj.AmountUsedData(),", DataTotal=",series_obj.DataTotal(),", Bars=",series_obj.Bars()
           );
     }
   Print("");
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

В первую очеред обязательно устанавливаем для класса таймсерий символа наименование символа, далее у нас определён и закомментирован идентификатор в виде макроподстановки, наличие/отсутствие которого и будет определять какую версию кода нужно компилировать — для создания двух таймсерий (при отсутствии идентификатора), или для создания всех таймсерий (при наличии идентификатора)

В общем случае и по состоянию текущей версии класса объекта всех таймсерий символа, для создания таймсерии, нужно:

  1. установить символ объекта всех таймсерий символа,
  2. вызвать метод установки глубины истории таймсерии и проверки синхронизации данных таймсерии с сервером,
  3. на основании установленной глубины истории создать таймсерию.

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

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

Теперь в обработчике OnTick(), в самом его конце, вставим блок кода для обновления данных всех созданных таймсерий символа:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer();       // Работа в таймере
      PressButtonsControl();  // Контроль нажатия кнопок
      EventsHandling();       // Работа с событиями
     }
//--- Если установлен флаг трейлинга
   if(trailing_on)
     {
      TrailingPositions();    // Трейлинг позиций
      TrailingOrders();       // Трейлинг отложенных ордеров
     }
//--- Обновление созданных таймсерий
   CArrayObj *list=timeseries.GetList();
   for(int i=0;i<list.Total();i++)
     {
      CSeries *series_obj=timeseries.GetSeriesByIndex((uchar)i);
      if(series_obj==NULL || series_obj.DataTotal()==0)
         continue;
      series_obj.Refresh();
      if(series_obj.IsNewBar(0))
        {
         Print(TextByLanguage("Новый бар на ","New bar on "),series_obj.Symbol()," ",TimeframeDescription(series_obj.Timeframe())," ",TimeToString(series_obj.Time(0)));
         if(series_obj.Timeframe()==Period())
            engine.PlaySoundByDescription(SND_NEWS);
        }
     }
  }
//+------------------------------------------------------------------+

Здесь: получаем список таймсерий из объекта всех таймсерий символа, в цикле по списку таймсерий получаем очередной объект-таймсерию по индексу цикла, если таймсерию получить не удалось или в ней нет данных (баров) — переходим к следующей таймсерии очередного таймфрейма. Если объект-таймсерия получена — обновляем её. Если у таймсерии установлен флаг нового бара — выводим об этом сообщение (для таймсерии текущего периода дополнительно проиграем звук "news.wav")

Скомпилируем советник (строка 176 с определением макроподстановки #define TIMESERIES_ALL должна быть закомментирована) — будет скомпилирована версия советника, создающего две таймсерии.
Запустим его в терминале на графике с периодом M30 — в журнал будут выведены записи о параметрах двух созданных таймсерий и, спустя некоторое время, появятся записи об открытии новых баров на периодах графиков M15 и M30:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
Work only with the current symbol. The number of symbols used: 1
Data of created timeseries:
OnInit: 8: EURUSD M15: AmountUsedData=2, DataTotal=2, Bars=5000
OnInit: 10: EURUSD M30: AmountUsedData=10, DataTotal=10, Bars=5030
 
New bar on EURUSD M15 2020.02.20 20:45
New bar on EURUSD M15 2020.02.20 21:00
New bar on EURUSD M30 2020.02.20 21:00
New bar on EURUSD M15 2020.02.20 21:15
New bar on EURUSD M15 2020.02.20 21:30
New bar on EURUSD M30 2020.02.20 21:30

Теперь раскомментируем строку 176 с определением макроподстановки #define TIMESERIES_ALL и скомпилируем советник — будет создана версия советника, создающего все таймсерии со значениями по умолчанию.

Запустим его на графике символа. В журнал будут выведены записи о параметрах всех созданных таймсерий и, спустя некоторое время, появятся записи об открытии новых баров на периодах графиков созданных таймсерий:

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
Work only with the current symbol. The number of symbols used: 1
Data of created timeseries:
OnInit: 0: EURUSD M1: AmountUsedData=1000, DataTotal=1000, Bars=5140
OnInit: 1: EURUSD M2: AmountUsedData=1000, DataTotal=1000, Bars=4010
OnInit: 2: EURUSD M3: AmountUsedData=1000, DataTotal=1000, Bars=3633
OnInit: 3: EURUSD M4: AmountUsedData=1000, DataTotal=1000, Bars=3445
OnInit: 4: EURUSD M5: AmountUsedData=1000, DataTotal=1000, Bars=3332
OnInit: 5: EURUSD M6: AmountUsedData=1000, DataTotal=1000, Bars=3256
OnInit: 6: EURUSD M10: AmountUsedData=1000, DataTotal=1000, Bars=3106
OnInit: 7: EURUSD M12: AmountUsedData=1000, DataTotal=1000, Bars=3068
OnInit: 8: EURUSD M15: AmountUsedData=1000, DataTotal=1000, Bars=5004
OnInit: 9: EURUSD M20: AmountUsedData=1000, DataTotal=1000, Bars=2993
OnInit: 10: EURUSD M30: AmountUsedData=1000, DataTotal=1000, Bars=5032
OnInit: 11: EURUSD H1: AmountUsedData=1000, DataTotal=1000, Bars=5352
OnInit: 12: EURUSD H2: AmountUsedData=1000, DataTotal=1000, Bars=6225
OnInit: 13: EURUSD H3: AmountUsedData=1000, DataTotal=1000, Bars=6212
OnInit: 14: EURUSD H4: AmountUsedData=1000, DataTotal=1000, Bars=5292
OnInit: 15: EURUSD H6: AmountUsedData=1000, DataTotal=1000, Bars=5182
OnInit: 16: EURUSD H8: AmountUsedData=1000, DataTotal=1000, Bars=5443
OnInit: 17: EURUSD H12: AmountUsedData=1000, DataTotal=1000, Bars=5192
OnInit: 18: EURUSD D1: AmountUsedData=1000, DataTotal=1000, Bars=5080
OnInit: 19: EURUSD W1: AmountUsedData=1000, DataTotal=1000, Bars=2562
OnInit: 20: EURUSD MN1: AmountUsedData=589, DataTotal=589, Bars=589

New bar on EURUSD M1 2020.02.20 21:41
New bar on EURUSD M1 2020.02.20 21:42
New bar on EURUSD M2 2020.02.20 21:42
New bar on EURUSD M3 2020.02.20 21:42
New bar on EURUSD M6 2020.02.20 21:42
New bar on EURUSD M1 2020.02.20 21:43
New bar on EURUSD M1 2020.02.20 21:44
New bar on EURUSD M2 2020.02.20 21:44
New bar on EURUSD M4 2020.02.20 21:44
New bar on EURUSD M1 2020.02.20 21:45
New bar on EURUSD M3 2020.02.20 21:45
New bar on EURUSD M5 2020.02.20 21:45
New bar on EURUSD M15 2020.02.20 21:45

Запустим в тестере в визуальном режиме на периоде графика M5:


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

Всё работает так, как задумывалось на данном этапе создания функционала для работы с таймсериями одного символа.

Что дальше

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

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

К содержанию

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

Работа с таймсериями в библиотеке DoEasy (Часть 35): Объект "Бар" и список-таймсерия символа


Прикрепленные файлы |
MQL5.zip (3711.04 KB)
MQL4.zip (3711.04 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (23)
Artyom Trishkin
Artyom Trishkin | 28 фев 2020 в 15:26
Sergey Pavlov:

Эх Артём... откуда это у Вас?

Я говорил о таланте троллинга. 
Sergey Pavlov
Sergey Pavlov | 28 фев 2020 в 15:29
Artyom Trishkin:
Никто вас не называл человеком второго сорта, кроме вас самих.
Но обсуждать тему оплаты статей нет желания. Одно скажу - это хорошо, что их оплачивают. Без этого тут не было бы подавляющего количества статей. И мне не стыдно за свою работу получать вознаграждения. Если вы хотели это обсудить, то, думаю, я вам ответил.

Очень жаль... Вы так ничего и не поняли...

Удачи вам.

Artyom Trishkin
Artyom Trishkin | 28 фев 2020 в 16:53
Fast235:

Артем, я очень уважаю Ваш труд (с большой буквы пишу исключительно)

если думаете я тут нуб, поверьте, я прочитал здесь в совокупности, скорее всего больше чем кто либо из участников

на самом деле написано хорошо, просто страшно использовать такие гиганские шюпальца, вместо - купить/лот/сл/тк/слидер/коммент

в этом вообщем-то и обвиняют MQL5

Спасибо. В библиотеке реализуется то, что чаще всего требуется. Просто купить ... - для этого не нужна библиотека.
Artyom Trishkin
Artyom Trishkin | 28 фев 2020 в 16:53
Sergey Pavlov:

Очень жаль... Вы так ничего и не поняли...

Удачи вам.

Прошу прощения если задел. Спасибо за помощь.
Fast235
Fast235 | 29 фев 2020 в 02:49
Artyom Trishkin:
Спасибо. В библиотеке реализуется то, что чаще всего требуется. Просто купить ... - для этого не нужна библиотека.

да, я помню, работа с историей,

разработчик(MQL5) большой энтузиаст, для себя сделал

может лучше и нет решения

Прогнозирование временных рядов (Часть 2): метод наименьших квадратов опорных векторов (LS-SVM) Прогнозирование временных рядов (Часть 2): метод наименьших квадратов опорных векторов (LS-SVM)

В статье рассмотрена теория и практическое применение алгоритма прогнозирования временных рядов на основе метода опорных векторов, предложена его реализация на MQL, предоставлены тестовые индикаторы и эксперты. Данная технология до сих пор не была ещё реализована на MQL. Но сначала нам потребуется познакомиться с некоторым математическим аппаратом.

Непрерывная скользящая оптимизация (Часть 5): Обзор проекта автооптимизатора, а также создание графического интерфейса Непрерывная скользящая оптимизация (Часть 5): Обзор проекта автооптимизатора, а также создание графического интерфейса

Продолжаем описание скользящей оптимизации в терминале MetaTrader 5. Рассмотрев в прошлых статьях методы формирования отчета оптимизации и способ его фильтрации, мы перешли к описанию внутренней структуры приложения, отвечающего за сам процесс оптимизации. Автооптимизатор, выполненный как приложение на C#, имеет собственный графический интерфейс. Именно созданию данного графического интерфейса и посвящена текущая статья.

Применение OLAP в трейдинге (Часть 4): Количественный и визуальный анализ отчетов тестера Применение OLAP в трейдинге (Часть 4): Количественный и визуальный анализ отчетов тестера

Статья предлагает базовый инструментарий для OLAP-анализа отчетов тестера об одиночных проходах и результатах оптимизации в виде файлов стандартных форматов (tst и opt), а также интерактивный графический интерфейс к нему. Исходные коды MQL прилагаются.

Мультивалютный мониторинг торговых сигналов (Часть 3): Внедряем алгоритмы поиска Мультивалютный мониторинг торговых сигналов (Часть 3): Внедряем алгоритмы поиска

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