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

20 ноября 2020, 11:02
Artyom Trishkin
0
1 089

Содержание


Концепция

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

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

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


Доработка классов библиотеки

Сначала, как это стало обычным, добавим новые сообщения библиотеки.
В файле \MQL5\Include\DoEasy\Data.mqh впишем индексы новых сообщений:

//--- CIndicatorsCollection
   MSG_LIB_SYS_FAILED_ADD_IND_TO_LIST,                // Ошибка. Не удалось добавить объект-индикатор в список
   MSG_LIB_SYS_INVALID_IND_POINTER,                   // Ошибка. Передан неверный указатель на объект-индикатор
   MSG_LIB_SYS_IND_ID_EXIST,                          // Ошибка. Уже существует объект-индикатор с идентификатором
   
//--- CDataInd
   MSG_LIB_TEXT_IND_DATA_IND_BUFFER_NUM,              // Номер буфера индикатора
   MSG_LIB_TEXT_IND_DATA_BUFFER_VALUE,                // Значение буфера индикатора
   
  };
//+------------------------------------------------------------------+

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

   {"Ошибка. Не удалось добавить объект-индикатор в список","Error. Failed to add indicator object to list"},
   {"Ошибка. Передан неверный указатель на объект-индикатор","Error. Invalid pointer to indicator object passed"},
   {"Ошибка. Уже существует объект-индикатор с идентификатором","Error. There is already exist an indicator object with ID"},
   
   {"Номер буфера индикатора","Indicator buffer number"},
   {"Значение буфера индикатора","Indicator buffer value"},
   
  };
//+---------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Данные для работы с индикаторными данными                        |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Целочисленные свойства данных индикатора                         |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_INTEGER
  {
   IND_DATA_PROP_TIME = 0,                                  // Время начала периода бара данных индикатора
   IND_DATA_PROP_PERIOD,                                    // Период данных индикатора (таймфрейм)
   IND_DATA_PROP_INDICATOR_TYPE,                            // Тип индикатора
   IND_DATA_PROP_IND_BUFFER_NUM,                            // Номер буфера данных индикатора
   IND_DATA_PROP_IND_ID,                                    // Идентификатор индикатора
  }; 
#define IND_DATA_PROP_INTEGER_TOTAL (5)                     // Общее количество целочисленных свойств данных индикатора
#define IND_DATA_PROP_INTEGER_SKIP  (0)                     // Количество неиспользуемых в сортировке свойств данных индикатора
//+------------------------------------------------------------------+
  • Сортировка по времени — это основной вид сортировки, при котором все данные будут расположены в порядке их следования данных буфера индикатора в терминале.
  • Значение таймфрейма включено в целочисленные свойства для того, чтобы можно было сравнивать в последующем значения двух буферов индикатора на разных таймфреймах.
  • Тип индикатора будет содержать значение типа из перечисления ENUM_INDICATOR.
  • Номер буфера индикатора — порядковый номер от нуля и далее по количеству буферов индикатора.
  • Идентификатор индикатора — по этому свойству можно будет найти данные нужного индикатора, которому в программе был присвоен идентификатор. Его мы обсуждали в прошлой статье.

Вещественные свойства объекта:

//+------------------------------------------------------------------+
//| Вещественные свойства данных индикатора                          |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_DOUBLE
  {
//--- данные бара
   IND_DATA_PROP_BUFFER_VALUE = IND_DATA_PROP_INTEGER_TOTAL,// Значение величины данных индикатора
  }; 
#define IND_DATA_PROP_DOUBLE_TOTAL  (1)                     // Общее количество вещественных свойств данных индикатора
#define IND_DATA_PROP_DOUBLE_SKIP   (0)                     // Количество неиспользуемых в сортировке свойств данных индикатора
//+------------------------------------------------------------------+

Здесь у нас есть лишь одно свойство — значение в буфере индикатора, соответствующее бару, для которого создан объект данных индикатора.

Строковые свойства объекта:

//+------------------------------------------------------------------+
//| Строковые свойства данных индикатора                             |
//+------------------------------------------------------------------+
enum ENUM_IND_DATA_PROP_STRING
  {
   IND_DATA_PROP_SYMBOL = (IND_DATA_PROP_INTEGER_TOTAL+IND_DATA_PROP_DOUBLE_TOTAL), // Символ данных индикатора
   IND_DATA_PROP_IND_NAME,                                  // Наименование индикатора
   IND_DATA_PROP_IND_SHORTNAME,                             // Короткое наименование индикатора
  };
