Работа с таймсериями в библиотеке DoEasy (Часть 49): Мультипериодные мультисимвольные многобуферные стандартные индикаторы
Содержание
Концепция
В продолжение темы о выводе данных в индикаторах с любого символа/периода на график текущего символа, займёмся сегодня созданием функционала для отображения стандартных индикаторов, использующих несколько буферов для отрисовки своих данных. Мы уже научились получать данные с любого символа/периода графика и отображать их на графике текущего символа (не всё гладко, но постепенно выявим и исправим все недочёты — мы же учимся разрабатывать с нуля, что не исключает возможности чего-то не учесть, и тут важно не оставить как есть, а имея в запасе много времени наблюдений, отметить по возможности как можно больше допущенных недочётов, и постепенно, шаг за шагом, избавиться от них), и сегодня создадим возможность построения многобуферных стандартных индикаторов.
Главное отличие от однобуферных индикаторов в концепции построения библиотеки является то, что нам необходимо как-то пометить рисуемые и расчётные буферы индикатора так, чтобы библиотека могла их все отнести к одному общему индикатору.
На данный момент у нас уже есть тип стандартного индикатора, который записывается в свойства объектов-буферов и идентификатор буферов:
- Параметр "Тип индикатора" — служит для указания к какому стандартному индикатору относится рисуемый и расчётный объекты-буферы создаваемого индикатора;
- Параметр "Идентификатор индикатора" — служит для указания к какому из двух созданных индикаторов одного и того же типа принадлежат рисуемый и расчётный объекты-буферы (например, разные индикаторы МА — один имеет идентификатор 1, второй — 2, третий — 3, и т.д. При этом все они имеют один и тот же тип индикатора IND_MA)
Эти параметры хорошо подходят для идентификации принадлежности объектов-буферов к однобуферным стандартным индикаторам. В многобуферных же индикаторах нам нужно ещё различать к какой линии стандартного индикатора относятся расчётный и рисуемый объекты-буферы — ведь линий-то несколько. И здесь приходим к выводу о необходимости задания ещё как минимум одного параметра — тип линии (верхняя, нижняя, средняя, и т.п.).
И тогда уже мы сможем чётко различать принадлежность объектов-буферов к каждому из созданных индикаторов и легко обращаться к нужной линии любого из созданных индикаторов по типу линии:
- Параметр "Тип индикатора" — служит для указания к какому стандартному индикатору относится рисуемый и расчётный объекты-буферы создаваемого индикатора;
- Параметр "Идентификатор индикатора" — служит для указания к какому из двух созданных индикаторов одного и того же типа принадлежат рисуемый и расчётный объекты-буферы (например, разные индикаторы МА — один имеет идентификатор 1, второй — 2, третий — 3, и т.д. При этом все они имеют один и тот же тип индикатора IND_MA);
- Параметр "Линия индикатора" — указывает на конкретную линию индикатора, полученного при помощи указания двух предыдущих параметров
Помимо этого параметра добавим ещё один: "Короткое наименование индикатора", в котором будем хранить наименование стандартного индикатора в соответствии с тем, которое отображается стандартными индикаторами в подокне, но с добавлением символа и периода. Например, для стохастика это наименование будет выглядеть так:
Рис.1. Стандартный индикатор Stochastic Oscillator
Рис.2. Мультисимвольный мультипериодный стандартный индикатор Stochastic Oscillator
Доработка классов библиотеки
Поменяем название файла \MQL5\Include\DoEasy\Datas.mqh на \MQL5\Include\DoEasy\Data.mqh (почему так ранее назвал, не знаю, скорее всего — ошибки моего перевода на английский язык), и впишем константы индексов новых сообщений библиотеки:
//--- CBuffer MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE, // Индекс базового буфера данных MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT, // Порядковый номер рисуемого буфера MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR, // Индекс буфера цвета MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS, // Количество буферов данных MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE, // Индекс массива для назначения следующим индикаторным буфером MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT, // Индекс следующего по счёту рисуемого буфера MSG_LIB_TEXT_BUFFER_TEXT_ID, // Идентификатор буферов индикатора MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_MODE, // Линия индикатора MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE, // Хэндл индикатора, использующего буфер MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE, // Тип индикатора, использующего буфер MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME, // Период данных буфера (таймфрейм) MSG_LIB_TEXT_BUFFER_TEXT_STATUS, // Статус буфера MSG_LIB_TEXT_BUFFER_TEXT_TYPE, // Тип буфера MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE, // Активен MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE, // Код стрелки MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT, // Смещение стрелок по вертикали MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN, // Количество начальных баров без отрисовки и значений в DataWindow MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE, // Тип графического построения MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA, // Отображение значений построения в окне DataWindow MSG_LIB_TEXT_BUFFER_TEXT_SHIFT, // Сдвиг графического построения индикатора по оси времени в барах MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE, // Стиль линии отрисовки MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH, // Толщина линии отрисовки MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE, // Размер значка стрелки MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM, // Количество цветов MSG_LIB_TEXT_BUFFER_TEXT_COLOR, // Цвет отрисовки MSG_LIB_TEXT_BUFFER_TEXT_EMPTY_VALUE, // Пустое значение для построения, для которого нет отрисовки MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL, // Символ буфера MSG_LIB_TEXT_BUFFER_TEXT_LABEL, // Имя индикаторной графической серии, отображаемое в окне DataWindow MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME, // Наименование индикатора, использующего буфер MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME_SHORT, // Короткое наименование индикатора, использующего буфер MSG_LIB_TEXT_BUFFER_TEXT_STATUS_NAME, // Индикаторный буфер с типом графического построения MSG_LIB_TEXT_BUFFER_TEXT_INVALID_PROPERTY_BUFF, // Неправильно указано количество буферов индикатора (#property indicator_buffers) MSG_LIB_TEXT_BUFFER_TEXT_MAX_BUFFERS_REACHED, // Достигнуто максимально возможное количество индикаторных буферов MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ, // Нет ни одного объекта-буфера для стандартного индикатора
Добавим текстовые сообщения, соответствующие вновь добавленным индексам:
{"Индекс базового буфера данных","Index of Base data buffer"}, {"Порядковый номер рисуемого буфера","Plot buffer sequence number"}, {"Индекс буфера цвета","Color buffer index"}, {"Количество буферов данных","Number of data buffers"}, {"Индекс массива для назначения следующим индикаторным буфером","Array index for assignment as the next indicator buffer"}, {"Индекс следующего по счёту рисуемого буфера","Index of the next drawable buffer"}, {"Идентификатор буферов индикатора","Indicator Buffer Id"}, {"Линия индикатора","Indicator line"}, {"Хэндл индикатора, использующего буфер","Indicator handle that uses the buffer"}, {"Тип индикатора, использующего буфер","Indicator type that uses the buffer"}, {"Период данных буфера (таймфрейм)","Buffer data Period (Timeframe)"}, {"Статус буфера","Buffer status"}, {"Тип буфера","Buffer type"}, {"Активен","Active"}, {"Код стрелки","Arrow code"}, {"Смещение стрелок по вертикали","Vertical shift of arrows"}, {"Количество начальных баров без отрисовки и значений в DataWindow","Number of initial bars without drawing and values in the DataWindow"}, {"Тип графического построения","Type of graphical construction"}, {"Отображение значений построения в окне DataWindow","Display construction values in the DataWindow"}, {"Сдвиг графического построения индикатора по оси времени в барах","Shift of indicator plotting along the time axis in bars"}, {"Стиль линии отрисовки","Drawing line style "}, {"Толщина линии отрисовки","The thickness of the drawing line"}, {"Размер значка стрелки","Arrow icon size"}, {"Количество цветов","The number of colors"}, {"Цвет отрисовки","The index of a buffer containing the drawing color"}, {"Пустое значение для построения, для которого нет отрисовки","An empty value for plotting, for which there is no drawing"}, {"Символ буфера","Buffer Symbol"}, {"Имя индикаторной графической серии, отображаемое в окне DataWindow","The name of the indicator graphical series to display in the DataWindow"}, {"Наименование индикатора, использующего буфер","The name of the indicator that uses the buffer"}, {"Короткое наименование индикатора, использующего буфер","The shortname of the indicator that uses the buffer"}, {"Индикаторный буфер с типом графического построения","Indicator buffer with graphic plot type"}, {"Неправильно указано количество буферов индикатора (#property indicator_buffers)","The number of indicator buffers is incorrect (#property indicator_buffers)"}, {"Достигнуто максимально возможное количество индикаторных буферов","The maximum number of indicator buffers has been reached"}, {"Нет ни одного объекта-буфера для стандартного индикатора","There is no buffer object for the standard indicator"},
Оставшийся старый файл Datas.mqh удалим из папки библиотеки \MQL5\Include\DoEasy\ — чтобы не засорял пространство файлов библиотеки — теперь он нам не нужен, и его функции выполняет новый файл Data.mqh.
Дополним файл \MQL5\Include\DoEasy\Defines.mqh всеми необходимыми новыми данными, требующимися для реализации функционала работы с многобуферными стандартными индикаторами.
В первую очередь в строке подключения переименованного файла подаравим его название на новое:
//+------------------------------------------------------------------+ //| Включаемые файлы | //+------------------------------------------------------------------+ #include "DataSND.mqh" #include "DataIMG.mqh" #include "Data.mqh" #ifdef __MQL4__ #include "ToMQL4.mqh" #endif //+------------------------------------------------------------------+
В блок с данными для работы с индикаторными буферами впишем новое перечисление типов индикаторных линий:
//+------------------------------------------------------------------+ //| Линии индикатора | //+------------------------------------------------------------------+ enum ENUM_INDICATOR_LINE_MODE { INDICATOR_LINE_MODE_MAIN, // Главная линия INDICATOR_LINE_MODE_SIGNAL, // Сигнальная линия INDICATOR_LINE_MODE_UPPER, // Верхняя линия INDICATOR_LINE_MODE_MIDDLE, // Средняя линия INDICATOR_LINE_MODE_LOWER, // Нижняя линия INDICATOR_LINE_MODE_JAWS, // Линия Jaws INDICATOR_LINE_MODE_TEETH, // Линия Teeth INDICATOR_LINE_MODE_LIPS, // Линия Lips INDICATOR_LINE_MODE_DI_PLUS, // Линия +DI INDICATOR_LINE_MODE_DI_MINUS, // Линия -DI }; //+------------------------------------------------------------------+
У разных стандартных индикаторов есть линии, которые могут иметь свои наименования в зависимости от того, как эти линии назвал создатель этого индикатора. Здесь мы создали перечисление, в котором есть константы, указывающие на то, или иное наименование линии индикатора. Значениями этих констант мы и будем помечать принадлежность создаваемых объектов-буферов (расчётного и рисуемого) к той или иной линии стандартного индикатора. Таким образом, при обращении по наименованию константы к объекту-буферу, мы сможем однозначно получить требуемый объект для работы с ним (с учётом типа индикатора и его идентификатора).
Впоследствии мы назначим этим константам точные значения — чтобы сократить количество кода, которое сегодня напишем (так как многие индикаторы полностью повторяют расчёт таких же однотипных индикаторов, но имеющие иные наименования своих линий)
К перечислению целочисленных свойств объекта-буфера добавим новое значение и увеличим количество целочисленных свойств с 23 до 24:
//+------------------------------------------------------------------+ //| Целочисленные свойства буфера | //+------------------------------------------------------------------+ 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_BASE, // Индекс массива для назначения следующим индикаторным буфером BUFFER_PROP_INDEX_NEXT_PLOT, // Индекс следующего по счёту рисуемого буфера BUFFER_PROP_IND_LINE_MODE, // Линия индикатора BUFFER_PROP_ID, // Идентификатор множества буферов одного индикатора BUFFER_PROP_IND_HANDLE, // Хэндл индикатора, использующего буфер BUFFER_PROP_IND_TYPE, // Тип индикатора, использующего буфер BUFFER_PROP_NUM_DATAS, // Количество буферов данных BUFFER_PROP_INDEX_COLOR, // Индекс буфера цвета }; #define BUFFER_PROP_INTEGER_TOTAL (24) // Общее количество целочисленных свойств буфера #define BUFFER_PROP_INTEGER_SKIP (2) // Количество неиспользуемых в сортировке свойств буфера //+------------------------------------------------------------------+
Точно так же добавим новое строковое свойство и увеличим их количество до 4:
//+------------------------------------------------------------------+ //| Строковые свойства буфера | //+------------------------------------------------------------------+ enum ENUM_BUFFER_PROP_STRING { BUFFER_PROP_SYMBOL = (BUFFER_PROP_INTEGER_TOTAL+BUFFER_PROP_DOUBLE_TOTAL), // Символ буфера BUFFER_PROP_LABEL, // Имя индикаторной графической серии, отображаемое в окне DataWindow BUFFER_PROP_IND_NAME, // Наименование индикатора, использующего буфер BUFFER_PROP_IND_NAME_SHORT, // Короткое наименование индикатора, использующего буфер }; #define BUFFER_PROP_STRING_TOTAL (4) // Общее количество строковых свойств буфера //+------------------------------------------------------------------+
Так как мы добавили два новых свойства объекту-буферу, то нужно добавить и два новых критерия для поиска и сортировки объектов-буферов в их списке-коллекции по этим свойствам:
//+------------------------------------------------------------------+ //| Возможные критерии сортировки буферов | //+------------------------------------------------------------------+ #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_BASE, // Сортировать по индексу массива для назначения следующим индикаторным буфером SORT_BY_BUFFER_INDEX_NEXT_PLOT, // Сортировать по индексу следующего по счёту рисуемого буфера SORT_BY_BUFFER_IND_LINE_MODE, // Сортировать по линии индикатора SORT_BY_BUFFER_ID, // Сортировать по идентификатору множества буферов одного индикатора SORT_BY_BUFFER_IND_HANDLE, // Сортировать по хэндлу индикатора, использующего буфер SORT_BY_BUFFER_IND_TYPE, // Сортировать по типу индикатора, использующего буфер //--- Сортировка по вещественным свойствам SORT_BY_BUFFER_EMPTY_VALUE = FIRST_BUFFER_DBL_PROP, // Сортировать по пустому значению для построения, для которого нет отрисовки //--- Сортировка по строковым свойствам SORT_BY_BUFFER_SYMBOL = FIRST_BUFFER_STR_PROP, // Сортировать по символу буфера SORT_BY_BUFFER_LABEL, // Сортировать по имени индикаторной графической серии, отображаемого в окне DataWindow SORT_BY_BUFFER_IND_NAME, // Сортировать по наименованию индикатора, использующего буфер SORT_BY_BUFFER_IND_NAME_SHORT, // Сортировать по короткому наименованию индикатора, использующего буфер }; //+------------------------------------------------------------------+
В публичной секции класса напишем методы установки и получения новых свойств объекта-буфера
в файле \MQL5\Include\DoEasy\Objects\Indicators\Buffer.mqh:
public: //--- Выводит в журнал описание свойств буфера (full_prop=true - все свойства, false - только поддерживаемые) void Print(const bool full_prop=false); //--- Выводит в журнал краткое описание буфера (реализация в наследниках) virtual void PrintShort(void) {;} //--- Устанавливает (1) код стрелки, (2) смещение стрелок по вертикали, (3) символ, (4) таймфрейм, (5) флаг активности буфера //--- (6) тип рисования, (7) количество начальных баров без отрисовки, (8) признак отображения значений построения в окне DataWindow, //--- (9) сдвиг графического построения индикатора по оси времени, (10) стиль линии отрисовки, (11) толщину линии отрисовки, //--- (12) общее количество цветов, (13) один цвет отрисовки, (14) цвет отрисовки в указанный индекс цвета, //--- (15) цвета отрисовки из массива цветов, (16) пустое значение, (17) имя графической серии, отображаемое в окне DataWindow virtual void SetArrowCode(const uchar code) { return; } virtual void SetArrowShift(const int shift) { return; } void SetSymbol(const string symbol) { this.SetProperty(BUFFER_PROP_SYMBOL,symbol); } void SetTimeframe(const ENUM_TIMEFRAMES timeframe) { this.SetProperty(BUFFER_PROP_TIMEFRAME,timeframe); } void SetActive(const bool flag) { this.SetProperty(BUFFER_PROP_ACTIVE,flag); } void SetDrawType(const ENUM_DRAW_TYPE draw_type); void SetDrawBegin(const int value); void SetShowData(const bool flag); void SetShift(const int shift); void SetStyle(const ENUM_LINE_STYLE style); void SetWidth(const int width); void SetColorNumbers(const int number); void SetColor(const color colour); void SetColor(const color colour,const uchar index); void SetColors(const color &array_colors[]); void SetEmptyValue(const double value); virtual void SetLabel(const string label); void SetID(const int id) { this.SetProperty(BUFFER_PROP_ID,id); } void SetIndicatorHandle(const int handle) { this.SetProperty(BUFFER_PROP_IND_HANDLE,handle); } void SetIndicatorType(const ENUM_INDICATOR type) { this.SetProperty(BUFFER_PROP_IND_TYPE,type); } void SetIndicatorName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME,name); } void SetIndicatorShortName(const string name) { this.SetProperty(BUFFER_PROP_IND_NAME_SHORT,name); } void SetLineMode(const ENUM_INDICATOR_LINE_MODE mode){ this.SetProperty(BUFFER_PROP_IND_LINE_MODE,mode); } //--- Возвращает (1) порядковый номер рисуемого буфера, (2) индекс связанного массива, (3) индекс буфера цвета, //--- (4) индекс первого свободного связанного массива, (5) индекс следующего рисуемого буфера, (6) период данных буфера (7) статус буфера, //--- (8) тип буфера, (9) флаг использования буфера, (10) код стрелки, (11) смещение стрелок для стиля DRAW_ARROW, //--- (12) Количество начальных баров без отрисовки и значений в DataWindow, (13) тип графического построения, //--- (14) флаг отображения значений построения в окне DataWindow, (15) сдвиг графического построения индикатора по оси времени, //--- (16) стиль линии отрисовки, (17) толщину линии отрисовки, (18) количество цветов, (19) цвет отрисовки, (20) количество буферов для построения //--- (21) установленное пустое значение, (22) символ буфера, (23) имя индикаторной графической серии, отображаемое в окне DataWindow //--- (24) идентификатор буфера, (25) хэндл индикатора, (26) тип стандартного индикатора, (27) имя стандартного индикатора, //--- (28) количество рассчитанных баров стандартного индикатора, (29) тип линии (main, signal, etc) int IndexPlot(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_PLOT); } int IndexBase(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_BASE); } int IndexColor(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_COLOR); } int IndexNextBaseBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_BASE); } int IndexNextPlotBuffer(void) const { return (int)this.GetProperty(BUFFER_PROP_INDEX_NEXT_PLOT); } ENUM_TIMEFRAMES Timeframe(void) const { return (ENUM_TIMEFRAMES)this.GetProperty(BUFFER_PROP_TIMEFRAME); } ENUM_BUFFER_STATUS Status(void) const { return (ENUM_BUFFER_STATUS)this.GetProperty(BUFFER_PROP_STATUS); } ENUM_BUFFER_TYPE TypeBuffer(void) const { return (ENUM_BUFFER_TYPE)this.GetProperty(BUFFER_PROP_TYPE); } bool IsActive(void) const { return (bool)this.GetProperty(BUFFER_PROP_ACTIVE); } uchar ArrowCode(void) const { return (uchar)this.GetProperty(BUFFER_PROP_ARROW_CODE); } int ArrowShift(void) const { return (int)this.GetProperty(BUFFER_PROP_ARROW_SHIFT); } int DrawBegin(void) const { return (int)this.GetProperty(BUFFER_PROP_DRAW_BEGIN); } ENUM_DRAW_TYPE DrawType(void) const { return (ENUM_DRAW_TYPE)this.GetProperty(BUFFER_PROP_DRAW_TYPE); } bool IsShowData(void) const { return (bool)this.GetProperty(BUFFER_PROP_SHOW_DATA); } int Shift(void) const { return (int)this.GetProperty(BUFFER_PROP_SHIFT); } ENUM_LINE_STYLE LineStyle(void) const { return (ENUM_LINE_STYLE)this.GetProperty(BUFFER_PROP_LINE_STYLE); } int LineWidth(void) const { return (int)this.GetProperty(BUFFER_PROP_LINE_WIDTH); } int ColorsTotal(void) const { return (int)this.GetProperty(BUFFER_PROP_COLOR_INDEXES); } color Color(void) const { return (color)this.GetProperty(BUFFER_PROP_COLOR); } int BuffersTotal(void) const { return (int)this.GetProperty(BUFFER_PROP_NUM_DATAS); } double EmptyValue(void) const { return this.GetProperty(BUFFER_PROP_EMPTY_VALUE); } string Symbol(void) const { return this.GetProperty(BUFFER_PROP_SYMBOL); } string Label(void) const { return this.GetProperty(BUFFER_PROP_LABEL); } int ID(void) const { return (int)this.GetProperty(BUFFER_PROP_ID); } int IndicatorHandle(void) const { return (int)this.GetProperty(BUFFER_PROP_IND_HANDLE); } ENUM_INDICATOR IndicatorType(void) const { return (ENUM_INDICATOR)this.GetProperty(BUFFER_PROP_IND_TYPE); } string IndicatorName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME); } string IndicatorShortName(void) const { return this.GetProperty(BUFFER_PROP_IND_NAME_SHORT); } int IndicatorBarsCalculated(void) const { return ::BarsCalculated((int)this.GetProperty(BUFFER_PROP_IND_HANDLE));} ENUM_INDICATOR_LINE_MODE LineMode(void) const { return (ENUM_INDICATOR_LINE_MODE)this.GetProperty(BUFFER_PROP_IND_LINE_MODE);}
В конструкторе класса зададим новым свойствам их значения по умолчанию:
//+------------------------------------------------------------------+ //| Закрытый параметрический конструктор | //+------------------------------------------------------------------+ CBuffer::CBuffer(ENUM_BUFFER_STATUS buffer_status, ENUM_BUFFER_TYPE buffer_type, const uint index_plot, const uint index_base_array, const int num_datas, const uchar total_arrays, const int width, const string label) { this.m_type=COLLECTION_BUFFERS_ID; this.m_act_state_trigger=true; this.m_total_arrays=total_arrays; //--- Сохранение целочисленных свойств this.m_long_prop[BUFFER_PROP_STATUS] = buffer_status; this.m_long_prop[BUFFER_PROP_TYPE] = buffer_type; this.m_long_prop[BUFFER_PROP_ID] = WRONG_VALUE; this.m_long_prop[BUFFER_PROP_IND_LINE_MODE] = INDICATOR_LINE_MODE_MAIN; this.m_long_prop[BUFFER_PROP_IND_HANDLE] = INVALID_HANDLE; this.m_long_prop[BUFFER_PROP_IND_TYPE] = WRONG_VALUE; ENUM_DRAW_TYPE type= ( !this.TypeBuffer() || !this.Status() ? DRAW_NONE : this.Status()==BUFFER_STATUS_FILLING ? DRAW_FILLING : ENUM_DRAW_TYPE(this.Status()+8) ); this.m_long_prop[BUFFER_PROP_DRAW_TYPE] = type; this.m_long_prop[BUFFER_PROP_TIMEFRAME] = PERIOD_CURRENT; this.m_long_prop[BUFFER_PROP_ACTIVE] = true; this.m_long_prop[BUFFER_PROP_ARROW_CODE] = 0x9F; this.m_long_prop[BUFFER_PROP_ARROW_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_DRAW_BEGIN] = 0; this.m_long_prop[BUFFER_PROP_SHOW_DATA] = (buffer_type>BUFFER_TYPE_CALCULATE ? true : false); this.m_long_prop[BUFFER_PROP_SHIFT] = 0; this.m_long_prop[BUFFER_PROP_LINE_STYLE] = STYLE_SOLID; this.m_long_prop[BUFFER_PROP_LINE_WIDTH] = width; this.m_long_prop[BUFFER_PROP_COLOR_INDEXES] = (this.Status()>BUFFER_STATUS_NONE ? (this.Status()!=BUFFER_STATUS_FILLING ? 1 : 2) : 0); this.m_long_prop[BUFFER_PROP_COLOR] = clrRed; this.m_long_prop[BUFFER_PROP_NUM_DATAS] = num_datas; this.m_long_prop[BUFFER_PROP_INDEX_PLOT] = index_plot; this.m_long_prop[BUFFER_PROP_INDEX_BASE] = index_base_array; this.m_long_prop[BUFFER_PROP_INDEX_COLOR] = this.GetProperty(BUFFER_PROP_INDEX_BASE)+ (this.TypeBuffer()!=BUFFER_TYPE_CALCULATE ? this.GetProperty(BUFFER_PROP_NUM_DATAS) : 0); this.m_long_prop[BUFFER_PROP_INDEX_NEXT_BASE] = index_base_array+this.m_total_arrays; this.m_long_prop[BUFFER_PROP_INDEX_NEXT_PLOT] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? index_plot+1 : index_plot); //--- Сохранение вещественных свойств this.m_double_prop[this.IndexProp(BUFFER_PROP_EMPTY_VALUE)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? EMPTY_VALUE : 0); //--- Сохранение строковых свойств this.m_string_prop[this.IndexProp(BUFFER_PROP_SYMBOL)] = ::Symbol(); this.m_string_prop[this.IndexProp(BUFFER_PROP_LABEL)] = (this.TypeBuffer()>BUFFER_TYPE_CALCULATE ? label : NULL); this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME)] = NULL; this.m_string_prop[this.IndexProp(BUFFER_PROP_IND_NAME_SHORT)]= NULL; //--- Если не удалось изменить размер массива индикаторных буферов на новый - выводим об этом сообщение с указанием на строку if(::ArrayResize(this.DataBuffer,(int)this.GetProperty(BUFFER_PROP_NUM_DATAS))==WRONG_VALUE) ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_DRAWING_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError()); //--- Если не удалось изменить размер массива цветов на новый (только для не расчётного буфера) - выводим об этом сообщение с указанием на строку if(this.TypeBuffer()>BUFFER_TYPE_CALCULATE) if(::ArrayResize(this.ArrayColors,(int)this.ColorsTotal())==WRONG_VALUE) ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_SYS_FAILED_COLORS_ARRAY_RESIZE),". ",CMessage::Text(MSG_LIB_SYS_ERROR),": ",(string)::GetLastError()); //--- Для DRAW_FILLING заполняем массив цветов двумя цветами по умолчанию if(this.Status()==BUFFER_STATUS_FILLING) { this.SetColor(clrBlue,0); this.SetColor(clrRed,1); } //--- Связывание индикаторных буферов с массивами //--- В цикле по количеству индикаторных буферов int total=::ArraySize(DataBuffer); for(int i=0;i<total;i++) { //--- рассчитываем индекс очередного массива и //--- связываем индикаторный буфер по рассчитанному индексу с динамическим массивом, //--- находящимся по индексу цикла i в массиве DataBuffer int index=(int)this.GetProperty(BUFFER_PROP_INDEX_BASE)+i; ::SetIndexBuffer(index,this.DataBuffer[i].Array,(this.TypeBuffer()==BUFFER_TYPE_DATA ? INDICATOR_DATA : INDICATOR_CALCULATIONS)); //--- Установка всем массивам буферов флага индексации как в таймсерии ::ArraySetAsSeries(this.DataBuffer[i].Array,true); } //--- Связывание буфера цвета с массивом (только для не расчётного буфера и не для буфера заливки) if(this.Status()!=BUFFER_STATUS_FILLING && this.TypeBuffer()!=BUFFER_TYPE_CALCULATE) { ::SetIndexBuffer((int)this.GetProperty(BUFFER_PROP_INDEX_COLOR),this.ColorBufferArray,INDICATOR_COLOR_INDEX); ::ArraySetAsSeries(this.ColorBufferArray,true); } //--- Если буфер расчётный - закончили if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return; //--- Установка целочисленных параметров графической серии ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_TYPE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_TYPE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_CODE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_ARROW_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_ARROW_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_DRAW_BEGIN,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_DRAW_BEGIN)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHOW_DATA,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHOW_DATA)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_SHIFT,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_SHIFT)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_STYLE,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_STYLE)); ::PlotIndexSetInteger((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LINE_WIDTH,(ENUM_PLOT_PROPERTY_INTEGER)this.GetProperty(BUFFER_PROP_LINE_WIDTH)); this.SetColor((color)this.GetProperty(BUFFER_PROP_COLOR)); //--- Установка вещественных параметров графической серии ::PlotIndexSetDouble((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_EMPTY_VALUE,this.GetProperty(BUFFER_PROP_EMPTY_VALUE)); //--- Установка строковых параметров графической серии ::PlotIndexSetString((int)this.GetProperty(BUFFER_PROP_INDEX_PLOT),PLOT_LABEL,this.GetProperty(BUFFER_PROP_LABEL)); } //+------------------------------------------------------------------+
В методе, возвращающем описание целочисленных свойств буфера, пропишем возврат описания назначения линии индикатора:
//+------------------------------------------------------------------+ //| Возвращает описание целочисленного свойства буфера | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_INTEGER property) { return ( property==BUFFER_PROP_INDEX_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_STATUS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_STATUS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetStatusDescription() ) : property==BUFFER_PROP_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTypeBufferDescription() ) : property==BUFFER_PROP_TIMEFRAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_TIMEFRAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetTimeframeDescription() ) : property==BUFFER_PROP_ACTIVE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ACTIVE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetActiveDescription() ) : property==BUFFER_PROP_DRAW_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetDrawTypeDescription() ) : property==BUFFER_PROP_ARROW_CODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_CODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ARROW_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_LINE_STYLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_STYLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetLineStyleDescription() ) : property==BUFFER_PROP_LINE_WIDTH ? (this.Status()==BUFFER_STATUS_ARROW ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ARROW_SIZE) : CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LINE_WIDTH))+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_DRAW_BEGIN ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_DRAW_BEGIN)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_SHOW_DATA ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHOW_DATA)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetShowDataDescription() ) : property==BUFFER_PROP_SHIFT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SHIFT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR_INDEXES ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR_NUM)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT_BASE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_BASE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_INDEX_NEXT_PLOT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_INDEX_NEXT_PLOT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_ID ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_ID)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_IND_LINE_MODE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_LINE_MODE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::StringSubstr(::EnumToString((ENUM_INDICATOR_LINE_MODE)this.GetProperty(property)),10) ) : property==BUFFER_PROP_IND_HANDLE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_HANDLE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_IND_TYPE ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_TYPE)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+::StringSubstr(::EnumToString((ENUM_INDICATOR)this.GetProperty(property)),4) ) : property==BUFFER_PROP_NUM_DATAS ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NUM_DATAS)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(string)this.GetProperty(property) ) : property==BUFFER_PROP_COLOR ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_COLOR)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.GetColorsDescription() ) : "" ); } //+------------------------------------------------------------------+
А в методе, возвращающем описание строковых свойств буфера, пропишем возврат описания короткого имени индикатора:
//+------------------------------------------------------------------+ //| Возвращает описание строкового свойства буфера | //+------------------------------------------------------------------+ string CBuffer::GetPropertyDescription(ENUM_BUFFER_PROP_STRING property) { return ( property==BUFFER_PROP_SYMBOL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_SYMBOL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+this.Symbol() ) : property==BUFFER_PROP_LABEL ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_LABEL)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.Label()==NULL || this.Label()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.Label()+"\"") ) : property==BUFFER_PROP_IND_NAME ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IndicatorName()==NULL || this.IndicatorName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.IndicatorName()+"\"") ) : property==BUFFER_PROP_IND_NAME_SHORT ? CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_IND_NAME_SHORT)+ (!this.SupportProperty(property) ? ": "+CMessage::Text(MSG_LIB_PROP_NOT_SUPPORTED) : ": "+(this.IndicatorShortName()==NULL || this.IndicatorName()=="" ? CMessage::Text(MSG_LIB_PROP_NOT_SET) : "\""+this.IndicatorShortName()+"\"") ) : "" ); } //+------------------------------------------------------------------+
Так как мы внесли два новых свойства в объект-буфер, а у нас имеются объекты-наследники этого базового объекта абстрактного буфера, и у них в свою очередь имеются виртуальные методы, возвращающие флаги поддержания объектом тех, или иных свойств, то нам необходимо внести эти новые свойства в список разрешённых — только в этом случае мы сможем искать, выбирать и фильтровать объекты-буферы в списке-коллекции объектов-буферов.
У нас есть на каждый тип рисования свой объект-буфер, и в файл каждого такого объекта нам нужно внести доработки. Так как все изменения во всех объектах одинаковые, то рассмотрим доработку на примере объекта буфера-линии в файле \MQL5\Include\DoEasy\Objects\Indicators\BufferLine.mqh.
Изменения внесены в два виртуальных метода, возвращающих флаг поддержания объектом целочисленного и строкового свойства:
//+------------------------------------------------------------------+ //| Возвращает истину, если буфер поддерживает переданное | //| целочисленное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_INTEGER property) { if((property==BUFFER_PROP_ARROW_CODE || property==BUFFER_PROP_ARROW_SHIFT) || ( this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_TYPE && property!=BUFFER_PROP_INDEX_NEXT_BASE && property!=BUFFER_PROP_IND_LINE_MODE && property!=BUFFER_PROP_IND_HANDLE && property!=BUFFER_PROP_IND_TYPE && property!=BUFFER_PROP_ID ) ) return false; return true; } //+------------------------------------------------------------------+ //| Возвращает истину, если буфер поддерживает переданное | //| вещественное свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_DOUBLE property) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE) return false; return true; } //+------------------------------------------------------------------+ //| Возвращает истину, если буфер поддерживает переданное | //| строковое свойство, возвращает ложь в противном случае | //+------------------------------------------------------------------+ bool CBufferLine::SupportProperty(ENUM_BUFFER_PROP_STRING property) { if(this.TypeBuffer()==BUFFER_TYPE_CALCULATE && property!=BUFFER_PROP_IND_NAME_SHORT) return false; return true; } //+------------------------------------------------------------------+
Здесь проверяется, что если объект-буфер является расчётным буфером, то если в метод передано любое из свойств, отсутствующих в перечисленном списке, то метод возвращает false — объект не поддерживает такое свойство, иначе — объект такое свойство поддерживает, и из метода возвращается true.
Такие (или подобные — в BufferCalculate.mqh) изменения уже внесены во все файлы классов-наследников объекта абстрактного буфера: BufferArrow.mqh, BufferBars.mqh, BufferCalculate.mqh, BufferCandles.mqh, BufferFilling.mqh, BufferHistogram.mqh, BufferHistogram2.mqh, BufferSection.mqh, BufferZigZag.mqh и BufferLine.mqh, который мы уже рассмотрели. Ознакомиться с изменениями можно в прикреплённых к статье файлах.
В прошлой статье нами были созданы методы создания объектов-буферов для мультисимвольных мультипериодных стандартных индикаторов, отображающих свои данные в подокне основного графика. Сегодня мы дополним библиотеку методами создания стандартных индикаторов, отображающих свои данные в главном окне графика — эти методы ничем не отличаются от уже созданных, они уже написаны для всех стандартных индикаторов основного окна, и мы рассмотрим такой метод на примере создания индикатора Moving Average:
//+------------------------------------------------------------------+ //| Создаёт мультисимвольный мультипериодный MA | //+------------------------------------------------------------------+ int CBuffersCollection::CreateMA(const string symbol,const ENUM_TIMEFRAMES timeframe, const int ma_period, const int ma_shift, const ENUM_MA_METHOD ma_method, const ENUM_APPLIED_PRICE applied_price, const int id=WRONG_VALUE) { //--- Создаём хэндл индикатора и устанавливаем идентификатор по умолчанию int handle=::iMA(symbol,timeframe,ma_period,ma_shift,ma_method,applied_price); int identifier=(id==WRONG_VALUE ? IND_MA : id); color array_colors[1]={clrRed}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Создаём буфер-линию this.CreateLine(); //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_MA); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetShowData(true); buff.SetLabel("MA("+symbol+","+TimeframeDescription(timeframe)+": "+(string)ma_period+")"); buff.SetIndicatorName("Moving Average"); buff.SetColors(array_colors); //--- Создаём расчётный буфер, в котором будут храниться данные стандартного индикатора this.CreateCalculate(); //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_MA); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetLabel("MA("+symbol+","+TimeframeDescription(timeframe)+": "+(string)ma_period+")"); buff.SetIndicatorName("Moving Average"); } return handle; } //+------------------------------------------------------------------+
Почитать подробнее о работе метода можно в предыдущей статье. Отмечу, что единственным отличием от ранее рассмотренных таких же методов, является установка нового свойства объекту-буферу — тип линии однобуферного индикатора устанавливаем как "Главная". Такие дополнения были внесены во все ранее написанные методы создания однобуферных стандартных индикаторов в подокне, рассмотренных в прошлой статье, и в методы создания однобуферных стандартных индикаторов в основном окне, которые уже добавлены в файл класса-коллекции объектов-буферов BuffersCollection.mqh — это методы CreateAMA(), CreateDEMA(), CreateFrAMA(), CreateMA(), CreateSAR(), CreateTEMA() и CreateVIDYA().
Да, в конструкторе класса мы и так устанавливаем это значение линии по умолчанию, но здесь я добавил безусловную установку этого свойства для того, чтобы эти методы соответствовали по стилю методам создания многобуферных стандартных индикаторов, которые сейчас и рассмотрим.
Рассмотрим методы создания многобуферных стандартных индикаторов на примере метода создания объекта стандартного индикатора Average Directional Movement Index:
//+------------------------------------------------------------------+ //| Создаёт мультисимвольный мультипериодный ADX | //+------------------------------------------------------------------+ int CBuffersCollection::CreateADX(const string symbol,const ENUM_TIMEFRAMES timeframe,const int adx_period,const int id=WRONG_VALUE) { //--- Создаём хэндл индикатора и устанавливаем идентификатор по умолчанию int handle=::iADX(symbol,timeframe,adx_period); int identifier=(id==WRONG_VALUE ? IND_ADX : id); color array_colors[1]={clrLightSeaGreen}; CBuffer *buff=NULL; if(handle!=INVALID_HANDLE) { //--- Создаём буфер-линию this.CreateLine(); //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры линии ADX buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); // Это главная линия индикатора buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel(buff.IndicatorShortName()); buff.SetColors(array_colors); //--- Создаём буфер-линию this.CreateLine(); //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры линии +DI buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_PLUS); // Это линия +DI buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel("+DI"); array_colors[0]=clrYellowGreen; buff.SetColors(array_colors); buff.SetStyle(STYLE_DOT); //--- Создаём буфер-линию this.CreateLine(); //--- Получаем последний созданный объект-буфер (рисуемый) и устанавливаем ему все необходимые параметры линии -DI buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_MINUS); // Это линия -DI buff.SetShowData(true); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetIndicatorShortName("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); buff.SetLabel("-DI"); array_colors[0]=clrWheat; buff.SetColors(array_colors); buff.SetStyle(STYLE_DOT); //--- Создаём расчётный буфер линии ADX, в котором будут храниться данные стандартного индикатора this.CreateCalculate(); //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры линии ADX buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_MAIN); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("ADX("+symbol+","+TimeframeDescription(timeframe)+": "+(string)adx_period+")"); //--- Создаём расчётный буфер линии +DI, в котором будут храниться данные стандартного индикатора this.CreateCalculate(); //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры линии +DI buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_PLUS); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("+DI"); //--- Создаём расчётный буфер линии -DI, в котором будут храниться данные стандартного индикатора this.CreateCalculate(); //--- Получаем последний созданный объект-буфер (расчётный) и устанавливаем ему все необходимые параметры линии -DI buff=this.GetLastCreateBuffer(); if(buff==NULL) return INVALID_HANDLE; buff.SetSymbol(symbol); buff.SetTimeframe(timeframe); buff.SetID(identifier); buff.SetIndicatorHandle(handle); buff.SetIndicatorType(IND_ADX); buff.SetLineMode(INDICATOR_LINE_MODE_DI_MINUS); buff.SetEmptyValue(EMPTY_VALUE); buff.SetIndicatorName("Average Directional Movement Index"); buff.SetLabel("-DI"); } return handle; } //+------------------------------------------------------------------+
Здесь логика совершенно идентична ранее созданным методам. Но создаём не по два объекта-буфера (рисуемый и расчётный), а по количеству линий, которые рисует каждый конкретный стандартный индикатор. И чтобы установить принадлежность каждого из создаваемых объектов-буферов своей линии стандартного индикатора, мы устанавливаем значение этой линии из перечисления ENUM_INDICATOR_LINE_MODE, которое сегодня добавили выше. Таким образом, каждый объект-буфер соответствует указанной для него линии индикатора. И на каждую линию индикатора мы имеем два объекта-буфера — один рисуемый, и второй — расчётный. В расчётный буфер будем записывать данные из хэндла созданного стандартного индикатора, а рисуемый буфер будет выводить на основной график данные расчётного буфера. Вот и вся логика методов.
Остальные методы практически идентичны, за исключением названий индикаторов, их коротких наименований и наименований каждой из их линий, что естественно — для каждой линии каждого стандартного индикатора мы написали свои наименования и описания, соответствующие каждому стандартному индикатору и тому, как он выводит названия своих линий в окне данных.
Были написаны такие методы для создания объектов многобуферных стандартных индикаторов: CreateADX(), CreateADXWilder(), CreateMACD(), CreateRVI(), CreateStochastic(), CreateBands(), CreateEnvelopes() и CreateFractals(). Методы идентичны вышерассмотренному, и описывать их нет смысла — их можно изучить самостоятельно в прикреплённых к статье файлах.
Методы, подготавливающие данные расчётного буфера, очищающие данные буфера и устанавливающие значения в рисуемый буфер, мы начали делать в прошлой статье. При помощи оператора switch действия, проводимые с буферами распределяются по типу индикатора. Оказалось, что с минимальными доработками мы можем в эти методы прописать обработку и созданных сегодня объектов стандартных индикаторов.
Метод, подготавливающий данные расчётного буфера указанного стандартного индикатора:
//+------------------------------------------------------------------+ //| Подготавливает данные расчётного буфера | //| указанного стандартного индикатора | //+------------------------------------------------------------------+ int CBuffersCollection::PreparingDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int total_copy) { CArrayObj *list_ind=this.GetListBufferByTypeID(std_ind,id); CArrayObj *list0=NULL,*list1=NULL,*list2=NULL; list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); if(list_ind==NULL || list_ind.Total()==0) { ::Print(DFUN_ERR_LINE,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return 0; } CBufferCalculate *buffer=NULL; int copied=WRONG_VALUE; int idx0=0,idx1=1,idx2=2; switch((int)std_ind) { //--- Однобуферные стандартные индикаторы case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : buffer=list_ind.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),0,0,total_copy); return copied; //--- Многобуферные стандартные индикаторы case IND_ALLIGATOR : case IND_GATOR : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_JAWS,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TEETH,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LIPS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_BANDS : idx0=1; idx1=0; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_ENVELOPES : case IND_FRACTALS : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; return copied; //--- Многобуферные стандартные индикаторы в подокне case IND_ADX : case IND_ADXW : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx2,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_MACD : case IND_RVI : case IND_STOCHASTIC : idx0=0; idx1=1; list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx0,0,total_copy); if(copied<total_copy) return 0; list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer=list1.At(0); if(buffer==NULL) return 0; copied=buffer.FillAsSeries(buffer.IndicatorHandle(),idx1,0,total_copy); if(copied<total_copy) return 0; return copied; case IND_ICHIMOKU : break; default: break; } return 0; } //+------------------------------------------------------------------+
Как видим, все проводимые над объектами-буферами действия, которые идентичны для разных типов стандартных индикаторов, но отличаются от других типов стандартных индикаторов, мы поместили в отдельные блоки обработки — каждому блоку соответствуют свои индикаторы, обработка которых одинакова. Для индикатора Bollinger Bands пришлось менять местами индексы его буферов, так как почему-то индексация его буферов (верхняя линия, средняя линия и нижняя линия отличаются от индексации этих буферов в окне данных — верхняя линия, нижняя линия, средняя линия). Поэтому пришлось вводить дополнительные две переменные idx0 и idx1, в которые прописываем реальные индексы линий стандартного индикатора для каждого из типов. Для всех типов индикаторов индексы их линий идут подряд: 0, 1 и 2, а для Bollinger Bands первые два индекса поменяны местами: 1, 0 и 2.
Остальную логику метода мы рассматривали в прошлой статье. Здесь же мы просто добавили обработку вновь созданных сегодня объектов стандартных индикаторов.
Метод, очищающий данные буфера указанного стандартного индикатора по индексу таймсерии:
//+------------------------------------------------------------------+ //| Очищает данные буфера указанного стандартного индикатора | //| по индексу таймсерии | //+------------------------------------------------------------------+ void CBuffersCollection::ClearDataBufferStdInd(const ENUM_INDICATOR std_ind,const int id,const int series_index) { //--- Получаем список объектов-буферов по типу и идентификатору CArrayObj *list_ind=this.GetListBufferByTypeID(std_ind,id); CArrayObj *list0=NULL,*list1=NULL,*list2=NULL; if(list_ind==NULL || list_ind.Total()==0) return; list_ind=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); if(list_ind.Total()==0) return; CBuffer *buffer=NULL; switch((int)std_ind) { //--- Однобуферные стандартные индикаторы case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : buffer=list_ind.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; //--- Многобуферные стандартные индикаторы case IND_ALLIGATOR: case IND_GATOR : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_JAWS,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_TEETH,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LIPS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ADX : case IND_ADXW : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_BANDS : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list2=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer=list2.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ENVELOPES : case IND_FRACTALS : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_MACD : case IND_RVI : case IND_STOCHASTIC : list0=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer=list0.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); list1=CSelect::ByBufferProperty(list_ind,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer=list1.At(0); if(buffer==NULL) return; buffer.SetBufferValue(0,series_index,buffer.EmptyValue()); break; case IND_ICHIMOKU : break; default: break; } } //+------------------------------------------------------------------+
Здесь мы точно так же, как и в прошлом методе, сгруппировали воедино одинаковые обработки для разных объектов стандартных индикаторов.
Приглядевшись внимательней, можно увидеть, что многие индикаторы, разнесённые по разным группам, имеют идентичную логику обработки, а всё отличие заключается только в наименовании констант их линий. Об этом я упоминал в начале статьи, и после того, как создадим объекты оставшихся трёх стандартных индикаторов, у которых изначально линии выводятся со смещением на график — Alligator, Gator и Ishimoku, мы оптимизируем эти методы, задав одинаковые значения индексов линий для разных констант, одинаковых по своему назначению, что позволит сократить код методов.
Метод, устанавливающий значения для текущего графика в буферы указанного стандартного индикатора по индексу таймсерии:
//+------------------------------------------------------------------+ //| Устанавливает значения для текущего графика в буферы указанного | //| стандартного индикатора по индексу таймсерии в соответствии | //| с символом/периодом объекта-буфера | //+------------------------------------------------------------------+ bool CBuffersCollection::SetDataBufferStdInd(const ENUM_INDICATOR ind_type,const int id,const int series_index,const datetime series_time,const char color_index=WRONG_VALUE) { //--- Получаем список объектов-буферов по типу и идентификатору CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) { ::Print(DFUN,CMessage::Text(MSG_LIB_TEXT_BUFFER_TEXT_NO_BUFFER_OBJ)); return false; } //--- Получаем список рисуемых буферов с идентификатором CArrayObj *list_data=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_DATA,EQUAL); list_data=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Получаем список расчётных буферов с идентификатором CArrayObj *list_calc=CSelect::ByBufferProperty(list,BUFFER_PROP_TYPE,BUFFER_TYPE_CALCULATE,EQUAL); list_calc=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_TYPE,ind_type,EQUAL); //--- Если любой из списков пустой - уходим if(list_data.Total()==0 || list_calc.Total()==0) return false; //--- Объявляем необходимые объекты и переменные CBuffer *buffer_data0=NULL; CBuffer *buffer_data1=NULL; CBuffer *buffer_data2=NULL; CBuffer *buffer_calc0=NULL; CBuffer *buffer_calc1=NULL; CBuffer *buffer_calc2=NULL; int index_period=0; int series_index_start=0; int num_bars=1,index=0; uchar clr=color_index; long vol0=0,vol1=0; datetime time_period=0; double value00=EMPTY_VALUE, value01=EMPTY_VALUE; double value10=EMPTY_VALUE, value11=EMPTY_VALUE; double value20=EMPTY_VALUE, value21=EMPTY_VALUE; //--- В зависимости от типа стандартного индикатора switch((int)ind_type) { //--- Однобуферные стандартные индикаторы case IND_AC : case IND_AD : case IND_AMA : case IND_AO : case IND_ATR : case IND_BEARS : case IND_BULLS : case IND_BWMFI : case IND_CCI : case IND_CHAIKIN : case IND_DEMA : case IND_DEMARKER : case IND_FORCE : case IND_FRAMA : case IND_MA : case IND_MFI : case IND_MOMENTUM : case IND_OBV : case IND_OSMA : case IND_RSI : case IND_SAR : case IND_STDDEV : case IND_TEMA : case IND_TRIX : case IND_VIDYA : case IND_VOLUMES : case IND_WPR : //--- Получаем объекты рисуемого и расчётного буферов buffer_data0=list_data.At(0); buffer_calc0=list_calc.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; //--- Находим индекс бара на периоде, соответствующий времени начала текущего бара index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Получаем значение по этому индексу из буфера индикатора value00=buffer_calc0.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Получаем время бара, в который попадает бар с индексом index_period на периоде и символе расчётного буфера time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Получаем соответствующий времени бар текущего графика series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Рассчитываем количество баров на текущем графике, которые нужно заполнить данными расчётного буфера num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Берём значения для расчёта цвета value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); if(ind_type!=IND_BWMFI) clr=(color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); else { vol0=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); vol1=::iVolume(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period+1); clr= ( value00>value01 && vol0>vol1 ? 0 : value00<value01 && vol0<vol1 ? 1 : value00>value01 && vol0<vol1 ? 2 : value00<value01 && vol0>vol1 ? 3 : 4 ); } buffer_data0.SetBufferColorIndex(index,clr); } return true; //--- Многобуферные стандартные индикаторы case IND_ADX : case IND_ADXW : //--- Получаем объекты рисуемого и расчётного буферов list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_PLUS,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_DI_MINUS,EQUAL); buffer_calc2=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; //--- Находим индекс бара на периоде, соответствующий времени начала текущего бара index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Получаем значение по этому индексу из буфера индикатора value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); value20=buffer_calc2.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Получаем время бара, в который попадает бар с индексом index_period на периоде и символе расчётного буфера time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Получаем соответствующий времени бар текущего графика series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Рассчитываем количество баров на текущем графике, которые нужно заполнить данными расчётного буфера num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Берём значения для расчёта цвета value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(2,series_index_start+num_bars)); //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data2.SetBufferValue(2,index,value20); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); } return true; case IND_BANDS : //--- Получаем объекты рисуемого и расчётного буферов list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer_data2=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_calc1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MIDDLE,EQUAL); buffer_calc2=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; if(buffer_calc2==NULL || buffer_data2==NULL || buffer_calc2.GetDataTotal(0)==0) return false; //--- Находим индекс бара на периоде, соответствующий времени начала текущего бара index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Получаем значение по этому индексу из буфера индикатора value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); value20=buffer_calc2.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Получаем время бара, в который попадает бар с индексом index_period на периоде и символе расчётного буфера time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Получаем соответствующий времени бар текущего графика series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Рассчитываем количество баров на текущем графике, которые нужно заполнить данными расчётного буфера num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Берём значения для расчёта цвета value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); value21=(series_index_start+num_bars>buffer_data2.GetDataTotal()-1 ? value20 : buffer_data2.GetDataBufferValue(2,series_index_start+num_bars)); //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data2.SetBufferValue(2,index,value20); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); buffer_data2.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value20>value21 ? 0 : value20<value21 ? 1 : 2) : color_index); } return true; case IND_ENVELOPES : case IND_FRACTALS : //--- Получаем объекты рисуемого и расчётного буферов list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_UPPER,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_LOWER,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; //--- Находим индекс бара на периоде, соответствующий времени начала текущего бара index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Получаем значение по этому индексу из буфера индикатора value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Получаем время бара, в который попадает бар с индексом index_period на периоде и символе расчётного буфера time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Получаем соответствующий времени бар текущего графика series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Рассчитываем количество баров на текущем графике, которые нужно заполнить данными расчётного буфера num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Берём значения для расчёта цвета value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); } return true; case IND_MACD : case IND_RVI : case IND_STOCHASTIC : //--- Получаем объекты рисуемого и расчётного буферов list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_data0=list.At(0); list=CSelect::ByBufferProperty(list_data,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer_data1=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_MAIN,EQUAL); buffer_calc0=list.At(0); list=CSelect::ByBufferProperty(list_calc,BUFFER_PROP_IND_LINE_MODE,INDICATOR_LINE_MODE_SIGNAL,EQUAL); buffer_calc1=list.At(0); if(buffer_calc0==NULL || buffer_data0==NULL || buffer_calc0.GetDataTotal(0)==0) return false; if(buffer_calc1==NULL || buffer_data1==NULL || buffer_calc1.GetDataTotal(0)==0) return false; //--- Находим индекс бара на периоде, соответствующий времени начала текущего бара index_period=::iBarShift(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),series_time,true); if(index_period==WRONG_VALUE || index_period>buffer_calc0.GetDataTotal()-1) return false; //--- Получаем значение по этому индексу из буфера индикатора value00=buffer_calc0.GetDataBufferValue(0,index_period); value10=buffer_calc1.GetDataBufferValue(0,index_period); if(buffer_calc0.Symbol()==::Symbol() && buffer_calc0.Timeframe()==::Period()) { series_index_start=series_index; num_bars=1; } else { //--- Получаем время бара, в который попадает бар с индексом index_period на периоде и символе расчётного буфера time_period=::iTime(buffer_calc0.Symbol(),buffer_calc0.Timeframe(),index_period); if(time_period==0) return false; //--- Получаем соответствующий времени бар текущего графика series_index_start=::iBarShift(::Symbol(),::Period(),time_period,true); if(series_index_start==WRONG_VALUE) return false; //--- Рассчитываем количество баров на текущем графике, которые нужно заполнить данными расчётного буфера num_bars=::PeriodSeconds(buffer_calc0.Timeframe())/::PeriodSeconds(PERIOD_CURRENT); if(num_bars==0) num_bars=1; } //--- Берём значения для расчёта цвета value01=(series_index_start+num_bars>buffer_data0.GetDataTotal()-1 ? value00 : buffer_data0.GetDataBufferValue(0,series_index_start+num_bars)); value11=(series_index_start+num_bars>buffer_data1.GetDataTotal()-1 ? value10 : buffer_data1.GetDataBufferValue(1,series_index_start+num_bars)); //--- В цикле по количеству баров в num_bars заполняем рисуемый буфер значением из расчётного буфера, взятых по индексу index_period //--- и устанавливаем цвет рисуемого буфера в зависимости от соотношения значений value00 и value01 for(int i=0;i<num_bars;i++) { index=series_index_start-i; buffer_data0.SetBufferValue(0,index,value00); buffer_data1.SetBufferValue(1,index,value10); buffer_data0.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value00>value01 ? 0 : value00<value01 ? 1 : 2) : color_index); buffer_data1.SetBufferColorIndex(index,color_index==WRONG_VALUE ? uchar(value10>value11 ? 0 : value10<value11 ? 1 : 2) : color_index); } return true; case IND_ALLIGATOR: break; case IND_GATOR : break; case IND_ICHIMOKU : break; default: break; } return false; } //+------------------------------------------------------------------+
Данный метод построен идентично двум вышерассмотренным методам. Здесь точно так же сгруппированы однотипные обработки разных стандартных индикаторов воедино. И этот метод точно так же будет оптимизирован после задания значений константам однотипных линий индикаторов.
Метод, возвращающий описание буфера стандартного индикатора по его типу и идентификатору ранее у нас был реализован в файле главного класса библиотеки CEngine. Перенесём его реализацию в класс-коллекцию буферов. Для этого объявим в самом конце тела класса этот и ещё один метод, возвращающий короткое наименование индикатора:
//--- Возвращает описание буфера стандартного индикатора по типу и идентификатору string GetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode=INDICATOR_LINE_MODE_MAIN); //--- Возвращает короткое наименование стандартного индикатора по типу и идентификатору string GetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id); //--- Конструктор CBuffersCollection(); //--- Получение указателей на коллекцию таймсерий (метод вызывается в методе CollectionOnInit() объекта CEngine) void OnInit(CTimeSeriesCollection *timeseries) { this.m_timeseries=timeseries; } }; //+------------------------------------------------------------------+
За пределами тела класса напишем их реализацию:
//+------------------------------------------------------------------+ //| Возвращает описание буфера стандартного индикатора | //| по типу и идентификатору | //+------------------------------------------------------------------+ string CBuffersCollection::GetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode=INDICATOR_LINE_MODE_MAIN) { CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); list=CSelect::ByBufferProperty(list,BUFFER_PROP_IND_LINE_MODE,line_mode,EQUAL); if(list==NULL || list.Total()==0) return ""; CBuffer *buff=list.At(0); if(buff==NULL) return ""; return buff.Label(); } //+------------------------------------------------------------------+ //| Возвращает короткое наименование стандартного индикатора | //| по типу и идентификатору | //+------------------------------------------------------------------+ string CBuffersCollection::GetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id) { CArrayObj *list=this.GetListBufferByTypeID(ind_type,id); if(list==NULL || list.Total()==0) return ""; CBuffer *buff=list.At(0); if(buff==NULL) return ""; return buff.IndicatorShortName(); } //+------------------------------------------------------------------+
Логику подобных методов мы много раз рассматривали и, думаю, здесь всё понятно. В любом случае, если что-то не понятно, то всегда можно задать уточняющие вопросы в обсуждении статьи, и я на них отвечу.
Теперь нам нужно прописать доступ ко всем созданным методам в основном классе библиотеки CEngine. Но так, как мы уже прописали все методы для создания объектов стандартных индикаторов ещё в прошлой статье, то сегодня нам нужно в публичной секции класса лишь поменять вызов методов, возвращающих описание линий индикатора и его короткое наименование:
//--- Подготавливает данные расчётного буфера всех созданных стандартных индикаторов bool BufferPreparingDataAllBuffersStdInd(void) { return this.m_buffers.PreparingDataAllBuffersStdInd(); } //--- Возвращает описание буфера стандартного индикатора по типу и идентификатору string BufferGetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id,const ENUM_INDICATOR_LINE_MODE line_mode) { return this.m_buffers.GetLabelByTypeID(ind_type,id,line_mode); } //--- Возвращает короткое наименование стандартного индикатора по типу и идентификатору string BufferGetIndicatorShortNameByTypeID(const ENUM_INDICATOR ind_type,const int id) { return this.m_buffers.GetIndicatorShortNameByTypeID(ind_type,id); } //--- Выводит краткое описание всех индикаторных буферов коллекции буферов
Методы возвращают результат вызова одноимённых методов класса-коллекции буферов.
Осталось удалить реализацию метода, который мы перенесли в класс-коллекцию буферов из класса CEngine:
//+------------------------------------------------------------------+
//| Возвращает описание буфера стандартного индикатора |
//| по типу и идентификатору |
//+------------------------------------------------------------------+
string CEngine::BufferGetLabelByTypeID(const ENUM_INDICATOR ind_type,const int id)
{
CArrayObj *list=m_buffers.GetListBufferByTypeID(ind_type,id);
if(list==NULL || list.Total()==0)
return "";
CBuffer *buff=list.At(0);
if(buff==NULL)
return "";
return buff.Label();
}
//+------------------------------------------------------------------+
На сегодня это все изменения, которые необходимо было сделать для создания объектов многобуферных стандартных индикаторов.
Тестирование
Для тестирования возьмём индикатор из прошлой статьи, и на его основе создадим два новых — один будет выводить мультисимвольные мультипериодные стандартные индикаторы в подокне, а второй — в главном окне графика символа.
Логика индикаторов по сравнению с уже рассмотренным тестовым индикатором из прошлой статьи, никак не поменялась. Мы лишь добавим вызов методов создания нужных индикаторов в обработчике OnInit().
Сохраним индикатор из прошлой статьи в новой папке \MQL5\Indicators\TestDoEasy\Part49\ под именем TestDoEasyPart49_1.mq5.
Этот индикатор будет создавать и отображать стандартные индикаторы в подокне текущего графика символа. Его обработчик OnInit() будет таким:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Запишем в переменную InpUsedTFs наименование выбранного в настройках рабочего таймфрейма InpUsedTFs=TimeframeDescription(InpPeriod); //--- Инициализация библиотеки DoEasy OnInitDoEasy(); //--- Установка глобальных переменных индикатора prefix=engine.Name()+"_"; //--- Рассчитываем количество баров текущего периода, умещающихся в максимальном используемом периоде //--- Используем полученное значение если оно больше 2, иначе используем 2 int num_bars=NumberBarsInTimeframe(InpPeriod); min_bars=(num_bars>2 ? num_bars : 2); //--- Проверка и удаление неудалённых графических объектов индикатора if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Создание панели кнопок //--- Проверка воспроизведения стандартного звука по макроподстановкам engine.PlaySoundByDescription(SND_OK); //--- Ждём 600 милисекунд engine.Pause(600); engine.PlaySoundByDescription(SND_NEWS); //--- indicator buffers mapping //--- Создаём все необходимые объекты-буферы для построения выбранного стандартного индикатора bool success=false; switch(InpIndType) { //--- Однобуферные стандартные индикаторы в подокне case IND_AC : success=engine.BufferCreateAC(InpUsedSymbols,InpPeriod,1); break; case IND_AD : success=engine.BufferCreateAD(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; case IND_AO : success=engine.BufferCreateAO(InpUsedSymbols,InpPeriod,1); break; case IND_ATR : success=engine.BufferCreateATR(InpUsedSymbols,InpPeriod,14,1); break; case IND_BEARS : success=engine.BufferCreateBearsPower(InpUsedSymbols,InpPeriod,13,1); break; case IND_BULLS : success=engine.BufferCreateBullsPower(InpUsedSymbols,InpPeriod,13,1); break; case IND_BWMFI : success=engine.BufferCreateBWMFI(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; case IND_CHAIKIN : success=engine.BufferCreateChaikin(InpUsedSymbols,InpPeriod,3,10,MODE_EMA,VOLUME_TICK,1); break; case IND_CCI : success=engine.BufferCreateCCI(InpUsedSymbols,InpPeriod,14,PRICE_TYPICAL,1); break; case IND_DEMARKER : success=engine.BufferCreateDeMarker(InpUsedSymbols,InpPeriod,14,1); break; case IND_FORCE : success=engine.BufferCreateForce(InpUsedSymbols,InpPeriod,13,MODE_SMA,VOLUME_TICK,1); break; case IND_MOMENTUM : success=engine.BufferCreateMomentum(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_MFI : success=engine.BufferCreateMFI(InpUsedSymbols,InpPeriod,14,VOLUME_TICK,1); break; case IND_OSMA : success=engine.BufferCreateOsMA(InpUsedSymbols,InpPeriod,12,26,9,PRICE_CLOSE,1); break; case IND_OBV : success=engine.BufferCreateOBV(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; case IND_RSI : success=engine.BufferCreateRSI(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_STDDEV : success=engine.BufferCreateStdDev(InpUsedSymbols,InpPeriod,20,0,MODE_SMA,PRICE_CLOSE,1); break; case IND_TRIX : success=engine.BufferCreateTriX(InpUsedSymbols,InpPeriod,14,PRICE_CLOSE,1); break; case IND_WPR : success=engine.BufferCreateWPR(InpUsedSymbols,InpPeriod,14,1); break; case IND_VOLUMES : success=engine.BufferCreateVolumes(InpUsedSymbols,InpPeriod,VOLUME_TICK,1); break; //--- Многобуферные стандартные индикаторы в подокне case IND_ADX : success=engine.BufferCreateADX(InpUsedSymbols,InpPeriod,14,1); break; case IND_ADXW : success=engine.BufferCreateADXWilder(InpUsedSymbols,InpPeriod,14,1); break; case IND_MACD : success=engine.BufferCreateMACD(InpUsedSymbols,InpPeriod,12,26,9,PRICE_CLOSE,1); break; case IND_RVI : success=engine.BufferCreateRVI(InpUsedSymbols,InpPeriod,10,1); break; case IND_STOCHASTIC : success=engine.BufferCreateStochastic(InpUsedSymbols,InpPeriod,5,3,3,MODE_SMA,STO_LOWHIGH,1); break; default: break; } if(!success) { Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created")); return INIT_FAILED; } //--- Проверяем количество буферов, указанных в блоке properties if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal()); //--- Создаём массив цветов и зададём всем буферам в коллекции значения цвета не по умолчанию //--- (закомментировано так как в методах создания стандартных индикаторов по умолчанию уже заданы цвета) //--- (всегда можно задать нужные цвета либо для всех индикаторов как здесь, либо для каждого индивидуально) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- Выведем краткие описания созданных индикаторных буферов engine.BuffersPrintShort(); //--- Установим уровни где они требуются и определим разрядность данных int digits=(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS); switch(InpIndType) { case IND_AD : case IND_CHAIKIN : case IND_OBV : case IND_VOLUMES : digits=0; break; case IND_AO : case IND_BEARS : case IND_BULLS : case IND_FORCE : case IND_STDDEV : case IND_AMA : case IND_DEMA : case IND_FRAMA : case IND_MA : case IND_TEMA : case IND_VIDYA : case IND_BANDS : case IND_ENVELOPES : case IND_MACD : digits+=1; break; case IND_AC : case IND_OSMA : digits+=2; break; case IND_MOMENTUM : digits=2; break; case IND_CCI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,100); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-100); digits=2; break; case IND_DEMARKER : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.7); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,0.3); digits=3; break; case IND_MFI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); break; case IND_RSI : IndicatorSetInteger(INDICATOR_LEVELS,3); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,70); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,50); IndicatorSetDouble(INDICATOR_LEVELVALUE,2,30); digits=2; break; case IND_STOCHASTIC : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); digits=2; break; case IND_WPR : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20); digits=2; break; case IND_ATR : break; case IND_SAR : break; case IND_TRIX : break; default: IndicatorSetInteger(INDICATOR_LEVELS,0); break; } //--- Установим короткое имя индикатора и разрядность данных string label=engine.BufferGetIndicatorShortNameByTypeID(InpIndType,1); IndicatorSetString(INDICATOR_SHORTNAME,label); IndicatorSetInteger(INDICATOR_DIGITS,digits); //--- CArrayObj *list=engine.GetListBuffers(); int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; buff.Print(); } //return INIT_FAILED; //--- //--- Успешно return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Полный код индикатора можно посмотреть в прикреплённых к статье файлах.
Скомпилируем индикатор и запустим его на графике EURUSD, H1, предварительно выставив в настройках индикатора используемый символ EURUSD, а используемый таймфрейм 4 Hours. Таким образом, мы будем отображать выбираемые в настройках стандартные индикаторы на графике H1 с графика H4:
Теперь создадим индикатор, отображающий стандартные индикаторы в основном окне графика символа.
Сохраним индикатор из прошлой статьи в новой папке \MQL5\Indicators\TestDoEasy\Part49\ под именем TestDoEasyPart49_2.mq5.
Обработчик OnInit() этого индикатора будет таким:
//+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { //--- Запишем в переменную InpUsedTFs наименование выбранного в настройках рабочего таймфрейма InpUsedTFs=TimeframeDescription(InpPeriod); //--- Инициализация библиотеки DoEasy OnInitDoEasy(); //--- Установка глобальных переменных индикатора prefix=engine.Name()+"_"; //--- Рассчитываем количество баров текущего периода, умещающихся в максимальном используемом периоде //--- Используем полученное значение если оно больше 2, иначе используем 2 int num_bars=NumberBarsInTimeframe(InpPeriod); min_bars=(num_bars>2 ? num_bars : 2); //--- Проверка и удаление неудалённых графических объектов индикатора if(IsPresentObectByPrefix(prefix)) ObjectsDeleteAll(0,prefix); //--- Создание панели кнопок //--- Проверка воспроизведения стандартного звука по макроподстановкам engine.PlaySoundByDescription(SND_OK); //--- Ждём 600 милисекунд engine.Pause(600); engine.PlaySoundByDescription(SND_NEWS); //--- indicator buffers mapping //--- Создаём все необходимые объекты-буферы для построения выбранного стандартного индикатора bool success=false; switch(InpIndType) { //--- Однобуферные стандартные индикаторы в основном окне case IND_AMA : success=engine.BufferCreateAMA(InpUsedSymbols,InpPeriod,9,2,30,0,PRICE_CLOSE,1); break; case IND_DEMA : success=engine.BufferCreateDEMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_FRAMA : success=engine.BufferCreateFrAMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_MA : success=engine.BufferCreateMA(InpUsedSymbols,InpPeriod,10,0,MODE_SMA,PRICE_CLOSE,1); break; case IND_SAR : success=engine.BufferCreateSAR(InpUsedSymbols,InpPeriod,0.02,0.2,1); break; case IND_TEMA : success=engine.BufferCreateTEMA(InpUsedSymbols,InpPeriod,14,0,PRICE_CLOSE,1); break; case IND_VIDYA : success=engine.BufferCreateVIDYA(InpUsedSymbols,InpPeriod,9,12,0,PRICE_CLOSE,1); break; //--- Многобуферные стандартные индикаторы в основном окне case IND_BANDS : success=engine.BufferCreateBands(InpUsedSymbols,InpPeriod,20,0,2.0,PRICE_CLOSE,1); break; case IND_ENVELOPES : success=engine.BufferCreateEnvelopes(InpUsedSymbols,InpPeriod,14,0,MODE_SMA,PRICE_CLOSE,0.1,1); break; case IND_FRACTALS : success=engine.BufferCreateFractals(InpUsedSymbols,InpPeriod,1); break; default: break; } if(!success) { Print(TextByLanguage("Ошибка. Индикатор не создан","Error. Indicator not created")); return INIT_FAILED; } //--- Проверяем количество буферов, указанных в блоке properties if(engine.BuffersPropertyPlotsTotal()!=indicator_plots) Alert(TextByLanguage("Внимание! Значение \"indicator_plots\" должно быть ","Attention! Value of \"indicator_plots\" should be "),engine.BuffersPropertyPlotsTotal()); if(engine.BuffersPropertyBuffersTotal()!=indicator_buffers) Alert(TextByLanguage("Внимание! Значение \"indicator_buffers\" должно быть ","Attention! Value of \"indicator_buffers\" should be "),engine.BuffersPropertyBuffersTotal()); //--- Создаём массив цветов и зададём всем буферам в коллекции значения цвета не по умолчанию //--- (закомментировано так как в методах создания стандартных индикаторов по умолчанию уже заданы цвета) //--- (всегда можно задать нужные цвета либо для всех индикаторов как здесь, либо для каждого индивидуально) //color array_colors[]={clrGreen,clrRed,clrGray}; //engine.BuffersSetColors(array_colors); //--- Выведем краткие описания созданных индикаторных буферов engine.BuffersPrintShort(); //--- Установим уровни где они требуются и определим разрядность данных int digits=(int)SymbolInfoInteger(InpUsedSymbols,SYMBOL_DIGITS); switch(InpIndType) { case IND_AD : case IND_CHAIKIN : case IND_OBV : case IND_VOLUMES : digits=0; break; case IND_AO : case IND_BEARS : case IND_BULLS : case IND_FORCE : case IND_STDDEV : case IND_AMA : case IND_DEMA : case IND_FRAMA : case IND_MA : case IND_TEMA : case IND_VIDYA : case IND_BANDS : case IND_ENVELOPES : case IND_MACD : digits+=1; break; case IND_AC : case IND_OSMA : digits+=2; break; case IND_MOMENTUM : digits=2; break; case IND_CCI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,100); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-100); digits=2; break; case IND_DEMARKER : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,0.7); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,0.3); digits=3; break; case IND_MFI : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); break; case IND_RSI : IndicatorSetInteger(INDICATOR_LEVELS,3); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,70); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,50); IndicatorSetDouble(INDICATOR_LEVELVALUE,2,30); digits=2; break; case IND_STOCHASTIC : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,20); digits=2; break; case IND_WPR : IndicatorSetInteger(INDICATOR_LEVELS,2); IndicatorSetDouble(INDICATOR_LEVELVALUE,0,-80); IndicatorSetDouble(INDICATOR_LEVELVALUE,1,-20); digits=2; break; case IND_ATR : break; case IND_SAR : break; case IND_TRIX : break; default: IndicatorSetInteger(INDICATOR_LEVELS,0); break; } //--- Установим короткое имя индикатора и разрядность данных string label=engine.BufferGetIndicatorShortNameByTypeID(InpIndType,1); IndicatorSetString(INDICATOR_SHORTNAME,label); IndicatorSetInteger(INDICATOR_DIGITS,digits); //--- CArrayObj *list=engine.GetListBuffers(); int total=list.Total(); for(int i=0;i<total;i++) { CBuffer *buff=list.At(i); if(buff==NULL) continue; buff.Print(); } //return INIT_FAILED; //--- //--- Успешно return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+
Полный код индикатора можно посмотреть в прикреплённых к статье файлах.
Скомпилируем индикатор и запустим его на графике EURUSD, H1, предварительно выставив в настройках индикатора используемый символ EURUSD, а используемый таймфрейм 4 Hours. Таким образом, мы будем отображать выбираемые в настройках стандартные индикаторы на графике H1 с графика H4:
Что дальше
В следующей статье продолжим дорабатывать классы библиотеки для создания мультисимвольных мультипериодных стандартных индикаторов, оптимизируем методы и избавимся от лишнего кода в основной программе-индикаторе, создаваемом на основе библиотеки.
Ниже прикреплены все файлы текущей версии библиотеки и файлы тестовых индикаторов. Их можно скачать и протестировать всё самостоятельно.
При возникновении вопросов, замечаний и пожеланий, вы можете озвучить их в комментариях к статье.
Хочу обратить внимание на то, что в данной статье мы сделали тестовый индикатор на 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): Классы объектов индикаторных буферов
Работа с таймсериями в библиотеке DoEasy (Часть 44): Класс-коллекция объектов индикаторных буферов
Работа с таймсериями в библиотеке DoEasy (Часть 45): Мультипериодные индикаторные буферы
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования