Практическая реализация цифровых фильтров на MQL5 для начинающих

19 марта 2010, 15:35
Nikolay Kositsin
3
5 357

Введение

Итак, в своей предыдущей статье я сделал анализ кода простейшего индикатора и немного коснулся темы взаимодействия этого индикатора с клиентским терминалом MetaTrader 5. Теперь, прежде чем идти дальше, нам следовало  бы повнимательнее присмотреться к результату компиляции эксперта, который отображается в закладке "Ошибки" окна "Инструменты" редактора MetaEditor. С этого можно и начать дальнейшее изучение предложенного мною ранее кода индикатора SMA.

Ошибки компиляции индикатора

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

  

который говорит о том, что компиляция прошла успешно, никаких ошибок нет, а значит рядом с файлом индикаторного кода с расширением .mq5 появился аналогичный с расширением .ex5.


  

Как правило, при обычной повседневной работе с программным кодом без ошибок работать невозможно. И они регулярно делаются программистом. На такой случай в редакторе имеется встроенный механизм проверки компилируемого кода на всевозможные ошибки, и, в случае их обнаружения, при компиляции MetaEditor выдаст полный перечень возникших ошибок.

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

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

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

Я приведу предполагаемый перечень возможных вариантов деструктивного изменения кода индикатора:

  1. Сделать пробел в каком-нибудь операторе или переменной
  2. Убрать знак точки с запятой ";"
  3. Добавить знак ";" в разных местах кода
  4. Удалить оператор
  5. Удалить или добавить одну из фигурных или круглых скобок
  6. Убрать знак запятой ","
  7. Добавить лишний входной параметр в функцию OnCalculate()
  8. Разделить переменную на ноль
  9. Заменить в строке оператора "if" знак "==" на "="
  10. Изменить в операторе цикла направление изменения переменной bar++ на bar--

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

Использование сленга на MQL5 

Если внимательно присмотреться к речи какого-нибудь конкретного индивида, то, при всём богатстве возможностей любого человеческого языка мира, оказывается, что в ходу у него далеко не весь этот, в общем-то обширный, перечень средств выражения своих мыслей и потребностей. В большинстве ситуаций получается, что действительный востребованный словарный запас значительно ниже имеющегося в наличии. Тот же самый принцип можно абсолютно аналогично использовать и в MQL5. На первых порах, пока происходит первоначальное освоение языка MQL5, стоит остановиться на сленге из наиболее общеупотребительных операторов и выражений этого языка программирования. А по мере усвоения этого языка постепенно расширять границы употребляемых средств.

Например, вполне можно остановиться на всего четырёх типах переменных - int, double, bool, string, условном операторе if-else, операторе цикла for, составном операторе {} и операторе return. Также следует основательно разобраться с использованием точки с запятой ";" и запятой ",". Пожалуй, было бы не лишним сразу определиться с математическими и тригонометрическими функциями. Чтобы набить руку и получить первоначальные навыки программирования, этого арсенала средств более чем достаточно! 

Дальнейшая косметическая доработка индикатора

Сами возможности MQL5  по косметическому улучшению индикатора, отображаемого в окнах клиентского терминала Metatrader, достаточно просты и стандартны. И состоят из операторов глобального уровня:

//---- авторство индикатора
#property copyright "2010, MetaQuotes Software Corp."
//---- ссылка на сайт автора
#property link      "https://www.mql5.com"
//---- номер версии индикатора
#property version   "1.00"
//---- отрисовка индикатора в основном окне
#property indicator_chart_window
//---- для расчёта и отрисовки индикатора использован один буфер
#property indicator_buffers 1
//---- использовано всего одно графическое построение
#property indicator_plots   1
//---- отрисовка индикатора в виде линии
#property indicator_type1   DRAW_LINE
//---- в качестве цвета линии индикатора использован красный цвет
#property indicator_color1  Red
//---- линия индикатора - непрерывная кривая
#property indicator_style1  STYLE_SOLID
//---- толщина линии индикатора равна 1
#property indicator_width1  1
//---- отображение метки индикатора
#property indicator_label1  "SMA"

И вызовов функций блока OnInit():

   //---- инициализации переменной для короткого имени индикатора
   string shortname;
   StringConcatenate(shortname, "SMA(", MAPeriod, ",", MAShift, ")");
   //--- создание метки для отображения в Окне данных
   PlotIndexSetString(0, PLOT_LABEL, shortname);   
   //--- создание имени для отображения в отдельном подокне и во всплывающей подсказке
   IndicatorSetString(INDICATOR_SHORTNAME, shortname);
   //--- определение точности отображения значений индикатора
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1);
   //--- запрет на отрисовку индикатором пустых значений
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
 