#define IND_DATA_PROP_STRING_TOTAL  (3)                     // Общее количество строковых свойств данных индикатора
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Возможные критерии сортировки данных индикаторов                 |
//+------------------------------------------------------------------+
#define FIRST_IND_DATA_DBL_PROP          (IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_INTEGER_SKIP)
#define FIRST_IND_DATA_STR_PROP          (IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_INTEGER_SKIP+IND_DATA_PROP_DOUBLE_TOTAL-IND_DATA_PROP_DOUBLE_SKIP)
enum ENUM_SORT_IND_DATA_MODE
  {
//--- Сортировка по целочисленным свойствам
   SORT_BY_IND_DATA_TIME = 0,                               // Сортировать по времени начала периода бара данных индикатора
   SORT_BY_IND_DATA_PERIOD,                                 // Сортировать по периоду данных индикатора (таймфрейм)
   SORT_BY_IND_DATA_INDICATOR_TYPE,                         // Сортировать по типу индикатора
   SORT_BY_IND_DATA_IND_BUFFER_NUM,                         // Сортировать по номеру буфера данных индикатора
   SORT_BY_IND_DATA_IND_ID,                                 // Сортировать по идентификатору индикатора
//--- Сортировка по вещественным свойствам
   SORT_BY_IND_DATA_BUFFER_VALUE = FIRST_IND_DATA_DBL_PROP, // Сортировать по значению величины данных индикатора
//--- Сортировка по строковым свойствам
   SORT_BY_IND_DATA_SYMBOL = FIRST_IND_DATA_STR_PROP,       // Сортировать по символу данных индикатора
   SORT_BY_IND_DATA_IND_NAME,                               // Сортировать по наименованию индикатора
   SORT_BY_IND_DATA_IND_SHORTNAME,                          // Сортировать по короткому наименованию индикатора
  };
//+------------------------------------------------------------------+

Объект данных буфера индикатора

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

В каталоге библиотеки  \MQL5\Include\DoEasy в папке \Objects\Indicators\ создадим новый класс CDataInd в файле DataInd.mqh:

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

В принципе, объект содержит стандартные для объектов библиотеки поля и методы, и идентичен объекту-бару, рассмотренному нами в статье 35, но, в отличие от объекта-бара, имеет меньше свойств (все свойства объекта-данных буфера индикатора нами были описаны в перечислениях выше).

Рассмотрим тело класса объекта данных индикаторного буфера:

//+------------------------------------------------------------------+
//|                                                      DataInd.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 "..\BaseObj.mqh"
//+------------------------------------------------------------------+
//| Класс данных индикатора                                          |
//+------------------------------------------------------------------+
class CDataInd : public CBaseObj
  {
private:
   int               m_digits;                                    // Значение Digits данных индикатора
   int               m_index;                                     // Индекс бара
   string            m_period_description;                        // Строковое описание таймфрейма
   long              m_long_prop[IND_DATA_PROP_INTEGER_TOTAL];    // Целочисленные свойства
   double            m_double_prop[IND_DATA_PROP_DOUBLE_TOTAL];   // Вещественные свойства
   string            m_string_prop[IND_DATA_PROP_STRING_TOTAL];   // Строковые свойства

//--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство объекта
   int               IndexProp(ENUM_IND_DATA_PROP_DOUBLE property)   const { return(int)property-IND_DATA_PROP_INTEGER_TOTAL;                            }
   int               IndexProp(ENUM_IND_DATA_PROP_STRING property)   const { return(int)property-IND_DATA_PROP_INTEGER_TOTAL-IND_DATA_PROP_DOUBLE_TOTAL; }

public:
//--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство данных индикатора
   void              SetProperty(ENUM_IND_DATA_PROP_INTEGER property,long value) { this.m_long_prop[property]=value;                               }
   void              SetProperty(ENUM_IND_DATA_PROP_DOUBLE property,double value){ this.m_double_prop[this.IndexProp(property)]=value;             }
   void              SetProperty(ENUM_IND_DATA_PROP_STRING property,string value){ this.m_string_prop[this.IndexProp(property)]=value;             }
//--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство данных индикатора
   long              GetProperty(ENUM_IND_DATA_PROP_INTEGER property)   const { return this.m_long_prop[property];                                 }
   double            GetProperty(ENUM_IND_DATA_PROP_DOUBLE property)    const { return this.m_double_prop[this.IndexProp(property)];               }
   string            GetProperty(ENUM_IND_DATA_PROP_STRING property)    const { return this.m_string_prop[this.IndexProp(property)];               }

//--- Возвращает флаг поддержания объектом данного свойства
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_INTEGER property)     { return true; }
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_DOUBLE property)      { return true; }
   virtual bool      SupportProperty(ENUM_IND_DATA_PROP_STRING property)      { return true; }
//--- Возвращает себя
   CDataInd         *GetObject(void)                                          { return &this;}
//--- Устанавливает (1) символ, таймфрейм и время объекта, (2) тип индикатора, (3) количество буферов, (4) номер буфера данных,
//--- (5) идентификатор, (6) значение величины данных, (7) наименование, (8) короткое наименование индикатора
   void              SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time);
   void              SetIndicatorType(const ENUM_INDICATOR type)              { this.SetProperty(IND_DATA_PROP_INDICATOR_TYPE,type);               }
   void              SetBufferNum(const int num)                              { this.SetProperty(IND_DATA_PROP_IND_BUFFER_NUM,num);                }
   void              SetIndicatorID(const int id)                             { this.SetProperty(IND_DATA_PROP_IND_ID,id);                         }
   void              SetBufferValue(const double value)                       { this.SetProperty(IND_DATA_PROP_BUFFER_VALUE,value);                }
   void              SetIndicatorName(const string name)                      { this.SetProperty(IND_DATA_PROP_IND_NAME,name);                     }
   void              SetIndicatorShortname(const string shortname)            { this.SetProperty(IND_DATA_PROP_IND_SHORTNAME,shortname);           }
   
//--- Сравнивает объекты CDataInd между собой по всем возможным свойствам (для сортировки списков по указанному свойству объекта)
   virtual int       Compare(const CObject *node,const int mode=0) const;
//--- Сравнивает объекты CDataInd между собой по всем свойствам (для поиска равных объектов)
   bool              IsEqual(CDataInd* compared_data) const;
//--- Конструкторы
                     CDataInd(){;}
                     CDataInd(const ENUM_INDICATOR ind_type,
                              const int ind_id,
                              const int buffer_num,
                              const string symbol,
                              const ENUM_TIMEFRAMES timeframe,
                              const datetime time);
                     
//+------------------------------------------------------------------+ 
//| Методы упрощённого доступа к свойствам объекта                   |
//+------------------------------------------------------------------+
//--- Возвращает (1) время начала периода бара, (2) таймфрейм, (3) тип индикатора, (4) количество буферов, (5) номер буфера, (6) идентификатор индикатора
   datetime          Time(void)                                         const { return (datetime)this.GetProperty(IND_DATA_PROP_TIME);                   }
   ENUM_TIMEFRAMES   Timeframe(void)                                    const { return (ENUM_TIMEFRAMES)this.GetProperty(IND_DATA_PROP_PERIOD);          }
   ENUM_INDICATOR    IndicatorType(void)                                const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_INDICATOR_TYPE);   }
   int               BufferNum(void)                                    const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_IND_BUFFER_NUM);   }
   int               IndicatorID(void)                                  const { return (ENUM_INDICATOR)this.GetProperty(IND_DATA_PROP_IND_ID);           }

//--- Возвращает цену данных буфера индикатора
   double            PriceValue(void)                                   const { return this.GetProperty(IND_DATA_PROP_BUFFER_VALUE);                     }
   
//--- Возвращает (1) символ данных, (2) наименование, (3) короткое наименование индикатора
   string            Symbol(void)                                       const { return this.GetProperty(IND_DATA_PROP_SYMBOL);                           }
   string            IndicatorName(void)                                const { return this.GetProperty(IND_DATA_PROP_IND_NAME);                         }
   string            IndicatorShortName(void)                           const { return this.GetProperty(IND_DATA_PROP_IND_SHORTNAME);                    }
//--- Возвращает индекс бара на указанном таймфрейме, в который попадает время бара объекта
   int               Index(const ENUM_TIMEFRAMES timeframe)  const
                       { return ::iBarShift(this.Symbol(),(timeframe==PERIOD_CURRENT ? ::Period() : timeframe),this.Time());                             }
//--- Возвращает Digits, установленный объекту
   int               Digits(void)                                       const { return this.m_digits;                                                    }
//+------------------------------------------------------------------+
//| Описания свойств объекта-данных индикатора                       |
//+------------------------------------------------------------------+
//--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства объекта
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_INTEGER property);
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_DOUBLE property);
   string            GetPropertyDescription(ENUM_IND_DATA_PROP_STRING property);

//--- Возвращает описание типа индикатора
   string            IndicatorTypeDescription(void)                     const { return ::IndicatorTypeDescription(this.IndicatorType());                 }
//--- Выводит в журнал описание свойств объекта (full_prop=true - все свойства, false - только поддерживаемые)
   void              Print(const bool full_prop=false);
//--- Выводит в журнал краткое описание объекта
   virtual void      PrintShort(void);
//---
  };
//+------------------------------------------------------------------+

Вкратце рассмотрим состав класса.

В приватной секции класса расположены:

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

В публичной секции класса расположены:

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

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

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

Виртуальный метод Compare() предназначен для сравнения двух объектов по указанному свойству. Определён в классе базового объекта стандартной библиотеки CObject, и должен возвращать ноль, если значения равны, и 1 или -1 если одно из сравниваемых значений соответственно больше или меньше. Для поиска и сортировки используется в методе Search() стандартной библиотеки, и должен переопределяться в классах-наследниках:

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

Метод для определения двух одинаковых объектов данных индикаторных буферов служит для сравнения двух объектов-данных, и возвращает true только в случае, если все поля двух сравниваемых объектов равны:

//+------------------------------------------------------------------+
//| Сравнивает объекты CDataInd между собой по всем свойствам        |
//+------------------------------------------------------------------+
bool CDataInd::IsEqual(CDataInd *compared_obj) const
  {
   int beg=0, end=BAR_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_INTEGER prop=(ENUM_IND_DATA_PROP_INTEGER)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=IND_DATA_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_DOUBLE prop=(ENUM_IND_DATA_PROP_DOUBLE)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   beg=end; end+=IND_DATA_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_STRING prop=(ENUM_IND_DATA_PROP_STRING)i;
      if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; 
     }
   return true;
  }
//+------------------------------------------------------------------+

Метод для установки символа, таймфрейма и индекса объекта-данных буфера в таймсерии:

