Основы написания пользовательских индикаторов

Введение

Целью данной статьи является объяснение принципов построения пользовательских индикаторов на одном примере и рассмотрение «подводных камней», с которыми могут встретиться программисты, делающие первые шаги в освоении языка NTL+.


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


В качестве примера создадим индикатор, который будет показывать разность между количеством баров с ценой закрытия больше цены открытия и количеством баров с ценой закрытия меньше цены открытия; эти разности накапливаются за последние n интервалов. Будем отображать наш индикатор в отдельном окне и показывать в виде гистограммы.


Написание кода скрипта

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

img

Рассмотрим код такого индикатора.


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

#set_indicator_separate
double ExtMapBuffer1[];
int ExtCountedBars=0;
extern int period = 10;

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


В массиве ExtMapBuffer1 фактически хранятся координаты ординат точек, участвующих в отображении, а их абсциссы определяются индексом элементов массива. Затем мы инициализируем переменную ExtCountedBars значением 0. В этой переменной будет храниться количество уже рассчитанных баров. Таким образом, нам не нужно каждый раз производить вычисления для всех баров, что значительно ускоряет вычисление значений индикатора; передвижение графика индикатора будет происходить быстрее. В следующей строке объявлена глобальная переменная period для хранения количества интервалов, на которых индикатор вычисляет заданную статистику. Обратите внимание на то, что использование модификатора extern дает нам возможность менять параметр period через свойства индикатора без необходимости перекомпиляции.


Рассмотрим функцию Initialize(), в которой будем указывать базовые настройки для нашего индикатора.


int Initialize() { Indicator.SetIndexCount(1); Indicator.SetIndexBuffer(0,ExtMapBuffer1); Indicator.SetIndexStyle(0,ltHistogram,lsSolid,5,clrBlue); return(0); }

Метод SetIndexCount объекта Indicator устанавливает количество буферов значений индикаторов. Буфер со значениями ExtMapBuffer1 у нас один, поэтому указываем единицу в качестве единственного параметра. Также нам будет нужно связать номер буфера и массив со значениями буфера. Эта связка задается в строчке Indicator.SetIndexBuffer(0,ExtMapBuffer1), где определено, что свойства нулевого индикатора используются для отрисовки значений из буфера ExtMapBuffer1.


В следующей строке мы задаем свойства для нашего индикатора. Первый параметр метода SetIndexStyle задает номер буфера; это значение 0, которое мы указали в SetIndexBuffer. Второй параметр определяет тип отрисовки: гистограмма. В третьем параметре задаем стиль линий lsSolid (фактически тут можно задать любое значение, так этот параметр влияет только на линии и гистограммы с единичной толщиной). В следующих параметрах задаем толщину линии 5 и цвет синий в виде константы clrBlue (цвет можно также задавать в RGB формате, например, 0x0000FF).


Затем идет функция Run(), которая выполняет базовые проверки и запускает пользовательскую функцию draw(). Функция draw() выполняет весь расчет.


int Run() { ExtCountedBars=Indicator.Calculated; if (ExtCountedBars < 0) { System.Print("Error"); return(-1); } draw(); return(0); }

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


void draw() { int pos=Chart.Bars-ExtCountedBars-1; int value; while(pos>=0) { value=0; for(int i=pos;i < pos+period && i < Chart.Bars-1;i++) { if(Open[i] < Close[i]) value+=1; else value-=1; } ExtMapBuffer1[pos]=value; pos--; } }

В строчке «int pos=Chart.Bars-ExtCountedBars-1;» определяем позицию, с которой мы начинаем рассчитывать значения, двигаясь от более старых данных к более новым. При общем количестве баров равном Chart.Bars самый "дальний" элемент будет иметь индекс Chart.Bars-1, а с учетом уже рассчитанных баров: Chart.Bars-ExtCountedBars-1 . Переменная value используется для накопления нашей статистики. Затем, в цикле от Chart.Bars-ExtCountedBars-1 до 0 для каждого бара собираем статистику не более чем на period позиций назад (мы не должны рассчитывать значения для неподгруженных данных).


Соберем весь код воедино:


#set_indicator_separate double ExtMapBuffer1[]; int ExtCountedBars=0; extern int period = 10; int Initialize() { Indicator.SetIndexCount(1); Indicator.SetIndexBuffer(0,ExtMapBuffer1); Indicator.SetIndexStyle(0,ltHistogram,lsDot,5,clrBlue); return(0); } int Run() { ExtCountedBars=Indicator.Calculated; if (ExtCountedBars < 0) { System.Print("Error"); return(-1); } draw(); System.Print("ExtCountedBars="+ExtCountedBars); return(0); } void draw() { int pos=Chart.Bars-ExtCountedBars-1; int value; while(pos>=0) { value=0; for(int i=pos;i < pos+period && i < Chart.Bars-1;i++) { if(Open[i] < Close[i]) value+=1; else value-=1; } ExtMapBuffer1[pos]=value; pos--; } }

Теперь сделаем небольшое усовершенствование нашего индикатора: отрицательные столбцы будем отображать одним цветом, а положительные – другим. При этом каждый бар также будет двуцветным. Для этого нам понадобится 4 буфера.


#set_indicator_separate double ExtMapBuffer1[]; double ExtMapBuffer2[]; double ExtMapBuffer3[]; double ExtMapBuffer4[]; int ExtCountedBars=0; extern int period = 10; int Initialize() { Indicator.SetIndexCount(4); Indicator.SetIndexBuffer(0,ExtMapBuffer1); Indicator.SetIndexStyle(0,ltHistogram,lsDot,5,clrBlue); Indicator.SetIndexBuffer(1,ExtMapBuffer2); Indicator.SetIndexStyle(1,ltHistogram,lsDot,5,clrGreen); Indicator.SetIndexBuffer(2,ExtMapBuffer3); Indicator.SetIndexStyle(2,ltHistogram,lsDot,5,clrRed); Indicator.SetIndexBuffer(3,ExtMapBuffer4); Indicator.SetIndexStyle(3,ltHistogram,lsDot,5,clrLime); return(0); } int Run() { ExtCountedBars=Indicator.Calculated; if (ExtCountedBars < 0) { System.Print("Error"); return(-1); } draw(); return(0); } void draw() { int pos=Chart.Bars-ExtCountedBars-1; int value; while(pos>=0) { value=0; for(int i=pos;i < pos+period && i < Chart.Bars-1;i++) { if(Open[i] < Close[i]) value+=1; else value-=1; } if(value>0) { ExtMapBuffer1[pos]=value; ExtMapBuffer2[pos]=0; ExtMapBuffer3[pos]=1; ExtMapBuffer4[pos]=0; } if(value==0) { ExtMapBuffer1[pos]=0; ExtMapBuffer2[pos]=0; ExtMapBuffer3[pos]=0; ExtMapBuffer4[pos]=0; } if(value < 0) { ExtMapBuffer1[pos]=0; ExtMapBuffer2[pos]=value; ExtMapBuffer3[pos]=0; ExtMapBuffer4[pos]=-1; } } pos--; } }

Новая версия индикатора c разноцветными барами:

img

При написании кода индикатора Вы можете столкнуться с сообщением в журнале: «run fuction call failed» (неудачный вызов функции run). Такая ошибка чаще всего возникает при записи значений в элементы с несуществующими индексами, поэтому при появлении такого сообщения нужно тщательно проверить значения для индексов буферных массивов.


Резюме

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

IFCMARKETS. CORP. не является компанией, регулиремой ЦБ РФ, и не предоставляет финансовые услуги на территории РФ. Переведенная страница не ориентирована для граждан РФ.