Функция StringConcatenate() делает сборку строки для имени индикатора по формуле:

   shortname = shortname + "SMA(" + MAPeriod + "," + MAShift + ")";

В блок OnCalculate() согласно рекомендациям статьи Индикатор от индикатора в MQL5 не мешало бы добавить вызов функции PlotIndexSetInteger():

   //---- расчёт стартового номера first для цикла пересчёта баров
   if (prev_calculated == 0) // проверка на первый старт расчёта индикатора
    { 
     first = MAPeriod - 1 + begin; // стартовый номер для расчёта всех баров
     //--- увеличим позицию начала данных на begin баров, вследствие расчетов на данных другого индикатора
     if(begin > 0)
      PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, begin + MAPeriod);
    }
   else first = prev_calculated - 1; // стартовый номер для расчёта новых баров

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

Результат предшествующего труда, как шаблон для построения новых индикаторов

Всё это, конечно, интересно, но возникает один вполне естественный, вопрос — а зачем было изобретать велосипед и ещё раз повторять в программном коде индикатор, который и так, в общем-то, имеется в наличии в клиентском терминале, да ещё в двух вариантах? В виде технического индикатора Moving Average.mq5 и пользовательского индикатора Custom Moving Average.mq5. Да хотя бы для того, чтобы научиться быстро писать код аналогичных индикаторов, просто используя предложенный мной ранее код индикатора SMA в качестве шаблона, по максимуму экономя свои интеллектуальные ресурсы! Например, можно попытаться построить код на MQL5 для такого цифрового фильтра, как FATL от компании Finware.

В общем виде формула для расчёта цифрового фильтра: 

FILTER = SUM (K(i) * CLOSE (i), FilterPeriod)

где:

  • SUM — сумма;
  • K(i) —весовой коэффициент;
  • CLOSE (i) — цена закрытия текущего бара;
  • FilterPeriod — число баров для усреднения. 
  •  

Эта формула по своему виду не очень-то и отличается от формулы индикатора SMA:

SMA = SUM ((1 / MAPeriod ) * CLOSE (i), MAPeriod)

Вся разница в том, что период, на котором производятся вычисления у цифрового фильтра, оказывается строго фиксированным и индивидуальным для конкретного цифрового фильтра, как, впрочем, и коэффициенты K(i). Сами весовые коэффициенты и период цифрового фильтра вычисляются с помощью специализированных алгоритмов, анализ которых выходит за границы темы данной статьи, так что в данной статье мы ограничимся только использованием уже готовых значений для цифрового фильтра FATL. Кому интересна сама идея цифровой фильтрации сигналов, могут зайти, например, на сайт Генератор цифровых методов. Сама формула одного из вариантов индикатора FATL на MQL4 уже давно не является тайной:

     FATL =  0.4360409450 * Close[bar + 0]
           + 0.3658689069 * Close[bar + 1]
           + 0.2460452079 * Close[bar + 2]
           + 0.1104506886 * Close[bar + 3]
           - 0.0054034585 * Close[bar + 4]
           - 0.0760367731 * Close[bar + 5]
           - 0.0933058722 * Close[bar + 6]
           - 0.0670110374 * Close[bar + 7]
           - 0.0190795053 * Close[bar + 8]
           + 0.0259609206 * Close[bar + 9]
           + 0.0502044896 * Close[bar + 10]
           + 0.0477818607 * Close[bar + 11]
           + 0.0249252327 * Close[bar + 12]
           - 0.0047706151 * Close[bar + 13]
           - 0.0272432537 * Close[bar + 14]
           - 0.0338917071 * Close[bar + 15]
           - 0.0244141482 * Close[bar + 16]
           - 0.0055774838 * Close[bar + 17]
           + 0.0128149838 * Close[bar + 18]
           + 0.0226522218 * Close[bar + 19]
           + 0.0208778257 * Close[bar + 20]
           + 0.0100299086 * Close[bar + 21]
           - 0.0036771622 * Close[bar + 22]
           - 0.0136744850 * Close[bar + 23]
           - 0.0160483392 * Close[bar + 24]
           - 0.0108597376 * Close[bar + 25]
           - 0.0016060704 * Close[bar + 26]
           + 0.0069480557 * Close[bar + 27]
           + 0.0110573605 * Close[bar + 28]
           + 0.0095711419 * Close[bar + 29]
           + 0.0040444064 * Close[bar + 30]
           - 0.0023824623 * Close[bar + 31]
           - 0.0067093714 * Close[bar + 32]
           - 0.0072003400 * Close[bar + 33]
           - 0.0047717710 * Close[bar + 34]
           + 0.0005541115 * Close[bar + 35]
           + 0.0007860160 * Close[bar + 36]
           + 0.0130129076 * Close[bar + 37]
           + 0.0040364019 * Close[bar + 38]; 

