Скачать MetaTrader 5

Как написать индикатор на основе другого индикатора

19 июля 2010, 16:59
Dmitry Fedoseev
2
6 033

Введение

На MQL5 можно не только создать новый пользовательский индикатор с чистого листа, как показано в статье "Как написать индикатор в MQL5", но и написать индикатор на базе другого, уже существующего индикатора, встроенного в терминал или пользовательского. Есть два способа: первый - доработать индикатор, добавить к нему новые вычисления и графические построения (этот вариант применим только к пользовательским индикаторам с открытым исходным кодом), второй - использовать встроенный в терминал индикатор или существующий пользовательский индикатор при помощи функций iCustom() или IndicatorCreate().

Первый способ. Добавление графического построения

Подробно рассмотрим этот способ создания индикатора на примере выполнения доработки индикатора True_Strength_Index_ver3 из статьи "Индикатор от индикатора в MQL5". Добавим индикатору сигнальную линию с возможностью выбора типа сглаживания и периода сглаживания. Весь процесс будет состоять из 8-ми шагов.

1. Создание копии файла

Откроем индикатор True_Strength_Index_ver3 в редакторе MetaEditor и сохраним файл с новым именем, например, TSIs. Новый файл должен быть сохранен в каталоге MQL5/Indicators корневого каталога термина.

2. Изменение свойств индикатора

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

#property indicator_buffers 8 
#property indicator_plots 2

3. Определение свойств отображения нового буфера

Задаем свойства отображения нового буфера. Воспользуемся уже готовым кодом из этого же индикатора. Копируем мышкой весь код определяющий свойства отображения первой линии индикатора.

//---- plot TSI
#property indicator_label1 "TSI"
#property indicator_type1 DRAW_LINE
#property indicator_color1 Blue
#property indicator_style1 STYLE_SOLID
#property indicator_width1 1

Вставляем его ниже свойств первой линии, редактируем его.

//---- plot TSI Signal
#property indicator_label2 "TSISignal" // Название линии, отображаемое во всплывающей подсказе при наведении на линии указателя мыши.
#property indicator_type2 DRAW_LINE    // Тип буфера 
#property indicator_color2 Red         // Цвет линии
#property indicator_style2 STYLE_SOLID // Стиль
#property indicator_width2 1           // Толщина линии

В первую очередь меняем номера свойств с 1 на 2, поскольку это свойства для второго буфера. Изменяем свойство indicator_label2 на TSISignal - это название линии, видимое во всплывающей подсказке при наведении на линию указателя мыши и отображаемое в окне данных. Свойство indicator_type2 оставляем без изменений - буфер должен отображаться в виде линии. Свойство indicator_color2 меняем на Red - новая линия будет красного цвета. Свойство indicator_style2 так и оставляем - сигнальная линия будет тоже сплошной, как и основная. Свойство indicator_width2 тоже оставляем без изменений - новая линия будет толщиной 1 пиксель.

4. Объявление внешних переменных

Ниже свойств линий в коде расположены внешние переменные индикатора, изменение которых возможно через окно свойств индикатора (строки, начинающиеся со слова "input"). У сигнальной линии тоже должны быть свои параметры - период и метод сглаживания.

Объявляем внешнюю переменную типа int с именем "sp" (smooth period - период сглаживания), значением 5 и переменную типа ENUM_MA_METHOD с именем "sm" (smooth method - метод сглаживания), значением MODE_EMA (по умолчанию у сигнальной линии будет период сглаживания 5 и экспоненциальный тип сглаживания). Теперь раздел кода с внешними переменными будет выглядеть так:

input int r=25;
input int s=13;
input int sp=5;
input ENUM_MA_METHOD sm=MODE_EMA;

5. Объявление массива для нового буфера

