Эффективные алгоритмы усреднения с минимальным лагом и их использование в индикаторах

Nikolay Kositsin | 24 января, 2007

Введение

Я думаю, что нет никакой необходимости объяснять значительную роль алгоритмов сглаживания для технического анализа и торговых систем. Достаточно взглянуть на код почти любого индикатора, чтобы обнаружить в нём в явном или неявном виде какой-нибудь алгоритм усреднения. Если присмотреться более внимательно, то обнаружится, что в большинстве торгово-аналитических платформ и клиентских терминалов применяются самые простые, хотя далеко не самые эффективные, алгоритмы усреднения и большинство индикаторов построены на базе этих алгоритмов.

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

Предмет статьи


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

- JJMASeries () - алгоритм адаптивного JMA сглаживания;
- JLiteSeries() - алгоритм JMA сглаживания без алгоритма адаптации;
- JurXSeries () - алгоритм ультралинейного сглаживания, взятый из индикатора JRSX;
- ParMASeries() - алгоритм сглаживания на основе параболической аппроксимации;
- LRMASeries () - алгоритм сглаживания на основе линейной регрессии;
- T3Series () - алгоритм сглаживания на основе алгоритма Тильсона.

Реализация функций сглаживания


Функции представлены в виде файлов: JJMASeries.mqh, JLiteSeries.mqh, JurXSeries. mqh, ParMASeries.mqh, LRMASeries.mqh, T3Series.mqh.

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


Функции JJMASeries ()

Своё знакомство начнём с функции JJMASeries():

double JJMASeries(int number, int din, int MaxBar, int limit, 
                  int Phase, int Length, double series, int bar,
                  int& reset)

Файл JJMASeries.mqh содержит четыре функции: JJMASeries(), JJMASeriesResize(), JJMASeriesAlert() и JMA_ErrDescr(). Помимо функций файл содержит переменные, которые объявлены как глобальные.

Функция JJMASeries() предназначена для использования алгоритма JMA при написании любых индикаторов теханализа и экспертов, для замены расчёта классического усреднения на этот алгоритм. Функция не работает, если параметр limit принимает значение, равное нулю! Все индикаторы, сделанные мною для JJMASeries, выполнены с учётом этого ограничения. Файл следует положить в папку MetaTrader\experts\include\ . Следует учесть, что если значение переменной bar больше, чем значение переменной MaxBar, то функция JJMASeries() возвращает на этом баре значение равное нулю! И, следовательно, такое значение не может присутствовать в знаменателе какой-либо дроби в расчёте индикатора! На последующих тридцати барах функция JJMASeries() тоже возвращает ноль!

Эта версия функции JJMASeries() поддерживает экспертов при её использовании в пользовательских индикаторах, к которым обращается эксперт. Кроме того, эта версия функции JJMASeries() поддерживает экспертов при её использовании в коде индикатора, который полностью помещён в код эксперта с сохранением всех операторов цикла и переменных! При написании индикаторов и экспертов с использованием функции JJMASeries не рекомендуется переменным давать имена, начинающиеся с nJMA... или dJMA... Функция JJMASeries() может быть использована во внутреннем коде других пользовательских функций, но при этом следует учитывать тот факт, что в каждом обращении к такой пользовательской функции у каждого обращения к JJMASeries() должен быть свой уникальный номер (number). Данная версия функции JJMASeries() предназначена для обработки переменных, связанных с массивами таймсерий текущего графика! Если применить эту функцию к обработке переменных, вычисленных на основе массивов таймсерий с других графиков, то в расчёте будет присутствовать ошибка!

Входные параметры:
- number - порядковый номер обращения к функции JJMASeries() в тексте индикатора (0, 1, 2, 3 и так далее);
- din - параметр, позволяющий изменять параметры Length и Phase на каждом баре. 0 - запрет изменения параметров, любое другое значение - разрешение;
- MaxBar - максимальное значение, которое может принимать номер рассчитываемого бара (bar). Обычно равно Bars-1-period; Где "period" - это количество баров, на которых исходная величина series не рассчитывается;
- limit - Количество ещё не посчитанных баров плюс один или номер последнего непосчитанного бара. Должно быть обязательно равно Bars-IndicatorCounted()-1;
- Length - глубина сглаживания;
- Phase - параметр, изменяющийся в пределах -100 .. . +100, влияет на качество переходного процесса;
- series - входной параметр, по которому производится расчёт функции JJMASeries();
- bar - номер рассчитываемого бара, параметр должен изменяться оператором цикла от максимального значения к нулевому. Причём его максимальное значение всегда должно равняться значению параметра limit!


Выходные параметры:
- JMASeries() - значение функции JMA. При значениях параметра bar больше, чем MaxBar-30 функция JJMASeries() всегда возвращает ноль!
- reset - параметр, возвращающий по ссылке значение, отличенное от 0, если произошла ошибка в расчёте функции, и 0, если расчёт прошёл нормально. Этот параметр может быть только переменной, но не значением!


Инициализация функции
Перед обращениями к функции JJMASeries(), когда количество уже посчитанных баров равно 0, (а ещё лучше это сделать в блоке инициализации пользовательского индикатора или эксперта) следует изменить размеры внутренних буферных переменных функции. Для этого необходимо обратиться к переменным функции JJMASeries() через вспомогательную функцию JJMASeriesResize() со следующими параметрами: JJMASeriesResize(number+1); необходимо сделать параметр number (MaxJMA.number) равным количеству обращений к функции JJMASeries, то есть на единицу больше, чем максимальный number. Помимо изменения размеров буферов функции JJMASeries() в блоке инициализации можно проверить значения входных параметров индикатора Length и Phase, являющихся входными параметрами функции JJMASeries(), на соответствие диапозону их изменения с помощью функции JJMASeriesAlert():

JJMASeriesAlert(int Number, string name, int ExternVar)

- Number - параметр принимающий два значения: 0 - для проверки входного параметра ExternVar на соответствие диапозону изменения входного параметра Length функции JJMASeries() и 1 - для проверки входного параметра ExternVar на соответствие диапазону изменения входного параметра Phase функции JJMASeries();
- name - стринговое имя входного параметра ExternVar для подачи алерта;

- ExternVar - входной параметр индикатора

Индикация ошибок

При отладке индикаторов и экспертов их коды могут содержать ошибки, для выяснения причин которых следует смотреть логфайл. Все ошибки функция JJMASeries() пишет в лог файл в папке \MetaTrader\EXPERTS\LOGS\. Если перед обращением к функции JJMASeries() в коде, который предшествовал функции, возникла MQL4-ошибка, то функция запишет в лог файл код ошибки и содержание ошибки. Если при выполнении функции JJMASeries() в алгоритме JJMASeries() произошла MQL4-ошибка, то функция также запишет в лог файл код ошибки и содержание ошибки. При неправильном задании номера number обращения к функции JJMASeries() или неверном определении размера буферных переменных nJJMAResize. Size в лог файл будет записаны сообщения о неверном определении этих параметров. Также в лог файл пишется информация при неправильном определении параметра limit.

Если при выполнении функции инициализации init() произошёл сбой при изменении размеров буферов функции JJMASeries, то функция JJMASeriesResize() запишет в лог файл информацию о неудачном изменении размеров. Если при обращении к функции JJMASeries() через внешний оператор цикла была нарушена правильная последовательность изменения параметра bar, то в лог файл будет записана и эта информация. Следует учесть, что некоторые ошибки программного кода будут порождать дальнейшие ошибки в его исполнении и поэтому, если функция JJMASeries() пишет в лог файл сразу несколько ошибок, то устранять их следует в порядке времени возникновения. В правильно написанном индикаторе функция JJMASeries() может делать записи в лог файл только при нарушениях работы операционной системы. Исключение составляет запись изменения размеров буферных переменных при перезагрузке индикатора или эксперта, которая происходит при каждом вызове функции init(). Все MQL4-ошибки в лог файл пишутся с помощью функции JMA_ErrDescr(), которая сбрасывает в лог файл код и содержание ошибки по её коду, полученному с помощью функции GetLastError().

Пример обращения к функции JJMASeries() (двойное JMA сглаживание входной цены):

/*
Для  работы  индикатора  следует  положить файлы 
JJMASeries.mqh 
PriceSeries.mqh 
в папку (директорию): MetaTrader\experts\include\
Heiken Ashi#.mq4
в папку (директорию): MetaTrader\indicators\
*/
//+------------------------------------------------------------------+   
//|                                                        J2JMA.mq4 | 
//|                       JMA code: Copyright © 2005, Jurik Research | 
//|                                         http://www.jurikres.com/ | 
//|    MQL4 JJMASeries+J2JMA: Copyright © 2006,     Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+   
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru" 
//---- отрисовка индикатора в главном окне
#property indicator_chart_window 
//---- количество индикаторных буферов
#property indicator_buffers 1 
//---- цвет индикатора
#property indicator_color1 Magenta 
//---- ВХОДНЫЕ ПАРАМЕТРЫ ИНДИКАТОРА 
extern int Length1 = 5; // глубина  первого сглаживания 
extern int Length2 = 5; // глубина  второго сглаживания 
// параметр первого сглаживания, изменяющийся в пределах -100 ... +100, 
//влияет на качество переходного процесса; 
extern int Phase1  = 100;
// параметр второго сглаживания, изменяющийся в пределах -100 ... +100, 
//влияет на качество переходного процесса; 
extern int Phase2  = 100;
// cдвиг индикатора вдоль оси времени 
extern int Shift   = 0;
/* Выбор цен, по которым производится расчёт индикатора 
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 
11-Heiken Ashi Low, 12-Heiken Ashi High,  13-Heiken Ashi Open, 
14-Heiken Ashi Close.) */
extern int Input_Price_Customs = 0;
//---- индикаторные буферы
double J2JMA[];
//---- переменные с плавающей точкой  
double Temp_Series; 
//----+ Введение функции JJMASeries 
//----+ Введение функции JJMASeriesResize 
//----+ Введение функции JJMASeriesAlert  
//----+ Введение функции JMA_ErrDescr  
#include <JJMASeries.mqh>   
//----+ Введение функции PriceSeries
//----+ Введение функции PriceSeriesAlert 
#include <PriceSeries.mqh>
//+------------------------------------------------------------------+ 
//| J2JMA indicator initialization function                          | 
//+------------------------------------------------------------------+ 
int init() 
  {  
//---- определение стиля исполнения графика
   SetIndexStyle (0, DRAW_LINE); 
//---- 1 индикаторный буфер использован для счёта
   SetIndexBuffer(0, J2JMA);
//---- горизонтальный сдвиг индикаторной линии 
   SetIndexShift (0, Shift);  
//---- установка значений индикатора, которые не будут видимы на графике
   SetIndexEmptyValue(0, 0); 
//---- имя для окон данных и лэйба для субъокон 
   IndicatorShortName ("J2JMA(Length1=" + Length1 + ", Phase1=" + Phase1 +
                       ", Length2=" + Length2 + ", Phase2=" + Phase2 + 
                       ", Shift=" + Shift + ")"); 
   SetIndexLabel (0, "J2JMA"); 
//---- Установка формата точности отображения индикатора
   IndicatorDigits(Digits);
//----+ Изменение размеров буферных переменных функции JJMASeries, 
//nJMAnumber=2(Два обращения к функции JJMASeries)
   if(JJMASeriesResize(2) != 2)
       return(-1);
//---- установка алертов на недопустимые значения внешних переменных
   JJMASeriesAlert (0,"Length1", Length1);
   JJMASeriesAlert (0,"Length2", Length2);
   JJMASeriesAlert (1,"Phase1", Phase1 );
   JJMASeriesAlert (1,"Phase2", Phase2 );
   PriceSeriesAlert(Input_Price_Customs);
//---- завершение инициализации
   return(0); 
  } 
//+------------------------------------------------------------------+   
//| J2JMA iteration function                                         | 
//+------------------------------------------------------------------+ 
int start() 
  { 
//---- Проверка количества баров на достаточность для дальнейшего расчёта
   if(Bars - 1 < 61)
       return(0);
//----+ Введение целых переменных и получение уже подсчитанных баров
   int reset, MaxBar1, MaxBar2, counted_bars = IndicatorCounted();
//---- проверка на возможные ошибки
   if(counted_bars < 0) 
       return(-1);
//---- последний подсчитанный бар должен быть пересчитан 
//---- (без этого пересчёта для counted_bars функция JJMASeries будет 
//     работать некорректно!!!)
   if(counted_bars > 0) 
       counted_bars--;
//---- определение номера самого старого бара, начиная с которого будет
//     произедён пересчёт новых баров
   int limit = Bars - counted_bars - 1; 
   MaxBar1 = Bars - 1; 
   MaxBar2 = MaxBar1 - 30;
 
//----+ ОСНОВНОЙ ЦИКЛ ВЫЧИСЛЕНИЯ ИНДИКАТОРА 
   for(int bar = limit; bar >= 0; bar--)
     {
       // Обращение к функции PriceSeries для получения входной цены Series
       Temp_Series = PriceSeries(Input_Price_Customs, bar);
       // Два обращения к функции JJMASeries за номерами 0, 1. Параметры 
       //nJMA.Phase и nJMA.Length 
       //не меняются на каждом баре (nJMA.din=0)
       //(Во втором  обращении параметр nJMA.MaxBar  уменьшен на 30  т. к. это 
       //повторное JMA сглаживание)
       Temp_Series = JJMASeries(0,0,MaxBar1,limit,Phase1,Length1,
                                Temp_Series,bar,reset);
       // проверка на отсутствие ошибки в предыдущей операции
       if(reset != 0)
           return(-1);
       Temp_Series = JJMASeries(1,0,MaxBar2,limit,Phase2,Length2,
                                Temp_Series,bar,reset);
       // проверка на отсутствие ошибки в предыдущей операции
       if(reset != 0)
           return(-1);
       J2JMA[bar] = Temp_Series;
     }
//---- завершение вычислений значений индикатора
   return(0); 
  } 
//+--------------------------------------------------------+


Таким образом в применении этой функции можно выделить следующие моменты:
1. Объявление функций, входящих в состав файла JJMASeries. mqh строкой #include <JJMASeries.mqh> в начале текста индикатора. Объявляются переменные и четыре функции: JJMASeries(), JJMASeriesResize(), JJMASeriesAlert() и JMA_ErrDescr();
2. Изменение размеров буферных элементов, с которыми работает функция JJMASeries() с помощью функции JJMASeriesResize() в блоке инициализации;
3. Проверка на корректность значений внешних переменных индикатора, которые являются внешними переменными функции JJMASeries() с помощью функции JJMASeriesAlert() в блоке инициализации;
4. Сами обращения к функции JJMASeries(), сделанные через оператор цикла с соответствующими проверками на ошибку.


Другие функции


Алгоритм обращения к остальным функциям абсолютно аналогичен рассмотренному выше алгоритму, но имеются некоторые отличия по количеству внешних переменных, присутствующих в функциях:
JJMASeries (int number, int din, int MaxBar, int limit, int Phase,
            int Length, double series, int bar, int&reset)