//+------------------------------------------------------------------+
//| Устанавливает символ, таймфрейм и время начала бара объекта      |
//+------------------------------------------------------------------+
void CDataInd::SetSymbolPeriod(const string symbol,const ENUM_TIMEFRAMES timeframe,const datetime time)
  {
   this.SetProperty(IND_DATA_PROP_TIME,time);
   this.SetProperty(IND_DATA_PROP_SYMBOL,symbol);
   this.SetProperty(IND_DATA_PROP_PERIOD,timeframe);
  }
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Выводит в журнал свойства объекта                                |
//+------------------------------------------------------------------+
void CDataInd::Print(const bool full_prop=false)
  {
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG)," (",this.IndicatorShortName(),") =============");
   int beg=0, end=IND_DATA_PROP_INTEGER_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_INTEGER prop=(ENUM_IND_DATA_PROP_INTEGER)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=IND_DATA_PROP_DOUBLE_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_DOUBLE prop=(ENUM_IND_DATA_PROP_DOUBLE)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("------");
   beg=end; end+=IND_DATA_PROP_STRING_TOTAL;
   for(int i=beg; i<end; i++)
     {
      ENUM_IND_DATA_PROP_STRING prop=(ENUM_IND_DATA_PROP_STRING)i;
      if(!full_prop && !this.SupportProperty(prop)) continue;
      ::Print(this.GetPropertyDescription(prop));
     }
   ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_END)," (",this.IndicatorShortName(),") =============\n");
  }
//+------------------------------------------------------------------+

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

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

//+------------------------------------------------------------------+
//| Выводит в журнал краткое описание объекта                        |
//+------------------------------------------------------------------+
void CDataInd::PrintShort(void)
  {
   ::Print
     (
      this.IndicatorShortName(),
      " [",CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_BUFFER)," ",this.BufferNum(),
      ", ",CMessage::Text(MSG_SYM_STATUS_INDEX)," ",this.Index(this.Timeframe()),"]"
     );
  }
//+------------------------------------------------------------------+

Метод выводит описание данных буфера стандартного индикатора в формате:

AMA(EURUSD,H1) [Буфер 0, Индекс 0]

для пользовательского индикатора:

Examples\Custom Moving Average.ex5(EURUSD,H1) [Буфер 0, Индекс 1]

Короткое наименование индикатора, входящего в состав описания данных буфера можно изменить при помощи метода SetIndicatorShortname(). Метод может быть изменён в объектах-наследниках — чтобы выводить иные описания объекта-данных, соответствующие данным, реализованным в объекте-наследнике.

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

//+------------------------------------------------------------------+
//| Возвращает описание целочисленного свойства объекта              |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_INTEGER property)
  {
   return
     (
      property==IND_DATA_PROP_TIME           ?  CMessage::Text(MSG_LIB_TEXT_BAR_TIME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::TimeToString(this.GetProperty(property),TIME_DATE|TIME_MINUTES|TIME_SECONDS)
         )  :
      property==IND_DATA_PROP_PERIOD         ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TIMEFRAME)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.m_period_description
         )  :
      property==IND_DATA_PROP_INDICATOR_TYPE ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TYPE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+this.IndicatorTypeDescription()
         )  :
      property==IND_DATA_PROP_IND_BUFFER_NUM ?  CMessage::Text(MSG_LIB_TEXT_IND_DATA_IND_BUFFER_NUM)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      property==IND_DATA_PROP_IND_ID         ?  CMessage::Text(MSG_LIB_TEXT_IND_TEXT_ID)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+(string)this.GetProperty(property)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание вещественного свойства объекта               |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_DOUBLE property)
  {
   int dg=(this.m_digits>0 ? this.m_digits : 1);
   return
     (
      property==IND_DATA_PROP_BUFFER_VALUE   ?  CMessage::Text(MSG_LIB_TEXT_IND_DATA_BUFFER_VALUE)+
         (!this.SupportProperty(property)    ?  ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) :
          ": "+::DoubleToString(this.GetProperty(property),dg)
         )  :
      ""
     );
  }
//+------------------------------------------------------------------+
//| Возвращает описание строкового свойства объекта                  |
//+------------------------------------------------------------------+
string CDataInd::GetPropertyDescription(ENUM_IND_DATA_PROP_STRING property)
  {
   return
     (
      property==IND_DATA_PROP_SYMBOL         ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SYMBOL)+": \""+this.GetProperty(property)+"\""     : 
      property==IND_DATA_PROP_IND_NAME       ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_NAME)+": \""+this.GetProperty(property)+"\""       : 
      property==IND_DATA_PROP_IND_SHORTNAME  ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SHORTNAME)+": \""+this.GetProperty(property)+"\""  : 
      ""
     );
  }
//+------------------------------------------------------------------+

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

Класс имеет два конструктора.

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

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

//+------------------------------------------------------------------+
//| Конструктор                                                      |
//+------------------------------------------------------------------+
CDataInd::CDataInd(const ENUM_INDICATOR ind_type,
                   const int ind_id,
                   const int buffer_num,
                   const string symbol,
                   const ENUM_TIMEFRAMES timeframe,
                   const datetime time)
  {
   this.m_type=COLLECTION_INDICATORS_ID;
   this.m_digits=(int)::SymbolInfoInteger(symbol,SYMBOL_DIGITS)+1;
   this.m_period_description=TimeframeDescription(timeframe);
   this.SetSymbolPeriod(symbol,timeframe,time);
   this.SetIndicatorType(ind_type);
   this.SetBufferNum(buffer_num);
   this.SetIndicatorID(ind_id);
  }
//+------------------------------------------------------------------+

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