Объявим массив, который будет использоваться индикаторным буфером. Ищем в коде функцию OnInit() и смотрим, где в ней находятся вызовы функции SetIndexBuffer(), чтобы сориентироваться в именах уже существующих в индикаторе массивов индикаторных буферов. В индикаторе True_Strength_Index_ver3 это массивы TSIBuffer, MTMBuffer, AbsMTMBuffer, EMA_MTMBuffer, EMA2_MTMBuffer, EMA_AbsMTMBuffer, EMA2_AbsMTMBuffer.

Для первого массива с именем TSIBuffer функция SetIndexBuffer() вызывается с параметром INDICATOR_DATA, что означает, что этот буфер отображается на графике, для остальных массивов - с параметром INDICATOR_CALCULATIONS. Это означает, что эти массивы являются вспомогательными и используются для промежуточных вычислений.

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

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

//--- indicator buffers
double TSIBuffer[];
double TSISigBuffer[]; // Массив для нового буфера сигнальной линии
double MTMBuffer[];
double AbsMTMBuffer[];
double EMA_MTMBuffer[];
double EMA2_MTMBuffer[];
double EMA_AbsMTMBuffer[];
double EMA2_AbsMTMBuffer[]; 

6. Связывание массива с буфером

Теперь довольно ответственный момент, требующий повышенного внимания и аккуратности - связывание массива с индикаторным буфером. Связывание выполняется функцией SetIndexBuffer(). Первым параметром при вызове функции указывается индекс буфера, вторым - имя массива, третьим - идентификатор, указывающий назначение буфера. В соответствии с индексом (первый параметр) буферы располагаются во вкладке "Цвета" окна свойств индикатора и в таком же порядке отрисовываются на графике, сначала буфер 0, поверх него буфер 1 и т.д.

Не обязательно, чтобы функция SetIndexBuffer() вызывалась последовательно для буферов с индексом 0, 1, 2..., но все же, сохраним порядок вызовов функции SetIndexBuffer(). Для массива TSISigBuffer выполним вызов функции после вызова функции для массива TSIBuffer. Буфер главной линии (массив TSIBuffer) имеет индекс 0, значит следующий буфер (массив TSISigBuffer) должен иметь индекс 1.

Третьим параметром при вызове функции SetIndexBuffer() для массива TSISigBuffer будет константа INDICATOR_DATA (буфер отображается на графике).

SetIndexBuffer(0,TSIBuffer,INDICATOR_DATA);
SetIndexBuffer(1,TSISigBuffer,INDICATOR_DATA);

Переиндексируем остальные буферы, чтобы вызов функции SetIndexBuffer() выполнялся с последовательно увеличивающимся значением первого параметра.