В MQL5 бары в индикаторных буферах считаются в направлении противоположном счёту баров в MQL4, так что для использования этой формулы в индикаторах в MQL5 следует заменить операцию инкремента внутри квадратных скобок на операцию декремента. Название массива таймсерии Close[] в виду его отсутствия в таком виде в MQL5 тоже следует заменить на более подходящий вариант price[]. Волне естественно, что легче всего это выполнить не вручную, а с помощью меню редактора:

Мы имеем регулярно встречающееся выражение "Close[bar +", которое следовало бы заменить на "price[bar -":

Нажимаем в этом окне кнопку "Заменить все". В итоге мы получим необходимую формулу для вычисления индикатора FATL в MQL5:

     FATL =  0.4360409450 * price[bar - 0]
           + 0.3658689069 * price[bar - 1]
           + 0.2460452079 * price[bar - 2]
           + 0.1104506886 * price[bar - 3]
           - 0.0054034585 * price[bar - 4]
           - 0.0760367731 * price[bar - 5]
           - 0.0933058722 * price[bar - 6]
           - 0.0670110374 * price[bar - 7]
           - 0.0190795053 * price[bar - 8]
           + 0.0259609206 * price[bar - 9]
           + 0.0502044896 * price[bar - 10]
           + 0.0477818607 * price[bar - 11]
           + 0.0249252327 * price[bar - 12]
           - 0.0047706151 * price[bar - 13]
           - 0.0272432537 * price[bar - 14]
           - 0.0338917071 * price[bar - 15]
           - 0.0244141482 * price[bar - 16]
           - 0.0055774838 * price[bar - 17]
           + 0.0128149838 * price[bar - 18]
           + 0.0226522218 * price[bar - 19]
           + 0.0208778257 * price[bar - 20]
           + 0.0100299086 * price[bar - 21]
           - 0.0036771622 * price[bar - 22]
           - 0.0136744850 * price[bar - 23]
           - 0.0160483392 * price[bar - 24]
           - 0.0108597376 * price[bar - 25]
           - 0.0016060704 * price[bar - 26]
           + 0.0069480557 * price[bar - 27]
           + 0.0110573605 * price[bar - 28]
           + 0.0095711419 * price[bar - 29]
           + 0.0040444064 * price[bar - 30]
           - 0.0023824623 * price[bar - 31]
           - 0.0067093714 * price[bar - 32]
           - 0.0072003400 * price[bar - 33]
           - 0.0047717710 * price[bar - 34]
           + 0.0005541115 * price[bar - 35]
           + 0.0007860160 * price[bar - 36]
           + 0.0130129076 * price[bar - 37]
           + 0.0040364019 * price[bar - 38];

Теперь можно приступать к построению кода индикатора, алгоритм вычисления которого только что был рассмотрен. Для этого прежде всего открываем индикатор SMA_1.mq5 в редакторе и сохраняем его под именем FATL.mq5. Шаблон индикатора готов и теперь нам предстоит заменить в нём сам алгоритм расчёта этого индикатора и произвести в индикаторе некоторые, большей частью косметические, изменения переменных. Прежде всего, следует выделить весь блок последней вышеозначенной формулы для расчёта фильтра FATL и скопировать его в буфер обмена Windows. После этого, теперь уже в коде индикатора Fatl.mq5  удаляем весь код внутри оператора цикла, кроме последней инициализации индикаторного буфера:

   //---- основной цикл расчёта индикатора
   for(bar = first; bar < rates_total; bar++)
    {


     
     //---- Инициализация ячейки индикаторного буфера полученным значением SMA
     ExtLineBuffer[bar] = SMA;
    }

А на место удалённого кода вставляем алгоритм расчёта цифрового фильтра FATL из буфера обмена Windows. Дальше следовало бы сделать чисто формальную замену регулярно повторяющегося в коде слова SMA на более подходящее по смыслу FATL с помощью описанной мной выше процедуры замены. Абсолютно аналогично, не мешало бы заменить и названия входных переменных MAPeriod и MAShift на FATLPeriod и FATLShft соответственно. А переменную FATLPeriod убрать из внешних переменных по той причине, что она имеет фиксированное значение, равное 39. По этой же причине её следует убрать из оператора сборки строки StringConcatenate() в функции OnInit(). Надобность в локальной переменной iii исчезла вообще, и потому её не мешало бы удалить. Ну и напоследок, можно поменять цвет линии индикатора на синий, а саму линию сделать потолще.