Помимо записи вышеперечисленных параметров, в конструкторе устанавливается количество знаков после запятой по умолчанию для вывода значений буфера индикатора (Digits символа + 1 знак), и записывается описание таймфрейма в переменную m_period_description — описание можно установить один раз при создании объекта. В переменную m_type, которая объявлена в родительском классе CObject стандартной библиотеки, запишем временно идентификатор коллекции индикаторов. В последующем — при создании коллекций данных индикаторных буферов, мы впишем в эту переменную идентификатор этой новой коллекции.

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

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

//+------------------------------------------------------------------+
//|                                                       Select.mqh |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                             https://mql5.com/ru/users/artmedia70 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://mql5.com/ru/users/artmedia70"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Включаемые файлы                                                 |
//+------------------------------------------------------------------+
#include <Arrays\ArrayObj.mqh>
#include "..\Objects\Orders\Order.mqh"
#include "..\Objects\Events\Event.mqh"
#include "..\Objects\Accounts\Account.mqh"
#include "..\Objects\Symbols\Symbol.mqh"
#include "..\Objects\PendRequest\PendRequest.mqh"
#include "..\Objects\Series\SeriesDE.mqh"
#include "..\Objects\Indicators\Buffer.mqh"
#include "..\Objects\Indicators\IndicatorDE.mqh"
#include "..\Objects\Indicators\DataInd.mqh"
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//| Методы работы с данными индикаторов                              |
//+------------------------------------------------------------------+
   //--- Возвращает список данных индикаторов, где одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode);
   static CArrayObj *ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode);
   //--- Возвращает индекс данных индикаторов в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства данных
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property);
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property);
   static int        FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property);
   //--- Возвращает индекс данных индикаторов в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства данных
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property);
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property);
   static int        FindIndDataMin(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property);
//---
  };
//+------------------------------------------------------------------+

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