SetIndexBuffer(0,TSIBuffer,INDICATOR_DATA);
SetIndexBuffer(1,TSISigBuffer,INDICATOR_DATA);
SetIndexBuffer(2,MTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(3,AbsMTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(4,EMA_MTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(5,EMA2_MTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(6,EMA_AbsMTMBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(7,EMA2_AbsMTMBuffer,INDICATOR_CALCULATIONS);

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

Теперь имеем восемь буферов, два из них отображаются на графике. Убедимся, что эти же значения указаны в свойствах индикатора indicator_buffers и indicator_plots (шаг 2.).

7. Вычисление значений сигнальной линии

Сигнальная линия представляет собой скользящую среднюю от данных основной линии индикатора. Каких-либо вычислений для нее делать не придется, в комплекте с терминалом поставляется библиотека для расчета скользящих средних по массиву данных (файл MovingAverages.mqh), кстати, она уже включена в код индикатора (строка 14):

#include <MovingAverages.mqh>

Так что, остается только воспользоваться ее возможностями.

В функции OnCalculate() ищем место, где заканчиваются вычисления основной линии индикатора (массив TSIBuffer). Воспользуемся функцией поиска, выделим название массива TSIBuffer, например , в части кода, где он объявлен (рис. 1). Затем выполним команду "Главное Меню" - "Правка" - "Поиск и замена" - "Поиск", или нажмем клавиши Ctrl+F


Рис. 1. Выделенное имя массива.

В открывшемся окне "Поиск" в поле "Найти" уже будет введено слово "TSIBuffer", в разделе "Направление" выбираем "Вверх". Теперь при открытом окне "Поиск" устанавливаем курсор сразу после функции OnCalculate(), нажимаем один раз кнопку "Найти далее" и сразу находим место, где заканчиваются вычисления массива TSIBuffer. Вычисления выполняются в цикле for. Сразу после этого цикла будем добавлять код вычисления сигнальной линии (рис. 2).



Рис 2. Найденное последнее место вычисления значения буфера TSIBuffer (красная стрелка). Цикл, в котором выполняются вычисления, обозначен красной рамкой.

В библиотеку MovingAverages.mqh входят функции для расчета всех четырех основных типов скользящих средних:

  • для простой - SimpleMAOnBuffer(),
  • для экспоненциальной - ExponentialMAOnBuffer(),
  • для линейно взвешенной - LinearWeightedMAOnBuffer(),
  • для сглаженной - SmoothedMAOnBuffer().

Все эти функции имеют одинаковый набор параметров: 

const int rates_total, const int prev_calculated, const int begin, const int period, const double& price[],double& buffer[]

Параметр price[] функции определяет массив с исходными данными, по которым выполняется расчет скользящей средней. Параметр buffer - массив, в котором будут находиться значения скользящей средней. Параметры rates_total и prev_calculated аналогичны параметрам rates_total и prev_calculated функции onCalculate() - определяют размер массива price[] и количество уже обработанных элементов массива. Параметр begin - индекс элемента массива данных, с которого начинаются значимые данные. 

Учитывая особенности алгоритмов расчета скользящих средних из библиотеки MovingAverages.mqh (эти особенности не касаются темы данной статьи), к заданию значения параметра begin нужно подойти особо внимательно.

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

Для определения допустимого значения begin обратим внимание на параметры цикла for, в котором вычисляются значения массива TSIBuffer - цикл начинается со значения переменной start. Необходимо узнать значение переменной start, которое было при первом расчете индикатора (когда значение prev_calculated равно 0). Значение переменной start вычисляется непосредственно перед циклом при prev_calculated=0, вычисление выполняется по следующей формуле:

start=begin+r+s-1;

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

После цикла вычисления массива TSIBuffer объявим переменную begin2 и присвоим ей значение begin+r+s-1.

int begin2=begin+r+s-1; 

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

switch(sm)
  {
   case MODE_EMA:
      ExponentialMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer);
      break;
   case MODE_LWMA:
      LinearWeightedMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer);
      break;
   case MODE_SMA:
      SimpleMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer);
      break;
   case MODE_SMMA:
      SmoothedMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer);
      break;
  }

После этого уже можно увидеть индикатор с сигнальной линией, нажимаем кнопку "Компилировать", открываем терминал, присоединяем индикатор на график (рис 3).



Рис 3. Индикатор TSIs, синяя - главная линия, красная - новая, сигнальная.

8. Обрезание начала отрисовки буфер

Если прокрутить график с индикатором к левому краю, видно, что индикатор рисует линии на участке, для которого не вычислялись значения, получается не очень красивый вид (рис 4).



Рис. 4. Отрисовка индикатора на участке, для которого не выполнялось вычислений. 

При помощи функции PlotIndexSetInteger() вызываемой с идентификатором PLOT_DRAW_BEGIN определим количество начальных баров без отрисовки буфера, вызов функции должен выполняться из функции OnInit() индикатора. Добавим вызов функции в самый конец функции OnInit(), но, разумеется, перед вызовом return(0).

PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,r+s+sp);

Нажимаем кнопку "Компилировать" еще раз, и получаемое правильное начало отрисовки индикатора (рис. 5).  



Рис. 5. Правильное начало отрисовки индикатора. 

Второй способ. Создание нового индикатора на базе существующего