JLiteSeries(int number, int din, int MaxBar, int limit, int Phase,
            int Length, double series, int bar, int&reset)
JurXSeries (int number, int din, int MaxBar, int limit,
            int Length, double series, int bar, int&reset)
T3Series   (int number, int din, int MaxBar, int limit, int Phase,
            int Length, double series, int bar, int&reset )
ParMASeries(int number, int MaxBar, int limit, int period, 
            double series, int bar, int&reset)
LRMASeries (int number, int MaxBar, int limit, int period, 
            double series, int bar, int&reset )

Следует учесть, что функции JJMASeries() и JLiteSeries() несовместимы в одном эксперте или индикаторе! На самом деле в файле JLiteSeries. mqh помещён тот же самый JMA код с названием функции JJMASeries() без адаптации! Для замены в эксперте или индикаторе функции JJMASeries() на JLiteSeries() достаточно поменять строку #include<JJMASeries. mqh> на строку #include<JLiteSeries. mqh>. Все обращения к функциям файла JLiteSeries. mqh идут как обращения к функциям, идентичным обращениям к функциям файла JJMASeries. mqh.


Остальные функции абсолютно совместимы в тексте одного индикатора или эксперта. В функциях ParMASeries() и LRMASeries() значение внешней переменной period ограниченно величиной 501. Если необходимы большие значения, то следует поменять первые (не нулевые!) измерения буферов
dParMA.TempBuffer[][501] и dParMA.TEMPBUFFER[][501] для функции ParMASeries() или dLRMA. TempBuffer[][501] и dLRMA.TEMPBUFFER[][501] для функции LRMASeries() в файлах ParMASeries. mqh и LRMASeries. mqh соотвественно.


Функциии JurXSeries()

Пример обращения к функциии JurXSeries() (Ультралинейное сглаживание входной цены дополнительным JMA сглаживанием):

/*
Для  работы  индикатора  следует  положить файлы 
JurXSeries.mqh, 
JJMASeries.mqh, 
PriceSeries.mqh,  
в папку (директорию): MetaTrader\experts\include\
Heiken Ashi#.mq4
в папку (директорию): MetaTrader\indicators\
В основе  этого  индикатора  лежит  алгоритм сглаживания от индикатора 
JRSX.    Конечный результат этого алгоритма имеет некоторое сходство с 
двойным JMA сглаживанием, но в силу большей простоты менее совершенен.
 
*/
//+------------------------------------------------------------------+
//|                                                        JJurX.mq4 | 
//|                           Copyright © 2006,     Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru" 
//---- отрисовка индикатора в главном окне
#property indicator_chart_window 
//---- количество индикаторных буферов
#property indicator_buffers 1 
//---- цвет индикатора
#property indicator_color1 Gold
//---- ВХОДНЫЕ ПАРАМЕТРЫ ИНДИКАТОРА 
extern int JurX_Length  = 5; // глубина JurX сглаживания 
extern int JJMA_Length  = 4; // глубина JJMA сглаживания 
// параметр JJMA сглаживания, изменяющийся в пределах -100 ... +100, 
// влияет на качество переходного процесса; 
extern int JJMA_Phase   = -100;
extern int Shift        = 0;      // cдвиг индикатора вдоль оси времени 
/* Выбор цен, по которым производится расчёт индикатора 
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 
11-Heiken Ashi Low, 12-Heiken Ashi High,  13-Heiken Ashi Open, 
14-Heiken Ashi Close.) */
extern int Input_Price_Customs = 0;
//---- индикаторные буферы
double Ind_Buffer[];
//---- переменные с плавающей точкой  
double Price,JurX,JJurX,Error;
//+------------------------------------------------------------------+
//----+ Введение функции JJMASeries 
//----+ Введение функции JJMASeriesResize 
//----+ Введение функции JJMASeriesAlert  
//----+ Введение функции JMA_ErrDescr  
#include <JJMASeries.mqh> 
//+------------------------------------------------------------------+
//----+ Введение функции JurXSeries
//----+ Введение функции JurXSeriesResize
//----+ Введение функции JurXSeriesAlert 
//----+ Введение функции JurX_ErrDescr  
#include <JurXSeries.mqh> 
//+------------------------------------------------------------------+ 
//----+ Введение функции PriceSeries
//----+ Введение функции PriceSeriesAlert 
#include <PriceSeries.mqh>
//+------------------------------------------------------------------+
//| JJurX indicator initialization function                          | 
//+------------------------------------------------------------------+ 
int init() 
{  
//---- определение стиля исполнения графика
SetIndexStyle (0,DRAW_LINE); 
//---- 1 индикаторный буфер использован для счёта
SetIndexBuffer(0,Ind_Buffer);
//---- горизонтальный сдвиг индикаторной линии 
SetIndexShift (0, Shift); 
//---- установка значений индикатора, которые не будут видимы на графике
SetIndexEmptyValue(0,0); 
//---- имя для окон данных и лэйба для субъокон 
IndicatorShortName ("JJurX( JurX_Length="+JurX_Length+", Shift="+Shift+")"); 
SetIndexLabel (0, "JJurX"); 
//---- Установка формата точности отображения индикатора
IndicatorDigits(Digits);
//----+ Изменение размеров буферных переменных функции JurXSeries, 
//      nJurXnumber=2
//(Два обращения к функции JurXSeries)
if (JurXSeriesResize(2)!=2)return(-1);
//----+ Изменение размеров буферных переменных функции JJMASeries,
//      nJMAnumber=1
//(Одно обращение к функции JJMASeries)
if (JJMASeriesResize(1)!=1)return(-1);
//---- установка алертов на недопустимые значения внешних переменных  
JurXSeriesAlert(0,"JurX_Length",JurX_Length); 
JJMASeriesAlert(0,"JJMA_Length",JJMA_Length); 
JJMASeriesAlert(1,"JJMA_Phase",JJMA_Phase); 
PriceSeriesAlert(Input_Price_Customs);
//---- завершение инициализации
return(0); 
} 
//+------------------------------------------------------------------+
//| JJurX iteration function                                         | 
//+------------------------------------------------------------------+
int start() 
{ 
//---- Проверка количества баров на достаточность для дальнейшего расчёта
if (Bars-1<JurX_Length+32)return(0);
//----+ Введение целых переменных и получение уже подсчитанных баров
int reset,MaxBar,counted_bars=IndicatorCounted();
//---- проверка на возможные ошибки
if (counted_bars<0)return(-1);
//---- последний подсчитанный бар должен быть пересчитан 
//(без этого пересчёта для counted_bars функция JurXSeries будет 
// работать некорректно!!!)
if (counted_bars>0) counted_bars--;
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт новых баров
int limit=Bars-counted_bars-1; 
//---- определение номера самого старого бара, начиная с которого 
//     будет произедён пересчёт всех баров
MaxBar=Bars-1; 
 
//----+ ОСНОВНОЙ ЦИКЛ ВЫЧИСЛЕНИЯ ИНДИКАТОРА 
for(int bar=limit;bar>=0;bar--)
 {
  //----+ Обращение к функции PriceSeries для получения входной
  //      цены Series
  Price=PriceSeries(Input_Price_Customs,bar);
  //----+ Одно обращение к функции JurXSeries за номером 0. 
  //Параметр nJJurX.Length не меняется на каждом баре (nJurXdin=0)
  JurX=JurXSeries(0,0,MaxBar,limit,JurX_Length,Price,bar,reset); 
  //----+ проверка на отсутствие ошибки в предыдущей операции
  if(reset!=0)return(-1); 
  //----+ определение ошибки вычисления  параметра JurX
  //----+ второе обращение к функции JurXSeries за номером 1. 
  //Параметр nJJurX.Length не меняtтся на каждом баре (nJurXdin=0)
  Error=JurXSeries(1,0,MaxBar,limit,JurX_Length,100,bar,reset); 
  //----+ проверка на отсутствие ошибки в предыдущей операции
  if(reset!=0)return(-1);
  if(Error==0)Error=100;
  JurX*=100/Error;
  //----+ Обращение к функции JJMASeries за номерам 0. 
  //      Параметры nJMA.Phase и nJMA.Length не меняются на каждом баре 
  //      (nJMA.din=0)
  JJurX=JJMASeries(0,0,MaxBar,limit,JJMA_Phase,JJMA_Length,JurX,bar,reset);
  //----+ проверка на отсутствие ошибки в предыдущей операции
  if(reset!=0)return(-1);
  Ind_Buffer[bar]=JJurX;                 
 }
//---- завершение вычислений значений индикатора
return(0); 
} 
//+-------------------------------------------------------------------------+

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