//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Методы работы со списками данных индикаторов                     |
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Возвращает список данных индикаторов, где одно из целочисленных  |
//| свойств удовлетворяет заданному критерию                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   int total=list_source.Total();
   for(int i=0; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      long obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Возвращает список данных индикаторов, где одно из вещественных   |
//| свойств удовлетворяет заданному критерию                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      double obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Возвращает список данных индикаторов, где одно из строковых      |
//| свойств удовлетворяет заданному критерию                         |
//+------------------------------------------------------------------+
CArrayObj *CSelect::ByIndicatorDataProperty(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode)
  {
   if(list_source==NULL) return NULL;
   CArrayObj *list=new CArrayObj();
   if(list==NULL) return NULL;
   list.FreeMode(false);
   ListStorage.Add(list);
   for(int i=0; i<list_source.Total(); i++)
     {
      CDataInd *obj=list_source.At(i);
      if(!obj.SupportProperty(property)) continue;
      string obj_prop=obj.GetProperty(property);
      if(CompareValues(obj_prop,value,mode)) list.Add(obj);
     }
   return list;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс данных индикаторов в списке                    |
//| с максимальным значением целочисленного свойства                 |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_INTEGER property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      long obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс данных индикаторов в списке                    |
//| с максимальным значением вещественного свойства                  |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_DOUBLE property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      double obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс данных индикаторов в списке                    |
//| с максимальным значением строкового свойства                     |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMax(CArrayObj *list_source,ENUM_IND_DATA_PROP_STRING property)
  {
   if(list_source==NULL) return WRONG_VALUE;
   int index=0;
   CDataInd *max_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      max_obj=list_source.At(index);
      string obj2_prop=max_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,MORE)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс данных индикаторов в списке                    |
//| с минимальным значением целочисленного свойства                  |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_INTEGER property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      long obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      long obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс данных индикаторов в списке                    |
//| с минимальным значением вещественного свойства                   |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_DOUBLE property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total== 0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      double obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      double obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+
//| Возвращает индекс данных индикаторов в списке                    |
//| с минимальным значением строкового свойства                      |
//+------------------------------------------------------------------+
int CSelect::FindIndDataMin(CArrayObj* list_source,ENUM_IND_DATA_PROP_STRING property)
  {
   int index=0;
   CDataInd *min_obj=NULL;
   int total=list_source.Total();
   if(total==0) return WRONG_VALUE;
   for(int i=1; i<total; i++)
     {
      CDataInd *obj=list_source.At(i);
      string obj1_prop=obj.GetProperty(property);
      min_obj=list_source.At(index);
      string obj2_prop=min_obj.GetProperty(property);
      if(CompareValues(obj1_prop,obj2_prop,LESS)) index=i;
     }
   return index;
  }
//+------------------------------------------------------------------+

Работа данных методов подробно рассматривалась нами в третьей статье описания создания библиотеки.

Итак. Новый объект данных индикаторных буферов создан, и теперь можно протестировать его работу.


Тест

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

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

Пока не созданы коллекции данных индикаторных буферов, доступ к созданному классу организуем прямо из советника.
Для этого подключим данный класс к файлу советника:

//+------------------------------------------------------------------+
//|                                             TestDoEasyPart57.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\Indicators\DataInd.mqh>
//--- enums

Там же — в области глобальных переменных программы, добавим переменные-указатели на объекты данных индикаторов:

//--- Массивы параметров пользовательских индикаторов
MqlParam       param_ma1[];
MqlParam       param_ma2[];
//--- Указатели на объекты данных индикаторов
CDataInd      *data_ma1_0=NULL;
CDataInd      *data_ma1_1=NULL;
CDataInd      *data_ma2_0=NULL;
CDataInd      *data_ma2_1=NULL;
CDataInd      *data_ama1_0=NULL;
CDataInd      *data_ama1_1=NULL;
CDataInd      *data_ama2_0=NULL;
CDataInd      *data_ama2_1=NULL;
//+------------------------------------------------------------------+

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

Так как все объекты будем создавать посредством оператора new, то их необходимо все удалить самостоятельно, что и сделаем в обработчике OnDeinit() советника (после создания коллекции данных индикаторных буферов необходимость в этих действиях в советнике отпадёт):

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//--- Удаление графических объектов советника по префиксу имени объектов
   ObjectsDeleteAll(0,prefix);
   Comment("");
//--- Удаление созданных объектов данных индикаторов MA
   if(CheckPointer(data_ma1_0)==POINTER_DYNAMIC)
      delete data_ma1_0;
   if(CheckPointer(data_ma1_1)==POINTER_DYNAMIC)
      delete data_ma1_1;
   if(CheckPointer(data_ma2_0)==POINTER_DYNAMIC)
      delete data_ma2_0;
   if(CheckPointer(data_ma2_1)==POINTER_DYNAMIC)
      delete data_ma2_1;
//--- Удаление созданных объектов данных индикаторов AMA
   if(CheckPointer(data_ama1_0)==POINTER_DYNAMIC)
      delete data_ama1_0;
   if(CheckPointer(data_ama1_1)==POINTER_DYNAMIC)
      delete data_ama1_1;
   if(CheckPointer(data_ama2_0)==POINTER_DYNAMIC)
      delete data_ama2_0;
   if(CheckPointer(data_ama2_1)==POINTER_DYNAMIC)
      delete data_ama2_1;
//--- Деинициализация библиотеки
   engine.OnDeinit();
  }
//+------------------------------------------------------------------+

В обработчике OnTick() создадим новые объекты (только в случае, если они ещё не созданы), заполним их всеми необходимыми данными и значениями и выведем в журнал описания объектов, а на график — значения буферов описываемых объектами индикаторов:

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

//--- Если работа в тестере
   if(MQLInfoInteger(MQL_TESTER))
     {
      engine.OnTimer(rates_data);   // Работа в таймере
      PressButtonsControl();        // Контроль нажатия кнопок
      engine.EventsHandling();      // Работа с событиями
     }
//--- Получаем объекты-пользовательские индикаторы
   CIndicatorDE *ma1=engine.GetIndicatorsCollection().GetIndByID(MA1);
   CIndicatorDE *ma2=engine.GetIndicatorsCollection().GetIndByID(MA2);
   CIndicatorDE *ama1=engine.GetIndicatorsCollection().GetIndByID(AMA1);
   CIndicatorDE *ama2=engine.GetIndicatorsCollection().GetIndByID(AMA2);
   
//--- Записываем время начала баров 0 и 1 для указания его в свойствах далее создаваемых объектов
   datetime time0=iTime(ma1.Symbol(),ma1.Timeframe(),0);
   datetime time1=iTime(ma1.Symbol(),ma1.Timeframe(),1);
   if(time0==0 || time1==0)
      return;
      
//--- Создаём объекты данных МА1 и МА2 для баров 0 и 1 (только, если объекты ещё не созданы)
   if(data_ma1_0==NULL) data_ma1_0=new CDataInd(ma1.TypeIndicator(),ma1.ID(),0,ma1.Symbol(),ma1.Timeframe(),time0);
   if(data_ma1_1==NULL) data_ma1_1=new CDataInd(ma1.TypeIndicator(),ma1.ID(),0,ma1.Symbol(),ma1.Timeframe(),time1);
   if(data_ma2_0==NULL) data_ma2_0=new CDataInd(ma2.TypeIndicator(),ma2.ID(),0,ma2.Symbol(),ma2.Timeframe(),time0);
   if(data_ma2_1==NULL) data_ma2_1=new CDataInd(ma2.TypeIndicator(),ma2.ID(),0,ma2.Symbol(),ma2.Timeframe(),time1);
   if(data_ma1_0==NULL || data_ma1_1==NULL || data_ma2_0==NULL || data_ma2_1==NULL) return;
   
//--- Устанавливаем параметры объекта данных индикатора МА1, бар 0
//--- и записываем данные буфера индикатора в объект
   data_ma1_0.SetIndicatorType(ma1.TypeIndicator());
   data_ma1_0.SetIndicatorName(ma1.Name());
   data_ma1_0.SetIndicatorShortname(ma1.ShortName());
   data_ma1_0.SetBufferValue(ma1.GetDataBuffer(0,time0));
//--- Устанавливаем параметры объекта данных индикатора МА1, бар 1
//--- и записываем данные буфера индикатора в объект
   data_ma1_1.SetIndicatorType(ma1.TypeIndicator());
   data_ma1_1.SetIndicatorName(ma1.Name());
   data_ma1_1.SetIndicatorShortname(ma1.ShortName());
   data_ma1_1.SetBufferValue(ma1.GetDataBuffer(0,time1));
//--- Устанавливаем параметры объекта данных индикатора МА2, бар 0
//--- и записываем данные буфера индикатора в объект
   data_ma2_0.SetIndicatorType(ma2.TypeIndicator());
   data_ma2_0.SetIndicatorName(ma2.Name());
   data_ma2_0.SetIndicatorShortname(ma2.ShortName());
   data_ma2_0.SetBufferValue(ma2.GetDataBuffer(0,time0));
//--- Устанавливаем параметры объекта данных индикатора МА2, бар 1
//--- и записываем данные буфера индикатора в объект
   data_ma2_1.SetIndicatorType(ma2.TypeIndicator());
   data_ma2_1.SetIndicatorName(ma2.Name());
   data_ma2_1.SetIndicatorShortname(ma2.ShortName());
   data_ma2_1.SetBufferValue(ma2.GetDataBuffer(0,time1));
   
//--- Создаём объекты данных АМА1 и АМА2 для баров 0 и 1 (только, если объекты ещё не созданы)
   if(data_ama1_0==NULL) data_ama1_0=new CDataInd(ama1.TypeIndicator(),ama1.ID(),0,ama1.Symbol(),ama1.Timeframe(),time0);
   if(data_ama1_1==NULL) data_ama1_1=new CDataInd(ama1.TypeIndicator(),ama1.ID(),0,ama1.Symbol(),ama1.Timeframe(),time1);
   if(data_ama2_0==NULL) data_ama2_0=new CDataInd(ama2.TypeIndicator(),ama2.ID(),0,ama2.Symbol(),ama2.Timeframe(),time0);
   if(data_ama2_1==NULL) data_ama2_1=new CDataInd(ama2.TypeIndicator(),ama2.ID(),0,ama2.Symbol(),ama2.Timeframe(),time1);
   if(data_ama1_0==NULL || data_ama1_1==NULL || data_ama2_0==NULL || data_ama2_1==NULL) return;
//--- Устанавливаем параметры объекта данных индикатора АМА1, бар 0
//--- и записываем данные буфера индикатора в объект
   data_ama1_0.SetIndicatorType(ama1.TypeIndicator());
   data_ama1_0.SetIndicatorName(ama1.Name());
   data_ama1_0.SetIndicatorShortname(ama1.ShortName());
   data_ama1_0.SetBufferValue(ama1.GetDataBuffer(0,time0));
//--- Устанавливаем параметры объекта данных индикатора АМА1, бар 1
//--- и записываем данные буфера индикатора в объект
   data_ama1_1.SetIndicatorType(ama1.TypeIndicator());
   data_ama1_1.SetIndicatorName(ama1.Name());
   data_ama1_1.SetIndicatorShortname(ama1.ShortName());
   data_ama1_1.SetBufferValue(ama1.GetDataBuffer(0,time1));
//--- Устанавливаем параметры объекта данных индикатора АМА2, бар 0
//--- и записываем данные буфера индикатора в объект
   data_ama2_0.SetIndicatorType(ama2.TypeIndicator());
   data_ama2_0.SetIndicatorName(ama2.Name());
   data_ama2_0.SetIndicatorShortname(ama2.ShortName());
   data_ama2_0.SetBufferValue(ama2.GetDataBuffer(0,time0));
//--- Устанавливаем параметры объекта данных индикатора АМА2, бар 1
//--- и записываем данные буфера индикатора в объект
   data_ama2_1.SetIndicatorType(ama2.TypeIndicator());
   data_ama2_1.SetIndicatorName(ama2.Name());
   data_ama2_1.SetIndicatorShortname(ama2.ShortName());
   data_ama2_1.SetBufferValue(ama2.GetDataBuffer(0,time1));

//--- При первом запуске распечатаем полные и краткие данные созданных объектов индикаторных данных
   static bool first_start=true;
   if(first_start)
     {
      //--- Полные данные буферов МА1 и МА2
      data_ma1_0.Print();
      data_ma1_1.Print();
      data_ma2_0.Print();
      data_ma2_1.Print();
      //--- Полные данные буферов АМА1 и АМА2
      data_ama1_0.Print();
      data_ama1_1.Print();
      data_ama2_0.Print();
      data_ama2_1.Print();
      //--- Краткие данные буферов МА1 и МА2
      data_ma1_0.PrintShort();
      data_ma1_1.PrintShort();
      data_ma2_0.PrintShort();
      data_ma2_1.PrintShort();
      //--- Краткие данные буферов АМА1 и АМА2
      data_ama1_0.PrintShort();
      data_ama1_1.PrintShort();
      data_ama2_0.PrintShort();
      data_ama2_1.PrintShort();
      //---
      first_start=false;
     }
//--- Выводим в комментарий на графике значения индикаторных буферов из объектов данных
   Comment
     (
      "ma1(1)=",DoubleToString(data_ma1_1.PriceValue(),6),", ma1(0)=",DoubleToString(data_ma1_0.PriceValue(),data_ma1_0.Digits()),", ",
      "ma2(1)=",DoubleToString(data_ma2_1.PriceValue(),6),", ma2(0)=",DoubleToString(data_ma2_0.PriceValue(),data_ma2_0.Digits()),"\n",
      "ama1(1)=",DoubleToString(data_ama1_1.PriceValue(),6),", ama1(0)=",DoubleToString(data_ama1_0.PriceValue(),data_ama1_0.Digits()),", ",
      "ama2(1)=",DoubleToString(data_ama2_1.PriceValue(),6),", ama2(0)=",DoubleToString(data_ama2_0.PriceValue(),data_ama2_0.Digits())
     );
   
//--- Если установлен флаг трейлинга
   if(trailing_on)
     {
      TrailingPositions();          // Трейлинг позиций
      TrailingOrders();             // Трейлинг отложенных ордеров
     }
  }
