|
|
|
|
|
|
|
|
|
Мы узнали, как создать индикатор, который рисует символы/стрелки на пересечениях двух средних с разным периодом. Теперь мы напишем индикатор, который строит простую скользящую среднюю. Скользящие средние одни из самых важных в техническом анализе, на их основе строится подавляющая часть других индикаторов, как стандартных, так и разрабатываемых самими трейдерами. Скользящая средняя характеризуется периодом расчета, типом цен, по которым рассчитывается и смещением.
Если мы хотим рассчитать простую среднюю с периодом 3 по ценам закрытия, то мы должны взять сумму: ( цена закрытия сегодня + цена закрытия вчера + цена закрытия позавчера)/3.
МА(3,Close)=(Close[0]+Close[1]+Close[2])/3.
Обратимся опять к «Мастеру создания Советника». Создадим параметр PeriodMA и ShiftMA (смещение).
|
|
|
|
Следующим шагом добавим индекс , тип и цвет оставим как есть, жмем «Готово».
|
|
|
|
Получили стандартный шаблон:
|
|
|
|
Добавим в блок start() часть кода из индикатора CrossMA-3, чтобы не писать заново цикл и служебные переменные.
|
|
|
|
Мы уже хорошо представляем как происходит работа в цикле for(), теперь мы можем заменить цикл for() на цикл while(). Выносим объявление переменной i (int i=limit;) за пределы for() перед циклом, операцию декремента (i--;) вносим в тело цикла в самый конец, цикл for переименовываем на while:
|
|
|
|
Как видим, цикл while() полностью заменил цикл for(). Также производится проверка выражения в скобках на истинность, и если оно соблюдается, то выполняется тело цикла. При этом важно не забыть вставить в цикл оператор уменьшения счетчика i, иначе мы получим бесконечный цикл. Компилятор такую ошибку обнаружить не сможет (так как она не синтаксическая, а логическая), и при использовании такого неправильного индикатора (с бесконечным циклом) терминал просто зависнет и перестанет реагировать на действия пользователя. Единственный способ выхода из такой ситуации – закрыть принудительно из диспетчера задач терминал, открыть MetaEditor, исправить индикатор и можно снова запускать MetaTrader4. Но в скриптах использовать бесконечный цикл можно.
|
|
|
|
Применение цикла while() или for() обычно является делом вкуса, хотя бывают ситуации, когда while() предпочтительней , например, если необходимо двигаясь от текущего бара в глубь истории (в сторону увеличения индекса бара) найти некий бар, удовлетворяющий какому-то условию. При этом необходимо не забыть, что искомый бар может и не быть найденным, а индекс бара будет расти до бесконечности. И чтобы избежать зацикливания, необходимо добавить проверку индекса на превышение числа Bars. Пример такого цикла:
|
|
|
|
Цикл у нас есть, теперь осталось написать алгоритм вычисления суммы на количестве баров PeriodMA, то есть что-то подобное:
val=(Close[i]+Close[i+1]+Close[i+2]+… Close[i+n])/PeriodMA
Чему будет равно n, если известно PeriodMA (период нашей средней)? Есть люди, которые легко вычислят нужную формулу в уме, я много раз ошибался сам, когда проделывал это без помощи бумаги и ручки, поэтому более надежно будет пойти методом индукции. Времени займет минуту, а правильность результата гарантирует.
Возьмем частный случай, n=5, то есть как будто мы ищем среднее за 5 баров. Тогда наша формула выглядит так:
Val=(Close[i]+Close[i+1]+Close[i+2]+Close[i+3]+Close[i+4])/5
или по-другому:
Val=(Close[i]+Close[i+1]+…+Close[i+PeriodMA-2]+Close[i+PeriodMA-1])/PeriodMA
Такое механическое суммирование мы легко поместим опять-таки в цикл for().
|
|
|
|
В данном случае мы использовали оба типа циклов, получилось удобное визуальное разделение алгоритма прохода по барам графика и алгоритма суммирования цен.
Теперь опять вернемся к началу блока start(). Как видно из рисунка, устанавливать ограничитель расчета limit при первом вызове индикатора в значение Bars-1 нет никакого обоснования, баров в глубине истории нет и усреднять еще нечего. Если мы для примера хотим рассчитать среднюю с периодом 5, то вычислять эту среднюю мы можем начать только с бара Bars-5 (см. статью «Что такое графики»).
|
|
|
|
Поэтому, в первый раз, когда counted_bars равно нулю логично записать:
|
|
|
|
На этом создание индикатор простой скользящей средней по ценам закрытия можно считать законченным, но иногда возникает соблазн создать максимально оптимизированный индикатор, и делается попытка улучшить и ту часть блока start(), где counted_bars уже больше нуля, то есть первоначальный расчет индикатора уже проводился. Исходя из соображений, что после первого прогона индикатора Bars не изменился, расчет индикатора производился на количестве баров от Bars-PeriodMA до нуля (то есть counted_bars предполагается равным примерно Bars-PeriodMA), пишется следующее :
|
|
|
|
Так как преследуется цель считать только на нулевом баре, то можно предположить, что limit=0 и 0=Bars-counted_bars-PeriodMA.
Отсюда следует, что мы исходим из того, что при втором прогоне индикатора counted_bars+PeriodMA==Bars. На самом деле это не так, это вторая ошибка оптимизации, я называю такой индикатор пережатым. Функция IndicatorCounted() не возвращает количество баров, для которых был сделан расчет значений данным конкретным индикатором, а возвращает количество баров, которые не изменились с последнего вызова индикатора (нулевой бар менялся, иначе бы не пришел этот тик, на котором мы и производим проверку). Поэтому, если Bars=1000, то при первом вызове индикатора IndicatorCounted() вернет 0 (индикатор еще не рассчитывался), а при втором вызове IndicatorCounted() вернет уже Bars-1. Вы сами может проверить это , создав пустой индикатор (в котором не производится вообще никаких расчетов) и вставив в блок start() вызов
Print("Bars=",Bars," counted_bars=",counted_bars);
Если мы все же не заметим этой ошибки и оставим наш индикатор таким как есть(переоптимизированным или пережатым) то произойдет следующее ( пусть у нас Barsравно 1000, а PeriodMA равно 5):
- в первый раз индикатор рассчитается правильно от бара Bars-PeriodMA до нулевого бара
- на следующем тике проверка counted_bars окажется больше нуля и ограничитель расчета станет равным limit=1000-999-5= 1-5=-4
- в блок while() (или for(), вид цикла не имеет значения) поступает ограничитель, который меньше нуля и производится проверка
while(-4>=0) , проверка оказывается неудачной , и пересчет на нулевом баре не производится.
Выявить такую ошибку можно двумя способами:
- запустить индикатор на мелком тайм-фрейме и наблюдать его поведение
- прогнать в тестере советник, содержащий вызов нашего индикатора.
Для данного индикатора наиболее простым вариантом проверки будет первый. Компилируем неправильный индикатор и набрасываем на EURUSD M15, ждем полчаса, чтобы появилось два новых бара и видим такую картину.
|
|
|
|
К счастью, в данном случае ошибка расчета индикатора при работе в он-лайн быстро становится явной(индикатор просто не рисуется), но в более сложных случаях значение индикатора просто плывет и на глаз увидеть это не всегда можно.
Скачать индикаторы Simply MA.mq4 и Simply MA wrong.mq4 можно здесь .
Перейти к статье «Построение средней (окончание)».
|
|
|
|
|
|