После этих, в общем-то, нехитрых манипуляций с кодом SMA_1.mq5 мы получаем желаемый код индикатора FATL.mq5:

//+------------------------------------------------------------------+
//|                                                         Fatl.mq5 |
//|                        Copyright 2010, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
//---- авторство индикатора
#property copyright "2010, MetaQuotes Software Corp."
//---- ссылка на сайт автора
#property link      "https://www.mql5.com"
//---- номер версии индикатора
#property version   "1.00"
//---- отрисовка индикатора в основном окне
#property indicator_chart_window
//---- для расчёта и отрисовки индикатора использован один буфер
#property indicator_buffers 1
//---- использовано всего одно графическое построение
#property indicator_plots   1
//---- отрисовка индикатора в виде линии
#property indicator_type1   DRAW_LINE
//---- в качестве цвета линии индикатора использован синий цвет
#property indicator_color1  Blue
//---- линия индикатора - непрерывная кривая
#property indicator_style1  STYLE_SOLID
//---- толщина линии индикатора равна 2
#property indicator_width1  2
//---- отображение метки индикатора
#property indicator_label1  "FATL"

//---- входные параметры индикатора
input int FATLShift = 0; // сдвиг Фатла по горизонтали в барах 

//---- объявление и инициализация переменной для хранения количества расчётных баров
int FATLPeriod = 39;

//---- объявление динамического массива, который будет в 
//     дальнейшем использован в качестве индикаторного буфера
double ExtLineBuffer[]; 
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
   //---- превращение динамического массива ExtLineBuffer в индикаторный буфер
   SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA);
   //---- осуществление сдвига индикатора по горизонтали на FATLShift
   PlotIndexSetInteger(0, PLOT_SHIFT, FATLShift);
   //---- установка позиции, с которой начинается отрисовка индикатора
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, FATLPeriod);
   //---- инициализации переменной для короткого имени индикатора
   string shortname;
   StringConcatenate(shortname, "FATL(", FATLShift, ")");
   //---- создание метки для отображения в DataWindow
   PlotIndexSetString(0, PLOT_LABEL, shortname);   
   //---- создание имени для отображения в отдельном подокне и во всплывающей подсказке
   IndicatorSetString(INDICATOR_SHORTNAME, shortname);
   //---- определение точности отображения значений индикатора
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1);
   //---- запрет на отрисовку индикатором пустых значений
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,    // количество истории в барах на текущем тике
                const int prev_calculated,// количество истории в барах на предыдущем тике
                const int begin,          // номер начала достоверного отсчёта баров
                const double &price[]     // ценовой массив для расчёта индикатора
              )
  {
//----+   
   //---- проверка количества баров на достаточность для расчёта
   if (rates_total < FATLPeriod - 1 + begin)
    return(0);
   
   //---- объявления локальных переменных 
   int first, bar;
   double Sum, FATL;
   
   //---- расчёт стартового номера first для цикла пересчёта баров
   if (prev_calculated == 0) // проверка на первый старт расчёта индикатора
    { 
     first = FATLPeriod - 1 + begin; // стартовый номер для расчёта всех баров
     //--- увеличим позицию начала данных на begin баров, вследствие расчетов на данных другого индикатора
     if(begin > 0)
      PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, begin + FATLPeriod);
    }
   else first = prev_calculated - 1; // стартовый номер для расчёта новых баров

   //---- основной цикл расчёта индикатора
   for(bar = first; bar < rates_total; bar++)
    {
     //---- 
     FATL =  0.4360409450 * price[bar - 0]
           + 0.3658689069 * price[bar - 1]
           + 0.2460452079 * price[bar - 2]
           + 0.1104506886 * price[bar - 3]
           - 0.0054034585 * price[bar - 4]
           - 0.0760367731 * price[bar - 5]
           - 0.0933058722 * price[bar - 6]
           - 0.0670110374 * price[bar - 7]
           - 0.0190795053 * price[bar - 8]
           + 0.0259609206 * price[bar - 9]
           + 0.0502044896 * price[bar - 10]
           + 0.0477818607 * price[bar - 11]
           + 0.0249252327 * price[bar - 12]
           - 0.0047706151 * price[bar - 13]
           - 0.0272432537 * price[bar - 14]
           - 0.0338917071 * price[bar - 15]
           - 0.0244141482 * price[bar - 16]
           - 0.0055774838 * price[bar - 17]
           + 0.0128149838 * price[bar - 18]
           + 0.0226522218 * price[bar - 19]
           + 0.0208778257 * price[bar - 20]
           + 0.0100299086 * price[bar - 21]
           - 0.0036771622 * price[bar - 22]
           - 0.0136744850 * price[bar - 23]
           - 0.0160483392 * price[bar - 24]
           - 0.0108597376 * price[bar - 25]
           - 0.0016060704 * price[bar - 26]
           + 0.0069480557 * price[bar - 27]
           + 0.0110573605 * price[bar - 28]
           + 0.0095711419 * price[bar - 29]
           + 0.0040444064 * price[bar - 30]
           - 0.0023824623 * price[bar - 31]
           - 0.0067093714 * price[bar - 32]
           - 0.0072003400 * price[bar - 33]
           - 0.0047717710 * price[bar - 34]
           + 0.0005541115 * price[bar - 35]
           + 0.0007860160 * price[bar - 36]
           + 0.0130129076 * price[bar - 37]
           + 0.0040364019 * price[bar - 38]; 
     
     //---- Инициализация ячейки индикаторного буфера полученным значением FATL
     ExtLineBuffer[bar] = FATL;
    }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

