Скачать MetaTrader 5

Пример индикатора, строящего линии поддержки и сопротивления

22 мая 2017, 10:23
Andrey Kisselyov
13
6 293

Введение

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

ВНИМАНИЕ: данное описание, как и все что написано в данной статье, не относится к рекомендациям по торговле и несет чисто информативный характер. Автор не несет ответственности за ваши действия, включая потери или прибыли, полученные вами при работе по описанной методике.


Что такое линии поддержки и сопротивления

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

Линия сопротивления (Resistance) — это линия движения цены на определенном промежутке времени, выше которой она не поднимается.

Линия поддержки (Support) — это линия движения цены на определенном промежутке времени, ниже которой она не опускается.

Линия поддержки может быть как горизонтальная, так и наклонная в какую-либо сторону. А вот роль линии будет меняться, в зависимости от того что мы видим на графике. Поговорим о линии поддержки. Все сказанное будет верным и для линии сопротивления, с  той лишь разницей что они зеркальны.
Когда линия горизонтальна, то цена находится в ценовом коридоре. Роль линии — открытие ордеров на покупку, фиксация прибыли ордеров на продажу.
Когда линия идет по восходящей — цена в восходящем тренде. Роль линии — уровень открытия ордеров на покупку.
Когда линия опускается — цена в нисходящей тенденции. Роль линии — уровень фиксации прибыли для ордеров на продажу.

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

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


Условия выбора начальной точки построения наклонной линии

Какие же параметры необходимо взять для правильного построения линий на графике в автоматическом режиме?

Так как линия сопротивления не дает цене подняться, логично строить ее по вершинам, а линию поддержки, которая не дает упасть цене, — по впадинам. Чтоб не мудрствовать лукаво, применим индикатор Фрактал Билла Вильямса из терминала MetaTrader 5 и увидим на графике возможные точки построения. Какой именно фрактал нам нужно взять как начальную точку отсчета, мы можем увидеть на графике. Для линии сопротивления желательно чтоб это был верхний фрактал на графике, выше которого нет ни одного фрактала, и выше которого цена не поднималась. Для линии поддержки, соответственно, это будет самый низко расположенный фрактал, ниже которого нет фракталов на графике. Тут рассматривается не весь график, а только та часть, которая нам видна и которую мы будем анализировать. Из этого логичным образом вытекает некоторое условие, которое мы можем применить в построении: верхний фрактал должен быть выше двух расположенных рядом с ним фракталов, а нижний — ниже двух его "соседей". Итак, точка отсчета у нас есть.


Условия выбора второй точки построения наклонной линии

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

Какие критерии мы будем применять к подбору второй точки? Во-первых, мы можем взять второй фрактал, который идет после нашей первой точки: это покажет ближайшую линию. Но мы можем применить и другие правила отбора — например, самую высокую точку на графике, линию через которую цена еще не пересекала вверх для линии сопротивления (или самую низкую точку на графике, линию через которую цена не пересекала вниз для линии поддержки). Вы сами можете определить свои правила для выбора второй точки. Мы же остановимся на ближайшем втором фрактале, по которому и будем строить линии поддержки и сопротивления.  

выбор точек построения линий


Условия сброса линий и поиска новых точек построения для наклонной линии

Точки построения линий у нас уже есть. Теперь неплохо было бы определить, когда же наши линии должны заканчиваться. Для линии сопротивления за условие окончания вполне естественно принять пробой вверх, а для линии поддержки — наоборот, пробой вниз. А так как цена иногда пробивает линии на несколько пунктов, но потом отскакивает, установим погрешность, до которой линия еще будет считаться не пробитой. Это послужит своеобразным предохранителем от ложной отмены линий.

Также нужно учесть, что цена иногда уходит далеко от уровня и долгое время не возвращается к линии. Зададим в настройках максимально допустимую длину линии в барах. Это даст нам возможность начать рисовать новую линию, если вдруг предыдущая "зависнет в воздухе". Так как движение цены не может одновременно иметь 2 линии сопротивления и 2 линии поддержки, логично предположить, что, сформировавшись, линия будет действовать до тех пор, пока не будет пробита.  Следовательно условия для построения новой линии мы будем искать уже после ее завершения.