Пример обращений к функциям JJMASeries() и JurXSeries() (Аналог индикатора CCI с дополнительным JMA сглаживанием):

/*
Для  работы  индикатора  следует  положить файлы 
JJMASeries.mqh
JurSeries.mqh 
PriceSeries.mqh 
в папку (директорию): MetaTrader\experts\include\
Heiken Ashi#.mq4
в папку (директорию): MetaTrader\indicators\
*/
//+------------------------------------------------------------------+   
//|                                                        JCCIX.mq4 |
//|                               Copyright © 2006, Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+    
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru" 
//---- отрисовка индикатора в отдельном окне
#property indicator_separate_window
//---- количество индикаторных буферов
#property indicator_buffers  1
//---- цвета индикатора
#property indicator_color1  BlueViolet
//---- параметры горизонтальных уровней индикатора
#property indicator_level1  0.5
#property indicator_level2 -0.5
#property indicator_level3  0.0
#property indicator_levelcolor MediumBlue
#property indicator_levelstyle 4
//---- ВХОДНЫЕ ПАРАМЕТРЫ ИНДИКАТОРА 
extern int  JJMA.Length = 8;  // глубина JJMA сглаживания входной цены
// глубина JurX сглаживания полученного индикатора 
extern int  JurX.Length = 8;
// параметр, изменяющийся в пределах -100 ... +100, влияет 
// на качество переходныx процессов сглаживания
extern int  JJMA.Phase = 100;
 /* Выбор цен, по которым производится расчёт индикатора 
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 
11-Heiken Ashi Low, 12-Heiken Ashi High,  13-Heiken Ashi Open, 
14-Heiken Ashi Close.) */
extern int Input_Price_Customs = 0;
//---- индикаторные буферы
double Ind_Buffer1[];
//---- целые константы 
int    w;
//+------------------------------------------------------------------+
//----+ Введение функции JJMASeries 
//----+ Введение функции JJMASeriesResize 
//----+ Введение функции JJMASeriesAlert  
//----+ Введение функции JMA_ErrDescr  
#include <JJMASeries.mqh> 
//+------------------------------------------------------------------+ 
//----+ Введение функции JurXSeries
//----+ Введение функции JurXSeriesResize
//----+ Введение функции JurXSeriesAlert 
//----+ Введение функции JurX_ErrDescr  
#include <JurXSeries.mqh> 
//+------------------------------------------------------------------+  
//----+ Введение функции PriceSeries
//----+ Введение функции PriceSeriesAlert 
#include <PriceSeries.mqh>
//+------------------------------------------------------------------+ 
//| JCCIX initialization function                                    |
//+------------------------------------------------------------------+
int init()
 {
//---- стили изображения индикатора
   SetIndexStyle(0,DRAW_LINE);
//---- 1 индикаторный буфер использован для счёта. 
   SetIndexBuffer(0,Ind_Buffer1);
//---- установка значений индикатора, которые не будут видимы на графике
   SetIndexEmptyValue(0,0); 
//---- имена для окон данных и лэйбы для субъокон
   SetIndexLabel(0,"JCCIX");
   IndicatorShortName("JCCIX(JJMA.Length="+JJMA.Length+", JurX.Length"+
                      JurX.Length+")");
//---- Установка формата точности (количество знаков после десятичной точки) 
//для визуализации значений индикатора  
   IndicatorDigits(2);
//----+ Изменение размеров буферных переменных функции JurXSeries, 
//      nJurXnumber=2
//(Два обращения к функции JurXSeries)
   if (JurXSeriesResize(2)!=2)return(-1);
//----+ Изменение размеров буферных переменных функции JJMASeries, 
//      nJMAnumber=1
//(Одно обращение к функции JJMASeries)
   if (JJMASeriesResize(1)!=1)return(-1);
//---- установка алертов на недопустимые значения внешних переменных
   JurXSeriesAlert (0,"JurX.Length",JurX.Length);
   JJMASeriesAlert (0,"JJMA.Length",JJMA.Length);
   JJMASeriesAlert (1,"JJMA.Phase",JJMA.Phase);
   PriceSeriesAlert(Input_Price_Customs);
//---- установка номера бара, начиная с которого будет отрисовываться 
//     индикатор  
   SetIndexDrawBegin(0,JurX.Length+31);
//---- инициализация коэффициентов для расчёта индикатора 
   if (JurX.Length>5) w=JurX.Length-1; else w=5;
//---- завершение инициализации
   return(0);
  }
//+------------------------------------------------------------------+
//|  JCommodity Channel IndexX                                       |
//+------------------------------------------------------------------+
int start()
  {
//---- Введение переменных с плавающей точкой    
double price,Jprice,JCCIX,UPCCI,DNCCI,JUPCCIX,JDNCCIX; 
//----+ Введение целых переменных и получение уже подсчитанных баров
int reset,MaxBar,MaxBarJ,limit,counted_bars=IndicatorCounted();
//---- проверка на возможные ошибки
if (counted_bars<0)return(-1);
//---- последний подсчитанный бар должен быть пересчитан 
//---- (без этого пересчёта для counted_bars функции JJMASeries 
//и JurXSeries будут работать некорректно!!!)
if (counted_bars>0) counted_bars--;
//---- определение номера самого старого бара, начиная с которого будет 
//произедён пересчёт новых баров
limit=Bars-counted_bars-1; 
//---- определение номера самого старого бара, начиная с которого будет 
//произедён пересчёт всех баров
MaxBar=Bars-1; MaxBarJ=MaxBar-30;
//---- корекция стартового расчётого бара в цикле
if(limit>=MaxBar)limit=MaxBar;
 
for(int bar=limit; bar>=0; bar--)
 { 
   //----+ Обращение к функции PriceSeries для получения входной 
   //      цены Series
   price=PriceSeries(Input_Price_Customs, bar);
   //+---------------------------------------------------------------
   //----+ Одно обращение к функции JJMASeries за номерам 0 
   //----+ Параметры nJMA.Phase и nJMA.Length не меняются на каждом 
   //      баре (nJMA.din=0)
   //+---------------------------------------------------------------+   
   Jprice=JJMASeries(0,0,MaxBar,limit,JJMA.Phase,JJMA.Length,price,
                     bar,reset);
   //----+ проверка на отсутствие ошибки в предыдущей операции
   if(reset!=0)return(-1);
   //+---------------------------------------------------------------+    
   UPCCI=price-Jprice;         
   DNCCI=MathAbs(UPCCI);
   //----+ Два обращения к функции JurXSeries за номерами 0 и 1. 
           Параметр nJJurXLength не 
   //меняtтся на каждом баре (nJurXdin=0)
   //----+ проверка на отсутствие ошибки в предыдущей операции
   JUPCCIX=JurXSeries(0,0,MaxBarJ,limit,JurX.Length,UPCCI,bar,reset); 
   if(reset!=0)return(-1); 
   JDNCCIX=JurXSeries(1,0,MaxBarJ,limit,JurX.Length,DNCCI,bar,reset); 
   if(reset!=0)return(-1); 
   //----+
   if (bar>MaxBarJ-w)JCCIX=0;
   else 
     if (JDNCCIX!=0)
       {
        JCCIX=JUPCCIX/JDNCCIX;
        if(JCCIX>1)JCCIX=1;
        if(JCCIX<-1)JCCIX=-1;
       }
     else JCCIX=0;
   Ind_Buffer1[bar]=JCCIX; 
   //----+
 }
//----
   return(0);
  }