После компиляции индикатор можно протестировать на графике клиентского терминала:

 

Вполне естественно, что полученный код индикатора FATL можно использовать в качестве шаблона для построения других аналогичных фильтров. Но теперь задача серьёзно упрощается. В имеющемся у нас в наличии коде вполне достаточно заменить формулу расчёта фильтра, сделать механическую замену регулярно встречающегося слова FATL на, например, более универсальное DIGFILTER и проинициализировать теперь уже переменную DIGFILTERPeriod необходимым размером цифрового фильтра.

Общее решение задачи по построению цифровых фильтров в клиентском терминале 

Только что рассмотренный вариант индикатора является частным, единичным случаем решения общей задачи цифровой фильтрации сигналов. Было бы здорово иметь под руками индикатор, который бы представлял собой общее решение и таким образом позволял бы построить любой цифровой фильтр с помощью всего одного индикатора. Такая задача была уже давно решена ещё для клиентского терминала MetaTrader4 с помощью модуля DF.dll Сергеем Ильюхиным. Так что никакого труда не составляет использовать его для решения нашей задачи и в клиентском терминале MetaTrader 5.   В этом модуле представлена функция DigitalFilter():

DigitalFilter(int FType, int P1, int D1, int A1, int P2, int D2, int A2, double Ripple, int Delay, double& array[]); 

Она позволяет получать коэффициенты цифрового фильтра в виде массива array[]. В этот массив размером 1500 функция пишет коэффициенты цифрового фильтра по ссылке (знак '&' после объявления типа переменной этого массива). Функция принимает значения десяти входных параметров и возвращает размер цифрового фильтра. Так что этого вполне достаточно, чтобы построить универсальный цифровой фильтр. Вся задача сводиться к тому, чтобы сделать на глобальном уровне в имеющемся индикаторе импорт DLL библиотеки, получить в блоке инициализации индикатора массив коэффициентов и сделать на основе этих коэффициентов универсальный расчёт фильтра в блоке OnCalculate(). Сами входные переменные функции DigitalFilter() следует вытащить во входные переменные индикатора. Что, собственно говоря, мы сейчас и сделаем.

Сам импорт файла DF.dll никакой сложности не представляет. Это всего три строки программного кода:

//---- импорт DLL файла
#import "DF.dll"
int DigitalFilter(int FType, int P1, int D1, int A1, int P2, int D2, int A2, double Ripple, int Delay, double& array[]); 
#import

После этого все внешние переменные функции DigitalFilter() делаем входными переменными индикатора:

//---- входные параметры индикатора
input int FType = LPF;        //Тип фильтра
                              //0 - ФНЧ (FATL/SATL/KGLP), 1 - ФВЧ (KGHP), 2 - полосовой (RBCI/KGBP), 3 - режекторный (KGBS)
input int    P1 = 28;         //Период отсечки 1, бар
input int    D1 = 19;         //Период отсечки переходного процесса 1, бар
input int    A1 = 40;         //Затухание в полосе задержки 1, дБ
input int    P2 = 0;          //Период отсечки 2, бар
input int    D2 = 0;          //Период отсечки переходного процесса 2, бар
input int    A2 = 0;          //Затухание в полосе задержки 2, дБ
input int    Delay = 0;       //Задержка, бар
input double Ripple = 0.08;   //Биения в полосе пропускания, дБ 
input int    FILTERShift = 0; //сдвиг мувинга по горизонтали в барах 

На глобальном уровне делаем только объявление переменной FILTERPeriod без инициализации:

//---- объявление переменной для хранения количества расчётных баров
int FILTERPeriod;


На глобальном уровне объявляем динамический массив для хранения коэффициентов фильтра:

//---- объявление массива для коэффициентов цифрового фильтра
double FILTERTable[];