Этот способ рассмотрим на примере создания индикатора схождения и расхождения главной и сигнальной линий индикатора TSIs. Индикатор будет отображаться в виде гистограммы и будет раскрашен двумя цветами, подобно индикаторам AO, AC - если значение индикатора увеличивается, гистограмм будет окрашена зеленым цветом, если уменьшается, то красным. Как уже упоминалось во введении, для обращения к другому индикатору можно использовать функцию iCustom() или функцию IndicatorCreate(). Сначала рассмотрим создание индикатора с использованием функции iCustom().

Создание индикатора с использованием функции iCustom()


1. Создание нового индикатора

Создадим новый индикатор. Для создания нового индикатора в редакторе MetaEditor необходимо выполнить команду Главное Меню - Файл - Создать - Пользовательский индикатор или нажать клавиши Ctrl+N. В открывшемся окне в поле "Имя" вводим имя нового индикатора - TSIsCDiCust, нажимаем кнопку "Добавить". Добавляем внешний параметр, неважно с каким именем, лишь бы в коде обозначилось место для внешних параметров, в дальнейшем будет проще скопировать все внешние параметры из индикатора TSIs (рис 6).



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

Нажимаем кнопку "Далее".

В следующем окне указываем, что индикатор будет отображаться в отдельном окне. Минимум и максимум не определяем. Нажимаем кнопку "Добавить" - в списке индикаторных буферов появится новый буфер, вводим название TSIsCD (это название будет отображаться во всплывающей подсказке при наведении указателя мыши на линию индикатора и в окне данных), выберем тип буфера Color Histogram.

После этого в поле "Цвета" появится ряд образцов цвета, зададим первому образцу цвет Green, второму - Red, остальные оставим без изменения, добавим еще один буфер с именем Tsi и еще один с именем TsiSignal, последние два буфера будут использоваться для получения и хранения значений индикатора TSIs (рис 7).


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

Нажимаем кнопку "Готово" и в редакторе откроется шаблон нового индикатора.

2. Редактирование свойств индикатора и индикаторных буферов

На шаге 1 было определено 3 буфера, однако значение #property indicator_buffers равно 4, дело в том, что цветная гистограмма использует два буфера, один из них отображается на графике и предназначен для значений индикатора, второй буфер предназначен для определения цвета отображения первого буфера. Значение #property indicator_buffers оставляем без изменений. Значение #property indicator_plots меняем на 1 - отображаться на графике должен только один буфер.

Буферы Tsi и TsiSignal не должны отображаться на графике, поэтому, удаляем все их свойства (все свойства индикатора заканчивающиеся на цифру 2 и 3).

