Статьи по MQL4
Оптимизация индикатора

Оптимизация индикатора


например, forex

Индикатор, который мы создали, несмотря на свой малый размер, обладает большим недостатком. Этот индикатор потребляет слишком много ресурсов процессора. Если посмотреть закладку «Эксперты» (в который видны логи, создаваемые программами MQL-4), то увидим множество выводов функции Print()(речь идет об индикаторе CrossMA-2.mq4):

 
 


Так как, функция start() вызывается на каждом тике, цикл for() выполняется при каждом изменении цены. Если вы обратили внимание, «Мастер создания советников» создал переменную counted_bars (посчитанные бары), рассмотрим ее поведение при работе индикатора. Я добавил новую переменную limit, а также сделал вывод на печать .

 
 


Логика работы индикатора не изменилась, по-прежнему цикл начинает считать от i=Bars-1 (limit=Bars-1). Но наблюдение за выводом Print() в блоке start() позволяет улучшить работу индикатора.

 
 


Видим, что при запуске индикатора, при первом вызове start() counted_bars равен нулю, Bars равен 4413, а limit равен 4412 (на единицу меньше, чем Bars). При втором вызове counted_bars уже равен 4412 и в дальнейшем не меняется на текущем баре. Получается, что значения индикатора рассчитываются полностью в первый раз, а потом на каждом тике происходит ненужный повторный расчет, вместо того, чтобы рассчитывать значения индикатора только на нулевом баре, который еще не завершился. Если мы сможем уклониться от ненужного расчета, то существенно разгрузим процессор компьютера. Представьте, что у нас открыто 10 графиков, и на каждом висят по три подобных неоптимизированных индикатора. При первом запуске counted_bars=0, проверим это условие, и если оно выполняется, то ограничитель limit оставим как есть.

 
 


Если индикатор уже рассчитывался, то будем считать только на нулевом баре. Ноль получим как Bars-counted_bars-1 (4413 – 4412 -1).

 
 


Новый модернизированный индикатор подтверждает наши предположения:

 
 


Теперь, в первый раз индикатор рассчитывается на 4413 барах, а последующие вычисления производятся только для одного (нулевого) бара. Что будет, когда текущий бар завершится и откроется новый бар? Ответ на этот вопрос легко получить самому, если пронаблюдать вывод индикатора на минутном тайм-фрейме. Полезно для любого нового индикатора, который вы написали, вставить именно эту строчку: Print("Bars=",Bars," limit=",limit," counted_bars=",counted_bars); Это позволит сразу увидеть первую возможную ошибку – неоптимизированный алгоритм расчета индикатора. Вторую распространенную ошибку (пережатость алгоритма расчета или переоптимизация) мы рассмотрим позднее.

#property copyright "MetaQuotes"
#property link      "http://forum.alpari-idc.ru/viewtopic.php?t=48186"

#property indicator_chart_window #property indicator_buffers 2 #property indicator_color1 Blue #property indicator_color2 Red
//---- input parameters extern int LongPeriod=21; extern int ShortPeriod=13;
//---- buffers double ExtMapBuffer1[]; double ExtMapBuffer2[];
//----
int init() { Print("Выполняется init()");
SetIndexStyle(0,DRAW_ARROW); SetIndexArrow(0,241); SetIndexBuffer(0,ExtMapBuffer1);
SetIndexEmptyValue(0,0.0); SetIndexStyle(1,DRAW_ARROW); SetIndexArrow(1,242);
SetIndexBuffer(1,ExtMapBuffer2); SetIndexEmptyValue(1,0.0); return(0);
} //---- int deinit() { Print("Выполняется deinit()"); return(0);
} //----
int start() { int counted_bars=IndicatorCounted(); int limit; // ограничитель расчетов
double maLongCur, maLongPrev, maShortCur, maShortPrev;
// объявим служебные переменные, в которых будем хранить ценовые координаты стрелок double value1,value2;

if (counted_bars==0) limit=Bars-1; // посчитанных баров еще нет, будет считать с самого начала

if (counted_bars>0) limit=Bars-counted_bars-1; // вычтем из числа доступных баров количество
//посчитанных баров и уменьшим на единицу
Print("Bars=",Bars," limit=",limit," counted_bars=",counted_bars);

for(int i=limit;i>=0;i--) {
// обнулим службные переменные value1=0.0; value2=0.0; maLongCur=iMA(NULL,0,LongPeriod,0,MODE_SMA,PRICE_CLOSE,i+1);// медленная средняя на предыдущем баре
maLongPrev=iMA(NULL,0,LongPeriod,0,MODE_SMA,PRICE_CLOSE,i+2);// медленная средняя на предпредыдущем баре
maShortCur=iMA(NULL,0,ShortPeriod,0,MODE_SMA,PRICE_CLOSE,i+1);// быстрая средняя на предпредыдущем баре
maShortPrev=iMA(NULL,0,ShortPeriod,0,MODE_SMA,PRICE_CLOSE,i+2);// быстрая средняя на предыдущем баре


//Если есть сигнал к покупке на предыдущем баре - поставим синюю стрелку вверх // по цене открытия бара if((maShortCur>maLongCur)&&(maShortPrev<maLongPrev)) { value1=Open[i];//запомним цену голубой стрелки
} //Если есть сигнал к продаже на предыдущем баре - поставим красную стрелку вниз // по цене открытия бара if((maShortCur<maLongCur)&&(maShortPrev>maLongPrev)) {
value2=Open[i];//запомним цену красной стрелки }
ExtMapBuffer1[i]=value1;
ExtMapBuffer2[i]=value2; }
//---- return(0); }