Теперь переходим в блок функции OnInit(). Использовать массив FILTERTable[] сразу в качестве параметра функции DigitalFilter() не совсем логично. Для этого следовало бы сделать его размер аж 1500 элементов, из которых в блоке OnCalculate() зачастую будет использовано от силы 100 - 200.  Гораздо экономнее будет в такой ситуации внутри функции OnInit() использовать локально объявленный массив Array[1500]. Необходимое количество данных из которого переписать в массив FILTERTable[]. После выхода из функции  OnInit() большой массив Array[] будет уничтожен, а необходимые данные останутся в массиве FILTERTable[], который будет иметь размер равный длине цифрового фильтра FILTERPeriod. Вот вариант кода, который использован для этой цели:

   //---- Вычисление коэффициентов цифрового фильтра и определение размера буфера FILTERTable[]
   double Array[1500];
   FILTERPeriod = DigitalFilter(FType, P1, D1, A1, P2, D2, A2, Ripple, Delay, Array); 
   //----  изменение размера буфера FILTERTable[] под требуемое количество коэффициентов цифрового фильтра
   if (FILTERPeriod <= 0)
    {
     Print("Входные параметры некорректны. Работа индикатора невозможна!");
     return;
    }
   //---- копируем данные из временного массива размером 1500 в основной массив с размером FILTERPeriod
   ArrayCopy(FILTERTable, Array, 0, 0, FILTERPeriod);

Внутри функции OnCalculate() код для расчёта фильтра в этой ситуации становится совсем простым:

     //---- формула для вычисления цифрового фильтра
     FILTER =  0.0;
     for(iii = 0; iii < FILTERPeriod; iii++)
      FILTER += FILTERTable[iii] * price[bar - iii];

Итоговый вариант кода этого индикатора представлен индикатором DFilter_0.mq5. Интерфейс этого индикатора можно самую малость улучшить. Дело в том, что входная переменная индикатора принимает значения от 0 и до 3,

input int FType = 0; //Тип фильтра
                     //0 - ФНЧ (FATL/SATL/KGLP), 1 - ФВЧ (KGHP), 2 - полосовой (RBCI/KGBP), 3 - режекторный (KGBS)

которые гораздо удобнее воспринимать не в численной форме, а в виде названий соответствующих фильтров: 0 - ФНЧ (FATL/SATL/KGLP), 1 - ФВЧ (KGHP), 2 - полосовой (RBCI/KGBP), 3 - режекторный (KGBS). Для такого случая в MQL5 существует особый тип переменных - перечисления. В нашем случае, мы до объявлений входных параметров индикатора объявляем и инициализируем перечисление:

//---- объявление и инициализация перечисления типов цифровых фильтров
enum FType_ //Тип фильтра
{
  LPF, //ФНЧ (FATL/SATL/KGLP)
  HPF, //ФВЧ (KGHP)
  BPF, //полосовой (RBCI/KGBP)
  BSF  //режекторный (KGBS)
};

После этого заменяем в объявлении внешнего параметра индикатора тип используемой переменной:

input FType_ FType = LPF; //Тип фильтра

В результате получается следующий вид выбора значений для этого параметра в диалоговом окне индикатора:

 

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