Как только текущая линия пробита, мы ждем, когда сформируются условия для отрисовки новой линии. Для каждой линии мы выделим отдельный  буфер и будем формировать каждую по отдельности. Это позволит видеть, как будет происходить борьба между быками и медведями. Как следствие, для первой точки мы можем выбрать самый высокий (для сопротивления) и самый низкий (для поддержки) фракталы. Второй точкой будет следующая аналогичная точка — высокая (для сопротивления) или низкая(для поддержки). Условия для построения готовы. Осталось реализовать это в виде кода индикатора. Приступим к его написанию.


Пример реализации построения линий на основе индикатора для наклонных линий

Первое, что мы делаем — создаем шаблон по стандартным настройкам. Затем создаем 2 буфера — для сопротивления и для поддержки. Внесем настройки Pips_ (величина, на которую была пробита линия) и MaxBars (максимально возможная длина линии). На этом автоматические процедуры создания индикатора закончились. Дальше начинается внимательная творческая работа.

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

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

   int limit=prev_calculated;
   if(limit>3)limit-=3;
   if(limit<2)limit=2;

Мы не будем копировать буфер из индикатора Фрактал Билла Вильямса, а напишем пару функций, которые будут проверять, является ли наш бар фракталом. Не забудем при этом обезопасить себя от ошибок доступа к буферу: включим отработку при незапланированных значениях.

bool up(const double &h[],int q)
  {
// проверяем бар на наличие еще не менее 2 баров после него, 
// если после нашего бара меньше 2 баров, по правилам он не может считаться фракталом
// в этом случае возвращаем false, не проверяя далее
   if(q>=ArraySize(h)-2)return(false);
// проверяем бар на наличие еще не менее 2 баров до него, 
// если до нашего бара меньше 2 баров, по правилам он не может считаться фракталом
// в этом случае возвращаем false, не проверяя далее
   if(q<2)return(false);
// проверяем, является ли наш бар фракталом. Если является, возвращаем true
   if(h[q]>=h[q-1])
      if(h[q]>=h[q-2])
         if(h[q]>h[q+1])
            if(h[q]>h[q+2])
               return(true);
// если мы прошли все проверки и до сих пор в блоке, то бар не является фракталом, возвращаем false
   return(false);
  }
//+------------------------------------------------------------------+
bool down(const double &h[],int q)
  {
// в данном блоке все аналогично предыдущему блоку
   if(q>=ArraySize(h)-2)return(false);
   if(q<2)return(false);
   if(h[q]<=h[q-1])
      if(h[q]<=h[q-2])
         if(h[q]<h[q+1])
            if(h[q]<h[q+2])
               return(true);
   return(false);
  }

Далее следует найти 1 бар в массиве HIGH и проверить его на соответствие нашим параметрам. Если соответствует, то запоминаем его и ищем точку 2 для построения линии. В противном случае переходим к другому бару.

for(int w=limit;w<rates_total;w++)
  {
   if(up(high,w))                  //проверяем наличие фрактала на данном баре
     {
      if(r1==0)r1=w;               //если фрактал и у нас нет 1 вершины, устанавливаем 1 вершину
     }
  }

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

if(high[w]>=high[r1])r1=w;                   //сравниваем с первой вершиной. Если фрактал выше, считаем данную вершину первой
else
   {
    r2=w;                                    //получили значение второй вершины     
    speedr=(high[r2]-high[r1])/(r2-r1);      //рассчитали скорость линии
    w=r1-1;                                  //возвращаемся для прорисовки новой линии
   }

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

ResistanceBuffer[w]=high[r1]+speedr*(w-r1);               //чтоб погрешность была минимальной, считаем от самой вершины
if(w>r2)                                                  //проверяем, пробита линия или нет
if(high[w]>ResistanceBuffer[w]+Pips_*_Point){r1=0;r2=0;}

Вот так выглядит индикатор с разными настройками.

ind1

ind2

ind3

А это несколько индикаторов на графике одновременно.

ind4

ind5

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

Для построения горизонтальных уровней нам необходима только одна точка. Это облегчает нам задачу. Вариантов выбора точек построения очень много. Приведу лишь некоторые из них:

  • уровни по Фибоначчи из расчета предыдущего дня (бара);
  • уровни по Фибоначчи из расчета среднего АТР дня, от среднего диапазона цен прошлого дня;
  • максимальное количество фракталов на определенном диапазоне;
  • скопление максимумов или минимумов, цен открытия или закрытия баров в определенном диапазоне;
  • уровни скопления объемов по определенной цене (профиль рынка);
  • уровень разворота цен на основе фигур;
  • и так далее.

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

дневное движение