//+-------------------------------------------------------------------+

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

Пример обращений к функциям JJMASeries() и иJurXSeries (Аналог индикатора RSI с дополнительным JMA сглаживанием):

/*
Для работы индикатора  следует  положить  файлы  
JurXSeries.mqh
JJMASeries.mqh  
PriceSeries.mqh 
в папку (директорию): MetaTrader\experts\include\
Heiken Ashi#.mq4
в папку (директорию): MetaTrader\indicators\
*/
//+------------------------------------------------------------------+ 
//|                                                        JJRSX.mq4 |
//|    MQL4 JJRSX: Copyright © 2006,                Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+ 
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru" 
//---- отрисовка индикатора в отдельном окне
#property indicator_separate_window
//---- количество индикаторных буферов
#property indicator_buffers  1
//---- цвета индикатора
#property indicator_color1  BlueViolet
//---- параметры горизонтальных уровней индикатора
#property indicator_level1  0.5
#property indicator_level2 -0.5
#property indicator_level3  0.0
#property indicator_levelcolor MediumBlue
#property indicator_levelstyle 4
//---- ВХОДНЫЕ ПАРАМЕТРЫ ИНДИКАТОРА 
extern int  Length = 8;  // глубина JurX сглаживания индикатора
// глубина JJMA сглаживания полученного индикатора
extern int  Smooth = 3;
// параметр, изменяющийся в пределах -100 ... +100, влияет на 
//качество переходныx процессов сглаживания
extern int  Phase = 100;
/* Выбор цен, по которым производится расчёт индикатора 
(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW,
11-Heiken Ashi Low, 12-Heiken Ashi High,  13-Heiken Ashi Open, 
14-Heiken Ashi Close.) */
extern int Input_Price_Customs = 0;
//---- индикаторные буферы
double Ind_Buffer[];
//---- целые переменные 
int    w;  
//+------------------------------------------------------------------+  
//----+ Введение функции JJMASeries 
//----+ Введение функции JJMASeriesResize 
//----+ Введение функции JJMASeriesAlert  
//----+ Введение функции JMA_ErrDescr  
#include <JJMASeries.mqh> 
//+------------------------------------------------------------------+ 
//----+ Введение функции JurXSeries
//----+ Введение функции JurXSeriesResize
//----+ Введение функции JurXSeriesAlert 
//----+ Введение функции JurX_ErrDescr  
#include <JurXSeries.mqh> 
//+------------------------------------------------------------------+ 
//----+ Введение функции PriceSeries
//----+ Введение функции PriceSeriesAlert 
#include <PriceSeries.mqh>
//+------------------------------------------------------------------+ 
//| JJRSX initialization function                                    |
//+------------------------------------------------------------------+ 
int init()
  {
//---- стили изображения индикатора
   SetIndexStyle(0,DRAW_LINE);
//---- 1 индикаторныQ буфер использован для счёта. 
   SetIndexBuffer(0,Ind_Buffer);
//---- установка значений индикатора, которые не будут видимы на графике
   SetIndexEmptyValue(0,0); 
//---- имена для окон данных и лэйбы для субъокон
   SetIndexLabel(0,"JRSX");
   IndicatorShortName("JRSX(Length="+Length+", Input_Price_Customs="+
                      Input_Price_Customs+")");
//---- Установка формата точности (количество знаков после десятичной 
//     точки) для визуализации значений индикатора  
   IndicatorDigits(2);
//----+ Изменение размеров буферных переменных функции JurXSeries,
        nJurXnumber=2
//(Два обращения к функции JurXSeries)
   if (JurXSeriesResize(2)!=2)return(-1);
//----+ Изменение размеров буферных переменных функции JJMASeries,
        nJMAnumber=1
//(Одно обращение к функции JJMASeries)
   if (JJMASeriesResize(1)!=1)return(-1);
//---- установка алертов на недопустимые значения внешних переменных
   JurXSeriesAlert (0,"Length",Length);
   JJMASeriesAlert (0,"Smooth",Smooth);
   JJMASeriesAlert (1,"Phase",Phase);
   PriceSeriesAlert(Input_Price_Customs);
//---- установка номера бара, начиная с которого будет отрисовываться
       индикатор  
   SetIndexDrawBegin(0,Length+31);
//---- корекция недопустимого значения параметра Length
   if(Length<1)Length=1; 
//---- инициализация коэффициентов для расчёта индикатора 
   if (Length>5) w=Length-1; else w=5;
//---- завершение инициализации
return(0);
  }
//+------------------------------------------------------------------+ 
//| JJRSX iteration function                                         |
//+------------------------------------------------------------------+ 
int start()
{
//---- Введение переменных с плавающей точкой 
double dPrice,dPriceA,UPJRSX,DNJRSX,JRSX,JJRSX; 
//----+ Введение целых переменных и получение уже подсчитанных баров
int bar,limit,reset,MaxBar,MaxBarJ,counted_bars=IndicatorCounted();
//---- проверка на возможные ошибки
if (counted_bars<0)return(-1);
//---- последний подсчитанный бар должен быть пересчитан
if (counted_bars>0) counted_bars--;
//---- определение номера самого старого бара, начиная с которого 
//будет произедён пересчёт всех баров
MaxBar=Bars-2; MaxBarJ=MaxBarJ-w-1; 
//---- определение номера самого старого бара, начиная с которого 
//будет произедён пересчёт новых баров
limit=Bars-counted_bars-1; 
//----+ 
if (limit>MaxBar){limit=MaxBar;Ind_Buffer[MaxBar]=0.0;}
 
for(bar=limit;bar>=0;bar--)
  {
   //----+ два обращения к функции PriceSeries для получения разницы
   //      входных цен dPrice
   dPrice = PriceSeries(Input_Price_Customs, bar)-
                        PriceSeries(Input_Price_Customs, bar+1);
   //----+  
   dPriceA=MathAbs(dPrice);
   //----+ Два обращения к функции JurXSeries за номерами 0 и 1. 
   //      Параметр nJJurXLength 
   //не меняtтся на каждом баре (nJurXdin=0) 
   //проверка на отсутствие ошибки в предыдущей операции
   UPJRSX=JurXSeries(0,0,MaxBar,limit,Length,dPrice, bar,reset); 
   if(reset!=0)return(-1);
   DNJRSX=JurXSeries(1,0,MaxBar,limit,Length,dPriceA,bar,reset);
   if(reset!=0)return(-1); 
   //----+
   if (bar>MaxBar-w)JRSX=0;
   else if (DNJRSX!=0){JRSX=UPJRSX/DNJRSX;
   if(JRSX>1)JRSX=1;
   if(JRSX<-1)JRSX=-1;}else JRSX=0;
   //+---------------------------------------------------------------+ 
   //----+ Одно обращение к функции JJMASeries за номерам 0 
   //----+ Параметры nJMA.Phase и nJMA.Length не меняются на каждом 
   //      баре (nJMA.din=0)
   //+---------------------------------------------------------------+   
   JJRSX=JJMASeries(0,0,MaxBarJ,limit,Phase,Smooth,JRSX,bar,reset);
   //----+ проверка на отсутствие ошибки в предыдущей операции
   if(reset!=0)return(-1);
   //+---------------------------------------------------------------+  
   Ind_Buffer[bar]=JJRSX;  
}
//---- завершение вычислений значений индикатора
return(0);
}
//+------------------------------------------------------------------+