/*
* <<< ЦИФРОВЫЕ ФИЛЬТРЫ ДЛЯ METATRADER 5 >>>
*
* Файл DF.dll следует положить в "\MetaTrader5\MQL5\Libraries\"
* Внимание! Для работы DF.dll требуется три дополнительных DLL, 
* содержащих блок математической обработки - bdsp.dll, lapack.dll, mkl_support.dll,
* которые должны быть установлены в "C:\Windows\System32\" для 32-х разрядных операционных систем Windows
* или в "C:\Windows\SysWOW64\" для 64-х разрядных операционных систем Windows.
*
* Перед использованием убедитесь:
* 
* 1. что установлен пункт "Разрешить импорт DLL" в настройках Меню->Сервис->Настройки->Советники
* 2. Что в директории "C:\Windows\System32\" или в "C:\Windows\SysWOW64\" имеются 
* Bdsp.dll, lapack.dll, mkl_support.dll - вспомогательные математические библиотеки
*
* Описание входных параметров:
* 
* Ftype -  Тип фильтра: 0 - ФНЧ (FATL/SATL/KGLP), 1 - ФВЧ (KGHP), 
*          2 - полосовой (RBCI/KGBP), 3 - режекторный (KGBS)
* P1 -     Период отсечки P1, бар
* D1 -     Период отсечки переходного процесса D1, бар
* A1 -     Затухание в полосе задержки А1, дБ
* P2 -     Период отсечки P2, бар
* D2 -     Период отсечки переходного процесса D2, бар
* A2 -     Затухание в полосе задержки А2, дБ
* Ripple - Биения в полосе пропускания, дБ
* Delay -  Задержка, бар
*
* Для ФНЧ и ФВЧ значения параметров P2,D2,A2 игнорируются.
* Условия работы:
* ФНЧ: P1>D1
* ФВЧ: P1<D1
* Полосовой и режекторный: D2>P2>P1>D1
*/
//+------------------------------------------------------------------+
//|         Digital Low Pass (FATL/SATL, KGLP) Filter    DFilter.mq5 | 
//|                    Digital Filter: Copyright (c) Sergey Ilyukhin |
//|                           Moscow, qpo@mail.ru  http://fx.qrz.ru/ |
//|                              MQL5 CODE: 2010,   Nikolay Kositsin |
//|                              Khabarovsk,   farria@mail.redcom.ru | 
//+------------------------------------------------------------------+
//---- авторство индикатора
#property copyright "2005, Sergey Ilyukhin, Moscow"
//---- ссылка на сайт автора
#property link      "http://fx.qrz.ru/"
//---- номер версии индикатора
#property version   "1.00"
//---- отрисовка индикатора в основном окне
#property indicator_chart_window
//---- для расчёта и отрисовки индикатора использован один буфер
#property indicator_buffers 1
//---- использовано всего одно графическое построение
#property indicator_plots   1
//---- отрисовка индикатора в виде линии
#property indicator_type1   DRAW_LINE
//---- в качестве цвета линии индикатора использован синий цвет
#property indicator_color1  DarkViolet
//---- линия индикатора - непрерывная кривая
#property indicator_style1  STYLE_SOLID
//---- толщина линии индикатора равна 2
#property indicator_width1  2
//---- отображение метки индикатора
#property indicator_label1  "DFilter"

//---- объявление и инициализация перечисления типов цифровых фильтров
enum FType_ //Тип фильтра
{
  LPF, //ФНЧ (FATL/SATL/KGLP)
  HPF, //ФВЧ (KGHP)
  BPF, //полосовой (RBCI/KGBP)
  BSF, //режекторный (KGBS)
};

//---- входные параметры индикатора
input FType_ FType = LPF;     //Тип фильтра
                              //0 - ФНЧ (FATL/SATL/KGLP), 1 - ФВЧ (KGHP), 2 - полосовой (RBCI/KGBP), 3 - режекторный (KGBS)
input int    P1 = 28;         //Период отсечки 1, бар
input int    D1 = 19;         //Период отсечки переходного процесса 1, бар
input int    A1 = 40;         //Затухание в полосе задержки 1, дБ
input int    P2 = 0;          //Период отсечки 2, бар
input int    D2 = 0;          //Период отсечки переходного процесса 2, бар
input int    A2 = 0;          //Затухание в полосе задержки 2, дБ
input int    Delay = 0;       //Задержка, бар
input double Ripple = 0.08;   //Биения в полосе пропускания, дБ 
input int    FILTERShift = 0; //сдвиг мувинга по горизонтали в барах 

//---- импорт DLL файла
#import "DF.dll"
int DigitalFilter(int FType, int P1, int D1, int A1, int P2, int D2, int A2, double Ripple, int Delay, double& array[]); 
#import

//---- объявление и инициализация переменной для хранения количества расчётных баров
int FILTERPeriod;

//---- объявление динамического массива, который будет в 
//     дальнейшем использован в качестве индикаторного буфера
double ExtLineBuffer[]; 

//---- объявление и инициализация массива для коэффициентов цифрового фильтра
double FILTERTable[]; 

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+  
void OnInit()
  {
//----+
   //---- превращение динамического массива ExtLineBuffer в индикаторный буфер
   SetIndexBuffer(0, ExtLineBuffer, INDICATOR_DATA);
   //---- осуществление сдвига индикатора по горизонтали на FILTERShift
   PlotIndexSetInteger(0, PLOT_SHIFT, FILTERShift);
   //---- установка позиции, с которой начинается отрисовка индикатора
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, FILTERPeriod);
   //---- инициализации переменной для короткого имени индикатора
   string shortname;
   StringConcatenate(shortname, "FILTER(", FILTERShift, ")");
   //---- создание метки для отображения в DataWindow
   PlotIndexSetString(0, PLOT_LABEL, shortname);   
   //---- создание имени для отображения в отдельном подокне и во всплывающей подсказке
   IndicatorSetString(INDICATOR_SHORTNAME, shortname);
   //---- определение точности отображения значений индикатора
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 1);
   //---- запрет на отрисовку индикатором пустых значений
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, 0.0);
   //---- Вычисление коэффициентов цифрового фильтра и определение размера буфера FILTERTable[]
   double Array[1500];
   FILTERPeriod = DigitalFilter(FType, P1, D1, A1, P2, D2, A2, Ripple, Delay, Array); 
   //----  изменение размера буфера FILTERTable[] под требуемое количество коэффициентов цифрового фильтра
   if (FILTERPeriod <= 0)
    {
     Print("Входные параметры некорректны. Работа индикатора невозможна!");
     return;
    }
   //---- копируем данные из временного массива размером 1500 в основной массив с размером FILTERPeriod
   ArrayCopy(FILTERTable, Array, 0, 0, FILTERPeriod);
