
Работа с таймсериями в библиотеке DoEasy (Часть 53): Класс абстрактного базового индикатора
Содержание
Концепция
По мере развития библиотеки DoEasy мы подошли к необходимости создать объект-индикатор. Наличие такого объекта позволит нам удобно хранить и использовать все создаваемые и используемые в программе индикаторы. Концепция построения объектов-индикаторов ничем не отличается от концепции основных объектов библиотеки — базовый абстрактный объект и его наследники, уточняющие принадлежность объекта по его статусу (для индикаторов — пользовательский и стандартный). Подробно о создании таких объектов было рассказано в самых первых статьях.
Сегодня создадим объект базового абстрактного индикатора и проверим успешность его создания. В последующих статьях создадим объекты стандартных и пользовательских индикаторов.
Каждый из создаваемых объектов-индикаторов, помимо принадлежности по статусу (стандартный и пользовательский), будет иметь принадлежность по типу (группы) индикатора:
- Трендовый индикатор
- Осциллятор
- Объёмы
- Стрелочный индикатор
Таким образом мы сможем сортировать индикаторы по группам в своих программах. Индикаторы Билла Вильямса отдельной группой мы вносить не будем по причине, что каждый из них имеет свою принадлежность к одной из перечисленных групп, поэтому считаю лишним вводить ещё одну отдельную группу, в которой будут содержаться индикаторы всех перечисленных выше групп.
Доработка классов библиотеки
В первую очередь пропишем необходимые текстовые сообщения библиотеки для объектов-индикаторов.
В файле \MQL5\Include\DoEasy\Data.mqh пропишем индексы новых сообщений:
//--- CBuffer //--- удалено для экономии места //--- ... //--- ... //--- ... MSG_LIB_TEXT_BUFFER_TEXT_STYLE_SOLID, // Сплошная линия MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASH, // Прерывистая линия MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DOT, // Пунктирная линия MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOT, // Штрих-пунктирная линия MSG_LIB_TEXT_BUFFER_TEXT_STYLE_DASHDOTDOT, // Штрих - две точки //--- CIndicatorDE MSG_LIB_TEXT_IND_TEXT_STATUS, // Статус индикатора MSG_LIB_TEXT_IND_TEXT_STATUS_STANDART, // Стандартный индикатор MSG_LIB_TEXT_IND_TEXT_STATUS_CUSTOM, // Пользовательский индикатор MSG_LIB_TEXT_IND_TEXT_TIMEFRAME, // Таймфрейм индикатора MSG_LIB_TEXT_IND_TEXT_HANDLE, // Хэндл индикатора MSG_LIB_TEXT_IND_TEXT_GROUP, // Группа индикатора MSG_LIB_TEXT_IND_TEXT_GROUP_TREND, // Трендовый индикатор MSG_LIB_TEXT_IND_TEXT_GROUP_OSCILLATOR, // Осциллятор MSG_LIB_TEXT_IND_TEXT_GROUP_VOLUMES, // Объёмы MSG_LIB_TEXT_IND_TEXT_GROUP_ARROWS, // Стрелочный индикатор MSG_LIB_TEXT_IND_TEXT_EMPTY_VALUE, // Пустое значение для построения, для которого нет отрисовки MSG_LIB_TEXT_IND_TEXT_SYMBOL, // Символ индикатора MSG_LIB_TEXT_IND_TEXT_NAME, // Имя индикатора MSG_LIB_TEXT_IND_TEXT_SHORTNAME, // Короткое имя индикатора }; //+------------------------------------------------------------------+
... и далее, в том же файле — текстовые сообщения, соответствующие вновь добавленным индексам:
{"Сплошная линия","Solid line"}, {"Прерывистая линия","Broken line"}, {"Пунктирная линия","Dotted line"}, {"Штрих-пунктирная линия","Dash-dot line"}, {"Штрих - две точки","Dash - two points"}, {"Статус индикатора","Indicator status"}, {"Стандартный индикатор","Standard indicator"}, {"Пользовательский индикатор","Custom indicator"}, {"Таймфрейм индикатора","Indicator timeframe"}, {"Хэндл индикатора","Indicator handle"}, {"Группа индикатора","Indicator group"}, {"Трендовый индикатор","Trend indicator"}, {"Осциллятор","Solid lineOscillator"}, {"Объёмы","Volumes"}, {"Стрелочный индикатор","Arrow indicator"}, {"Пустое значение для построения, для которого нет отрисовки","Empty value for plotting, for which there is no drawing"}, {"Символ индикатора","Indicator symbol"}, {"Имя индикатора","Indicator name"}, {"Короткое имя индикатора","Indicator shortname"}, }; //+---------------------------------------------------------------------+
В файле E:\MetaQuotes\MetaTrader 5\MQL5\Include\DoEasy\Defines.mqh пропишем уже ставшими стандартными для библиотечных объектов параметры объекта-индикатора.
Так как все эти объекты в итоге будут храниться в списке-коллекции объектов-индикаторов, то введём для них свой собственный идентификатор:
//--- Идентификаторы списков коллекций #define COLLECTION_HISTORY_ID (0x777A) // Идентификатор списка исторической коллекции #define COLLECTION_MARKET_ID (0x777B) // Идентификатор списка рыночной коллекции #define COLLECTION_EVENTS_ID (0x777C) // Идентификатор списка коллекции событий #define COLLECTION_ACCOUNT_ID (0x777D) // Идентификатор списка коллекции аккаунтов #define COLLECTION_SYMBOLS_ID (0x777E) // Идентификатор списка коллекции символов #define COLLECTION_SERIES_ID (0x777F) // Идентификатор списка коллекции таймсерий #define COLLECTION_BUFFERS_ID (0x7780) // Идентификатор списка коллекции индикаторных буферов #define COLLECTION_INDICATORS_ID (0x7781) // Идентификатор списка коллекции индикаторов //--- Параметры данных для файловых операций
И в самом конце листинга файла пропишем все необходимые перечисления свойств и критериев сортировки объектов-индикаторов в списке-коллекции:
//+------------------------------------------------------------------+ //| Данные для работы с индикаторами | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Статус абстрактного индикатора | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_STATUS { INDICATOR_STATUS_STANDART, // Стандартный индикатор INDICATOR_STATUS_CUSTOM, // Пользовательский индикатор }; //+------------------------------------------------------------------+ //| Группа индикатора | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_GROUP { INDICATOR_GROUP_TREND, // Трендовый индикатор INDICATOR_GROUP_OSCILLATOR, // Осциллятор INDICATOR_GROUP_VOLUMES, // Объёмы INDICATOR_GROUP_ARROWS, // Стрелочный индикатор }; //+------------------------------------------------------------------+ //| Целочисленные свойства индикатора | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_PROP_INTEGER { INDICATOR_PROP_STATUS = 0, // Статус индикатора (из перечисления ENUM_INDICATOR_STATUS) INDICATOR_PROP_TIMEFRAME, // Таймфрейм индикатора INDICATOR_PROP_HANDLE, // Хэндл индикатора INDICATOR_PROP_GROUP, // Группа индикатора }; #define INDICATOR_PROP_INTEGER_TOTAL (4) // Общее количество целочисленных свойств индикатора #define INDICATOR_PROP_INTEGER_SKIP (0) // Количество неиспользуемых в сортировке свойств индикатора //+------------------------------------------------------------------+ //| Вещественные свойства индикатора | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_PROP_DOUBLE { INDICATOR_PROP_EMPTY_VALUE = INDICATOR_PROP_INTEGER_TOTAL,// Пустое значение для построения, для которого нет отрисовки }; #define INDICATOR_PROP_DOUBLE_TOTAL (1) // Общее количество вещественных свойств индикатора #define INDICATOR_PROP_DOUBLE_SKIP (0) // Количество неиспользуемых в сортировке свойств индикатора //+------------------------------------------------------------------+ //| Строковые свойства индикатора | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_PROP_STRING { INDICATOR_PROP_SYMBOL = (INDICATOR_PROP_INTEGER_TOTAL+INDICATOR_PROP_DOUBLE_TOTAL), // Символ индикатора INDICATOR_PROP_NAME, // Имя индикатора INDICATOR_PROP_SHORTNAME, // Короткое имя индикатора }; #define INDICATOR_PROP_STRING_TOTAL (3) // Общее количество строковых свойств индикатора //+------------------------------------------------------------------+ //| Возможные критерии сортировки индикаторов | //+------------------------------------------------------------------+ #define FIRST_INDICATOR_DBL_PROP (INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP) #define FIRST_INDICATOR_STR_PROP (INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_INTEGER_SKIP+INDICATOR_PROP_DOUBLE_TOTAL-INDICATOR_PROP_DOUBLE_SKIP) enum ENUM_SORT_INDICATOR_MODE { //--- Сортировка по целочисленным свойствам SORT_BY_INDICATOR_INDEX_STATUS = 0, // Сортировать по статусу индикатора SORT_BY_INDICATOR_TIMEFRAME, // Сортировать по таймфрейму индикатора SORT_BY_INDICATOR_HANDLE, // Сортировать по хэндлу индикатора SORT_BY_INDICATOR_GROUP, // Сортировать по группе индикатора //--- Сортировка по вещественным свойствам SORT_BY_INDICATOR_EMPTY_VALUE = FIRST_INDICATOR_DBL_PROP,// Сортировать по пустому значению для построения, для которого нет отрисовки //--- Сортировка по строковым свойствам SORT_BY_INDICATOR_SYMBOL = FIRST_INDICATOR_STR_PROP, // Сортировать по символу индикатора SORT_BY_INDICATOR_NAME, // Сортировать по имени индикатора SORT_BY_INDICATOR_SHORTNAME, // Сортировать по короткому имени индикатора }; //+------------------------------------------------------------------+
Здесь, думаю, всё понятно без пояснений.
Класс абстрактного индикатора
Приступим к созданию класса базового абстрактного индикатора.
В папке \MQL5\Include\DoEasy\Objects\Indicators\ создадим новый класс CIndicatorDE в файле с именем IndicatorDE.mqh. Так как в стандартной библиотеке уже имеется класс CIndicator, то мы к названию класса и файла добавили аббревиатуру DE (DoEasy).
Новый класс будет унаследован от класса базового объекта всех объектов библиотеки CBaseObj.
В теле класса расположены уже неоднократно ранее рассматриваемые нами методы установки и получения свойств объекта, просто посмотрим их листинг, а далее разберём некоторые методы класса:
//+------------------------------------------------------------------+ //| Ind.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 "..\..\Services\Select.mqh" #include "..\..\Objects\BaseObj.mqh" //+------------------------------------------------------------------+ //| Класс абстрактного индикатора | //+------------------------------------------------------------------+ class CIndicatorDE : public CBaseObj { private: long m_long_prop[INDICATOR_PROP_INTEGER_TOTAL]; // Целочисленные свойства double m_double_prop[INDICATOR_PROP_DOUBLE_TOTAL]; // Вещественные свойства string m_string_prop[INDICATOR_PROP_STRING_TOTAL]; // Строковые свойства MqlParam m_mql_params[]; // Массив параметров индикатора //--- Возвращает индекс массива, по которому фактически расположено (1) double-свойство и (2) string-свойство буфера int IndexProp(ENUM_INDICATOR_PROP_DOUBLE property) const { return(int)property-INDICATOR_PROP_INTEGER_TOTAL; } int IndexProp(ENUM_INDICATOR_PROP_STRING property) const { return(int)property-INDICATOR_PROP_INTEGER_TOTAL-INDICATOR_PROP_DOUBLE_TOTAL;} //--- Сравнивает (1) структуры MqlParam, (2) массив структур MqlParam между собой bool IsEqualMqlParams(MqlParam &struct1,MqlParam &struct2) const; bool IsEqualMqlParamArrays(MqlParam &compared_struct[]) const; protected: public: //--- Защищённый параметрический конструктор CIndicatorDE(ENUM_INDICATOR ind_type, string symbol, ENUM_TIMEFRAMES timeframe, ENUM_INDICATOR_STATUS status, ENUM_INDICATOR_GROUP group, string name, string shortname, MqlParam &indicator_params[]); public: //--- Конструктор по умолчанию CIndicatorDE(void){;} //--- Деструктор ~CIndicatorDE(void); //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство буфера void SetProperty(ENUM_INDICATOR_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_INDICATOR_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_INDICATOR_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство буфера long GetProperty(ENUM_INDICATOR_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_INDICATOR_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_INDICATOR_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства буфера string GetPropertyDescription(ENUM_INDICATOR_PROP_INTEGER property); string GetPropertyDescription(ENUM_INDICATOR_PROP_DOUBLE property); string GetPropertyDescription(ENUM_INDICATOR_PROP_STRING property); //--- Возвращает флаг поддержания буфером данного свойства virtual bool SupportProperty(ENUM_INDICATOR_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_INDICATOR_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_INDICATOR_PROP_STRING property) { return true; } //--- Сравнивает объекты CIndicatorDE между собой по всем возможным свойствам (для сортировки списков по указанному свойству объекта-индикатора) virtual int Compare(const CObject *node,const int mode=0) const; //--- Сравнивает объекты CIndicatorDE между собой по всем свойствам (для поиска равных объектов-индикаторов) bool IsEqual(CIndicatorDE* compared_obj) const; //--- Устанавливает (1) группу, (2) пустое значение буферов, (3) имя, (4) короткое имя индикатора void SetGroup(const ENUM_INDICATOR_GROUP group) { this.SetProperty(INDICATOR_PROP_GROUP,group); } void SetEmptyValue(const double value) { this.SetProperty(INDICATOR_PROP_EMPTY_VALUE,value); } void SetName(const string name) { this.SetProperty(INDICATOR_PROP_NAME,name); } void SetShortName(const string shortname) { this.SetProperty(INDICATOR_PROP_SHORTNAME,shortname); } //--- Возвращает (1) статус, (2) группу, (3) таймфрейм, (4) хэндл, (5) пустое значение буферов, (6) имя, (7) короткое имя, (8) символ индикатора ENUM_INDICATOR_STATUS Status(void) const { return (ENUM_INDICATOR_STATUS)this.GetProperty(INDICATOR_PROP_STATUS);} ENUM_INDICATOR_GROUP Group(void) const { return (ENUM_INDICATOR_GROUP)this.GetProperty(INDICATOR_PROP_GROUP); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(INDICATOR_PROP_TIMEFRAME); } int Handle(void) const { return (int)this.GetProperty(INDICATOR_PROP_HANDLE); } double EmptyValue(void) const { return this.GetProperty(INDICATOR_PROP_EMPTY_VALUE); } string Name(void) const { return this.GetProperty(INDICATOR_PROP_NAME); } string ShortName(void) const { return this.GetProperty(INDICATOR_PROP_SHORTNAME); } string Symbol(void) const { return this.GetProperty(INDICATOR_PROP_SYMBOL); } //--- Возвращает описание (1) статуса, (2) группы, (3) таймфрейма, (4) пустого значения индикатора string GetStatusDescription(void) const; string GetGroupDescription(void) const; string GetTimeframeDescription(void) const; string GetEmptyValueDescription(void) const; //--- Выводит в журнал описание свойств объекта-индикатора (full_prop=true - все свойства, false - только поддерживаемые) void Print(const bool full_prop=false); //--- Выводит в журнал краткое описание объекта-индикатора (реализация в наследниках) virtual void PrintShort(void) {;} }; //+------------------------------------------------------------------+
Закрытый параметрический конструктор класса сделаем временно публичным — нам сегодня потребуется протестировать успешность создания одиночного объекта класса, и по этой причине данный конструктор должен быть открыт для внешних вызовов.
Как видим, все методы класса повторяют по своей логике все ранее рассматриваемые нами объекты библиотеки, поэтому их назначение и работа уже должны быть знакомы читателям. В отличие от всех других объектов, где нет деструктора и он создаётся неявно, здесь мы добавили деструктор, так как нам потребуется уничтожать созданный для объекта индикатор. Рассмотрим реализацию методов, которая выполнена за пределами тела класса.
В деструкторе класса уничтожаем созданный объект индикатора:
//+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CIndicatorDE::~CIndicatorDE(void) { ::IndicatorRelease(this.Handle()); } //+------------------------------------------------------------------+
Закрытый параметрический конструктор класса:
//+------------------------------------------------------------------+ //| Закрытый параметрический конструктор | //+------------------------------------------------------------------+ CIndicatorDE::CIndicatorDE(ENUM_INDICATOR ind_type, string symbol, ENUM_TIMEFRAMES timeframe, ENUM_INDICATOR_STATUS status, ENUM_INDICATOR_GROUP group, string name, string shortname, MqlParam &indicator_params[]) { //--- Устанавливаем идентификатор коллекции объекту this.m_type=COLLECTION_INDICATORS_ID; //--- Если размер переданного в конструктор массива параметров больше нуля, то //--- заполняем массив параметров объекта данными из переданного в конструктор массива int count=::ArrayResize(m_mql_params,::ArraySize(indicator_params)); for(int i=0;i<count;i++) { this.m_mql_params[i].type=indicator_params[i].type; this.m_mql_params[i].double_value=indicator_params[i].double_value; this.m_mql_params[i].integer_value=indicator_params[i].integer_value; this.m_mql_params[i].string_value=indicator_params[i].string_value; } //--- Создаём хэндл индикатора int handle=::IndicatorCreate(symbol,timeframe,ind_type,count,indicator_params); //--- Сохранение целочисленных свойств this.m_long_prop[INDICATOR_PROP_STATUS] = status; this.m_long_prop[INDICATOR_PROP_GROUP] = group; this.m_long_prop[INDICATOR_PROP_TIMEFRAME] = timeframe; this.m_long_prop[INDICATOR_PROP_HANDLE] = handle; //--- Сохранение вещественных свойств this.m_double_prop[this.IndexProp(INDICATOR_PROP_EMPTY_VALUE)]=EMPTY_VALUE; //--- Сохранение строковых свойств this.m_string_prop[this.IndexProp(INDICATOR_PROP_SYMBOL)] = (symbol==NULL || symbol=="" ? ::Symbol() : symbol); this.m_string_prop[this.IndexProp(INDICATOR_PROP_NAME)] = name; this.m_string_prop[this.IndexProp(INDICATOR_PROP_SHORTNAME)]= shortname; } //+------------------------------------------------------------------+
В конструктор передаются все необходимые параметры для создания объекта-индикатора.
Если массив параметров MqlParam &indicator_params[] имеет нулевой размер, то функция IndicatorCreate() при создании индикатора не будет использовать параметры, которые должны содержаться в массиве.
Далее просто сохраняем все переданные в метод значения в полях свойств объекта.
Для сравнения двух объектов-индикаторов между собой для сортировки и поиска двух одинаковых объектов нам необходимо сравнить все поля двух объектов. Объект-индикатор содержит в себе массив структур MqlParam — их тоже нужно будет сравнивать между собой. И сравнивать мы их будем поэлементно. Для этого у нас есть два метода — метод для сравнения двух структур MqlParam между собой, и метод для сравнения двух массивов этих структур.
Метод сравнения двух структур MqlParam между собой:
//+------------------------------------------------------------------+ //| Сравнивает структуры MqlParam между собой | //+------------------------------------------------------------------+ bool CIndicatorDE::IsEqualMqlParams(MqlParam &struct1,MqlParam &struct2) const { bool res= (struct1.type!=struct2.type ? false : (struct1.type==TYPE_STRING && struct1.string_value==struct2.string_value) || (struct1.type<TYPE_STRING && struct1.type>TYPE_ULONG && struct1.double_value==struct2.double_value) || (struct1.type<TYPE_FLOAT && struct1.integer_value==struct2.integer_value) ? true : false ); return res; } //+------------------------------------------------------------------+
Здесь просто.
Проверяем поля типов данных структур, и если типы данных сравниваемых структур не соответствуют — возвращаем false.
Если тип данных строковый и эти данные двух структур равны — возвращаем true.
Если тип данных вещественный и эти данные двух структур равны — возвращаем true.
Если тип данных целочисленный и эти данные двух структур равны — возвращаем true.
В любом ином случае — возвращаем false.
Метод, сравнивающий массивы структур MqlParam между собой:
//+------------------------------------------------------------------+ //| Сравнивает массив структур MqlParam между собой | //+------------------------------------------------------------------+ bool CIndicatorDE::IsEqualMqlParamArrays(MqlParam &compared_struct[]) const { int total=::ArraySize(this.m_mql_params); int size=::ArraySize(compared_struct); if(total!=size || total==0 || size==0) return false; for(int i=0;i<total;i++) { if(!this.IsEqualMqlParams(this.m_mql_params[i],compared_struct[i])) return false; } return true; } //+------------------------------------------------------------------+
Если размеры двух массивов не равны или любой из них имеет нулевой размер — возвращаем false.
Далее в цикле по количеству структур в массиве сравниваем каджые последующие структуры двух массивов, и если они не равны — возвращаем false.
После успешной проверки всех структур, содержащихся в двух массивах, возвращаем true.
Метод сравнения объектов CIndicatorDE между собой по всем возможным свойствам:
//+------------------------------------------------------------------+ //| Сравнивает объекты CIndicatorDE между собой | //| по всем возможным свойствам | //+------------------------------------------------------------------+ int CIndicatorDE::Compare(const CObject *node,const int mode=0) const { const CIndicatorDE *compared_obj=node; //--- сравнение целочисленных свойств двух индикаторов if(mode<INDICATOR_PROP_INTEGER_TOTAL) { long value_compared=compared_obj.GetProperty((ENUM_INDICATOR_PROP_INTEGER)mode); long value_current=this.GetProperty((ENUM_INDICATOR_PROP_INTEGER)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- сравнение вещественных свойств двух индикаторов else if(mode<INDICATOR_PROP_INTEGER_TOTAL+INDICATOR_PROP_DOUBLE_TOTAL) { double value_compared=compared_obj.GetProperty((ENUM_INDICATOR_PROP_DOUBLE)mode); double value_current=this.GetProperty((ENUM_INDICATOR_PROP_DOUBLE)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } //--- сравнение строковых свойств двух индикаторов else if(mode<INDICATOR_PROP_INTEGER_TOTAL+INDICATOR_PROP_DOUBLE_TOTAL+INDICATOR_PROP_STRING_TOTAL) { string value_compared=compared_obj.GetProperty((ENUM_INDICATOR_PROP_STRING)mode); string value_current=this.GetProperty((ENUM_INDICATOR_PROP_STRING)mode); return(value_current>value_compared ? 1 : value_current<value_compared ? -1 : 0); } return 0; } //+------------------------------------------------------------------+
Метод сравнения объектов CIndicatorDE между собой по всем свойствам:
//+------------------------------------------------------------------+ //| Сравнивает объекты CIndicatorDE между собой по всем свойствам | //+------------------------------------------------------------------+ bool CIndicatorDE::IsEqual(CIndicatorDE *compared_obj) const { if(!IsEqualMqlParamArrays(compared_obj.m_mql_params)) return false; int beg=0, end=INDICATOR_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_INTEGER prop=(ENUM_INDICATOR_PROP_INTEGER)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=INDICATOR_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_DOUBLE prop=(ENUM_INDICATOR_PROP_DOUBLE)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } beg=end; end+=INDICATOR_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_STRING prop=(ENUM_INDICATOR_PROP_STRING)i; if(this.GetProperty(prop)!=compared_obj.GetProperty(prop)) return false; } return true; } //+------------------------------------------------------------------+
Два метода сравнения объектов CIndicatorDE между собой по своей логике идентичны одноимённым методам других объектов библиотеки и неоднократно нами рассматривались. Единственное отличие во втором методе (IsEqual) — это то, что сначала вызывается метод сравнения двух массивов структур MqlParam сравниваемых объектов, и если они не равны, то и объекты не равны — возвращаем false. А далее уже сравниваются объекты по всем их полям.
Методы, возвращающие описание целочисленного, вещественного и строкового свойства индикатора:
//+------------------------------------------------------------------+ //| Возвращает описание целочисленного свойства индикатора | //+------------------------------------------------------------------+ string CIndicatorDE::GetPropertyDescription(ENUM_INDICATOR_PROP_INTEGER property) { return ( property==INDICATOR_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==INDICATOR_PROP_GROUP ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetGroupDescription() ) : property==INDICATOR_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==INDICATOR_PROP_HANDLE ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : "" ); } //+------------------------------------------------------------------+ //| Возвращает описание вещественного свойства индикатора | //+------------------------------------------------------------------+ string CIndicatorDE::GetPropertyDescription(ENUM_INDICATOR_PROP_DOUBLE property) { return ( property==INDICATOR_PROP_EMPTY_VALUE ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_EMPTY_VALUE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetEmptyValueDescription() ) : "" ); } //+------------------------------------------------------------------+ //| Возвращает описание строкового свойства индикатора | //+------------------------------------------------------------------+ string CIndicatorDE::GetPropertyDescription(ENUM_INDICATOR_PROP_STRING property) { return ( property==INDICATOR_PROP_SYMBOL ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SYMBOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.Symbol() ) : property==INDICATOR_PROP_NAME ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_NAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.Name()==NULL || this.Name()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.Name()+"\"") ) : property==INDICATOR_PROP_SHORTNAME ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_SHORTNAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.ShortName()==NULL || this.ShortName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.ShortName()+"\"") ) : "" ); } //+------------------------------------------------------------------+
Подобные методы есть в каждом объекте библиотеки и точно так же нами были рассмотрены ранее.
Остальные методы для вывода описаний различных свойств объекта-индикатора тоже идентичны таким же методам других объектов библиотеки, поэтому просто посмотрим их листинг для самостоятельного изучения:
//+------------------------------------------------------------------+ //| Возвращает описание статуса индикатора | //+------------------------------------------------------------------+ string CIndicatorDE::GetStatusDescription(void) const { return ( this.Status()==INDICATOR_STATUS_CUSTOM ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_STATUS_CUSTOM) : this.Status()==INDICATOR_STATUS_STANDART ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_STATUS_STANDART) : "Unknown" ); } //+------------------------------------------------------------------+ //| Возвращает описание группы индикатора | //+------------------------------------------------------------------+ string CIndicatorDE::GetGroupDescription(void) const { return ( this.Group()==INDICATOR_GROUP_TREND ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP_TREND) : this.Group()==INDICATOR_GROUP_OSCILLATOR ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP_OSCILLATOR) : this.Group()==INDICATOR_GROUP_VOLUMES ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP_VOLUMES) : this.Group()==INDICATOR_GROUP_ARROWS ? CMessage::Text(MSG_LIB_TEXT_IND_TEXT_GROUP_ARROWS) : "Any" ); } //+------------------------------------------------------------------+ //| Возвращает описание используемого таймфрейма | //+------------------------------------------------------------------+ string CIndicatorDE::GetTimeframeDescription(void) const { string timeframe=TimeframeDescription(this.Timeframe()); return(this.Timeframe()==PERIOD_CURRENT ? CMessage::Text(MSG_LIB_TEXT_PERIOD_CURRENT)+" ("+timeframe+")" : timeframe); } //+------------------------------------------------------------------+ //| Возвращает описание установленного пустого значения | //+------------------------------------------------------------------+ string CIndicatorDE::GetEmptyValueDescription(void) const { double value=fabs(this.EmptyValue()); return(value<EMPTY_VALUE ? ::DoubleToString(this.EmptyValue(),(this.EmptyValue()==0 ? 1 : 8)) : (this.EmptyValue()>0 ? "EMPTY_VALUE" : "-EMPTY_VALUE")); } //+------------------------------------------------------------------+ //| Выводит в журнал свойства индикатора | //+------------------------------------------------------------------+ void CIndicatorDE::Print(const bool full_prop=false) { ::Print("============= ",CMessage::Text(MSG_LIB_PARAMS_LIST_BEG),": \"",this.GetStatusDescription(),"\" ============="); int beg=0, end=INDICATOR_PROP_INTEGER_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_INTEGER prop=(ENUM_INDICATOR_PROP_INTEGER)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=INDICATOR_PROP_DOUBLE_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_DOUBLE prop=(ENUM_INDICATOR_PROP_DOUBLE)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("------"); beg=end; end+=INDICATOR_PROP_STRING_TOTAL; for(int i=beg; i<end; i++) { ENUM_INDICATOR_PROP_STRING prop=(ENUM_INDICATOR_PROP_STRING)i; if(!full_prop && !this.SupportProperty(prop)) continue; ::Print(this.GetPropertyDescription(prop)); } ::Print("================== ",CMessage::Text(MSG_LIB_PARAMS_LIST_END),": \"",this.GetStatusDescription(),"\" ==================\n"); } //+------------------------------------------------------------------+
Это весь состав объекта-индикатора. Вполне вероятно, что далее мы его доработаем, но для проверки создания объекта на сегодня этого достаточно.
Теперь нужно дать возможность в библиотеке полноценно работать со списком новых объектов-индикаторов — сортировать их и выбирать нужные по заданным критериям. Для этого в файле \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" //+------------------------------------------------------------------+
и в конце тела класса объявим методы для работы со списком объектов-индикаторов:
//+------------------------------------------------------------------+ //| Методы работы с индикаторами | //+------------------------------------------------------------------+ //--- Возвращает список индикаторов, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс индикатора в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства индикатора static int FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_INTEGER property); static int FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_DOUBLE property); static int FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_STRING property); //--- Возвращает индекс индикатора в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства индикатора static int FindIndicatorMin(CArrayObj *list_source,ENUM_INDICATOR_PROP_INTEGER property); static int FindIndicatorMin(CArrayObj *list_source,ENUM_INDICATOR_PROP_DOUBLE property); static int FindIndicatorMin(CArrayObj *list_source,ENUM_INDICATOR_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
Все методы по работе с объектами-индикаторами тоже абсолютно стандартны и идентичны ранее прописанным методам для поиска и сортировки в списках-коллекциях других объектов библиотеки, и все они были нами рассмотрены ранее. Просто посмотрим их реализацию для самостоятельного изучения и повторения пройденного материала:
//+------------------------------------------------------------------+ //| Методы работы со списками индикаторов | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Возвращает список индикаторов, у которых одно из целочисленных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_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++) { CIndicatorDE *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::ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_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++) { CIndicatorDE *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::ByIndicatorProperty(CArrayObj *list_source,ENUM_INDICATOR_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++) { CIndicatorDE *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::FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CIndicatorDE *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CIndicatorDE *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::FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CIndicatorDE *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CIndicatorDE *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::FindIndicatorMax(CArrayObj *list_source,ENUM_INDICATOR_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CIndicatorDE *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CIndicatorDE *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::FindIndicatorMin(CArrayObj* list_source,ENUM_INDICATOR_PROP_INTEGER property) { int index=0; CIndicatorDE *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CIndicatorDE *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::FindIndicatorMin(CArrayObj* list_source,ENUM_INDICATOR_PROP_DOUBLE property) { int index=0; CIndicatorDE *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CIndicatorDE *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::FindIndicatorMin(CArrayObj* list_source,ENUM_INDICATOR_PROP_STRING property) { int index=0; CIndicatorDE *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CIndicatorDE *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; } //+------------------------------------------------------------------+
На данный момент у нас в библиотеке индикаторы создаются и используются внутри класса-коллекции индикаторных буферов CBuffersCollection. Мы же создаём отдельный класс для объектов-индикаторов, которые в свою очередь будут собираться в свой класс-коллекцию, и уже из этой коллекции можно будет получать доступ к объектам индикаторов. Но пока будем работать внутри класса CBuffersCollection, так как сегодня нам необходимо лишь создать объект-индикатор и проверить успешность его создания.
Уже в последующих статьях, когда будут созданы классы-наследники абстрактного индикатора, мы их соберём в коллекцию, и в классе CBuffersCollection будем работать не напрямую с индикаторами (как сделано сейчас), а посредством класса-коллекции индикаторов.
Наша цель на сегодня — создать и проверить факт создания объекта-индикатора. Поэтому доработки коснутся лишь одного метода класса-коллекции буферов — метода создания индикатора Accelerator Oscillator.
Внесём изменения в методе для кроссплатформенности и создадим объект-индикатор, распечатаем его данные в журнал и тут же удалим этот объект. Это всё, что нужно будет сделать для проверки создания объекта абстрактного индикатора.
Откроем файл класса-коллекции индикаторных буферов \MQL5\Include\DoEasy\Collections\BuffersCollection.mqh и внесём необходимые доработки.
Для создания объекта индикатора мы используем функцию IndicatorCreate(), в которую для многих индикаторов необходимо передать нужные параметры. Такие параметры передаются посредством массива специально для этого разработанной структуры MqlParam.
Объявим такой массив в приватной секции класса:
//+------------------------------------------------------------------+ //| Коллекция индикаторных буферов | //+------------------------------------------------------------------+ class CBuffersCollection : public CObject { private: CListObj m_list; // Список объектов-буферов CTimeSeriesCollection *m_timeseries; // Указатель на объект-коллекцию таймсерий MqlParam m_mql_param[]; // Массив параметров индикатора //--- Возвращает индекс (1) последнего, (2) следующего рисуемого, (3) базового буфера int GetIndexLastPlot(void); int GetIndexNextPlot(void); int GetIndexNextBase(void); //--- Создаёт новый объект-буфер и помещает его в список-коллекцию bool CreateBuffer(ENUM_BUFFER_STATUS status); //--- Получает данные нужных таймсерий и баров для работы с одним баром буфера, возвращает количество баров int GetBarsData(CBuffer *buffer,const int series_index,int &index_bar_period); public:
Так как проверять создание объекта-индикатора будем только в одном методе — в методе создания индикатора Accelerator Oscillator CreateAC(), то все доработки коснутся только этого метода. Все остальные методы для создания индикаторных буферов будем дорабатывать в последующих статьях.
Разделим метод на два блока — для MQL5 и для MQL4 для кроссплатформенности, но для MQL4 пока сделаем лишь создание второго буфера для отображения второго цвета (Accelerator Oscillator двухцветный). Но делать изменение цвета гистограмм, а по сути чередование отображения двух разных буферов для визуального отображения разных цветов индикатора в MQL4 мы не будем — сегодня мы делаем совсем другое.
В самом начале метода пропишем строки создания объекта-индикатора, вывод данных созданного объекта в журнал и удаления объекта:
//+------------------------------------------------------------------+ //| Создаёт мультисимвольный мультипериодный AC | //+------------------------------------------------------------------+ int CBuffersCollection::CreateAC(const string symbol,const ENUM_TIMEFRAMES timeframe,const int id=WRONG_VALUE) { //--- Для проверки создаём объёкт-индикатор, выводим его данные и сразу удаляем ::ArrayResize(this.m_mql_param,0); CIndicatorDE *indicator=new CIndicatorDE(IND_AC,symbol,timeframe,INDICATOR_STATUS_STANDART,INDICATOR_GROUP_OSCILLATOR,"Accelerator Oscillator","AC("+symbol+","+TimeframeDescription(timeframe)+")",this.m_mql_param); indicator.Print(); delete indicator; //--- Создаём хэндл индикатора и устанавливаем идентификатор по умолчанию int handle= #ifdef __MQL5__ ::iAC(symbol,timeframe) #else 0 #endif ; int identifier=(id==WRONG_VALUE ? IND_AC : id); color array_colors[3]={clrGreen,clrRed,clrGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Создаём буфер-гистограмму от нулевой линии this.CreateHistogram(); //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AC); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetShowData(true); buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accelerator Oscillator"); buff.SetIndicatorShortName("AC("+symbol+","+TimeframeDescription(timeframe)+")"); #ifdef __MQL5__ buff.SetColors(array_colors); #else buff.SetColor(array_colors[0]); buff.SetIndicatorLineAdditionalNumber(0); #endif //--- MQL5 #ifdef __MQL5__ //--- Создаём расчётный буфер, в котором будут храниться данные стандартного индикатора this.CreateCalculate(); //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AC); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accelerator Oscillator"); buff.SetIndicatorShortName("AC("+symbol+","+TimeframeDescription(timeframe)+")"); //--- MQL4 #else //--- Создаём буфер-гистограмму от нулевой линии для буфера второго цвета this.CreateHistogram(); //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_AC); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetShowData(true); buff.SetLabel("AC("+symbol+","+TimeframeDescription(timeframe)+")"); buff.SetIndicatorName("Accelerator Oscillator"); buff.SetIndicatorShortName("AC("+symbol+","+TimeframeDescription(timeframe)+")"); #ifdef __MQL5__ buff.SetColors(array_colors); #else buff.SetColor(array_colors[1]); buff.SetIndicatorLineAdditionalNumber(1); #endif #endif } return handle; } //+------------------------------------------------------------------+
Так как стандартный индикатор Accelerator Oscillator не имеет параметров, то массив параметров индикатора при создании хэндла индикатора функцией IndicatorCreate() использоваться не будет.
Обнулим размер массива параметров.
Создадим новый объект-индикатор, передав в его конструктор все необходимые данные для создания объекта,
сразу же распечатаем данные вновь созданного объекта (проверять успешность создания объекта не будем, так как это всего лишь тест),
и удалим этот объект — чтобы не было утечки памяти.
В последующих статьях, после создания объектов-наследников базового абстрактного индикатора и размещения их в коллекцию индикаторов при их создании, мы допишем и остальные методы создания индикаторных буферов. Сегодня же нам достаточно будет лишь такой проверки.
В методе установки значения для текущего графика в буферы указанного стандартного индикатора по индексу таймсерии в соответствии с символом/периодом объекта-буфера сделаем заготовку для организации кроссплатформенности, добавив разделение на блоки кода для MQL5 и MQL4 только для однобуферных стандартных индикаторов (реализовывать код для MQL4 не будем — сегодня это нам не нужно):
//+------------------------------------------------------------------+ //| Устанавливает значения для текущего графика в буферы указанного | //| стандартного индикатора по индексу таймсерии в соответствии | //| с символом/периодом объекта-буфера | //+------------------------------------------------------------------+ bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE) { //--- Получаем список объектов-буферов по типу и идентификатору CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return false; } //--- Получаем список рисуемых буферов с идентификатором CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Получаем список расчётных буферов с идентификатором CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Если любой из списков пустой - уходим if(list_data.Total()==0 #ifdef __MQL5__ || list_calc.Total()==0 #endif ) return false; //--- Объявляем необходимые объекты и переменные CBuffer *buffer_data0=NULL,*buffer_data1=NULL,*buffer_data2=NULL,*buffer_data3=NULL,*buffer_data4=NULL,*buffer_tmp0=NULL,*buffer_tmp1=NULL; CBuffer *buffer_calc0=NULL,*buffer_calc1=NULL,*buffer_calc2=NULL,*buffer_calc3=NULL,*buffer_calc4=NULL; #ifdef __MQL4__ CBuffer *buff_add=NULL; #endif double value00=EMPTY_VALUE, value01=EMPTY_VALUE; double value10=EMPTY_VALUE, value11=EMPTY_VALUE; double value20=EMPTY_VALUE, value21=EMPTY_VALUE; double value30=EMPTY_VALUE, value31=EMPTY_VALUE; double value40=EMPTY_VALUE, value41=EMPTY_VALUE; double value_tmp0=EMPTY_VALUE,value_tmp1=EMPTY_VALUE; long vol0=0,vol1=0; int series_index_start=series_index,index_period=0, index=0,num_bars=1; uchar clr=0; //--- В зависимости от типа стандартного индикатора switch((int)ind_type) { //--- Однобуферные стандартные индикаторы case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); #ifdef __MQL5__ list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); #endif if(buffer_data0==NULL #ifdef __MQL5__ || buffer_calc0==NULL || buffer_calc0.GetDataTotal(0)==0 #endif ) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); if(ind_type!=IND_BWMFI) clr=(color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); else { vol0=::iVolume(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period); vol1=::iVolume(buffer_data0.Symbol(),buffer_data0.Timeframe(),index_period+1); clr= ( value00>value01 && vol0>vol1 ? 0 : value00<value01 && vol0<vol1 ? 1 : value00>value01 && vol0<vol1 ? 2 : value00<value01 && vol0>vol1 ? 3 : 4 ); } #ifdef __MQL5__ buffer_data0.SetBufferColorIndex(index,clr); #else #endif } return true; //--- Многобуферные стандартные индикаторы case IND_ENVELOPES : case IND_FRACTALS : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); } return true; case IND_ADX : case IND_ADXW : case IND_BANDS : case IND_MACD : case IND_RVI : case IND_STOCHASTIC : case IND_ALLIGATOR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,2,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,2,EQUAL); buffer_calc2=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(0,index,value10); buffer_data2.SetBufferValue(0,index,value20); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); } return true; case IND_ICHIMOKU : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL); buffer_data3=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL); buffer_data4=list.At(0); //--- Получаем список объектов-буферов, у которых есть идентификатор вспомогательной линии, а из него - объект-буфер с номером линии 0 list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,0,EQUAL); buffer_tmp0=list.At(0); //--- Получаем список объектов-буферов, у которых есть идентификатор вспомогательной линии, а из него - объект-буфер с номером линии 1 list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_ADDITIONAL,EQUAL); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_ADDITIONAL_NUM,1,EQUAL); buffer_tmp1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TENKAN_SEN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_KIJUN_SEN,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANA,EQUAL); buffer_calc2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SENKOU_SPANB,EQUAL); buffer_calc3=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_CHIKOU_SPAN,EQUAL); buffer_calc4=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; if(buffer_calc3==NULL || buffer_data3==NULL || buffer_calc3.GetDataTotal(0)==0) return false; if(buffer_calc4==NULL || buffer_data4==NULL || buffer_calc4.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(0,index,value10); buffer_data2.SetBufferValue(0,index,value20); buffer_data3.SetBufferValue(0,index,value30); buffer_data4.SetBufferValue(0,index,value40); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); buffer_data3.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value30>value31 ? 0 : value30<value31 ? 1 : 2) : color_index); buffer_data4.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value40>value41 ? 0 : value40<value41 ? 1 : 2) : color_index); //--- Установим значения для вспомогательных линий индикатора в зависимости от взаимного расположения линий Senkou Span A и Senkou Span B value_tmp0=buffer_data2.GetDataBufferValue(0,index); value_tmp1=buffer_data3.GetDataBufferValue(0,index); if(value_tmp0<value_tmp1) { buffer_tmp0.SetBufferValue(0,index,buffer_tmp0.EmptyValue()); buffer_tmp0.SetBufferValue(1,index,buffer_tmp0.EmptyValue()); buffer_tmp1.SetBufferValue(0,index,value_tmp0); buffer_tmp1.SetBufferValue(1,index,value_tmp1); } else { buffer_tmp0.SetBufferValue(0,index,value_tmp0); buffer_tmp0.SetBufferValue(1,index,value_tmp1); buffer_tmp1.SetBufferValue(0,index,buffer_tmp1.EmptyValue()); buffer_tmp1.SetBufferValue(1,index,buffer_tmp1.EmptyValue()); } } return true; case IND_GATOR : list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,0,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,1,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; series_index_start=PreparingSetDataStdInd(buffer_data0,buffer_data1,buffer_data2,buffer_data3,buffer_data4, buffer_calc0,buffer_calc1,buffer_calc2,buffer_calc3,buffer_calc4, ind_type,series_index,series_time,index_period,num_bars, value00,value01,value10,value11,value20,value21,value30,value31,value40,value41); if(series_index_start==WRONG_VALUE) return false; //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10<value11 ? 0 : value10>value11 ? 1 : 2) : color_index); } return true; default: break; } return false; } //+------------------------------------------------------------------+
Это все изменения, которые нам необходимо было сделать сегодня.
Тест
Для проверки создания объекта-индикатора возьмём тестовый индикатор из прошлой статьи ,
сохраним его в новой папке \MQL5\Indicators\TestDoEasy\Part53\ под новым именем TestDoEasyPart53.mq5 и заменим строки, указывающие на работу с индикатором AD на работу с индикатором AC:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Запишем в переменную InpUsedTFs наименование выбранного в настройках рабочего таймфрейма InpUsedTFs=TimeframeDescription(InpPeriod); //--- Инициализация библиотеки DoEasy OnInitDoEasy(); //--- Установка глобальных переменных индикатора prefix=engine.Name()+"_"; //--- Рассчитываем количество баров текущего периода, умещающихся в максимальном используемом периоде //--- Используем полученное значение если оно больше 2, иначе используем 2 int num_bars=NumberBarsInTimeframe(InpPeriod); min_bars=(num_bars>2 ? num_bars : 2); //--- Проверка и удаление неудалённых графических объектов индикатора if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Создание панели кнопок //--- Проверка воспроизведения стандартного звука по макроподстановкам engine.PlaySoundByDescription(SND_OK); //--- Ждём 600 милисекунд engine.Pause(600); engine.PlaySoundByDescription(SND_NEWS); //--- indicator buffers mapping //--- Создаём все необходимые объекты-буферы для построения выбранного стандартного индикатора if(!engine.BufferCreateAC(InpUsedSymbols,InpPeriod,1)) { Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created")); return INIT_FAILED; } //--- Проверяем количество буферов, указанных в блоке properties engine.CheckIndicatorsBuffers(indicator_buffers,indicator_plots); //--- Создаём массив цветов и зададём всем буферам в коллекции значения цвета не по умолчанию //--- (закомментировано так как в методах создания стандартных индикаторов по умолчанию уже заданы цвета) //--- (всегда можно задать нужные цвета либо для всех индикаторов как здесь, либо для каждого индивидуально) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- Выведем краткие описания созданных индикаторных буферов engine.BuffersPrintShort(); //--- Установим короткое имя индикатора, разрядность данных и уровни string label=engine.BufferGetIndicatorShortNameByTypeID(IND_AC,1); IndicatorSetString(INDICATOR_SHORTNAME,label); SetIndicatorLevels(InpUsedSymbols,IND_AC); //--- Успешно return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { //--- Удаление графических объектов индикатора по префиксу имени объектов ObjectsDeleteAll(0,prefix); Comment(""); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime &time[], const double &open[], const double &high[], const double &low[], const double &close[], const long &tick_volume[], const long &volume[], const int &spread[]) { //+------------------------------------------------------------------+ //| OnCalculate Блок кода для работы с библиотекой: | //+------------------------------------------------------------------+ //--- Передача в структуру цен текущих данных массивов из OnCalculate() и установка массивам флага "как таймсерия" CopyDataAsSeries(rates_total,prev_calculated,time,open,high,low,close,tick_volume,volume,spread); //--- Проверка на минимальное количество баров для расчёта if(rates_total<min_bars || Point()==0) return 0; //--- Обработка события Calculate в библиотеке //--- Если метод OnCalculate() библиотеки вернул ноль - значит не все таймсерии готовы - уходим до следующего тика if(engine.OnCalculate(rates_data)==0) return 0; //--- Если работа в тестере if(MQLInfoInteger(MQL_TESTER)) { engine.OnTimer(rates_data); // Работа в таймере библиотеки engine.EventsHandling(); // Работа с событиями библиотеки } //+------------------------------------------------------------------+ //| OnCalculate Блок кода для работы с индикатором: | //+------------------------------------------------------------------+ //--- Проверка и расчёт количества просчитываемых баров //--- Если limit = 0, значит новых баров нет - рассчитываем текущий //--- Если limit = 1, значит появился новый бар - рассчитываем первый и текущий //--- Если limit > 1, значит первый запуск, или есть изменения в истории - полный перерасчёт всех данных int limit=rates_total-prev_calculated; //--- Перерасчёт всей истории if(limit>1) { limit=rates_total-1; engine.BuffersInitPlots(); engine.BuffersInitCalculates(); } //--- Подготовка данных //--- Заполняем данными расчётные буферы всех созданных стандартных индикаторов int bars_total=engine.SeriesGetBarsTotal(InpUsedSymbols,InpPeriod); int total_copy=(limit<min_bars ? min_bars : fmin(limit,bars_total)); if(!engine.BufferPreparingDataAllBuffersStdInd()) return 0; //--- Расчёт индикатора //--- Основной цикл расчёта индикатора for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { engine.GetBuffersCollection().SetDataBufferStdInd(IND_AC,1,i,time[i]); } //--- return value of prev_calculated for next call return(rates_total); } //+------------------------------------------------------------------+
Это всё, что необходимо на сегодня для проверки работы вновь созданного класса объекта абстрактного индикатора в тестовом индикаторе.
Полный код индикатора можно посмотреть в прикреплённых к статье файлах.
Скомпилируем индикатор и запустим его. В журнал "Эксперты" будут выведены данные по созданному объекту-индикатору:
Счёт 8550475: Artyom Trishkin (MetaQuotes Software Corp.) 10425.23 USD, 1:100, Hedge, Демонстрационный счёт MetaTrader 5 --- Инициализация библиотеки "DoEasy" --- Работа только с текущим символом. Количество используемых символов: 1 "EURUSD" Работа с заданным списком таймфреймов: "H4" "H1" Таймсерия символа EURUSD: - Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 0, Создано: 0, На сервере: 0 - Таймсерия "EURUSD" H4: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 6237 Время инициализации библиотеки: 00:00:00.156 ============= Начало списка параметров: "Стандартный индикатор" ============= Статус индикатора: Стандартный индикатор Таймфрейм индикатора: H4 Хэндл индикатора: 10 Группа индикатора: Осциллятор ------ Пустое значение для построения, для которого нет отрисовки: EMPTY_VALUE ------ Символ индикатора: EURUSD Имя индикатора: "Accelerator Oscillator" Короткое имя индикатора: "AC(EURUSD,H4)" ================== Конец списка параметров: "Стандартный индикатор" ================== Буфер(P0/B0/C1): Гистограмма от нулевой линии EURUSD H4 Буфер[P0/B2/C2]: Расчётный буфер Таймсерия "EURUSD" H1 создана успешно: - Таймсерия "EURUSD" H1: Запрошено: 1000, Фактически: 1000, Создано: 1000, На сервере: 6256
Что дальше
В следующей статье начнём создавать классы объектов-наследников базового объекта абстрактного индикатора, созданного нами сегодня.
Ниже прикреплены все файлы текущей версии библиотеки и файл тестового индикатора для 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): Кроссплатформенность мультипериодных мультисимвольных однобуферных стандартных индикаторов





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