На первый взгляд может показаться, что проблема оптимального расчета пользовательского индикатора не настолько серьезна, тем более, что с каждым годом выходят все более мощные процессоры, и новые компьютеры становятся настоящими числомолотилками. Тем не менее, навыки правильного написания индикатора пригодятся в любом виде деятельности, так как они приучают мыслить логически и экономно, а для трейдера эта черта крайне необходима. Чтобы показать важность правильного подхода, мы немного забежим вперед и прогоним простейшего эксперта, который не открывает сделок и вообще не совершает никаких торговых операций, а только содержит вызов нашего индикатора на каждом новом баре один раз. Я создал два советника, один содержит вызовы «правильного» - оптимизированного индикатора, а второй содержит вызовы «неправильного» индикатора, наш самый первый рабочий вариант индикатора. Для вызова пользовательских индикаторов используется функция iCustom(Инструмент,Тайм_фрейм,Имя_индикатора, параметры_пользовательского_индикатора, номер_индекса, номер_бара). Фактически, проверяются первые три параметра и два последних параметра, а остальные параметры, которые расположены между ними, считаются параметрами индикатора и передаются непосредственно пользовательскому индикатору. Правильность передаваемых параметров компилятором не проверяется, так как связывание(вызов) пользовательского индикатора происходит динамически, на лету. Вызов нашего индикатора будет выглядеть так:
blueArrow=iCustom(NULL,0,"CrossMA-3",21,13,0,0);
redArrow=iCustom(NULL,0,"CrossMA-3",21,13,1,0);

Вызов пользовательского индикатора без параметров будет равнозначен вызову индикатора с параметрами по умолчанию (в нашем случае 21 и 13), и поэтому такая запись будет аналогична предыдущей:
 blueArrow=iCustom(NULL,0,"CrossMA-3",0,0);
   redArrow=iCustom(NULL,0,"CrossMA-3",1,0);


 
 


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

 
 


Я сделал два советника, один содержит вызов оптимизированного индикатора (CrossMA-3) с именем TestCrossMA, а второй содержит вызов неоптимизированного индикатора(CrossMA-2) , назван TestCrossMAwrong. Скачать их можно здесь . Сохраните их в папке /experts, скомпилируйте, затем в терминале откройте «Тестер стратегий» (Ctrl+R) , установите необходимые параметры и нажмите кнопку «Старт». На моем компьютере (Celeron-2000) прогон оптимизированного индикатора занял чуть больше 1 секунды, а неоптимизированного более 30 секунд

 
 


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

 
 


Перейти к статье «Построение средней».
 
+7 (495) 710-76-76
8 (800) 200-01-31
по России бесплатно

закрыть

Вход в личный кабинет

Для счета alpari.classic введите номер счета (буква и 4 цифры) и пароль в ЛК.

Для счетов alpari.micro и alpari.partner введите логин и пароль в МТ.

Зарегистрироваться!Забыли пароль?

 
Rambler's Top100