Функции T3Series()

Пример обращения к функции T3Series() (Три полосы Боллинджера с дополнительным T3 сглаживанием):
/*
Для  работы  индикатора  следует  положить файлы 
T3Series.mqh 
PriceSeries.mqh 
в папку (директорию): MetaTrader\experts\include\
Heiken Ashi#.mq4
в папку (директорию): MetaTrader\indicators\
*/
//+------------------------------------------------------------------+ 
//|                                          T3.6Bollinger Bands.mq4 | 
//|                        Copyright © 2006,        Nikolay Kositsin | 
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+ 
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru" 
//---- отрисовка индикатора в главном окне
#property indicator_chart_window 
//---- количество индикаторных буферов
#property indicator_buffers 7
//---- цвета индикатора
#property indicator_color1 Gray 
#property indicator_color2 Red
#property indicator_color3 Blue 
#property indicator_color4 Lime
#property indicator_color5 Blue
#property indicator_color6 Red
#property indicator_color7 Gray 
//---- стиль линий индикатора
#property indicator_style1 4
#property indicator_style2 2
#property indicator_style3 4
#property indicator_style4 4
#property indicator_style5 4
#property indicator_style6 2
#property indicator_style7 4
//---- ВХОДНЫЕ ПАРАМЕТРЫ ИНДИКАТОРА 
// период  усреднения J2Bollinger Bands
extern int        Bands_Period = 100;
extern double Bands_Deviations = 2.0; // девиатация 
extern int         MA_method = 0;   // метод усреднения
// глубина сглаживания полученного Moving Avereges
extern int         MA_Smooth = 20;
// глубина сглаживания полученных Bollinger Bands
extern int        Bands_Smooth = 20;
// параметр сглаживания, изменяющийся в пределах -100 ... +100, 
// влияет на качество переходного процесса; 
extern int    Smooth_Curvature = 100;
// cдвиг индикатора вдоль оси времени 
extern int         Bands_Shift = 0;
//Выбор цен, по которым производится расчёт индикатора 
/*(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW,
11-Heiken Ashi High, 12-Heiken Ashi Low, 13-Heiken Ashi Open, 
14-Heiken Ashi Close.)*/
extern int Input_Price_Customs = 0;
//---- индикаторные буферы
double UpperBuffer3  [];
double UpperBuffer2  [];
double UpperBuffer1  [];
double T3MovingBuffer[];
double LowerBuffer1  [];
double LowerBuffer2  [];
double LowerBuffer3  [];
double Series_buffer [];
//+------------------------------------------------------------------+ 
//----+ Введение функции T3Series 
//----+ Введение функции T3SeriesResize 
//----+ Введение функции T3SeriesAlert 
//----+ Введение функции T3_ErrDescr  
#include <T3Series.mqh> 
//+------------------------------------------------------------------+ 
//----+ Введение функции PriceSeries
//----+ Введение функции PriceSeriesAlert 
#include <PriceSeries.mqh>
//+------------------------------------------------------------------+  
//| T3.6Bollinger Bands initialization function        | 
//+------------------------------------------------------------------+  
int init()
  {
//---- определение стиля исполнения графика
   SetIndexStyle(0,DRAW_LINE); 
   SetIndexStyle(1,DRAW_LINE);
   SetIndexStyle(2,DRAW_LINE);
   SetIndexStyle(3,DRAW_LINE); 
   SetIndexStyle(4,DRAW_LINE);
   SetIndexStyle(5,DRAW_LINE); 
   SetIndexStyle(6,DRAW_LINE);
//---- 4 индикаторных буфера использованы для счёта  
   IndicatorBuffers(8);
   SetIndexBuffer(0,UpperBuffer3 );
   SetIndexBuffer(1,UpperBuffer2 );
   SetIndexBuffer(2,UpperBuffer1 );
   SetIndexBuffer(3,T3MovingBuffer);
   SetIndexBuffer(4,LowerBuffer1 );
   SetIndexBuffer(5,LowerBuffer2 );
   SetIndexBuffer(6,LowerBuffer3 );
   SetIndexBuffer(7,Series_buffer);
//---- установка значений индикатора, которые не будут видимы на графике
   SetIndexEmptyValue(0,0);
   SetIndexEmptyValue(1,0);
   SetIndexEmptyValue(2,0);
   SetIndexEmptyValue(3,0);
   SetIndexEmptyValue(4,0);
   SetIndexEmptyValue(5,0);
   SetIndexEmptyValue(6,0);
//---- установка номера бара, начиная с которого будет отрисовываться
//     индикатор  
   int drawbegin=100+Bands_Shift;
   SetIndexDrawBegin(0,drawbegin);
   SetIndexDrawBegin(1,drawbegin);
   SetIndexDrawBegin(2,drawbegin);
   SetIndexDrawBegin(3,drawbegin);
   SetIndexDrawBegin(4,drawbegin);
   SetIndexDrawBegin(5,drawbegin);
   SetIndexDrawBegin(6,drawbegin);
//---- горизонтальный сдвиг индикаторных линий  
   SetIndexShift (0, Bands_Shift); 
   SetIndexShift (1, Bands_Shift); 
   SetIndexShift (2, Bands_Shift); 
   SetIndexShift (3, Bands_Shift); 
   SetIndexShift (4, Bands_Shift); 
   SetIndexShift (5, Bands_Shift); 
   SetIndexShift (6, Bands_Shift); 
//---- имя для окон данных и лэйба для субъокон
   IndicatorShortName ("T3.4Bollinger Bands( Period="+Bands_Period+
        ", Deviations="+Bands_Deviations+")"); 
   SetIndexLabel (0, "Upper3 Bands");
   SetIndexLabel (1, "Upper2 Bands");
   SetIndexLabel (2, "Upper1 Bands"); 
   SetIndexLabel (4, "Lower1 Bands"); 
   SetIndexLabel (5, "Lower2 Bands"); 
   SetIndexLabel (6, "Lower3 Bands"); 
   string Moving;
   switch(MA_method)
       {
        case  0: Moving= "T3SMA";break;
        case  1: Moving= "T3EMA";break;
        case  2: Moving="T3SSMA";break;
        case  3: Moving="T3LWMA";break;
        default: Moving="T3SMA";
       }
   SetIndexLabel (3, "Moving Avereges "+Moving+" ("+Bands_Period+")");
//---- Установка формата точности отображения индикатора
   IndicatorDigits(Digits);
//----+ Изменение размеров буферных переменных функции T3Series, 
//nT3.number=7(Семь обращений к функции T3Series)
   if (Bands_Smooth<=1){if (T3SeriesResize(1)!=1)return(-1);}
   else if (T3SeriesResize(7)!=7)return(-1);
//---- установка алертов на недопустимые значения внешних переменных
   T3SeriesAlert(0,"MA_Smooth",MA_Smooth);
   T3SeriesAlert(0,"Bands_Period",Bands_Period);
   PriceSeriesAlert(Input_Price_Customs);
   if((MA_method<0)||(MA_method>3))
        Alert("Параметр MA_method должен быть от 0 до 3" 
        + " Вы ввели недопустимое " 
       +MA_method+ " будет использовано 0");
//---- корекция недопустимого значения параметра Bands_Period
   if(Bands_Period<1)Bands_Period=1; 
//---- завершение инициализации
   return(0);
  }