//----+
  }
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(
                const int rates_total,    // количество истории в барах на текущем тике
                const int prev_calculated,// количество истории в барах на предыдущем тике
                const int begin,          // номер начала достоверного отсчёта баров
                const double &price[]     // ценовой массив для расчёта индикатора
               )
  {
//----+   
   //---- проверка количества баров на достаточность для расчёта
   if (rates_total < FILTERPeriod - 1 + begin)
    return(0);
   
   //---- объявления локальных переменных 
   int first, bar, iii;
   double Sum, FILTER;
   
   //---- расчёт стартового номера first для цикла пересчёта баров
   if (prev_calculated == 0)           // проверка на первый старт расчёта индикатора
    { 
     first = FILTERPeriod - 1 + begin; // стартовый номер для расчёта всех баров
     //---- увеличим позицию начала данных на begin баров, вследствие расчетов на данных другого индикатора
     if(begin > 0)
      PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, begin + FILTERPeriod);
    }
   else first = prev_calculated - 1;   // стартовый номер для расчёта новых баров

   //---- основной цикл расчёта индикатора
   for(bar = first; bar < rates_total; bar++)
    {
     //---- формула для вычисления цифрового фильтра
     FILTER =  0.0;
     for(iii = 0; iii < FILTERPeriod; iii++)
      FILTER += FILTERTable[iii] * price[bar - iii];

     //---- Инициализация ячейки индикаторного буфера полученным значением FILTER
     ExtLineBuffer[bar] = FILTER;
    }
//----+     
   return(rates_total);
  }
//+------------------------------------------------------------------+

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

Заключение

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

Прикрепленные файлы |
dll.rar (1302.47 KB)
sma_.mq5 (3.62 KB)
sma_1.mq5 (4.56 KB)
fatl.mq5 (6.12 KB)
dfilter.mq5 (8.31 KB)
dfilter_0.mq5 (8.08 KB)
anderson83ad
anderson83ad | 17 авг 2013 в 05:40

DLL не грузится почему-то пишет что библиотека не 64х

Сергей
Сергей | 18 окт 2014 в 17:47
anderson83ad:

DLL не грузится почему-то пишет что библиотека не 64х

Подниму тему :)

Тоже пишет: "DF.dll' is not 64-bit version. Зашел на fx.qrz.ru, качнул оттуда четыре нужных дллки, но все так-же.

Может, как-то можно их подключить к 64 разрядному МТ? 

А нет ли у кого 64 битной версии этих библиотек?

Лучше бы, конечно, формулы для расчета коэффициентов ФНЧ :) Тогда можно было бы самому написать их расчет в MQL. 

 

Спасибо тем, кто выручит!

PS   Если таких не найдется :) , то придется поставить на виртуальную машину 32 разрядный МТ, там написать эксперт для получения массивов коэффициентов и записи их в файл. И потом грузить их из файла. 

Мне нужны эти коэфф. для всех TF с М1 по 250 минут с дискретностью минута. Поэтому не могу использовать только стандартные ТФ


Eugeniy Lugovoy
Eugeniy Lugovoy | 18 окт 2014 в 18:45
Автор, прикрепи исходники DLL, чтобы ребята скомпилировать могли под x64 или сам скомпилируй и выложи.
MQL5: Анализ и обработка отчетов Commodity Futures Trading Commission (CFTC) в MetaTrader 5 MQL5: Анализ и обработка отчетов Commodity Futures Trading Commission (CFTC) в MetaTrader 5

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

Знакомство с MQL5: написание простого советника и индикатора Знакомство с MQL5: написание простого советника и индикатора

В этой статье проведен краткий обзор языка MQL5, приведен пример написания советника и индикатора. Данная статья ориентирована как на читателей, знакомых с программированием на языке MQL4, так и на тех, кто только начинает знакомство с программированием торговых систем и индикаторов.

Построение излучений индикаторов в MQL5 Построение излучений индикаторов в MQL5

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

Когда нужно использовать указатели в MQL5 Когда нужно использовать указатели в MQL5

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