//--- plot Tsi
#property indicator_label2 "Tsi"
#property indicator_type2 DRAW_LINE
#property indicator_color2 Red
#property indicator_style2 STYLE_SOLID
#property indicator_width2 1
//--- plot TsiSignal
#property indicator_label3 "TsiSignal"
#property indicator_type3 DRAW_LINE
#property indicator_color3 Red
#property indicator_style3 STYLE_SOLID
#property indicator_width3 1
Находим в функции OnInit() вызов функций SetIndexBuffer() для этих буферов (имена массивов TsiBuffer и TsiSignalBuffer, меняем значение третьего параметра с INDICATOR_DATA на INDICATOR_CALCULATIONS.
SetIndexBuffer(2,TsiBuffer,INDICATOR_CALCULATIONS);
SetIndexBuffer(3,TsiSignalBuffer,INDICATOR_CALCULATIONS);

Отредактируем свойство indicator_color1 - оставим только два первых цвета. 

#property indicator_color1 Green,Red

3. Объявление внешних параметров

Откроем в редакторе MetaEditor индикатор TSIs и скопируем из него все внешние переменные, заменим этими параметрами существующую внешнюю переменную Input1.

//--- input parameters
input int r=25;
input int s=13;
input int sp=5;                 // Период сглаживания
input ENUM_MA_METHOD sm=MODE_EMA; // Метод сглаживания

4. Объявление переменной для хэндла индикатора и вызов индикатора

В общей секции индикатора объявим переменную типа int с именем Handle. В функции OnInit(), в самом низу функции вызываем функцию iCustom(). Функция возвращает хэндл создаваемого индикатора, который нам понадобится в дальнейшем для получения значений индикатора. Присвоим значение возвращаемое функцией переменной Handle.

Первые два параметра функции iCustom() определяют символ и таймфрейм, по данным которых должен рассчитываться вызываемый индикатор. Укажем символ и таймфрейм графика, на который прикреплен индикатор - _Symbol и PERIOD_CURRENT, третий параметр - имя пользовательского индикатора, в нашем случае это TSIs. Далее перечисляются все внешние параметры вызываемого индикатора:

Handle=iCustom(_Symbol,PERIOD_CURRENT,"TSIs",r,s,sp,sm);

5. Подготовка функции OnCalculate()

Переходим к функции OnCalculate(). Индикатор TSIs рассчитывается по одному буферу данных, соответственно будем использовать первую форму функции OnCalculate(). Меняем существующую в шаблоне вторую форму функции OnCalculate() на первую.

int OnCalculate(const int rates_total,       // размер массива price[]
                const int prev_calculated, // обработано баров на предыдущем вызове
                const int begin,            // откуда начинаются значимые данные
                const double &price[]      // массив для расчета
                )
  {
   return(rates_total);
  }

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

6. Определение пределов вычисление индикатора

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

При первом выполнении функции OnCalculate() необходимо определить индекс первого бара, с которого должны выполняться вычисления. Его значение определяется количеством баров, необходимых для вычисления значения индикатора (индексация выполняется слева направо от нуля).

Определение наклона линии индикатора выполняется по двум барам, значит, нужен еще один предшествующий бар. Индекс бара, с которого начинаются значимые данные массива price[], известен - это значение переменой begin, значит, начинать обсчет баров будем с бара start=begin+1.

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

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

Поскольку индикатор пользуется для своих вычислений данными другого индикатора, необходимо получить этих данных. Получение данных другого индикатора выполняется функцией CopyBuffer() - первым параметром функции указывается хэндл индикатора, данные которого необходимо скопировать (хэндл получен на шаге 4), вторым - индекс копируемого буфера (необходимое значение можно определить по вкладке "Цвета" окна свойств копируемого индикатора, отсчет начинается с нуля).

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

Если функции CopyBuffer() при попытке копирования данных возвращает значение -1, то это означает, что данные не удалось скопировать, соответственно проводить вычисления нет смысла. Эту ошибку необходимо обработать. В самом начале функции CopyBuffer() объявим статическую переменную типа bool с именем error. Если в процессе расчета индикатора произойдет ошибка - в частности ошибка копирования данных индикатора, присвоим этой переменной значение true и завершим работу функции OnCalculate().

На следующем тике, если по значению переменной error известно, что при предыдущем выполнении функции OnCalculate() была ошибка - пересчитаем весь индикатор заново. Таким образом, начало функции OnCalculate() будет иметь такой вид:

   static bool error=true; 
   int start;
   if(prev_calculated==0) // Первое выполнение функции OnCalculate() после запуска индикатора
     {
      error=true; // Установим значение true, что бы индикатор был рассчитан для всех баров
     }
   if(error) // Значение переменной error=true, значит это первое выполнение функции после 
             // запуска индикатора, или на предыдущем запуске была ошибка копирования данных
     {
      start=begin+1;
      error=false;
     }
   else
     {
      start=prev_calculated-1;
     }

   if(CopyBuffer(Handle,0,0,rates_total-start,TsiBuffer)==-1) // Копирования данных главной лини индикатора
     {
      error=true; // Не удалось скопировать данные, устанавливаем переменной error значение true, чтобы полностью пересчитать 
                 // индикатор при следующем вызове функции OnCalculate()
      return(0);  // Завершаем работу функции
     }
   if(CopyBuffer(Handle,1,0,rates_total-start,TsiSignalBuffer)==-1) // Копирование данных сигнальной линии индикатора
     {
      error=true; // Не удалось скопировать данные, устанавливаем переменной error значение true, чтобы полностью пересчитать
                 // индикатор при следующем вызове функции OnCalculate()
      return(0);  // Завершаем работу функции
     }

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

for(int i=start;i<rates_total;i++)
  {

  }

Вычислим значение индикатора (код располагается внутри только что организованного цикла).

TsiCDBuffer[i]=TsiBuffer[i]-TsiSignalBuffer[i];

Теперь самое интересное - раскрашивание буфера. Ранее, на шаге 1, было определено использование индикатором буфера типа цветной гистограммы, при таком способе рисования используется два буфера - один для значений индикатора, второй для цвета. В свойстве indicator_color1 задан список цветов, при установке элементу буфера значения 0 индикатор будет отображаться зеленым цветом, при установке значения 1 - красным (в соответствии с расположением их в списке indicator_color1, нумерация начинается с нуля).

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

TsiCDColors[i]=TsiCDColors[i-1];

При движении вверх раскрашиваем индикатор цветом Green:

if(TsiCDBuffer[i]>TsiCDBuffer[i-1])TsiCDColors[i]=0;

При движении вниз раскрашиваем индикатор цветом Red:

if(TsiCDBuffer[i]<TsiCDBuffer[i-1])TsiCDColors[i]=1;

На этом работа над индикатором почти закончена, осталось определить начало отрисовки индикатора.

7. Завершение работы над индикатором 

Несмотря на то, что для определения расчета индикатора использовалась переменная begin, это вовсе не означает, что данные индикатора начинаются с бара определенного переменной begin. Если не известен алгоритм пользовательского индикатора, то, практически невозможно определить количество баров, требуемых индикатору для вычислений, поэтому этот шаг можно пропустить или определиться со значением опытным путем. Однако алгоритм индикатора TSIs нам известен, и поэтому можно точно указать начало отрисовки индикатора. Добавляем в функцию OnInit() вызов функции PlotIndexSetInteger() с идентификатором PLOT_DRAW_BEGIN

PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,r+s+sp);