//+------------------------------------------------------------------+  
//| T3.6Bollinger Bands iteration function         | 
//+------------------------------------------------------------------+  
int start()
  {
//---- проверка количества баров на достаточность для расчёта
if(Bars-1<=Bands_Period) return(0);
//---- Введение переменных с плавающей точкой  
double deviation1,deviation2,deviation3,Temp_Series,sum,midline,
       priceswing,Resalt;
//----+ Введение целых переменных и получение уже подсчитанных баров
int reset,MaxBar,MaxBarBB,MaxBarBB1,bar,kk,counted_bars=IndicatorCounted();
//---- проверка на возможные ошибки
if (counted_bars<0)return(-1);
//---- последний подсчитанный бар должен быть пересчитан 
// (без этого пересчёта для counted_bars функция T3Series будет работать
// некорректно!!!)
if (counted_bars>0) counted_bars--;
//---- определение номера самого старого бара, начиная с которого
//будет произедён пересчёт новых баров
int limit=Bars-counted_bars-1;
//---- определение номера самого старого бара, начиная с которого 
//будет произедён пересчёт всех баров
MaxBar=Bars-1-Bands_Period; MaxBarBB=MaxBar-30-Bands_Period; 
MaxBarBB1=MaxBarBB-1;
//----+ загрузка входных цен в буфер для расчёта       
for(bar=limit;bar>=0;bar--)
    Series_buffer[bar]=PriceSeries(Input_Price_Customs,bar);
//---- проверка бара на достаточность для расчёта Bollinger Bands 
//---- инициализация нуля        
if (limit>MaxBar)
     {
      for(bar=limit;bar>=MaxBar;bar--)T3MovingBuffer[bar]=0;
      limit=MaxBar;
     }
//----+ цикл расчёта Moving Avereges
for(bar=limit;bar>=0;bar--)
     {
      //----+ формула для расчёта Moving Avereges
      Temp_Series=iMAOnArray(Series_buffer,0,Bands_Period,0,
                             MA_method, bar);
      //----+ сглаживание полученного Moving Avereges
      //----+ обращение к функции T3Series за номером 0. 
      // Параметры nT3.Curvature и nT3.Length не меняются на 
      // каждом баре (nT3.din=0)
      Resalt=T3Series(0,0,MaxBar,limit,Smooth_Curvature,MA_Smooth,
                      Temp_Series,bar,reset);
      //----+ проверка на отсутствие ошибки в предыдущей операции
      if(reset!=0)return(-1); 
      T3MovingBuffer[bar]=Resalt; 
     }     
//---- РАСЧЁТ Bollinger Bands 
//---- инициализация нуля      
if (limit>MaxBarBB)
     {
      for(bar=limit;bar>=MaxBarBB;bar--)
       {
        UpperBuffer2[bar]=0;
        UpperBuffer1[bar]=0;
        LowerBuffer1[bar]=0;
        LowerBuffer2[bar]=0;
       }
      limit=MaxBarBB;
     }
for(bar=limit;bar>=0;bar--)
   {       
     sum=0.0;
     midline=T3MovingBuffer[bar];
     kk=bar+Bands_Period-1;
     while(kk>=bar)
      {
       priceswing=PriceSeries(Input_Price_Customs,kk)-midline;
       sum+=priceswing*priceswing;
       kk--;
      }
     deviation2=Bands_Deviations*MathSqrt(sum/Bands_Period);     
     deviation1=0.5*deviation2;
     deviation3=1.5*deviation2;
     if (Bands_Smooth>1)
      {
       //----+ вычисление и T3 сглаживание Bollinger Bands      
       //----+ ------------------------------------------------------+        
       //----+ шесть параллельных обращений к функции T3Series за 
       //      номерами 1, 2, 3, 4, 5, 6. 
       //----+ Параметры nT3.Length не меняются на каждом баре 
       //      (nT3.din=0)
       //----+ ------------------------------------------------------+ 
       Resalt=T3Series(1,0,MaxBarBB1,limit,Smooth_Curvature,Bands_Smooth,
                       midline+deviation3,bar,reset);
       //----+ проверка на отсутствие ошибки в предыдущей операции
       if(reset!=0)return(-1); 
       UpperBuffer3[bar]=Resalt; 
       //----+ ------------------------------------------------------+ 
       Resalt=T3Series(2,0,MaxBarBB1,limit,Smooth_Curvature,Bands_Smooth,
                       midline+deviation2,bar,reset);
       //----+ проверка на отсутствие ошибки в предыдущей операции
       if(reset!=0)return(-1); 
       UpperBuffer2[bar]=Resalt; 
       //----+ ------------------------------------------------------+       
       Resalt=T3Series(3,0,MaxBarBB1,limit,Smooth_Curvature,Bands_Smooth,
                       midline+deviation1,bar,reset);
       //----+ проверка на отсутствие ошибки в предыдущей операции
       if(reset!=0)return(-1); 
       UpperBuffer1[bar]=Resalt; 
       //----+ ------------------------------------------------------+ 
       Resalt=T3Series(4,0,MaxBarBB1,limit,Smooth_Curvature,Bands_Smooth,
                       midline-deviation1,bar,reset);
       //----+ проверка на отсутствие ошибки в предыдущей операции
       if(reset!=0)return(-1); 
       LowerBuffer1[bar]=Resalt; 
       //----+ ------------------------------------------------------+ 
       Resalt=T3Series(5,0,MaxBarBB1,limit,Smooth_Curvature,Bands_Smooth,
                       midline-deviation2,bar,reset);
       //----+ проверка на отсутствие ошибки в предыдущей операции
       if(reset!=0)return(-1); 
       LowerBuffer2[bar]=Resalt; 
       //----+ ------------------------------------------------------+ 
       Resalt=T3Series(6,0,MaxBarBB1,limit,Smooth_Curvature,Bands_Smooth,
                       midline-deviation3,bar,reset);
       //----+ проверка на отсутствие ошибки в предыдущей операции
       if(reset!=0)return(-1); 
       LowerBuffer3[bar]=Resalt;        
       //----+ ------------------------------------------------------+ 
      }
     else 
      {
       //----+ вычисление Bollinger Bands без T3 сглаживания 
       UpperBuffer3[bar]=midline+deviation3; 
       UpperBuffer2[bar]=midline+deviation2;
       UpperBuffer1[bar]=midline+deviation1;
       LowerBuffer1[bar]=midline-deviation1;
       LowerBuffer2[bar]=midline-deviation2;
       LowerBuffer3[bar]=midline-deviation3;
      }
      
   }
//---- завершение вычислений значений индикатора
   return(0);
  }
//+-------------------------------------------------------------------+



Функции ParMASeries()

Пример обращения к функции ParMASeries() (ParMA мувинг с дополнительным JMA сглаживанием):

