
Работа с таймсериями в библиотеке DoEasy (Часть 44): Класс-коллекция объектов индикаторных буферов
Содержание
- Концепция
- Подготовка данных и доработка объектов-буферов
- Класс-коллекция объектов-буферов
- Тестируем создание индикатора с использованием коллекции буферов
- Что дальше
Концепция
Начиная со статьи 39 мы начали рассматривать использование библиотеки для создания пользовательских индикаторов. К настоящему моменту подготовлены объекты таймсерий и их коллекция, и объекты индикаторных буферов — базовый абстрактный буфер и индикаторные буферы на его основе.
В данной статье мы начнём создавать коллекцию индикаторных буферов для быстрого создания буферов в любом количестве (до 512 штук) в одной программе, и для удобного их получения и доступа к работе с их данными.
Связка <"коллекция таймсерий> — <коллекция буферов"> позволит нам создавать любые мультисимвольные и мультипериодные индикаторы. Этим мы начнём заниматься уже со следующей статьи. А сегодня создадим и опробуем коллекцию индикаторных буферов для создания в индикаторе любых буферов с одним из девяти стилей рисования в любом их количестве. На данный момент максимально возможное количество рисуемых буферов в индикаторе может быть не более 512. Но этого должно с лихвой хватить для создания любых сложных индикаторов с большим количеством графических построений. А при помощи создаваемого функционала создание и обслуживание такого количества графических построений упростится до простого обращения к созданным буферам по их стилю рисования и номеру в порядке создания, либо по индексу буфера в коллекции.
Подготовка данных и доработка объектов-буферов
В прошлой статье нами были созданы объекты "индикаторные буферы", являющиеся наследниками базового абстрактного буфера. Дополним их вспомогательными методами для доступа к записи и чтению данных в/из double-массивов, назначенных в качестве индикаторных буферов.
Хотя можно было обойтись и уже написанными методами базового абстрактного буфера, но создание в объектах-наследниках своих методов, присущих только их стилю рисования, пригодится для более удобного обращения к их double-массивам при обращении к буферу по его статусу (стилю рисования) и даст больше гибкости при создании своих индикаторов.
Добавим новые сообщения библиотеки. В файл \MQL5\Include\DoEasy\Datas.mqh впишем индексы новых сообщений:
MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE, // Не удалось изменить размер массива цветов MSG_LIB_SYS_FAILED_ADD_BUFFER, // Не удалось добавить объект-буфер в список MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ, // Не удалось создать объект \"Индикаторный буфер\" MSG_LIB_TEXT_YES, // Да
...
MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF, // Неправильно указано количество буферов индикатора (#property indicator_buffers) MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED, // Достигнуто максимально возможное количество индикаторных буферов MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NONE, // Нет отрисовки
и тексты сообщений, соответствующие вновь добавленным индексам:
{"Не удалось изменить размер массива цветов","Failed to resize color array"}, {"Не удалось добавить объект-буфер в список","Failed to add buffer object to list"}, {"Не удалось создать объект \"Индикаторный буфер\"","Failed to create object \"Indicator buffer\""}, {"Да","Yes"},
...
{"Неправильно указано количество буферов индикатора (#property indicator_buffers)","The number of indicator buffers is incorrect (#property indicator_buffers)"}, {"Достигнуто максимально возможное количество индикаторных буферов","The maximum number of indicator buffers has been reached"}, {"Нет отрисовки","No drawing"},
В индикаторе может быть максимально использовано 512 буферов.
Добавим в файл \MQL5\Include\DoEasy\Defines.mqh макроподстановку, указывающую на этот размер:
//+------------------------------------------------------------------+ //| Макроподстановки | //+------------------------------------------------------------------+ //--- "Описание функции с номером строки ошибки" #define DFUN_ERR_LINE (__FUNCTION__+(TerminalInfoString(TERMINAL_LANGUAGE)=="Russian" ? ", Стр. " : ", Line ")+(string)__LINE__+": ") #define DFUN (__FUNCTION__+": ") // "Описание функции" #define COUNTRY_LANG ("Russian") // Язык страны #define END_TIME (D'31.12.3000 23:59:59') // Конечная дата для запросов данных истории счёта #define TIMER_FREQUENCY (16) // Минимальная частота таймера библиотеки в милисекундах #define TOTAL_TRY (5) // Количество торговых попыток по умолчанию #define IND_COLORS_TOTAL (64) // Максимально возможное количество цветов индикаторного буфера #define IND_BUFFERS_MAX (512) // Максимально возможное количество буферов индикатора //--- Стандартные звуки
Можно конечно использовать значение "512", но макроподстановка удобнее тем, что если когда-нибудь это значение будет увеличено разработчиками, то для внесения изменений в код не нужно будет искать и исправлять это значение на новое по всем файлам, где есть обращение к данной величине, а просто поменять значение макроподстановки.
Нам потребуется осуществлять поиск и выбор объектов-буферов по свойствам, которые ранее были нами определены как ненужные для поиска и сортировки.
Все неиспользуемые в сортировке свойства у нас всегда расположены в самом конце списка перечисления свойств, и задана макроподстановка, указывающая количество неиспользуемых свойств в сортировке. Чтобы задать свойство как сортируемое, нужно переместить его из конца списка перечисления свойств ближе к тем свойствам, которые на данный момент разрешены для сортировки, и указать новое количество неиспользуемых в сортировке свойств.
И, естественно, нужно дополнить перечисление возможных критериев сортировки новыми свойствами — теми, которые мы разрешили использовать для сортировки. Расположение вновь добавляемых свойств в списке перечисления критериев сортировки должно совпадать с расположением этих свойств в списке перечисления свойств, в котором мы разрешили эти свойства использовать в сортировке.
Звучит запутанно, но на практике всё просто. Откроем файл \MQL5\Include\DoEasy\Defines.mqh и внесём необходимые изменения.
Ранее у нас целочисленные свойства объекта-буфера были расположены в таком порядке:
//+------------------------------------------------------------------+ //| Целочисленные свойства буфера | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_INDEX_PLOT = 0, // Порядковый номер рисуемого буфера BUFFER_PROP_STATUS, // Статус (по стилю рисования) буфера (из перечисления ENUM_BUFFER_STATUS) BUFFER_PROP_TYPE, // Тип буфера (из перечисления ENUM_BUFFER_TYPE) BUFFER_PROP_TIMEFRAME, // Период данных буфера (таймфрейм) BUFFER_PROP_ACTIVE, // Флаг использования буфера BUFFER_PROP_DRAW_TYPE, // Тип графического построения (из перечисления ENUM_DRAW_TYPE) BUFFER_PROP_ARROW_CODE, // Код стрелки для стиля DRAW_ARROW BUFFER_PROP_ARROW_SHIFT, // Смещение стрелок по вертикали для стиля DRAW_ARROW BUFFER_PROP_LINE_STYLE, // Стиль линии отрисовки BUFFER_PROP_LINE_WIDTH, // Толщина линии отрисовки BUFFER_PROP_DRAW_BEGIN, // Количество начальных баров без отрисовки и значений в DataWindow BUFFER_PROP_SHOW_DATA, // Признак отображения значений построения в окне DataWindow BUFFER_PROP_SHIFT, // Сдвиг графического построения индикатора по оси времени в барах BUFFER_PROP_COLOR_INDEXES, // Количество цветов BUFFER_PROP_COLOR, // Цвет отрисовки BUFFER_PROP_NUM_DATAS, // Количество буферов данных BUFFER_PROP_INDEX_BASE, // Индекс базового буфера данных BUFFER_PROP_INDEX_COLOR, // Индекс буфера цвета BUFFER_PROP_INDEX_NEXT, // Индекс свободного массива для назначения следующим индикаторным буфером }; #define BUFFER_PROP_INTEGER_TOTAL (19) // Общее количество целочисленных свойств буфера #define BUFFER_PROP_INTEGER_SKIP (6) // Количество неиспользуемых в сортировке свойств буфера //+------------------------------------------------------------------+
Здесь два свойства нам нужно сделать возможными для сортировки. Для этого переместим их выше и поменяем количество неиспользуемых в сортировке свойств с 6 на 2:
//+------------------------------------------------------------------+ //| Целочисленные свойства буфера | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_INTEGER { BUFFER_PROP_INDEX_PLOT = 0, // Порядковый номер рисуемого буфера BUFFER_PROP_STATUS, // Статус (по стилю рисования) буфера (из перечисления ENUM_BUFFER_STATUS) BUFFER_PROP_TYPE, // Тип буфера (из перечисления ENUM_BUFFER_TYPE) BUFFER_PROP_TIMEFRAME, // Период данных буфера (таймфрейм) BUFFER_PROP_ACTIVE, // Флаг использования буфера BUFFER_PROP_DRAW_TYPE, // Тип графического построения (из перечисления ENUM_DRAW_TYPE) BUFFER_PROP_ARROW_CODE, // Код стрелки для стиля DRAW_ARROW BUFFER_PROP_ARROW_SHIFT, // Смещение стрелок по вертикали для стиля DRAW_ARROW BUFFER_PROP_LINE_STYLE, // Стиль линии отрисовки BUFFER_PROP_LINE_WIDTH, // Толщина линии отрисовки BUFFER_PROP_DRAW_BEGIN, // Количество начальных баров без отрисовки и значений в DataWindow BUFFER_PROP_SHOW_DATA, // Признак отображения значений построения в окне DataWindow BUFFER_PROP_SHIFT, // Сдвиг графического построения индикатора по оси времени в барах BUFFER_PROP_COLOR_INDEXES, // Количество цветов BUFFER_PROP_COLOR, // Цвет отрисовки BUFFER_PROP_INDEX_BASE, // Индекс базового буфера данных BUFFER_PROP_INDEX_NEXT, // Индекс свободного массива для назначения следующим индикаторным буфером BUFFER_PROP_NUM_DATAS, // Количество буферов данных BUFFER_PROP_INDEX_COLOR, // Индекс буфера цвета }; #define BUFFER_PROP_INTEGER_TOTAL (19) // Общее количество целочисленных свойств буфера #define BUFFER_PROP_INTEGER_SKIP (2) // Количество неиспользуемых в сортировке свойств буфера //+------------------------------------------------------------------+
Здесь мы указали, что только два свойства, находящиеся в самом конце списка, не будут участвовать в сортировке.
Добавим эти новые свойства в критерии сортировки:
//+------------------------------------------------------------------+ //| Возможные критерии сортировки буферов | //+------------------------------------------------------------------+ #define FIRST_BUFFER_DBL_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP) #define FIRST_BUFFER_STR_PROP (BUFFER_PROP_INTEGER_TOTAL-BUFFER_PROP_INTEGER_SKIP+BUFFER_PROP_DOUBLE_TOTAL-BUFFER_PROP_DOUBLE_SKIP) enum ENUM_SORT_BUFFER_MODE { //--- Сортировка по целочисленным свойствам SORT_BY_BUFFER_INDEX_PLOT = 0, // Сортировать по порядковому номеру рисуемого буфера SORT_BY_BUFFER_STATUS, // Сортировать по стилю рисования (статусу) буфера (из перечисления ENUM_BUFFER_STATUS) SORT_BY_BUFFER_TYPE, // Сортировать по типу буфера (из перечисления ENUM_BUFFER_TYPE) SORT_BY_BUFFER_TIMEFRAME, // Сортировать по периоду данных буфера (таймфрейму) SORT_BY_BUFFER_ACTIVE, // Сортировать по флагу использования буфера SORT_BY_BUFFER_DRAW_TYPE, // Сортировать по типу графического построения (из перечисления ENUM_DRAW_TYPE) SORT_BY_BUFFER_ARROW_CODE, // Сортировать по коду стрелки для стиля DRAW_ARROW SORT_BY_BUFFER_ARROW_SHIFT, // Сортировать по смещению стрелок по вертикали для стиля DRAW_ARROW SORT_BY_BUFFER_LINE_STYLE, // Сортировать по стилю линии отрисовки SORT_BY_BUFFER_LINE_WIDTH, // Сортировать по толщине линии отрисовки SORT_BY_BUFFER_DRAW_BEGIN, // Сортировать по количеству начальных баров без отрисовки и значений в DataWindow SORT_BY_BUFFER_SHOW_DATA, // Сортировать по признаку отображения значений построения в окне DataWindow SORT_BY_BUFFER_SHIFT, // Сортировать по сдвигу графического построения индикатора по оси времени в барах SORT_BY_BUFFER_COLOR_INDEXES, // Сортировать по количеству цветов SORT_BY_BUFFER_COLOR, // Сортировать по цвету отрисовки SORT_BY_BUFFER_INDEX_BASE, // Сортировать по индексу базового буфера данных SORT_BY_BUFFER_INDEX_NEXT, // Сортировать по индексу свободного массива для назначения следующим индикаторным буфером //--- Сортировка по вещественным свойствам SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP, // Сортировать по пустому значению для построения, для которого нет отрисовки //--- Сортировка по строковым свойствам SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP, // Сортировать по символу буфера SORT_BY_BUFFER_LABEL, // Сортировать по имени индикаторной графической серии, отображаемого в окне DataWindow }; //+------------------------------------------------------------------+
Как видим — их расположение в списке перечисления критериев сортировки совпадает с расположением в перечислении целочисленных свойств. Это условие обязательно к выполнению — перечисление критериев — порядок следования в нём свойств сортировки, должен совпадать с порядком следования свойств в перечислении свойств объекта. Это всё обсуждалось ещё в третьей части описания библиотеки.
Раз уж мы заговорили о сортировке объектов-буферов по их свойствам, значит — настало время подготовить инструментарий для осуществления поиска объектов-буферов, который осуществляется по его свойствам. Мы уже сделали такую возможность для всех объектов библиотеки, хранящимся в коллекциях. Теперь напишем методы и для поиска объектов-буферов.
Откроем файл \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" //+------------------------------------------------------------------+
В самом низу тела класса — после блока с объявлением методов работы с барами таймсерий, впишем блок с объявлением методов работы с индикаторными буферами:
//+------------------------------------------------------------------+ //| Методы работы с барами таймсерии | //+------------------------------------------------------------------+ //--- Возвращает список баров, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBarProperty(CArrayObj *list_source,ENUM_BAR_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс бара в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property); static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property); static int FindBarMax(CArrayObj *list_source,ENUM_BAR_PROP_STRING property); //--- Возвращает индекс бара в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_INTEGER property); static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_DOUBLE property); static int FindBarMin(CArrayObj *list_source,ENUM_BAR_PROP_STRING property); //+------------------------------------------------------------------+ //| Методы работы с индикаторными буферами | //+------------------------------------------------------------------+ //--- Возвращает список буферов, у которых одно из (1) целочисленных, (2) вещественных и (3) строковых свойств удовлетворяет заданному критерию static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property,long value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property,double value,ENUM_COMPARER_TYPE mode); static CArrayObj *ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property,string value,ENUM_COMPARER_TYPE mode); //--- Возвращает индекс буфера в списке с максимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property); static int FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property); static int FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property); //--- Возвращает индекс буфера в списке с минимальным значением (1) целочисленного, (2) вещественного и (3) строкового свойства ордера static int FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property); static int FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property); static int FindBufferMin(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property); //--- }; //+------------------------------------------------------------------+
И в самый конец файла впишем все методы, объявленные в теле класса:
//+------------------------------------------------------------------+ //| Методы работы со списками буферов | //+------------------------------------------------------------------+ //+------------------------------------------------------------------+ //| Возвращает список буферов, у которых одно из целочисленных | //| свойств удовлетворяет заданному критерию | //+------------------------------------------------------------------+ CArrayObj *CSelect::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_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++) { CBuffer *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::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_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++) { CBuffer *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::ByBufferProperty(CArrayObj *list_source,ENUM_BUFFER_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++) { CBuffer *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::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_INTEGER property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBuffer *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *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::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_DOUBLE property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBuffer *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *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::FindBufferMax(CArrayObj *list_source,ENUM_BUFFER_PROP_STRING property) { if(list_source==NULL) return WRONG_VALUE; int index=0; CBuffer *max_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *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::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_INTEGER property) { int index=0; CBuffer *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *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::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_DOUBLE property) { int index=0; CBuffer *min_obj=NULL; int total=list_source.Total(); if(total== 0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *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::FindBufferMin(CArrayObj* list_source,ENUM_BUFFER_PROP_STRING property) { int index=0; CBuffer *min_obj=NULL; int total=list_source.Total(); if(total==0) return WRONG_VALUE; for(int i=1; i<total; i++) { CBuffer *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; } //+------------------------------------------------------------------+
Работу класса CSelect мы подробно разбирали в третьей статье описания создания библиотеки.
Немного доработаем классы астрактного буфера и его наследников.
Так как мы делаем поиск и сортировку по свойствам объектов-буферов,
то подключим файл класса CSelect к файлу класса абстрактнго буфера \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh:
//+------------------------------------------------------------------+ //| Buffer.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" //+------------------------------------------------------------------+
Теперь класс CSelect будет виден в классе CBuffer и во всех его наследниках.
В публичной секции класса напишем метод, устанавливающий пользовательское имя объекту-буферу:
public: //--- Устанавливает (1) целочисленное, (2) вещественное и (3) строковое свойство буфера void SetProperty(ENUM_BUFFER_PROP_INTEGER property,long value) { this.m_long_prop[property]=value; } void SetProperty(ENUM_BUFFER_PROP_DOUBLE property,double value) { this.m_double_prop[this.IndexProp(property)]=value; } void SetProperty(ENUM_BUFFER_PROP_STRING property,string value) { this.m_string_prop[this.IndexProp(property)]=value; } //--- Возвращает из массива свойств (1) целочисленное, (2) вещественное и (3) строковое свойство буфера long GetProperty(ENUM_BUFFER_PROP_INTEGER property) const { return this.m_long_prop[property]; } double GetProperty(ENUM_BUFFER_PROP_DOUBLE property) const { return this.m_double_prop[this.IndexProp(property)]; } string GetProperty(ENUM_BUFFER_PROP_STRING property) const { return this.m_string_prop[this.IndexProp(property)]; } //--- Возвращает описание (1) целочисленного, (2) вещественного и (3) строкового свойства буфера string GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property); string GetPropertyDescription(ENUM_BUFFER_PROP_DOUBLE property); string GetPropertyDescription(ENUM_BUFFER_PROP_STRING property); //--- Возвращает флаг поддержания буфером данного свойства virtual bool SupportProperty(ENUM_BUFFER_PROP_INTEGER property) { return true; } virtual bool SupportProperty(ENUM_BUFFER_PROP_DOUBLE property) { return true; } virtual bool SupportProperty(ENUM_BUFFER_PROP_STRING property) { return true; } //--- Сравнивает объекты CBuffer между собой по всем возможным свойствам (для сортировки списков по указанному свойству объекта-буфера) virtual int Compare(const CObject *node,const int mode=0) const; //--- Сравнивает объекты CBuffer между собой по всем свойствам (для поиска равных объектов-буферов) bool IsEqual(CBuffer* compared_obj) const; //--- Устанавливает имя буфера void SetName(const string name) { this.m_name=name; } //--- Конструктор по умолчанию CBuffer(void){;} protected:
Задание пользовательского имени буферу даёт возможность именовать буфер для последующего его поиска в списке-коллекции по этому имени.
У нас есть метод, возвращающий значение буфера из указанного индекса таймсерии, есть метод, возвращающий цвет, установленный буферу на указанном индексе таймсерии, но нет метода, возвращающего индекс цвета, установленный буферу в указанном индексе таймсерии. А ведь по сути, мы не устанавливаем в буфер значение цвета как такового. Мы устанавливаем в буфер индекс цвета — число, указывающее на то, какой по счёту из назначенных буферу цветов нужно использовать для раскраски линии в указанной позиции таймсерии.
Исправим это упущение — объявим ещё один метод, возвращающий индекс цвета, установленный буферу в указанной позиции таймсерии, а метод, возвращающий цвет буфера в указанной позиции таймсерии переименуем с GetColorBufferValue() на GetColorBufferValueColor():
//--- Возвращает размер массива буфера данных virtual int GetDataTotal(const uint buffer_index=0) const; //--- Возвращает значение из указанного индекса указанного массива буфера (1) данных, (2) индекса цвета, (3) цвета double GetDataBufferValue(const uint buffer_index,const uint series_index) const; int GetColorBufferValueIndex(const uint series_index) const; color GetColorBufferValueColor(const uint series_index) const; //--- Устанавливает значение в указанный индекс указанному массиву буфера (1) данных, (2) цвета
Теперь у нас объявлены два метода, работающие с цветом буфера. Один возвращает цвет, другой — индекс цвета.
За пределами тела класса напишем реализацию метода, возвращающего индекс цвета, и поправим реализацию метода, возвращающего цвет буфера:
//+------------------------------------------------------------------+ //| Возвращает значение индекса цвета из указанного индекса таймсерии| //| указанного массива буфера цвета | //+------------------------------------------------------------------+ int CBuffer::GetColorBufferValueIndex(const uint series_index) const { int data_total=this.GetDataTotal(0); if(data_total==0) return WRONG_VALUE; int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); return(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]); } //+------------------------------------------------------------------+ //| Возвращает значение цвета из указанного индекса таймсерии | //| указанного массива буфера цвета | //+------------------------------------------------------------------+ color CBuffer::GetColorBufferValueColor(const uint series_index) const { int data_total=this.GetDataTotal(0); if(data_total==0) return clrNONE; int color_index=this.GetColorBufferValueIndex(series_index); return(color_index>WRONG_VALUE ? (color)this.ArrayColors[color_index] : clrNONE); } //+------------------------------------------------------------------+
Раньше у нас был только один метод, и индекс цвета мы получали прямо внутри него:
//+------------------------------------------------------------------+ //| Возвращает значение из указанного индекса таймсерии | //| указанного массива буфера цвета | //+------------------------------------------------------------------+ color CBuffer::GetColorBufferValue(const uint series_index) const { int data_total=this.GetDataTotal(0); if(data_total==0) return clrNONE; int data_index=((int)series_index<data_total ? (int)series_index : data_total-1); int color_index=(this.ColorsTotal()==1 ? 0 : (int)this.ColorBufferArray[data_index]); return (color)this.ArrayColors[color_index]; } //+------------------------------------------------------------------+
Теперь этот расчёт вынесен в отдельный метод GetColorBufferValueIndex(), а в методе возврата цвета бара мы вместо расчёта индекса используем вызов этого нового метода.
При создании класса объекта стрелочного буфера, являющегося (как и все остальные классы буферов) наследником абстрактного буфера, мною было сделано упущение — в классе CBuffer были объявлены виртуальные методы для установки кода стрелок и их смещения, но про их реализацию в классе-наследнике я забыл. Исправим ситуацию.
Откроем файл класса объекта буфера стрелок \MQL5\Include\DoEasy\Objects\Indicators\BufferArrow.mqh и объявим эти методы. Заодно добавим ещё два метода для установки и возврата значений в/из массива, назначенного индикаторным буфером:
//+------------------------------------------------------------------+ //| Буфер со стилем рисования "Отрисовка стрелками" | //+------------------------------------------------------------------+ class CBufferArrow : public CBuffer { private: public: //--- Конструктор CBufferArrow(const uint index_plot,const uint index_base_array) : CBuffer(BUFFER_STATUS_ARROW,BUFFER_TYPE_DATA,index_plot,index_base_array,1,1,"Arrows") {} //--- Поддерживаемые целочисленные свойства буфера virtual bool SupportProperty(ENUM_BUFFER_PROP_INTEGER property); //--- Поддерживаемые вещественные свойства буфера virtual bool SupportProperty(ENUM_BUFFER_PROP_DOUBLE property); //--- Поддерживаемые строковые свойства буфера virtual bool SupportProperty(ENUM_BUFFER_PROP_STRING property); //--- Выводит в журнал краткое описание буфера virtual void PrintShort(void); //--- Устанавливает (1) код стрелки, (2) смещение стрелок по вертикали virtual void SetArrowCode(const uchar code); virtual void SetArrowShift(const int shift); //--- Устанавливает значение в массив буфера данных void SetData(const uint series_index,const double value) { this.SetBufferValue(0,series_index,value); } //--- Возвращает значение из массива буфера данных double GetData(const uint series_index) const { return this.GetDataBufferValue(0,series_index); } }; //+------------------------------------------------------------------+
За пределами тела класса напишем реализацию методов установки кода стрелоки их смещения:
//+------------------------------------------------------------------+ //| Устанавливает код стрелки | //+------------------------------------------------------------------+ void CBufferArrow::SetArrowCode(const uchar code) { this.SetProperty(BUFFER_PROP_ARROW_CODE,code); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,code); } //+------------------------------------------------------------------+ //| Устанавливает смещение стрелок по вертикали | //+------------------------------------------------------------------+ void CBufferArrow::SetArrowShift(const int shift) { this.SetProperty(BUFFER_PROP_ARROW_SHIFT,shift); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,shift); } //+------------------------------------------------------------------+
Методы записывают переданное в них значение в соответствующее свойство объекта-буфера и
устанавливают это свойство рисуемому буферу объекта-буфера.
В файлы объектов-буферов с одним буфером для отрисовки, а именно: линий (BufferLine.mqh), отрезков (BufferSection.mqh) и гистограммы от нуля (BufferHistogram.mqh) также впишем два метода установки/возврата значений в/из массива буфера данных объекта-буфера:
//--- Устанавливает значение в массив буфера данных void SetData(const uint series_index,const double value) { this.SetBufferValue(0,series_index,value); } //--- Возвращает значение из массива буфера данных double GetData(const uint series_index) const { return this.GetDataBufferValue(0,series_index); }
В файлы объектов-буферов с двумя буферами для отрисовки, а именно: гистограммы на двух массивах данных (BufferHistogram2.mqh), загзага (BufferZigZag.mqh) и заливки между двумя массивами данных (BufferFilling.mqh) впишем четыре метода установки/возврата значений в/из массивов буферов данных объекта-буфера:
//--- Устанавливает значение в (1) нулевой, (2) первый массив буфера данных void SetData0(const uint series_index,const double value) { this.SetBufferValue(0,series_index,value); } void SetData1(const uint series_index,const double value) { this.SetBufferValue(1,series_index,value); } //--- Возвращает значение из (1) нулевого, (2) первого массива буфера данных double GetData0(const uint series_index) const { return this.GetDataBufferValue(0,series_index); } double GetData1(const uint series_index) const { return this.GetDataBufferValue(1,series_index); }
В файлы объектов-буферов с четырьмя буферами для отрисовки, а именно: баров (BufferBars.mqh) и свечей (BufferCandlts.mqh) впишем восемь методов установки/возврата значений OHLC в/из массивов буферов данных объекта-буфера:
//--- Устанавливает значение (1) Open, (2) High, (3) Low, (4) Close в соответствующий массив буфера данных void SetDataOpen(const uint series_index,const double value) { this.SetBufferValue(0,series_index,value); } void SetDataHigh(const uint series_index,const double value) { this.SetBufferValue(1,series_index,value); } void SetDataLow(const uint series_index,const double value) { this.SetBufferValue(2,series_index,value); } void SetDataClose(const uint series_index,const double value) { this.SetBufferValue(3,series_index,value); } //--- Возвращает значение (1) Open, (2) High, (3) Low, (4) Close из соответствующего массива буфера данных double GetDataOpen(const uint series_index) const { return this.GetDataBufferValue(0,series_index); } double GetDataHigh(const uint series_index) const { return this.GetDataBufferValue(1,series_index); } double GetDataLow(const uint series_index) const { return this.GetDataBufferValue(2,series_index); } double GetDataClose(const uint series_index) const { return this.GetDataBufferValue(3,series_index); }
Можно конечно было обойтись уже написанными методами базового абстрактного объекта-буфера SetBufferValue() и GetBufferValue(), но эти методы требуют указаний номера нужного буфера. А мы делаем всё для облегчения работы конечного пользователя. Поэтому у нас будет выбор каким способом из предложенных воспользоваться.
У нас всё готово для создания класса-коллекции объектов индикаторных буферов.
Данный класс будет содержать в себе список всех создаваемых объектов-буферов и предоставлять возможность создания и получения любого буфера для работы с ним в программе. В отличии от предыдущих классов-коллекций здесь нам не нужно будет при добавлении очередного объекта-буфера проверять наличие уже имеющегося такого же объекта с точно такими же свойствами — ведь мы можем использовать в индикаторе абсолютно одинаковые буферы для визуализации разных событий.
Класс-коллекция объектов-буферов
В папке \MQL5\Include\DoEasy\Collections\ создадим новый файл BuffersCollection.mqh с базовым классом CObject стандартной библиотеки, и сразу же подключим к нему файлы классов базового списка библиотеки и объектов-буферов:
//+------------------------------------------------------------------+ //| BuffersCollection.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 "ListObj.mqh" #include "..\Objects\Indicators\BufferArrow.mqh" #include "..\Objects\Indicators\BufferLine.mqh" #include "..\Objects\Indicators\BufferSection.mqh" #include "..\Objects\Indicators\BufferHistogram.mqh" #include "..\Objects\Indicators\BufferHistogram2.mqh" #include "..\Objects\Indicators\BufferZigZag.mqh" #include "..\Objects\Indicators\BufferFilling.mqh" #include "..\Objects\Indicators\BufferBars.mqh" #include "..\Objects\Indicators\BufferCandles.mqh" //+------------------------------------------------------------------+ //| Коллекция индикаторных буферов | //+------------------------------------------------------------------+ class CBuffersCollection : public CObject {
Давайте наполним тело класса всем необходимым содержимым (благо его не много), и далее рассмотрим назначение всего необходимого:
//+------------------------------------------------------------------+ //| Коллекция индикаторных буферов | //+------------------------------------------------------------------+ class CBuffersCollection : public CObject { private: CListObj m_list; // Список объектов-буферов //--- Возвращает индекс следующего (1) рисуемого, (2) базового буфера int GetIndexNextPlot(void); int GetIndexNextBase(void); //--- Создаёт новый объект-буфер и помещает его в список-коллекцию bool CreateBuffer(ENUM_BUFFER_STATUS status); public: //--- Возвращает (1) себя, (2) список таймсерий CBuffersCollection *GetObject(void) { return &this; } CArrayObj *GetList(void) { return &this.m_list; } //--- Возвращает количество (1) рисуемых буферов, (2) всех массивов, использующихся для построения всех буферов в коллекции int PlotsTotal(void); int BuffersTotal(void); //--- Создаёт новый буфер (1) "Отрисовка стрелками", (2) "Линия", (3) "Отрезки", (4) "Гистограмма от нулевой линии", //--- (5) "Гистограмма на двух индикаторных буферах", (6) "Зигзаг", (7) "Цветовая заливка между двумя уровнями", //--- (8) "Отображение в виде баров", (9) "Отображение в виде свечей", bool CreateArrow(void) { return this.CreateBuffer(BUFFER_STATUS_ARROW); } bool CreateLine(void) { return this.CreateBuffer(BUFFER_STATUS_LINE); } bool CreateSection(void) { return this.CreateBuffer(BUFFER_STATUS_SECTION); } bool CreateHistogram(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM); } bool CreateHistogram2(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); } bool CreateZigZag(void) { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG); } bool CreateFilling(void) { return this.CreateBuffer(BUFFER_STATUS_FILLING); } bool CreateBars(void) { return this.CreateBuffer(BUFFER_STATUS_BARS); } bool CreateCandles(void) { return this.CreateBuffer(BUFFER_STATUS_CANDLES); } //--- Возвращает буфер по индексу Plot CBuffer *GetBufferByPlot(const int plot_index); //--- Возвращает буферы по стилю рисования по порядковому номеру //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) CBufferArrow *GetBufferArrow(const int number); CBufferLine *GetBufferLine(const int number); CBufferSection *GetBufferSection(const int number); CBufferHistogram *GetBufferHistogram(const int number); CBufferHistogram2 *GetBufferHistogram2(const int number); CBufferZigZag *GetBufferZigZag(const int number); CBufferFilling *GetBufferFilling(const int number); CBufferBars *GetBufferBars(const int number); CBufferCandles *GetBufferCandles(const int number); //--- Конструктор CBuffersCollection(); }; //+------------------------------------------------------------------+
Итак, m_list — список, в который будем "складывать" и хранить все создаваемые объекты-буферы. Является наследником класса динамического массива указателей на экземпляры класса CObject.
Приватный метод, возвращающий индекс следующего рисуемого буфера GetIndexNextPlot() необходим для указания индекса очередного создаваемого индикаторного буфера, а метод (также приватный), возвращающий индекс следующего базового буфера GetIndexNextBase(), необходим для указания индекса реального массива, который можно назначить в качестве индикаторного буфера для вновь создаваемого объекта-буфера.
Поясню. При создании буфера для индикатора мы ему задаём номер в окне данных (номер рисуемого буфера) и связываем с double-массивом (индекс базового массива буфера). Почему "базового"? Да просто буфер для отрисовки может использовать несколько массивов. Базовым будет самый первый назначаемый массив в качестве индикаторного. Остальные используемые для отрисовки массивы будут иметь индекс "базовый массив"+N.
Таким образом, для трёх цветных буферов, строящихся на двух массивах, индексы рисуемого и базового будут выглядеть таким образом:
Первый буфер:
- Рисуемый буфер — индекс 0
- Базовый массив — индекс 0
- Второй массив — индекс 1
- Массив цвета — индекс 2
Второй буфер:
- Рисуемый буфер — индекс 1
- Базовый массив — индекс 3
- Второй массив — индекс 4
- Массив цвета — индекс 5
Третий буфер:
- Рисуемый буфер — индекс 2
- Базовый массив — индекс 6
- Второй массив — индекс 7
- Массив цвета — индекс 8
Как видим, индексация для рисуемого буфера и базового массива для каждого буфера своя, и при достаточно большом количестве используемых буферов в индикаторе, можно легко запутаться что к чему относится. Класс-коллекция будет автоматически назначать верные индексы для рисуемых буферов и их массивов. Соответственно, всегда можно будет к ним обратиться из своей программы.
Метод CreateBuffer() создаёт новый буфер и помещает его в список-коллекцию.
Методы GetObject() и GetList() возвращают указатели на объект класса-коллекции и на список объектов-буферов класса-коллекции соответственно.
Методы PlotsTotal() и BuffersTotal() возвращают количество созданных рисуемых буферов в коллекции и общее количество используемых массивов для построения всех рисуемых буферов соответственно.
Публичные методы для создания объектов-буферов с конкретным стилем рисования:
//--- Создаёт новый буфер (1) "Отрисовка стрелками", (2) "Линия", (3) "Отрезки", (4) "Гистограмма от нулевой линии", //--- (5) "Гистограмма на двух индикаторных буферах", (6) "Зигзаг", (7) "Цветовая заливка между двумя уровнями", //--- (8) "Отображение в виде баров", (9) "Отображение в виде свечей", bool CreateArrow(void) { return this.CreateBuffer(BUFFER_STATUS_ARROW); } bool CreateLine(void) { return this.CreateBuffer(BUFFER_STATUS_LINE); } bool CreateSection(void) { return this.CreateBuffer(BUFFER_STATUS_SECTION); } bool CreateHistogram(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM); } bool CreateHistogram2(void) { return this.CreateBuffer(BUFFER_STATUS_HISTOGRAM2); } bool CreateZigZag(void) { return this.CreateBuffer(BUFFER_STATUS_ZIGZAG); } bool CreateFilling(void) { return this.CreateBuffer(BUFFER_STATUS_FILLING); } bool CreateBars(void) { return this.CreateBuffer(BUFFER_STATUS_BARS); } bool CreateCandles(void) { return this.CreateBuffer(BUFFER_STATUS_CANDLES); }
Методы возвращают результат работы приватного метода создания объекта-буфера CreateBuffer() с указанием стиля рисования создаваемого буфера.
Метод GetBufferByPlot() возвращает указатель на буфер по его индексу рисуемого буфера.
Методы, возвращающие указатели на объекты-буферы по их порядковому номеру:
//--- Возвращает буферы по стилю рисования по порядковому номеру //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) CBufferArrow *GetBufferArrow(const int number); CBufferLine *GetBufferLine(const int number); CBufferSection *GetBufferSection(const int number); CBufferHistogram *GetBufferHistogram(const int number); CBufferHistogram2 *GetBufferHistogram2(const int number); CBufferZigZag *GetBufferZigZag(const int number); CBufferFilling *GetBufferFilling(const int number); CBufferBars *GetBufferBars(const int number); CBufferCandles *GetBufferCandles(const int number);
Возвращают объект с конкретным стилем рисования по его номеру в порядке создания.
Поясню на примере:
Мы создали четыре буфера стрелок BufferArrow() с индексами рисуемого буфера 0, 1, 2 и 3.
Затем мы создали пять буферов линий BufferLine() с индексами рисуемого буфера 4, 5, 6, 7 и 8.
Теперь нам нужно поработать со третьим буфером стрелок (который с индексом 2), и с четвёртым буфером линий (с индексом 7).
Чтобы получить указатель на третий буфер стрелок, мы просто получаем его по порядковому номеру (не индексу, а именно номеру). Номер должен отсчитываться от нуля. Т.е., для получения третьего буфера стрелок, мы должны его получить так:
CBufferArrow *buffer_arrow=GetBufferArrow(2); // третий буфер стрелок (0,1,2)
Чтобы получить указатель на четвёртый буфер линий, мы должны получить его так:
CBufferLine *buffer_line=GetBufferLine(3); // четвёртый буфер линий (0,1,2,3)
Теперь рассмотрим реализацию всех объявленных методов.
Конструктор класса:
//+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CBuffersCollection::CBuffersCollection() { this.m_list.Clear(); this.m_list.Sort(); this.m_list.Type(COLLECTION_BUFFERS_ID); } //+------------------------------------------------------------------+
Очищаем список, устанавливаем списку флаг сортированного списка и устанавливаем типу коллекции идентификатор списка коллекции индикаторных буферов.
Метод, возвращающий индекс следующего рисуемого буфера и метод, возвращающий индекс следующего базового буфера:
//+------------------------------------------------------------------+ //| Возвращает индекс следующего рисуемого буфера | //+------------------------------------------------------------------+ int CBuffersCollection::GetIndexNextPlot(void) { //--- Получаем указатель на список, и если по какой-то причине список не создан - возвращаем -1 CArrayObj *list=this.GetList(); if(list==NULL) return WRONG_VALUE; //--- Получаем индекс рисуемого буфера с наибольшим значением, и если метод FindBufferMax() вернул -1, //--- то это означает, что список пустой - возвращаем индекс 0 для самого первого буфера в списке int index=CSelect::FindBufferMax(list,BUFFER_PROP_INDEX_PLOT); if(index==WRONG_VALUE) index=0; //--- если же индекс не -1, else { //--- получаем объект-буфер из списка по его индексу CBuffer *buffer=this.m_list.At(index); if(buffer==NULL) return WRONG_VALUE; //--- Возвращаем индекс, следующий за индексом Plot данного объекта-буфера index=buffer.IndexPlot()+1; } //--- Возвращаем значение индекса return index; } //+------------------------------------------------------------------+ //| Возвращает индекс следующего базового буфера | //+------------------------------------------------------------------+ int CBuffersCollection::GetIndexNextBase(void) { //--- Получаем указатель на список, и если по какой-то причине список не создан - возвращаем -1 CArrayObj *list=this.GetList(); if(list==NULL) return WRONG_VALUE; //--- Получаем наибольший индекс следующего массива, который можно назначить индикаторным буфером, //--- и если метод FindBufferMax() вернул -1, //--- то это означает, что список пустой - возвращаем индекс 0 для самого первого буфера в списке int index=CSelect::FindBufferMax(list,BUFFER_PROP_INDEX_NEXT); if(index==WRONG_VALUE) index=0; //--- если же индекс не -1, else { //--- получаем объект-буфер из списка по его индексу CBuffer *buffer=this.m_list.At(index); if(buffer==NULL) return WRONG_VALUE; //--- Возвращаем значение индекса следующего массива из свойств данного объекта-буфера index=buffer.IndexNextBuffer(); } //--- Возвращаем значение индекса return index; } //+------------------------------------------------------------------+
Логика двух этих методов идентична, и я её расписал в комментариях к строкам кода.
Метод, создающий новый объект-буфер и размещающий его в список-коллекцию:
//+------------------------------------------------------------------+ //| Создаёт новый объект-буфер и помещает его в список-коллекцию | //+------------------------------------------------------------------+ bool CBuffersCollection::CreateBuffer(ENUM_BUFFER_STATUS status) { //--- Получаем индекс рисуемого буфера и индекс, по которому назначается первый массив буфера в качестве индикаторного int index_plot=this.GetIndexNextPlot(); int index_base=this.GetIndexNextBase(); //--- Если любой из индексов не получен - возвращаем false if(index_plot==WRONG_VALUE || index_base==WRONG_VALUE) return false; //--- Если уже достигнуто максимально возможное количество буферов индикатора - сообщаем об этом и возвращаем false if(this.m_list.Total()==IND_BUFFERS_MAX) { ::Print(CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED)); return false; } //--- Создаём описание стиля рисования буфера string descript=::StringSubstr(::EnumToString(status),14); //--- Объявляем объект абстрактного буфера CBuffer *buffer=NULL; //--- В зависимости от переданного в метод статуса (стиля рисования) создаём объект-буфер switch(status) { case BUFFER_STATUS_ARROW : buffer=new CBufferArrow(index_plot,index_base); break; case BUFFER_STATUS_LINE : buffer=new CBufferLine(index_plot,index_base); break; case BUFFER_STATUS_SECTION : buffer=new CBufferSection(index_plot,index_base); break; case BUFFER_STATUS_HISTOGRAM : buffer=new CBufferHistogram(index_plot,index_base); break; case BUFFER_STATUS_HISTOGRAM2 : buffer=new CBufferHistogram2(index_plot,index_base); break; case BUFFER_STATUS_ZIGZAG : buffer=new CBufferZigZag(index_plot,index_base); break; case BUFFER_STATUS_FILLING : buffer=new CBufferFilling(index_plot,index_base); break; case BUFFER_STATUS_BARS : buffer=new CBufferBars(index_plot,index_base); break; case BUFFER_STATUS_CANDLES : buffer=new CBufferCandles(index_plot,index_base); break; default: break; } //--- Если не удалось создать буфер - сообщаем об этом и возвращаем false if(buffer==NULL) { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_CREATE_BUFFER_OBJ)," ",descript); return false; } //--- Если по какой-то причине не удалось добавить объект-буфер в список коллекции - //--- сообщаем о неудаче, удаляем созданный объект-буфер и возвращаем false if(!this.m_list.Add(buffer)) { ::Print(CMessage::Text(MSG_LIB_SYS_FAILED_ADD_BUFFER)); delete buffer; return false; } //--- Устанавливаем имя объекту-буферу и возвращаем true buffer.SetName("Buffer"+descript+"("+(string)buffer.IndexPlot()+")"); return true; } //+------------------------------------------------------------------+
Здесь также логика расписана в комментариях. Отмечу, что объявляем мы объект абстрактного буфера CBuffer, но создаём новый объект с типом рисования, переданным в метод статусом (статус у нас как раз описывает стиль рисования). Дело в том, что все объекты-буферы являются наследниками абстрактного объекта-буфера, поэтому такое объявление и создание объекта допустимо и удобно.
Метод, возвращающий буфер по его индексу Plot (по индексу в окне DataWindow):
//+------------------------------------------------------------------+ //| Возвращает буфер по индексу Plot | //+------------------------------------------------------------------+ CBuffer *CBuffersCollection::GetBufferByPlot(const int plot_index) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_INDEX_PLOT,plot_index,EQUAL); return(list!=NULL && list.Total()==1 ? list.At(0) : NULL); } //+------------------------------------------------------------------+
При помощи класса CSelect получаем список, содержащий только объект-буфер с указанным индексом (объект в списке будет всего один), и возвращаем из полученного списка объект-буфер, если он найден, либо NULL — если такого объекта нет в списке коллекции.
Методы, возвращающие объекты-буферы конкретного типа:
//+------------------------------------------------------------------+ //| Возвращает буфер "Отрисовка стрелками" по порядковому номеру | //| (0 - самый первый созданный буфер стрелок, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferArrow *CBuffersCollection::GetBufferArrow(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_ARROW,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Возвращает буфер "Линия" по порядковому номеру | //| (0 - самый первый созданный буфер линий, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferLine *CBuffersCollection::GetBufferLine(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_LINE,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Возвращает буфер "Отрезки" по порядковому номеру | //| (0 - самый первый созданный буфер отрезков, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferSection *CBuffersCollection::GetBufferSection(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_SECTION,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Возвращает буфер "Гистограмма от нулевой линии" по номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferHistogram *CBuffersCollection::GetBufferHistogram(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_HISTOGRAM,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Возвращает буфер "Гистограмма на двух буферах" по номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferHistogram2 *CBuffersCollection::GetBufferHistogram2(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_HISTOGRAM2,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Возвращает буфер "ZigZag" по порядковому номеру | //| (0 - самый первый созданный буфер зигзага, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferZigZag *CBuffersCollection::GetBufferZigZag(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_ZIGZAG,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //|Возвращает буфер "Цветовая заливка между двумя уровнями" по номеру| //| (0 - самый первый созданный буфер заливки, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferFilling *CBuffersCollection::GetBufferFilling(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_FILLING,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //| Возвращает буфер "Отображение в виде баров" по порядковому номеру| //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferBars *CBuffersCollection::GetBufferBars(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_BARS,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); } //+------------------------------------------------------------------+ //|Возвращает буфер "Отображение в виде свечей" по порядковому номеру| //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ CBufferCandles *CBuffersCollection::GetBufferCandles(const int number) { CArrayObj *list=CSelect::ByBufferProperty(this.GetList(),BUFFER_PROP_STATUS,BUFFER_STATUS_CANDLES,EQUAL); return(list!=NULL && list.Total()>0 ? list.At(number) : NULL); }
Все методы идентичны друг другу, поэтому рассмотрим один.
Получаем список, содержащий только объекты-буферы с нужным стилем рисования.
Если список получен, и он не пустой, то возвращаем объект из полученного списка по указанному индексу.
В списке объекты расположены а порядке возрастания индексов, поэтому никакой корректировки индекса не нужно.
Если же индекс выходит за пределы списка, то метод At() класса CArrayObj вернёт NULL.
Если список не получен, либо он пустой — возвращаем NULL.
Методы, возвращающие количество рисуемых буферов и количество всех индикаторных массивов:
//+------------------------------------------------------------------+ //| Возвращает количество рисуемых буферов | //+------------------------------------------------------------------+ int CBuffersCollection::PlotsTotal(void) { int index=CSelect::FindBufferMax(this.GetList(),BUFFER_PROP_INDEX_PLOT); CBuffer *buffer=this.m_list.At(index); return(buffer!=NULL ? buffer.IndexPlot()+1 : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает количество всех индикаторных массивов | //+------------------------------------------------------------------+ int CBuffersCollection::BuffersTotal(void) { int index=CSelect::FindBufferMax(this.GetList(),BUFFER_PROP_INDEX_NEXT); CBuffer *buffer=this.m_list.At(index); return(buffer!=NULL ? buffer.IndexNextBuffer() : WRONG_VALUE); } //+------------------------------------------------------------------+
Логика методов одинакова: получаем индекс с наибольшим значением нужного свойства, и по полученному индексу получаем из списка-коллекции объект-буфер. Если буфер получен — возвращаем его свойство, соответствующее методу, иначе — возвращаем -1.
На этом создание класса-коллекции индикаторных буферов завершено.
Теперь нам нужно предоставить доступ к методам класса для программ, работающих на основе библиотеки. Делается это у нас в классе базового объекта библиотеки CEngine.
Откроем файл \MQL5\Include\DoEasy\Engine.mqh и внесём в него требуемые изменения. Здесь нам нужно практически лишь продублировать уже созданные методы класса коллекции индикаторных буферов и добавить вспомогательные методы для удобства работы.
В первую очередь подключим файл класса и объявим объект класса-коллекции буферов:
//+------------------------------------------------------------------+ //| Engine.mqh | //| Copyright 2020, MetaQuotes Software Corp. | //| https://mql5.com/ru/users/artmedia70 | //+------------------------------------------------------------------+ #property copyright "Copyright 2020, MetaQuotes Software Corp." #property link "https://mql5.com/ru/users/artmedia70" #property version "1.00" //+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "Services\TimerCounter.mqh" #include "Collections\HistoryCollection.mqh" #include "Collections\MarketCollection.mqh" #include "Collections\EventsCollection.mqh" #include "Collections\AccountsCollection.mqh" #include "Collections\SymbolsCollection.mqh" #include "Collections\ResourceCollection.mqh" #include "Collections\TimeSeriesCollection.mqh" #include "Collections\BuffersCollection.mqh" #include "TradingControl.mqh" //+------------------------------------------------------------------+ //| Класс-основа библиотеки | //+------------------------------------------------------------------+ class CEngine { private: CHistoryCollection m_history; // Коллекция исторических ордеров и сделок CMarketCollection m_market; // Коллекция рыночных ордеров и сделок CEventsCollection m_events; // Коллекция событий CAccountsCollection m_accounts; // Коллекция аккаунтов CSymbolsCollection m_symbols; // Коллекция символов CTimeSeriesCollection m_time_series; // Коллекция таймсерий CBuffersCollection m_buffers; // Коллекция индикаторных буферов CResourceCollection m_resource; // Список ресурсов CTradingControl m_trading; // Объект управления торговлей CPause m_pause; // Объект "Пауза" CArrayObj m_list_counters; // Список счётчиков таймера
В публичной секции класса напишем методы, вызывающие и возвращающие результат работы одноимённых методов класса-коллекции буферов,
и объявим дополнительные методы для работы с классом коллекции буферов:
//--- Копирует в массив указанное double-свойство указанной таймсерии указанного символа //--- Независимо от направления индексации массива, копирование производится как в массив-таймсерию bool SeriesCopyToBufferAsSeries(const string symbol,const ENUM_TIMEFRAMES timeframe,const ENUM_BAR_PROP_DOUBLE property, double &array[],const double empty=EMPTY_VALUE) { return this.m_time_series.CopyToBufferAsSeries(symbol,timeframe,property,array,empty);} //--- Возвращает (1) коллекцию буферов, (2) список буферов из коллекции буферов, (3) буфер по индексу Plot CBuffersCollection *GetBuffersCollection(void) { return &this.m_buffers; } CArrayObj *GetListBuffers(void) { return this.m_buffers.GetList(); } CBuffer *GetBufferByPlot(const int plot_index) { return this.m_buffers.GetBufferByPlot(plot_index); } //--- Возвращает буферы по стилю рисования по порядковому номеру //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) CBufferArrow *GetBufferArrow(const int number) { return this.m_buffers.GetBufferArrow(number); } CBufferLine *GetBufferLine(const int number) { return this.m_buffers.GetBufferLine(number); } CBufferSection *GetBufferSection(const int number) { return this.m_buffers.GetBufferSection(number); } CBufferHistogram *GetBufferHistogram(const int number) { return this.m_buffers.GetBufferHistogram(number); } CBufferHistogram2 *GetBufferHistogram2(const int number) { return this.m_buffers.GetBufferHistogram2(number); } CBufferZigZag *GetBufferZigZag(const int number) { return this.m_buffers.GetBufferZigZag(number); } CBufferFilling *GetBufferFilling(const int number) { return this.m_buffers.GetBufferFilling(number); } CBufferBars *GetBufferBars(const int number) { return this.m_buffers.GetBufferBars(number); } CBufferCandles *GetBufferCandles(const int number) { return this.m_buffers.GetBufferCandles(number); } //--- Возвращает количество (1) рисуемых буферов, (2) всех индикаторных массивов int BufferPlotsTotal(void) { return this.m_buffers.PlotsTotal(); } int BuffersTotal(void) { return this.m_buffers.BuffersTotal(); } //--- Создаёт новый буфер (1) "Отрисовка стрелками", (2) "Линия", (3) "Отрезки", (4) "Гистограмма от нулевой линии", //--- (5) "Гистограмма на двух индикаторных буферах", (6) "Зигзаг", (7) "Цветовая заливка между двумя уровнями", //--- (8) "Отображение в виде баров", (9) "Отображение в виде свечей", bool BufferCreateArrow(void) { return this.m_buffers.CreateArrow(); } bool BufferCreateLine(void) { return this.m_buffers.CreateLine(); } bool BufferCreateSection(void) { return this.m_buffers.CreateSection(); } bool BufferCreateHistogram(void) { return this.m_buffers.CreateHistogram(); } bool BufferCreateHistogram2(void) { return this.m_buffers.CreateHistogram2(); } bool BufferCreateZigZag(void) { return this.m_buffers.CreateZigZag(); } bool BufferCreateFilling(void) { return this.m_buffers.CreateFilling(); } bool BufferCreateBars(void) { return this.m_buffers.CreateBars(); } bool BufferCreateCandles(void) { return this.m_buffers.CreateCandles(); } //--- Возвращает данные буфера по его порядковому номеру (1) стрелок, (2) линии, (3) отрезков, (4) гистограммы от нуля //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) double BufferDataArrow(const int number,const int series_index); double BufferDataLine(const int number,const int series_index); double BufferDataSection(const int number,const int series_index); double BufferDataHistogram(const int number,const int series_index); //--- Возвращает данные буфера по его порядковому номеру (1) нулевого, (2) первого буфера гистограммы на двух буферах //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) double BufferDataHistogram20(const int number,const int series_index); double BufferDataHistogram21(const int number,const int series_index); //--- Возвращает данные буфера по его порядковому номеру (1) нулевого, (2) первого буфера зигзага //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) double BufferDataZigZag0(const int number,const int series_index); double BufferDataZigZag1(const int number,const int series_index); //--- Возвращает данные буфера по его порядковому номеру (1) нулевого, (2) первого буфера заливки //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) double BufferDataFilling0(const int number,const int series_index); double BufferDataFilling1(const int number,const int series_index); //--- Возвращает данные буфера по его порядковому номеру (1) Open, (2) High, (3) Low, (4) Close буферов баров //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) double BufferDataBarsOpen(const int number,const int series_index); double BufferDataBarsHigh(const int number,const int series_index); double BufferDataBarsLow(const int number,const int series_index); double BufferDataBarsClose(const int number,const int series_index); //--- Возвращает данные буфера по его порядковому номеру (1) Open, (2) High, (3) Low, (4) Close буферов свечей //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) double BufferDataCandlesOpen(const int number,const int series_index); double BufferDataCandlesHigh(const int number,const int series_index); double BufferDataCandlesLow(const int number,const int series_index); double BufferDataCandlesClose(const int number,const int series_index); //--- Устанавливает данные буфера по его порядковому номеру (1) стрелок, (2) линии, (3) отрезков, (4) гистограммы от нуля //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) void BufferSetDataArrow(const int number,const int series_index,const double value); void BufferSetDataLine(const int number,const int series_index,const double value); void BufferSetDataSection(const int number,const int series_index,const double value); void BufferSetDataHistogram(const int number,const int series_index,const double value); //--- Устанавливает данные (1) нулевого, (2) первого, (3) всех буферов гистограммы на двух буферах по порядковому номеру созданного буфера //--- (0 - самый первый созданный буфер со стилем рисования HISTOGRAM2, 1,2,N - последующие) void BufferSetDataHistogram20(const int number,const int series_index,const double value); void BufferSetDataHistogram21(const int number,const int series_index,const double value); void BufferSetDataHistogram2(const int number,const int series_index,const double value0,const double value1); //--- Устанавливает данные (1) нулевого, (2) первого, (3) всех буферов зигзага по порядковому номеру созданного буфера //--- (0 - самый первый созданный буфер со стилем рисования ZIGZAG, 1,2,N - последующие) void BufferSetDataZigZag0(const int number,const int series_index,const double value); void BufferSetDataZigZag1(const int number,const int series_index,const double value); void BufferSetDataZigZag(const int number,const int series_index,const double value0,const double value1); //--- Устанавливает данные (1) нулевого, (2) первого, (3) всех буферов заливки по порядковому номеру созданного буфера //--- (0 - самый первый созданный буфер со стилем рисования FILLING, 1,2,N - последующие) void BufferSetDataFilling0(const int number,const int series_index,const double value); void BufferSetDataFilling1(const int number,const int series_index,const double value); void BufferSetDataFilling(const int number,const int series_index,const double value0,const double value1); //--- Устанавливает данные (1) Open, (2) High, (3) Low, (4) Close, (5) всех буферов баров по порядковому номеру созданного буфера //--- (0 - самый первый созданный буфер со стилем рисования BARS, 1,2,N - последующие) void BufferSetDataBarsOpen(const int number,const int series_index,const double value); void BufferSetDataBarsHigh(const int number,const int series_index,const double value); void BufferSetDataBarsLow(const int number,const int series_index,const double value); void BufferSetDataBarsClose(const int number,const int series_index,const double value); void BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close); //--- Устанавливает данные (1) Open, (2) High, (3) Low, (4) Close, (5) всех буферов свечей по порядковому номеру созданного буфера //--- (0 - самый первый созданный буфер со стилем рисования CANDLES, 1,2,N - последующие) void BufferSetDataCandlesOpen(const int number,const int series_index,const double value); void BufferSetDataCandlesHigh(const int number,const int series_index,const double value); void BufferSetDataCandlesLow(const int number,const int series_index,const double value); void BufferSetDataCandlesClose(const int number,const int series_index,const double value); void BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close); //--- Возвращает цвет буфера по его порядковому номеру (1) стрелок, (2) линии, (3) отрезков, (4) гистограммы от нуля //--- (5) гистограммы на двух буферах, (6) зигзага, (7) заливки, (8) баров, (9) свечей //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) color BufferColorArrow(const int number,const int series_index); color BufferColorLine(const int number,const int series_index); color BufferColorSection(const int number,const int series_index); color BufferColorHistogram(const int number,const int series_index); color BufferColorHistogram2(const int number,const int series_index); color BufferColorZigZag(const int number,const int series_index); color BufferColorFilling(const int number,const int series_index); color BufferColorBars(const int number,const int series_index); color BufferColorCandles(const int number,const int series_index); //--- Возвращает индекс цвета буфера по его порядковому номеру (1) стрелок, (2) линии, (3) отрезков, (4) гистограммы от нуля //--- (5) гистограммы на двух буферах, (6) зигзага, (7) заливки, (8) баров, (9) свечей //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) int BufferColorIndexArrow(const int number,const int series_index); int BufferColorIndexLine(const int number,const int series_index); int BufferColorIndexSection(const int number,const int series_index); int BufferColorIndexHistogram(const int number,const int series_index); int BufferColorIndexHistogram2(const int number,const int series_index); int BufferColorIndexZigZag(const int number,const int series_index); int BufferColorIndexFilling(const int number,const int series_index); int BufferColorIndexBars(const int number,const int series_index); int BufferColorIndexCandles(const int number,const int series_index); //--- Устанавливает индекс цвета в буфер цвета по его порядковому номеру (1) стрелок, (2) линии, (3) отрезков, (4) гистограммы от нуля //--- (5) гистограммы на двух буферах, (6) зигзага, (7) заливки, (8) баров, (9) свечей //--- (0 - самый первый созданный буфер со стилем рисования ХХХ, 1,2,N - последующие) void BufferSetColorIndexArrow(const int number,const int series_index,const int color_index); void BufferSetColorIndexLine(const int number,const int series_index,const int color_index); void BufferSetColorIndexSection(const int number,const int series_index,const int color_index); void BufferSetColorIndexHistogram(const int number,const int series_index,const int color_index); void BufferSetColorIndexHistogram2(const int number,const int series_index,const int color_index); void BufferSetColorIndexZigZag(const int number,const int series_index,const int color_index); void BufferSetColorIndexFilling(const int number,const int series_index,const int color_index); void BufferSetColorIndexBars(const int number,const int series_index,const int color_index); void BufferSetColorIndexCandles(const int number,const int series_index,const int color_index); //--- Устанавливает для торговых классов:
Методы, возвращающие данные конкретного буфера по его стилю рисования и по порядковому номеру буфера с таким стилем рисования:
//+------------------------------------------------------------------+ //| Возвращает данные буфера стрелок по его порядковому номеру | //| (0 - самый первый созданный буфер стрелок, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataArrow(const int number,const int series_index) { CBufferArrow *buff=this.m_buffers.GetBufferArrow(number); return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные буфера линии по его порядковому номеру | //| (0 - самый первый созданный буфер линии, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataLine(const int number,const int series_index) { CBufferLine *buff=this.m_buffers.GetBufferLine(number); return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные буфера отрезков по его порядковому номеру | //| (0 - самый первый созданный буфер отрезков, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataSection(const int number,const int series_index) { CBufferSection *buff=this.m_buffers.GetBufferSection(number); return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные буфера гистограммы от нуля | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataHistogram(const int number,const int series_index) { CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number); return(buff!=NULL ? buff.GetData(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные нулевого буфера гистограммы на двух буферах | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataHistogram20(const int number,const int series_index) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные первого буфера гистограммы на двух буферах | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataHistogram21(const int number,const int series_index) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные нулевого буфера зигзага | //| по его порядковому номеру | //| (0 - самый первый созданный буфер зигзага, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataZigZag0(const int number,const int series_index) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные первого буфера зигзага | //| по его порядковому номеру | //| (0 - самый первый созданный буфер зигзага, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataZigZag1(const int number,const int series_index) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные нулевого буфера заливки | //| по его порядковому номеру | //| (0 - самый первый созданный буфер заливки, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataFilling0(const int number,const int series_index) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); return(buff!=NULL ? buff.GetData0(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные первого буфера заливки | //| по его порядковому номеру | //| (0 - самый первый созданный буфер заливки, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataFilling1(const int number,const int series_index) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); return(buff!=NULL ? buff.GetData1(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные Open буфера баров по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataBarsOpen(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetDataOpen(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные High буфера баров по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataBarsHigh(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetDataHigh(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные Low буфера баров по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataBarsLow(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetDataLow(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные Close буфера баров по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataBarsClose(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetDataClose(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные Open буфера свечей по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataCandlesOpen(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetDataOpen(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные High буфера свечей по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataCandlesHigh(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetDataHigh(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные Low буфера свечей по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataCandlesLow(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetDataLow(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+ //| Возвращает данные Close буфера свечей по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ double CEngine::BufferDataCandlesClose(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetDataClose(series_index) : EMPTY_VALUE); } //+------------------------------------------------------------------+
Все методы идентичны. Рассмотрим метод, возвращающий значение буфера Close объекта-буфера со стилем рисования "Свечи".
В метод передаётся порядковый номер буфера со стилем Candles из всех созданных буферов со стилем Candles (выше мы подробно рассматривали что означает номер буфера с конкретным стилем рисования), индекс таймсерии, из которого необходимо получить данные буфера Close свечи.
При помощи метода GetBufferCandles() класса-коллекции буферов получаем указатель на требуемый буфер, и если буфер получен, возвращаем данные из его буфера Close по указанному индексу таймсерии. Иначе — возвращаем "Пустое значение".
Методы, противоположные только что рассмотренным, устанавливающие значения в указанный индекс таймсерии конкретному буферу соответствующего объекта-буфера по стилю его рисования и его порядковому номеру:
//+------------------------------------------------------------------+ //| Устанавливает данные буфера стрелок по его порядковому номеру | //| (0 - самый первый созданный буфер стрелок, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataArrow(const int number,const int series_index,const double value) { CBufferArrow *buff=this.m_buffers.GetBufferArrow(number); if(buff==NULL) return; buff.SetData(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера линии по его порядковому номеру | //| (0 - самый первый созданный буфер линии, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataLine(const int number,const int series_index,const double value) { CBufferLine *buff=this.m_buffers.GetBufferLine(number); if(buff==NULL) return; buff.SetData(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера отрезков по его порядковому номеру | //| (0 - самый первый созданный буфер отрезков, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataSection(const int number,const int series_index,const double value) { CBufferSection *buff=this.m_buffers.GetBufferSection(number); if(buff==NULL) return; buff.SetData(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера гистограммы от нуля | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataHistogram(const int number,const int series_index,const double value) { CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number); if(buff==NULL) return; buff.SetData(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные нулевого буфера гистограммы на двух буферах | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataHistogram20(const int number,const int series_index,const double value) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); if(buff==NULL) return; buff.SetData0(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные первого буфера гистограммы на двух буферах | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataHistogram21(const int number,const int series_index,const double value) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); if(buff==NULL) return; buff.SetData1(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные всех буферов гистограммы на двух буферах | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataHistogram2(const int number,const int series_index,const double value0,const double value1) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); if(buff==NULL) return; buff.SetData0(series_index,value0); buff.SetData1(series_index,value1); } //+------------------------------------------------------------------+ //| Устанавливает данные нулевого буфера зигзага | //| по его порядковому номеру | //| (0 - самый первый созданный буфер зигзага, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataZigZag0(const int number,const int series_index,const double value) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); if(buff==NULL) return; buff.SetData0(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные первого буфера зигзага | //| по его порядковому номеру | //| (0 - самый первый созданный буфер зигзага, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataZigZag1(const int number,const int series_index,const double value) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); if(buff==NULL) return; buff.SetData1(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные всех буферов зигзага | //| по его порядковому номеру | //| (0 - самый первый созданный буфер зигзага, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataZigZag(const int number,const int series_index,const double value0,const double value1) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); if(buff==NULL) return; buff.SetData0(series_index,value0); buff.SetData1(series_index,value1); } //+------------------------------------------------------------------+ //| Устанавливает данные нулевого буфера заливки | //| по его порядковому номеру | //| (0 - самый первый созданный буфер заливки, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataFilling0(const int number,const int series_index,const double value) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); if(buff==NULL) return; buff.SetData0(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные первого буфера заливки | //| по его порядковому номеру | //| (0 - самый первый созданный буфер заливки, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataFilling1(const int number,const int series_index,const double value) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); if(buff==NULL) return; buff.SetData1(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные всех буферов заливки | //| по его порядковому номеру | //| (0 - самый первый созданный буфер заливки, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataFilling(const int number,const int series_index,const double value0,const double value1) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); if(buff==NULL) return; buff.SetData0(series_index,value0); buff.SetData1(series_index,value1); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера Open баров | //| по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBarsOpen(const int number,const int series_index,const double value) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataOpen(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера High баров | //| по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBarsHigh(const int number,const int series_index,const double value) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataHigh(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера Low баров | //| по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBarsLow(const int number,const int series_index,const double value) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataLow(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера Close баров | //| по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBarsClose(const int number,const int series_index,const double value) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataClose(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера Open свечей | //| по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandlesOpen(const int number,const int series_index,const double value) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataOpen(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера High свечей | //| по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandlesHigh(const int number,const int series_index,const double value) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataHigh(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера Low свечей | //| по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandlesLow(const int number,const int series_index,const double value) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataLow(series_index,value); } //+------------------------------------------------------------------+ //| Устанавливает данные буфера Close свечей | //| по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandlesClose(const int number,const int series_index,const double value) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataClose(series_index,value); } //+------------------------------------------------------------------+
Все методы идентичны. Рассмотрим метод, устанавливающий значение по индексу таймсерии в буфер Close объекта-буфера со стилем рисования "Свечи" по его порядковому номеру.
При помощи метода GetBufferCandles() класса-коллекции буферов получаем объект-буфер со стилем рисования "Свечи" по его порядковому номеру.
Если объект получить не удалось — уходим из метода. Устанавливаем по индексу таймсерии переданное в метод значение в буфер Close полученного требуемого объекта-буфера.
Есть ещё два отдельных метода, устанавливающие одновременно всем буферам объектов-буферов "Бары" и "Свечи" значения OHLC по указанному индексу таймсерии:
//+------------------------------------------------------------------+ //| Устанавливает данные всех буферов баров | //| по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataBars(const int number,const int series_index,const double open,const double high,const double low,const double close) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); if(buff==NULL) return; buff.SetDataOpen(series_index,open); buff.SetDataHigh(series_index,high); buff.SetDataLow(series_index,low); buff.SetDataClose(series_index,close); } //+------------------------------------------------------------------+ //| Устанавливает данные всех буферов свечей | //| по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ void CEngine::BufferSetDataCandles(const int number,const int series_index,const double open,const double high,const double low,const double close) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); if(buff==NULL) return; buff.SetDataOpen(series_index,open); buff.SetDataHigh(series_index,high); buff.SetDataLow(series_index,low); buff.SetDataClose(series_index,close); } //+------------------------------------------------------------------+
Здесь всё точно так же, как было описано выше, но в методы передаются и устанавливаются все значения для всех четырёх буферов объектов-буферов.
Методы, возвращающие установленный цвет в указанный индекс таймсерии буфера цвета конкретного объекта-буфера по его стилю рисования и порядковому номеру:
//+------------------------------------------------------------------+ //| Возвращает цвет буфера стрелок по его порядковому номеру | //| (0 - самый первый созданный буфер стрелок, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorArrow(const int number,const int series_index) { CBufferArrow *buff=this.m_buffers.GetBufferArrow(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Возвращает цвет буфера линии по его порядковому номеру | //| (0 - самый первый созданный линии свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorLine(const int number,const int series_index) { CBufferLine *buff=this.m_buffers.GetBufferLine(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Возвращает цвет буфера отрезков по его порядковому номеру | //| (0 - самый первый созданный буфер отрезков, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorSection(const int number,const int series_index) { CBufferSection *buff=this.m_buffers.GetBufferSection(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Возвращает цвет буфера гистограммы от нуля | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorHistogram(const int number,const int series_index) { CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Возвращает цвет буфера гистограммы на двух буферах | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorHistogram2(const int number,const int series_index) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Возвращает цвет буфера зигзага по его порядковому номеру | //| (0 - самый первый созданный буфер зигзага, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorZigZag(const int number,const int series_index) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Возвращает цвет буфера заливки по его порядковому номеру | //| (0 - самый первый созданный буфер заливки, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorFilling(const int number,const int series_index) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Возвращает цвет буфера баров по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorBars(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+ //| Возвращает цвет буфера свечей по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ color CEngine::BufferColorCandles(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetColorBufferValueColor(series_index) : clrNONE); } //+------------------------------------------------------------------+
Здесь всё идентично: получаем требуемый объект-буфер по его номеру, если объект получить удалось — возвращаем цвет, установленный в его буфере цвета по указанному индексу таймсерии. Иначе — возвращаем "Цвет не задан".
Так как реально в буфер цвета записывается значение не цвета, а его индекса из установленных для буфера цветов, то у нас есть соответствующие
методы, возвращающие индекс цвета конкретного объекта-буфера из его буфера цвета по указанному индексу таймсерии:
//+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера стрелок по его порядковому номеру | //| (0 - самый первый созданный буфер стрелок, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexArrow(const int number,const int series_index) { CBufferArrow *buff=this.m_buffers.GetBufferArrow(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера линии по его порядковому номеру | //| (0 - самый первый созданный линии свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexLine(const int number,const int series_index) { CBufferLine *buff=this.m_buffers.GetBufferLine(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера отрезков по его порядковому номеру| //| (0 - самый первый созданный буфер отрезков, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexSection(const int number,const int series_index) { CBufferSection *buff=this.m_buffers.GetBufferSection(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера гистограммы от нуля | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexHistogram(const int number,const int series_index) { CBufferHistogram *buff=this.m_buffers.GetBufferHistogram(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера гистограммы на двух буферах | //| по его порядковому номеру | //| (0 - самый первый созданный буфер, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexHistogram2(const int number,const int series_index) { CBufferHistogram2 *buff=this.m_buffers.GetBufferHistogram2(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера зигзага по его порядковому номеру | //| (0 - самый первый созданный буфер зигзага, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexZigZag(const int number,const int series_index) { CBufferZigZag *buff=this.m_buffers.GetBufferZigZag(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера заливки по его порядковому номеру | //| (0 - самый первый созданный буфер заливки, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexFilling(const int number,const int series_index) { CBufferFilling *buff=this.m_buffers.GetBufferFilling(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера баров по его порядковому номеру | //| (0 - самый первый созданный буфер баров, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexBars(const int number,const int series_index) { CBufferBars *buff=this.m_buffers.GetBufferBars(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+ //| Возвращает индекс цвета буфера свечей по его порядковому номеру | //| (0 - самый первый созданный буфер свечей, 1,2,N - последующие) | //+------------------------------------------------------------------+ int CEngine::BufferColorIndexCandles(const int number,const int series_index) { CBufferCandles *buff=this.m_buffers.GetBufferCandles(number); return(buff!=NULL ? buff.GetColorBufferValueIndex(series_index) : WRONG_VALUE); } //+------------------------------------------------------------------+
Здесь всё идентично методам, возвращающим цвет, за исключением, что методы возвращают индекс цвета.
Это все доработки класса CEngine, необходимые для тестирования класса-коллекции индикаторных буферов.
Тестируем создание индикатора с использованием коллекции буферов
Для тестирования созданного класса-коллекции буферов возьмём индикатор из прошлой статьи
и сохраним его в новой папке \MQL5\Indicators\TestDoEasy\Part44\ под новым именем TestDoEasyPart44.mq5.
Весь заголовок у нас будет таким:
//+------------------------------------------------------------------+ //| TestDoEasyPart44.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> //--- properties #property indicator_chart_window #property indicator_buffers 28 #property indicator_plots 10 //--- classes //--- enums //--- defines //--- structures //--- input variables /*sinput*/ ENUM_SYMBOLS_MODE InpModeUsedSymbols= SYMBOLS_MODE_CURRENT; // Mode of used symbols list /*sinput*/ string InpUsedSymbols = "EURUSD,AUDUSD,EURAUD,EURGBP,EURCAD,EURJPY,EURUSD,GBPUSD,NZDUSD,USDCAD,USDJPY"; // List of used symbols (comma - separator) /*sinput*/ ENUM_TIMEFRAMES_MODE InpModeUsedTFs = TIMEFRAMES_MODE_CURRENT; // Mode of used timeframes list /*sinput*/ string InpUsedTFs = "M1,M5,M15,M30,H1,H4,D1,W1,MN1"; // List of used timeframes (comma - separator) sinput ENUM_INPUT_YES_NO InpDrawArrow = INPUT_YES; // Draw Arrow sinput ENUM_INPUT_YES_NO InpDrawLine = INPUT_NO; // Draw Line sinput ENUM_INPUT_YES_NO InpDrawSection = INPUT_NO; // Draw Section sinput ENUM_INPUT_YES_NO InpDrawHistogram = INPUT_NO; // Draw Histogram sinput ENUM_INPUT_YES_NO InpDrawHistogram2 = INPUT_NO; // Draw Histogram2 sinput ENUM_INPUT_YES_NO InpDrawZigZag = INPUT_NO; // Draw ZigZag sinput ENUM_INPUT_YES_NO InpDrawFilling = INPUT_NO; // Draw Filling sinput ENUM_INPUT_YES_NO InpDrawBars = INPUT_NO; // Draw Bars sinput ENUM_INPUT_YES_NO InpDrawCandles = INPUT_YES; // Draw Candles sinput bool InpUseSounds = true; // Use sounds //--- indicator buffers CArrayObj *list_buffers; // Указатель на список объектов-буферов //--- global variables CEngine engine; // Главный объект библиотеки CEngine string prefix; // Префикс имён графических объектов int min_bars; // Минимальное количество баров для расчёта индикатора int used_symbols_mode; // Режим работы с символами string array_used_symbols[]; // Массив для передачи в библиотеку используемых символов string array_used_periods[]; // Массив для передачи в библиотеку используемых таймфреймов //+------------------------------------------------------------------+
Из блока includes удалим подключение файлов всех объектов-буферов — теперь они уже подключены к библиотеке и здесь этого делать не нужно:
//--- includes #include <DoEasy\Engine.mqh> #include <DoEasy\Objects\Indicators\BufferArrow.mqh> // 1 буфер для построения + 1 буфер цвета #include <DoEasy\Objects\Indicators\BufferLine.mqh> // 1 буфер для построения + 1 буфер цвета #include <DoEasy\Objects\Indicators\BufferSection.mqh> // 1 буфер для построения + 1 буфер цвета #include <DoEasy\Objects\Indicators\BufferHistogram.mqh> // 1 буфер для построения + 1 буфер цвета #include <DoEasy\Objects\Indicators\BufferHistogram2.mqh> // 2 буфера для построения + 1 буфер цвета #include <DoEasy\Objects\Indicators\BufferZigZag.mqh> // 2 буфера для построения + 1 буфер цвета #include <DoEasy\Objects\Indicators\BufferFilling.mqh> // 2 буфера для построения + 1 буфер цвета #include <DoEasy\Objects\Indicators\BufferBars.mqh> // 4 буфера для построения + 1 буфер цвета #include <DoEasy\Objects\Indicators\BufferCandles.mqh> // 4 буфера для построения + 1 буфер цвета //--- Итого 18 буферов для построения + 9 буферов цвета: 27 индикаторных буферов. Из них 9 рисуемых //--- properties
Указание компилятору сколько планируется рисуемых и индикаторных буферов:
#property indicator_buffers 28 #property indicator_plots 10
Здесь, если предполагается большое количество различных буферов в индикаторе, то можно на начальном этапе не "считать на пальцах", а просто подставить любые значения — далее нам при первом запуске индикатора будут выданы Alert'ы, в которых будет указано верное количество рисуемых и индикаторных буферов в случае, если мы ошибочно напишем неправильное их количество.
Библиотека предполагает обращение к созданным индикаторным буферам по их стилям рисования и номерам в порядке создания. Пока не сделано обращение из программы напрямую к свойствам буфера (только к их массивам), но мы это ограничение сегодня обойдём, получив список объектов-буферов из коллекции и обращаясь к буферам напрямую из списка — такая возможность есть для любой из коллекции объектов — работать напрямую с созданными объектами. Поэтому в качестве "индикаторного буфера" будем использовать динамический массив указателей на объекты CObject.
В обработчике OnInit() подготовим нужные для тестирования буферы и проверим доступ к ним двумя различными способами:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Инициализация библиотеки DoEasy OnInitDoEasy(); //--- Установка глобальных переменных индикатора prefix=engine.Name()+"_"; //--- Получаем индекс максимального используемого таймфрейма в массиве, //--- рассчитываем количество баров текущего периода, умещающихся в максимальном используемом периоде //--- Используем полученное значение если оно больше 2, иначе используем 2 int index=ArrayMaximum(ArrayUsedTimeframes); int num_bars=NumberBarsInTimeframe(ArrayUsedTimeframes[index]); min_bars=(index>WRONG_VALUE ? (num_bars>2 ? num_bars : 2) : 2); //--- Проверка и удаление неудалённых графических объектов индикатора if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Создание панели кнопок //--- Проверка воспроизведения стандартного звука по макроподстановкам engine.PlaySoundByDescription(SND_OK); //--- Ждём 600 милисекунд engine.Pause(600); engine.PlaySoundByDescription(SND_NEWS); //--- indicator buffers mapping //--- Создаём все необходимые объекты-буферы engine.BufferCreateArrow(); engine.BufferCreateLine(); engine.BufferCreateSection(); engine.BufferCreateHistogram(); engine.BufferCreateHistogram2(); engine.BufferCreateZigZag(); engine.BufferCreateFilling(); engine.BufferCreateBars(); engine.BufferCreateCandles(); engine.BufferCreateArrow(); //--- Проверяем количество буферов, указанных в блоке properties if(engine.BufferPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BufferPlotsTotal()); if(engine.BuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersTotal()); //--- Создаём массив цветов color array_colors[]={clrDodgerBlue,clrRed,clrGray}; //--- Получаем указатель на список-коллекцию объектов-буферов и //--- в цикле по списку зададём буферам значения цвета не по умолчанию list_buffers=engine.GetListBuffers(); for(int i=0;i<list_buffers.Total();i++) { CBuffer *buff=list_buffers.At(i); buff.SetColors(array_colors); //--- Распечатаем данные очередного буфера buff.Print(); } //--- Задаём толщину линии для ZigZag (6-й по счёту рисуемый буфер) //--- Он имеет индекс 5 - с учётом начала отсчёта от нуля CBuffer *buff_zz=engine.GetBufferByPlot(5); if(buff_zz!=NULL) { buff_zz.SetWidth(2); } //--- Получаем второй буфер стрелок (созданный последним). //--- Первый буфер стрелок имеет номер 0, второй - номер 1 //--- Устанавливаем размер стрелки 2 и код 161 CBuffer *buff=engine.GetBufferArrow(1); if(buff!=NULL) { buff.SetWidth(2); buff.SetArrowCode(161); } //--- return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Теперь создание буферов происходит просто — используем методы библиотеки, предназначенные для создания индикаторного буфера того, или иного типа. Все назначения массивов библиотека берёт на себя, а изменить свойства созданных буферов мы сможем после их создания. Сразу проверим указанное количество буферов в #property в заголовке индикатора, и если мы ошиблись при указании числа буферов, то нам будет выдано об этом предупреждение. Такая проверка удобна при создании индикатора, и после её можно удалить из кода.
Для проверки получения доступа к буферу воспользуемся двумя способами:
сначала получим доступ к буферу зигзага по его индексу рисуемого буфера при помощи метода GetBufferByPlot(), в котором нужно указать индекс рисуемого буфера (в данном случае — это индекс 5 для зигзага),
а затем получим доступ к самому последнему буферу стрелок, который был создан в самом конце, и является вторым по счёту буфером стрелок. Доступ к нему получим при помощи метода GetBufferArrow(), в котором нужно указать порядковый номер нужного буфера стрелок (в данном случае — номер 1, так как отсчёт начинается с нуля)
В обработчике OnCalculate() здесь остаётся всё практически без изменения, кроме того, что в буферы свечей и баров будем записывать данные при помощи методов объекта-буфера свечей (поочерёдно запишем данные в Open, High, Low и Close), а в массивы буфера баров запишем все значения OHLC одним разом. Таким образом мы проверим работу всех созданных методов работы с объектами-буферами:
//--- Расчёт индикатора for(int i=limit; i>WRONG_VALUE && !IsStopped(); i--) { //--- В цикле по количеству буферов в списке for(int j=0;j<total;j++) { //--- получаем очередной буфер и CBuffer *buff=list_buffers.At(j); //--- очищаем его текущие данные buff.ClearData(0); //--- Если отрисовка не используется - идём к следующему if(!IsUse(buff.Status())) continue; //--- В зависимости от количества буферов, заполняем их ценовыми данными //--- один буфер if(buff.BuffersTotal()==1) buff.SetBufferValue(0,i,close[i]); //--- два буфера - в первый записываем цену open бара, во второй - close else if(buff.BuffersTotal()==2) { buff.SetBufferValue(0,i,open[i]); buff.SetBufferValue(1,i,close[i]); } //--- четыре буфера - в каждый записываем цены OHLC баров else if(buff.BuffersTotal()==4) { //--- Если это буфер свечей if(buff.Status()==BUFFER_STATUS_CANDLES) { //--- создаём указатель на объект-буфер свечей присваиванием ему указателя на объект абстрактного буфера //--- и записываем в буферы объекта "свечи" соответствующие данные таймсерий CBufferCandles *candle=buff; candle.SetDataOpen(i,open[i]); candle.SetDataHigh(i,high[i]); candle.SetDataLow(i,low[i]); candle.SetDataClose(i,close[i]); } //--- Если это буфер баров - используем доступ к первому (и единственному) созданному объекту-буферу баров //--- и записываем в буферы объекта "бары" соответствующие данные таймсерий одним разом else { engine.BufferSetDataBars(0,i,open[i],high[i],low[i],close[i]); } } //--- В зависимости от направления свечи устанавливаем цвет буфера if(open[i]<close[i]) buff.SetBufferColorIndex(i,0); else if(open[i]>close[i]) buff.SetBufferColorIndex(i,1); else buff.SetBufferColorIndex(i,2); } } //--- return value of prev_calculated for next call
Полный код индикатора можно посмотреть в прилагаемых к статье файлах.
Скомпилируем индикатор и запустим его на графике символа. Зададим ему в настройках отображение только одного буфера стрелок. Этот буфер должен отображаться точками на графике. Но у нас есть ещё второй буфер стрелок, созданный самым последним, и мы обращались ко второму буферу стрелок в OnInit() для изменения его кода и размера значка:
//--- Получаем второй буфер стрелок (созданный последним). //--- Первый буфер стрелок имеет номер 0, второй - номер 1 //--- Устанавливаем размер стрелки 2 и код 161 CBuffer *buff=engine.GetBufferArrow(1); if(buff!=NULL) { buff.SetWidth(2); buff.SetArrowCode(161); }
Если получение объекта методом обращения к конкретному типу буфера по его номеру работает, то у нас на графике должны отобразиться два буфера стрелок — первый точками, второй — кружками с размером 2.
Толщину линии загзага мы устанавливали, получив буфер загзага при помощи метода, возвращающего объект-буфер по его индексу Plot. Подключим отображение зигзага и убедимся, что толщина его линии соответствует установленной в OnInit():
//--- Задаём толщину линии для ZigZag (6-й по счёту рисуемый буфер) //--- Он имеет индекс 5 - с учётом начала отсчёта от нуля CBuffer *buff_zz=engine.GetBufferByPlot(5); if(buff_zz!=NULL) { buff_zz.SetWidth(2); }
И напоследок посмотрим как отображаются бары и свечи — если методы записи значений цен в массивы буферов работают, то бары и свечи должны правильно отобразиться на графике.
Проверим:
Как видим, всё задуманное и сделанное работает как и предполагалось.
Что дальше
В следующей статье продолжим разработку класса-коллекции индикаторных буферов в свете организиции работы индикаторов в мультисимвольном и мультипериодном режимах.
Ниже прикреплены все файлы текущей версии библиотеки и файлы тестового советника. Их можно скачать и протестировать всё самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Хочу обратить внимание на то, что в данной статье мы сделали тестовый индикатор на MQL5 для MetaTrader 5.
Приложенные файлы предназначены только для MetaTrader 5 и в MetaTrader 4 библиотека в её текущей версии не тестировалась.
После создания коллекции буферов индикаторов и её тестирования, некоторые вещи из MQL5 мы попробуем реализовать и для MetaTrader 4.
Статьи этой серии:
Работа с таймсериями в библиотеке DoEasy (Часть 35): Объект "Бар" и список-таймсерия символа
Работа с таймсериями в библиотеке DoEasy (Часть 36): Объект таймсерий всех используемых периодов символа
Работа с таймсериями в библиотеке DoEasy (Часть 37): Коллекция таймсерий - база данных таймсерий по символам и периодам
Работа с таймсериями в библиотеке DoEasy (Часть 38): Коллекция таймсерий - реалтайм обновление и доступ к данным из программы
Работа с таймсериями в библиотеке DoEasy (Часть 39): Индикаторы на основе библиотеки - подготовка данных и события таймсерий
Работа с таймсериями в библиотеке DoEasy (Часть 40): Индикаторы на основе библиотеки - реалтайм обновление данных
Работа с таймсериями в библиотеке DoEasy (Часть 41): Пример мультисимвольного мультипериодного индикатора
Работа с таймсериями в библиотеке DoEasy (Часть 42): Класс объекта абстрактного индикаторного буфера
Работа с таймсериями в библиотеке DoEasy (Часть 43): Классы объектов индикаторных буферов





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
У меня маленькая претензия к изложению материала - пугающе много слов "буфер" в тексте, где ими "напичкано" каждое предложение и каждый комментарий. Немного "режет" глаза.
А что поделать, если сказ про индикаторные буферы? При этом есть объект-буфер и есть массив-буфер, хранимый в объекте-буфере. :))
Работа действительно эпохальная
Господи, у меня просто голова кругом! Вы такую работу проделываете! А мне как новичку вообще чтобы что -то "вдуплить"... Подскажите пожалуйста, с чего начать, кроме того что с первой части?
С первой части :)
А если серьёзно, то библиотека ориентирована на средний уровень. Начните с постановки задачи, затем - документация и форум с вопросами, кодобаза с примерами. Тем, кто реально хочет научиться делать сам и пробует, и обращается с вопросами о том, что не получается, тут с удовольствием помогают, но отправляют во фриланс тех, кто просит сделать за него.
Работа действительно эпохальная