Индикатор TSIs отображает значения с точностью два знака после запятой, функцией IndicatorSetInteger() c идентификатором INDICATOR_DIGITS установим такую же точность:

IndicatorSetInteger(INDICATOR_DIGITS,2);

Компилируем индикатор, присоединяем его к графику (рис. 8).

 
Рис. 8. Индикатор TSIsCDiCust.

Создание индикатора с использованием функции IndicatorCreate() 


Создание индикатора с использованием функции IndicatorCreate() полностью идентично созданию индикатора с использованием функции iCustom(), за исключением шага 4 - вместо функции iCustom() используется функция IndicatorCreate().

1. Сохраняем копию индикатора TSIsCDiCust с именем  TSIsCDiCreate.

2. Находим в коде место вызова функции iCustom(). Теперь вместо вызова функции iCustom() будет выполняться вызов функции IndicatorCreate(). Так же как и у iCustom() первые два параметра функции IndicatorCreate() определяют символ и таймфрейм по данных которых вычисляется индикатор. Третий параметр - идентификатор типа индикатора, для пользовательского индикатора это IND_CUSTOM. Параметры создаваемого индикатора передаются в функцию при помощи массива структур MqlParam.

В структуру MqlParam входит четыре переменных - три из них это переменные различного типа для значений: double_value, integer_value и string_value, и еще одна - type, определяющая тип используемой переменной. У индикатора TSIs четыре внешних параметра. Поскольку индикатор является пользовательским, первый элемент массива определяет имя пользовательского индикатора, получается, что массив должен состоять из пяти элементов. Объявляем массив структур (код располагается в том месте, где вызвался индикатор iCustom()):

MqlParam Params[5];

Заполняем массив значениями:

   Params[0].type=TYPE_STRING;
   Params[0].string_value="TSIs"; // Первым параметров указывается имя вызываемого пользовательского индикатора
   
   Params[1].type=TYPE_INT;
   Params[1].integer_value=r;
   
   Params[2].type=TYPE_INT;
   Params[2].integer_value=s;   
   
   Params[3].type=TYPE_INT;
   Params[3].integer_value=sp;      
   
   Params[4].type=TYPE_INT;
   Params[4].integer_value=sm;  