/*
Скользящая средняя ParMA,рассчитаннная на основе параболической 
регрессии с полосами 
 
Для  работы  индикатора  следует  положить файлы 
JJMASeries.mqh 
ParMASeries.mqh 
PriceSeries.mqh 
в папку (директорию): MetaTrader\experts\include\
Heiken Ashi#.mq4
в папку (директорию): MetaTrader\indicators\
*/
//+------------------------------------------------------------------+ 
//|                                                       JParMA.mq4 |
//|                       ParMA MQL4 CODE: Copyright © 2006, alexjou |
//|             JParMA Indicator: Copyright © 2006, Nikolay Kositsin |
//+------------------------------------------------------------------+ 
#property copyright "Copyright © 2006, Nikolay Kositsin"
#property link "farria@mail.redcom.ru" 
//---- отрисовка индикатора в основном окне
#property indicator_chart_window
//---- количество индикаторных буферов
#property indicator_buffers 1
//---- цвет индикатора 
#property indicator_color1 Red
//---- ВХОДНЫЕ ПАРАМЕТРЫ ИНДИКАТОРА 
extern int MA_Period  = 8; // период ParMA
extern int Length = 3;   // глубина сглаживания 
// параметр, изменяющийся в пределах -100 ... +100, 
//влияет на качество переходного процесса; 
extern int Phase  = 100;
extern int Shift  = 0;   // cдвиг индикатора вдоль оси времени 
//Выбор цен, по которым производится расчёт индикатора 
/*(0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED,
7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 
11-Heiken Ashi High, 12-Heiken Ashi Low, 13-Heiken Ashi Open, 
14-Heiken Ashi Close.) */
extern int Input_Price_Customs = 0;
//---- индикаторные буферы
double IndBuffer[];
//---- переменные с плавающей точкой 
double JResalt, Price, Resalt;
//+------------------------------------------------------------------+  
//----+ Введение функции JJMASeries 
//----+ Введение функции JJMASeriesResize 
//----+ Введение функции JJMASeriesAlert  
//----+ Введение функции JMA_ErrDescr  
#include <JJMASeries.mqh> 
//+------------------------------------------------------------------+ 
//----+ Введение функции ParMAMASeries 
//----+ Введение функции ParMASeriesResize 
//----+ Введение функции ParMASeriesAlert 
//----+ Введение функции ParMA_ErrDescr 
#include <ParMASeries.mqh> 
//+------------------------------------------------------------------+  
//----+ Введение функции PriceSeries
//----+ Введение функции PriceSeriesAlert 
#include <PriceSeries.mqh>
//+------------------------------------------------------------------+   
//| JParMA initialization function                                   |
//+------------------------------------------------------------------+ 
int init()
 {
  //---- Установка формата точности отображения индикатора
  IndicatorDigits(Digits);
  //---- определение стиля исполнения графика
  SetIndexStyle(0, DRAW_LINE);
  //---- 1 индикаторный буфер использован для счёта
  SetIndexBuffer(0, IndBuffer);
  //---- горизонтальный сдвиг индикаторной линии 
  SetIndexShift (0, Shift); 
  //---- установка значений индикатора, которые не будут видимы на
  //     графике
  SetIndexEmptyValue(0, 0.0); 
  //---- имя для окон данных и лэйба для субъокон 
  IndicatorShortName ("JParMA( Length="+Length+", Phase="+Phase+", Shift="+Shift+")");   
  SetIndexLabel(0, "JParMA Line");
  //---- установка номера бара, начиная с которого будет отрисовываться
         индикатор 
  SetIndexDrawBegin(0, MA_Period);
  //----+ Изменение размеров буферных переменных функции JJMASeries, 
  //nJMAnumber=1(Одно обращение к функции JJMASeries)
  if (JJMASeriesResize(1)!=1)return(-1);
  //----+ Изменение размеров буферных переменных функции ParMASeries, 
  //nParMAnumber=1(Одно обращение к функции ParMASeries)
  if (ParMASeriesResize(1)!=1)return(-1);
  //---- установка алертов на недопустимые значения внешних переменных
  JJMASeriesAlert (0,"Length",Length);
  JJMASeriesAlert (1,"Phase", Phase );
  ParMASeriesAlert(0,"MA_Period",MA_Period);
  PriceSeriesAlert(Input_Price_Customs);
  return(0);
 }
//+------------------------------------------------------------------+ 
//| JParMA iteration function                                        |
//+------------------------------------------------------------------+ 
int start()
 {
 //---- проверка количества баров на достаточность для расчёта
if (Bars-1<MA_Period)return(0);
//----+ Введение целых переменных и получение уже подсчитанных баров
int reset,MaxBar,MaxBarP,bar,Limit,counted_bars=IndicatorCounted();
//---- проверка на возможные ошибки
if (counted_bars<0)return(-1);
//---- последний подсчитанный бар должен быть пересчитан 
if (counted_bars>0) counted_bars--;
//---- определение номера самого старого бара, начиная с которого 
//будет произедён пересчёт всех баров
MaxBar=Bars-1; MaxBarP=MaxBar-MA_Period;
//---- определение номера самого старого бара, начиная с которого 
//будет произедён пересчёт новых баров 
Limit=Bars-counted_bars-1; 
 
//---- Вычисление индикатора
for (bar=Limit; bar>=0; bar--)
   { 
    //----+ 
     Price=PriceSeries(Input_Price_Customs,bar);
     //----+ получение исходного индикатора
     //----+ Обращение к функции ParMASeries за номером 0
     Resalt=ParMASeries(0,MaxBar,Limit,MA_Period,Price,bar,reset); 
     //----+ проверка на отсутствие ошибки в предыдущей операции
     if(reset!=0)return(-1);
     //----+ JMA сглаживание полученного индикатора, 
     //параметр nJMA.MaxBar уменьшен на MA_Period 
     //----+ Обращение к функции JJMASeries за номером 0, 
     // параметры nJMA.Phase и nJMA.Length не меняются на каждом баре
     // (nJMA.din=0)
     JResalt=JJMASeries(0,0,MaxBarP,Limit,Phase,Length,Resalt,bar,reset);
     //----+ проверка на отсутствие ошибки в предыдущей операции
     if(reset!=0)return(-1);
     IndBuffer[bar]=JResalt;
   }
 //----
  return(0);
 }
 //+-------------------------------------------------------------------+
Во всех индикаторах вместо обычно применяемого массива таймсерии Close[] используется функция PriceSeries(), с применением которой не должно возникнуть никаких проблем:
double  PriceSeries(int Input_Price_Customs, int bar)

Входной параметр Input_Price_Customs может изменяться от 0 и до 14. В зависимости от значения этого параметра функция возвращает значение цены для текущего графика по номеру бара, используемого в качестве второго параметра: 0-CLOSE, 1-OPEN, 2-HIGH, 3-LOW, 4-MEDIAN, 5-TYPICAL, 6-WEIGHTED, 7-Heiken Ashi Close, 8-SIMPL, 9-TRENDFOLLOW, 10-0.5*TRENDFOLLOW, 11-Heiken Ashi High, 12-Heiken Ashi Low, 13-Heiken Ashi Open, 14-Heiken Ashi Close. При необходимости в кейсы функции можно дописать какие-либо другие алгебраические выражения для определения входных цен на основе массивов таймсерий. Индикаторы с применением функции PriceSeries() очень удобно использовать при оптимизации и тестировании советников.


Заключение

В архиве NK_library.zip находится более ста индикаторов, написанных с применением этих алгоритмов. Этих примеров более чем достаточно, что научиться применять рассмотренные в этой статье функции для написания любых других индикаторов. Все индикаторы из архива с данными версиями функций сглаживания поддерживают экспертов и работают с ними без ошибок. Исключение составляют индикаторы, название которых оканчиваются на HTF. Эти индикаторы в силу специфики расчёта использоваться с экспертами не могут! Индикаторы из архива следует положить в папку клиентского терминала Метатрейдер: \MetaTrader\EXPERTS\indicators. Сами функции находятся в архиве в папке INCLUDE. Всё содержимое этой папки следует поместить в папку клиентского терминала Метатрейдер: \MetaTrader\EXPERTS\INCLUDE.