Как видим, цена то поднималась вверх, то опускалась вниз. Дневной экстремум всегда является уровнем, от которого цена отскочила и пошла в другую сторону. Очень редко бывает такое, что цена на максимуме или минимуме окончила свое дневное движение. Следовательно, мы можем считать любой дневной максимум уровнем сопротивления, а минимум — уровнем поддержки.

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


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

Для начала создадим индикатор штатными свойствами. Назовем его так же, как и предыдущий, добавим только букву Н в конце, что будет означать "горизонтальный". Укажем таймфрейм, на котором будем брать данные, 3 коэффициента для каждой пары линий, 7 буферов для отрисовывания уровней.

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

void array_copy(int b_)
  {
   ArrayResize(time_tf,b_);                         //меняем размер буфера под имеющиеся данные
   ArrayResize(high_tf,b_);
   ArrayResize(low_tf,b_);

   int total=b_-bars_tf;                            //вычисляем, какое копирование данных необходимо произвести

   CopyTime(_Symbol,Period_TF,0,total,time_tf)      //копируем недостающие данные в массив
   CopyHigh(_Symbol,Period_TF,0,total,high_tf);
   CopyLow(_Symbol,Period_TF,0,total,low_tf);

   bars_tf=b_;                                      //запоминаем размер массива и количество данных
  }

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

void f1(double k_fibo,int q,int r,const datetime &time_[],double &b1[],double &b2[])
  {
   for(int w=q;w<r;w++)
     {
      int b=f2(time_[w]);         //ищем время текущего бара в массиве старшего таймфрейма
      double h=high_tf[b];        //получаем максимум
      double l=low_tf[b];         //получаем минимум
      double hl=h-l;              //находим диапазон движения
      b1[w]=h-hl*k_fibo;          //заносим высчитанное значение в буфер поддержки
      b2[w]=l+hl*k_fibo;          //заносим высчитанное значение в буфер сопротивления
     }
  }

Так как временные диапазоны не равны, нам необходимо найти время бара текущего массива в массиве времени старшего массива. Это мы и делаем в следующем фрагменте кода.

int f2(datetime t_)
  {
   int b_=ArrayBsearch(time_tf,t_);      //ищем бар стандартной процедурой поиска в отсортированных массивах
   if(time_tf[b_]>t_)b_--;               //если нам возвращают время ближайшего бара старше по времени, то уменьшаем его на 1
   return(MathMax(0,b_-1));              //не забываем вернуть бар с учетом ограничения по минимуму
  }
Вот и весь индикатор, который будет рисовать нам необходимые уровни поддержки и сопротивления относительно цен прошлого дня.
   int limit=prev_calculated;
   if(limit>0)limit--;

   int bar=Bars(_Symbol,Period_TF);
   if(bars_tf==0 || bar>bars_tf)array_copy(bar);

   f1(0.5,limit,rates_total,time,buffer7,buffer7);           //рисуем медиану
   f1(K_Fibo_1,limit,rates_total,time,buffer1,buffer2);      //рисуем уровни по 1 коэффициенту
   f1(K_Fibo_2,limit,rates_total,time,buffer3,buffer4);      //рисуем уровни по 2 коэффициенту
   f1(K_Fibo_3,limit,rates_total,time,buffer5,buffer6);      //рисуем уровни по 3 коэффициенту

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


Как видим на скриншоте, цена отталкивается от построенных индикатором уровней. Достаточно долго один из них служил поддержкой. Подбор более продуктивных уровней — дело техники и оптимизации параметров конкретной системы торговли.


Уровни поддержки и сопротивления на основе скользящих средних

Казалось бы, тема исчерпана: что по этому поводу можно сказать еще, кроме трендовых и горизонтальных линий поддержки и сопротивления?

Обратите внимание на индикаторы на основе скользящих средних. В числе первых мы назовем, конечно же, Moving Average, далее — Bollinger Bands и Envelopes, которые входят в стандартный набор терминала. Эти индикаторы как будто созданы показывать уровни поддержки и сопротивления на графике. Открываем свойства индикатора на вкладке "Уровни" и прописываем там пару уровней со знаком минус и пару — со знаком плюс. Получим примерно такое изображение:


Такие же результаты получены для остальных индикаторов. Цена "видит" уровни и отталкивается от них, как и от обычных уровней поддержки и сопротивления.



Заключение

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


Спасибо за внимание.

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (13)
Andrey Kisselyov
Andrey Kisselyov | 3 июл 2017 в 12:52
Konstantin Seredkin:

Отлично, с этим со всем разобрался

Теперь вот вопрос в другом

Реализация такая


При запуске визуального тестирования, линия на графике отрисовывается, но стоит на месте, свои значения линия не меняет по ходу тестирования, в mql5 что нужно дополнительно каким то образом давать расчетам пинка под зад ?

команда
if(!ObjectCreate(chart_ID,name,OBJ_HLINE,sub_window,0,price))
если уже существует объект с таким именем не реализуется, как следствие ваша цена, на уже существующей линии не измениться.
как вам правильно сказали, это нужно делать командой

ObjectSetDouble(chart_ID,name,OBJPROP_PRICE,price);
и еще одно замечание, старайтесь не лезть в классы и другие премудрости, если есть возможность написать проще, используя команды прямого доступа.
применяйте только те команды, которые необходимы,  не нужно при каждом обращении перебирать все параметры линии ведь вам нужно изменить только цену линии.

с уважением.
Konstantin Seredkin
Konstantin Seredkin | 3 июл 2017 в 14:33

Спасибо ребята, разобрался теперь.

Victor Ziborov
Victor Ziborov | 3 июл 2017 в 21:16
Alexandr Gavrilin:

А рисование наклонных уровней с другой стороны сможете реализовать?

т.е. линии поддержки и сопротивления рисовать не снизу при восходящем ,а наоборот сверху?

Александр, извините, но "наклонных уровней" не бывает. Например, уровень воды в бассейне, или уровень горизонта. Не могут быть наклонными. Слово уровень - это обязательно нечто горизонтальное. А сказать наклонная трендовая линия - это совершенно правомочно. Извините, ещё раз. 

Evgeniy Butakov
Evgeniy Butakov | 6 июл 2017 в 07:00

Хороший материал. Спасибо. Но вот при некоторых параметрах "Кол-во баров в истории" рисует вот так - не соображу почему.

И смею иметь предложение: А зачем собственно учитывать ложные пробои? Как по мне факт пробоя - это закрытие свечи ниже/выше трендовой. Заменив в строках

if(high[w]>ResistanceBuffer[w]+Pips_*_Point){r1=0;r2=0;ResistanceBuffer[w]=EMPTY_VALUE;}

...

if(low[w]<SupportBuffer[w]-Pips_*_Point){s1=0;s2=0;SupportBuffer[w+1]=EMPTY_VALUE;}

high и low на close мы избавляемся от параметра "Макс. пунктов ложного пробоя трендовой" (Pips_)

if(close[w]>ResistanceBuffer[w]){r1=0;r2=0;ResistanceBuffer[w+1]=EMPTY_VALUE;}

...

if(close[w]<SupportBuffer[w]){s1=0;s2=0;SupportBuffer[w+1]=EMPTY_VALUE;}

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

Andrey Kisselyov
Andrey Kisselyov | 6 июл 2017 в 14:46
Evgeniy Butakov:

Хороший материал. Спасибо. Но вот при некоторых параметрах "Кол-во баров в истории" рисует вот так - не соображу почему.

И смею иметь предложение: А зачем собственно учитывать ложные пробои? Как по мне факт пробоя - это закрытие свечи ниже/выше трендовой. Заменив в строках

high и low на close мы избавляемся от параметра "Макс. пунктов ложного пробоя трендовой" (Pips_)

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

добрый день.
для начала скажу что индикатор построен в качестве примера, реализация в коде пробоев по high low ценам наиболее правильная, потому что раз цена была  там, мы должны ее учитывать(***).
если вы не хотите учитывать ее на максимумах вы можете реализовать свой вариант, для того исходники и присутствуют, а в начале текста так и указано, что это пример реализации и вы можете изменить код и попробовать свою реализацию задумки.
также вы можете задать свой Pips_ для пробоя и он может быть любой, хоть 0.

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

с уважением.
Создание пользовательских индикаторов с использованием класса CCanvas Создание пользовательских индикаторов с использованием класса CCanvas

В статье рассмотрен пример создания рисованных пользовательских индикаторов с помощью графических примитивов класса CCanvas.

Наивный байесовский классификатор для сигналов набора индикаторов Наивный байесовский классификатор для сигналов набора индикаторов

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

Методы сортировки и их визуализация с помощью MQL5 Методы сортировки и их визуализация с помощью MQL5

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

Универсальный торговый эксперт: Доступ к свойствам инструмента (часть 8) Универсальный торговый эксперт: Доступ к свойствам инструмента (часть 8)

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