//+------------------------------------------------------------------+

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

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

Account 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Demo account MetaTrader 5
--- Initializing the "DoEasy" library ---
Work only with the current symbol: "EURUSD"
Work only with the current Period: H1
Symbol time series EURUSD: 
- Timeseries "EURUSD" H1: Required: 1000, Actual: 1000, Created: 1000, On server: 6351
Library initialization time: 00:00:00.000
============= The beginning of the parameter list: "Custom indicator" =============
Indicator status: Custom indicator
Indicator type: CUSTOM
Indicator timeframe: H1
Indicator handle: 10
Indicator group: Trend indicator
Indicator ID: 1
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
 --- Indicator parameters --- 
 - [1] Type int: 13
 - [2] Type int: 0
 - [3] Type int: 0
================== End of the parameter list: "Custom indicator" ==================

============= The beginning of the parameter list: "Custom indicator" =============
Indicator status: Custom indicator
Indicator type: CUSTOM
Indicator timeframe: H1
Indicator handle: 11
Indicator group: Trend indicator
Indicator ID: 2
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
 --- Indicator parameters --- 
 - [1] Type int: 13
 - [2] Type int: 0
 - [3] Type int: 0
 - [4] Type int: 2
================== End of the parameter list: "Custom indicator" ==================
 
============= The beginning of the parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 12
Indicator group: Trend indicator
Indicator ID: 3
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
 --- Indicator parameters --- 
 - Averaging period: 9
 - Fast MA period: 2
 - Slow MA period: 30
 - Horizontal shift of the indicator: 0
 - Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================