С первыми тремя параметрами, передаваемыми в функцию IndicatorCreate(), уже разобрались ранее. Четвертым параметром передается размер массива параметров, и последним - сам массив с параметрами:

Handle=IndicatorCreate(_Symbol,PERIOD_CURRENT,IND_CUSTOM,5,Params);

Осталось нажать кнопку "Компилировать" и проверить индикатор в терминале.

Заключение

Коротко повторим основные моменты, на которые надо обращать особое внимание при создании индикатора на основе другого индикатора.

При доработке индикатора надо правильно указать общее количество буферов и количество отрисовывающихся буферов (свойства indicator_buffers, indicator_plots), необходимо определить свойства новых буферов (свойства indicator_label, indicator_type, indicator_color, indicator_style, indicator_width). При вызове функции SetIndexBufer() для новых буферов, необходимо указать правильное значение третьего параметра (INDICATOR_DATA или INDICATOR_CALCULATIONS) и правильно указать значения первого параметра (индекс буфера), при необходимости выполнить переиндексацию буферов.

При создании нового индикатора с использованием другого пользовательского индикатора необходимо правильно передать параметры в функцию iCustom() или заполнить структуру параметров при использовании функции IndicatorCreate(). Здесь также, надо следить за правильным указанием параметров при вызове функции SetIndexBuffer(), и самое главное - не забыть выполнить вызов функции SetIndexBuffer() для новых буферов.

Во всех случаях работы над индикаторами необходимо уделить особое внимание определению диапазона обсчета баров - использовать значения переменных prev_calculated, rates_total, begin. С остальными ошибками, которые могут случаться при программировании поможет справиться редактор MetaEditor (при компиляции во вкладку "Ошибки" выводятся сообщения об ошибках).

Прикрепленные файлы |
tsis.mq5 (7.42 KB)
tsiscdicust.mq5 (3.08 KB)
tsiscdicreate.mq5 (3.43 KB)
Eugeniy Medvedev
Eugeniy Medvedev | 30 апр 2017 в 08:31

При компиляции выдаёт ошибку: 'LinearWeightedMAOnBuffer' - wrong parameters count tsis.mq5 155 10

Чтобы исправить, необходимо:

1. предварительно вычислить сумму весовых коэффициентов LWMA_weight:

int LWMA_weight=0;
for(int j=1; j<=sp; j++)
   LWMA_weight+=j; // Увеличение значения переменной LWMA_weight на j

2. передать LWMA_weight последним параметром в функцию LinearWeightedMAOnBuffer;

LinearWeightedMAOnBuffer(rates_total,prev_calculated,begin2,sp,TSIBuffer,TSISigBuffer,LWMA_weight)
Dmitry Fedoseev
Dmitry Fedoseev | 30 апр 2017 в 15:24
Eugeniy Medvedev:

...

Спасибо за конструктивное участие
Исследование быстродействия скользящих средних в MQL5 Исследование быстродействия скользящих средних в MQL5

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

Написание советника в MQL5 с использованием объектно-ориентированного подхода Написание советника в MQL5 с использованием объектно-ориентированного подхода

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

Перенос индикаторов из MQL4 в MQL5 Перенос индикаторов из MQL4 в MQL5

Статья посвящена особенностям переноса в MQL5 ценовых конструкций, используемых в индикаторах, написанных на MQL4. Для упрощения переноса индикаторных расчетов из MQL4 в MQL5 предложена библиотека функций mql4_2_mql5.mqh, применение которой рассмотрено на примере переноса индикаторов MACD, Stochastic и RSI.

Оптимальный метод подсчета объема совокупной позиции по заданному магическому номеру Оптимальный метод подсчета объема совокупной позиции по заданному магическому номеру

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