============= The beginning of the parameter list: "Standard indicator" =============
Indicator status: Standard indicator
Indicator type: AMA
Indicator timeframe: H1
Indicator handle: 13
Indicator group: Trend indicator
Indicator ID: 4
------
Empty value for plotting, for which there is no drawing: EMPTY_VALUE
------
Indicator symbol: EURUSD
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
 --- Indicator parameters --- 
 - Averaging period: 14
 - Fast MA period: 2
 - Slow MA period: 30
 - Horizontal shift of the indicator: 0
 - Price type or handle: CLOSE
================== End of the parameter list: "Standard indicator" ==================

Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 10, id #1]
Custom indicator Examples\Custom Moving Average.ex5 EURUSD H1 [handle 11, id #2]
Standard indicator Adaptive Moving Average EURUSD H1 [handle 12, id #3]
Standard indicator Adaptive Moving Average EURUSD H1 [handle 13, id #4]

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 1
------
Indicator buffer value: 1.186694
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 1
------
Indicator buffer value: 1.186535
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 2
------
Indicator buffer value: 1.186552
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: CUSTOM
Indicator buffer number: 0
Indicator ID: 2
------
Indicator buffer value: 1.186438
------
Indicator symbol: "EURUSD"
Indicator name: "Examples\Custom Moving Average.ex5"
Indicator shortname: "Examples\Custom Moving Average.ex5(EURUSD,H1)"
============= End of the parameter list (Examples\Custom Moving Average.ex5(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 3
------
Indicator buffer value: 1.186992
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 3
------
Indicator buffer value: 1.186725
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 10:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 4
------
Indicator buffer value: 1.186548
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

============= The beginning of the parameter list (AMA(EURUSD,H1)) =============
Period start time: 2020.11.18 09:00:00
Indicator timeframe: H1
Indicator type: AMA
Indicator buffer number: 0
Indicator ID: 4
------
Indicator buffer value: 1.186403
------
Indicator symbol: "EURUSD"
Indicator name: "Adaptive Moving Average"
Indicator shortname: "AMA(EURUSD,H1)"
============= End of the parameter list (AMA(EURUSD,H1)) =============

Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 0]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 1]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 0]
Examples\Custom Moving Average.ex5(EURUSD,H1) [Buffer 0, Index 1]
AMA(EURUSD,H1) [Buffer 0, Index 0]
AMA(EURUSD,H1) [Buffer 0, Index 1]
AMA(EURUSD,H1) [Buffer 0, Index 0]
AMA(EURUSD,H1) [Buffer 0, Index 1]

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


Что дальше

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

Ниже прикреплены все файлы текущей версии библиотеки и файл тестового советника для 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): Объект пользовательского индикатора, получение данных от объектов-индикаторов в коллекции


Прикрепленные файлы |
MQL5.zip (3895.29 KB)
Машинное обучение от Яндекс (CatBoost) без изучения Python и R Машинное обучение от Яндекс (CatBoost) без изучения Python и R

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

Нейросети — это просто (Часть 7): Адаптивные методы оптимизации Нейросети — это просто (Часть 7): Адаптивные методы оптимизации

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

Продвинутый ресемплинг и выбор CatBoost моделей брутфорс методом Продвинутый ресемплинг и выбор CatBoost моделей брутфорс методом

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

Брутфорс-подход к поиску закономерностей (Часть II): Погружение Брутфорс-подход к поиску закономерностей (Часть II): Погружение

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