Способ построения уровней сопротивления и поддержки средствами MQL5

Aleksandr Teleguz | 29 июня, 2015

Оглавление

  1. Введение
  2. Цель написания статьи
  3. Кратко о ранее представленных способах построения уровней сопротивления и поддержки
  4. Принцип нахождения экстремумов
  5. Функция для нахождения бара-экстремума с наименьшим индексом (первого бара) Ext_1
  6. Общая функция для нахождения всех последующих экстремумов Ext_2
  7. Обработка полученных результатов
  8. Пример индикатора, отображающего уровни сопротивления и поддержки
  9. Заключение

Введение

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

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

Таким образом, можно предположить, что точки максимума формируются, когда валюта перекуплена, а точки минимума — когда перепродана. Именно поэтому для построения линий сопротивления и поддержки я использую такой стандартный индикатор из набора MetaTrader, как Relative Strength Index (RSI), разработанный и опубликованный Дж. Уилдером в 1978 году. Данный индикатор определяет зоны перекупленности и перепроданности валюты.

Я использую индикатор RSI с периодом 8; данное значение не есть результат моих наблюдений — этот порядок RSI рекомендовал Эрик Л. Найман в книге под названием "Малая энциклопедия трейдера" для всех периодов графика за исключением дневного и больших. Меня полностью устраивает результат работы моего индикатора при работе с RSI(8).

Существует два расхожих мнения о том, стоит ли при поиске максимальных и минимальных цен учитывать "тени" свечей (цены High и Low). Я их учитываю и при поиске точек-экстремумов сравниваю именно High и Low цены. Если вы не захотите принимать их во внимание, то можете просто внести незначительные изменения в представленный ниже код индикатора.

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

Торговля на пробое уровней сопротивления и поддержки

Рис.1. Сигнал на покупку

Я считаю второй способ неэффективным, особенно если факт "отскока" цены от одной из линий играет основополагающую роль в принятии торговых решений. На рисунке №1 показан как раз тот случай, когда цена после "отскока" от линии сопротивления не достигает линии поддержки, разворачивается и "пробивает" линию сопротивления, данная ситуация делает сигнал на покупку более сильным. Анализируя углы наклона линий к оси времени, мы определяем общее направление тренда, а когда цена финансового инструмента пересекает одну из линий, то данный факт позволяет сделать вывод об усилении существующего тренда или его развороте. Зачастую перед тем как пересечь, например, линию поддержки, цена доходит только до середины линии сопротивления (данный фактор усиливает торговый сигнал).

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

Линии поддержки и сопротивления также используются при определении ускорения или замедления трендов. Увеличение угла наклона трендовой линии на бычьем тренде говорит о его ускорении, при появлении которого следует ожидать продолжения тренда, увеличение угла наклона трендовой линии на медвежьем тренде, напротив, говорит о его замедлении. На бычьем тренде в качестве трендовой линии выступает линия поддержки (построенная по низам цен), на медвежьем тренде в качестве трендовой линии выступает линия сопротивления (построенная по верхам цен).

Замедление бычьего тренда

Рис.2. Замедление бычьего тренда

Замедление медвежьего тренда

Рис.3. Замедление медвежьего тренда

Замедление трендов говорит о возможном скором их развороте.


Цель написания статьи

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

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


Кратко о ранее представленных способах построения уровней сопротивления и поддержки

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

Индикатор уровней сопротивления и поддержки, используемый в торговой стратегии Игоря Герасько, также определяет горизонтальные уровни.

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


Принцип нахождения экстремумов

Определение максимальных и минимальных цен на заданном промежутке времени не представляет никакой сложности. Здесь важно правильно выбрать анализируемый отрезок графика (промежуток времени), который постоянно изменяется и потому не может быть задан вручную. Для нахождения этого участка валютного графика я буду использовать индикатор Relative Strength Index (RSI), входящий в стандартный набор индикаторов терминала MetaTrader 5.

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

В качестве уровня перепроданности я буду принимать значение индикатора RSI, равное 35; перекупленности — 65 (нижний и верхний уровни равноудалены от середины RSI = 50). Период индикатора RSI равен 8.

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

Поиск точек-экстремумов

Рис.4. Зоны поиска баров-экстремумов

На рисунке, приведенном выше, цифрами 1, 2, 3 и 4 я обозначил зоны поиска баров-экстремумов: первого, второго, третьего и четвертого соответственно. Поскольку прежде чем пересечь нижнюю границу (RSI = 35), индикатор RSI, начиная с текущего бара, входит в зону перекупленности (RSI >= 65) трижды, то и отрезков времени, на которых осуществляется поиск первого экстремума, будет три. Далее по порядку определяются зоны для поиска последующих трех баров.

В 1 и 3 зонах я буду искать бары с наибольшими ценами, во 2 и 4 зонах — с наименьшими. В результате мы получим 4 точки-экстремума — если соединить их лучами, то мы получим восходящий канал цен.

Рис.5. Восходящий канал

Рис.5. Восходящий канал

Если вы обратили внимание, то наверняка заметили, что баров для поиска минимумов было немного, всего 4 (2 для каждой зоны). По причине преобладания восходящего тренда индикатор RSI едва касался минимального уровня, в то время как за границей уровня RSI = 65 индикатор находился практически столько же времени, сколько и в канале 35 < RSI < 65. Поэтому индикатор, код которого я представил в этой статье, для нахождения точек №2 и 4 будет использовать уровень RSI, смещенный ближе к середине (к 50). Будет это верхний или нижний уровень RSI, зависит от того, каким был первый экстремум (минимальным или максимальным).

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


Функция для нахождения бара-экстремума с наименьшим индексом (первого бара) Ext_1

Функция для нахождения баров-экстремумов, которую я назвал "Ext_1", имеет следующие входные параметры:

int Ext_1(double low,      //нижний уровень RSI, уровень перепроданности
          double high,     //верхний уровень RSI, уровень перекупленности
          int bars,        //число анализируемых баров, чтобы не копировать в массивы лишние данные
                           //можно задать bars = 300
          int h_rsi,       //хэндл индикатора RSI
          string symbol,   //символ графика
          float distans,   //дистанция для смещения одного из уровней индикатора
                           //позволяет определить границы поиска первого бара - экстремума
          ENUM_TIMEFRAMES period_trade) //период графика

Первые два входных параметра функции — это параметры самого индикатора RSI. Они не играют никакой роли при расчете линии индикатора и используются в нем только для удобства визуальной оценки значения индикаторной линии (ее расположения относительно данных уровней). Я использую эти уровни для определения интервалов цен, среди которых буду искать минимальные и максимальные значения. Параметры "low" и "high" получают значения соответствующих внешних переменных, заданных в коде индикатора на глобальном уровне:

input double Low_RSI = 35.0; // Нижний уровень RSI для нахождения экстремумов
input double High_RSI= 65.0; // Верхний уровень RSI для нахождения экстремумов

Входной параметр "bars" определяет количество элементов, копируемых в массивы, содержащие цены "Low" и "High" баров, а также значения индикатора RSI:

double m_rsi[],m_high[],m_low[];                              //инициализация массивов
int h_high = CopyHigh(symbol, period_trade, 0, bars, m_high); //заполняем массив максимальных цен свечей
int h_low = CopyLow(symbol, period_trade, 0, bars, m_low);    //заполняем массив минимальных цен свечей
if(CopyBuffer(h_rsi, 0, 0, bars, m_rsi)<bars)                 //заполняем массив с данными индикатора RSI
{
   Print("Не удалось скопировать буфер индикатора!");
}

Массивы m_rsi[],m_high[] и m_low[] имеют обратный порядок индексации:

ArraySetAsSeries(m_rsi,true); 
ArraySetAsSeries(m_high,true); 
ArraySetAsSeries(m_low,true);

Как я уже говорил, при преобладании бычьего тренда линия индикатора RSI, имеющая диапазон значений от 0 до 100, большую часть времени будет иметь значение от > 50. В моменты, когда формируется точка минимума, значение RSI будет менее удаленным от середины (50), нежели при формировании точки максимума. Поэтому при первом пересечении одного из уровней индикатора (low или high) другой уровень нужно сместить ближе к середине. Величину смещения определяет входной параметр "distans", которому передается значение соответствующей внешней переменной:

input float Distans=13.0;    // Смещение уровня RSI 

Входной параметр "h_rsi" функции "Ext_1" получает значение хэндла индикатора RSI, полученного при инициализации индикатора с помощью функции iRSI():

h_RSI=iRSI(Trade_Symbol,Period_Trade,Period_RSI,PRICE_CLOSE);  //возвращаем хэндл индикатора RSI

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

input uchar Period_RSI =8;   // Период RSI

После того как мы создали и заполнили массивы, содержащие цены low и high баров, а также соответствующее этим барам значение индикатора RSI, можно приступать собственно к поиску первого экстремума. Для того чтобы определить, где будет находиться первый экстремум (на линии поддержки или сопротивления) и в нужный момент остановить анализ баров, потребуются две переменные типа bool:

bool ext_max = true;    //переменные типа bool используются для того, чтобы в нужный момент прекратить
bool ext_min = true;    //анализ баров

Значение ext_max = true разрешает производить поиск максимального экстремума, значение ext_min = true, соответственно, разрешает производить поиск минимального экстремума. То есть при первом пересечении линии индикатора RSI одного из уровней (low или high) значение одной из переменных типа bool меняется на false, а пересечение уровня RSI, анализ баров которого запрещен, означает, что необходимое количество баров проанализировано, и первый экстремум уже найден. 

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

Запрет анализа на неполном диапазоне цен

Рис.6. Запрет анализа на неполном диапазоне цен

Для реализации такого алгоритма работы потребуется создание еще одной переменной типа bool:

bool flag=false;

Для определения максимальных и минимальных цен среди анализируемых баров нужно дополнительно создать следующие переменные типа double:

double min = 100000.0;  //переменные для выявления максимальных и минимальных цен
double max = 0.0;       //...

Весь цикл для поиска бара с первым экстремумом примет вид:

for(int i=0;i<bars;i++) //цикл по барам
{
   double rsi = m_rsi[i];                                   //получаем значения индикатора RSI
   double price_max = NormalizeDouble(m_high[i], digits);   //цены High
   double price_min = NormalizeDouble(m_low[i], digits);    //цены Low выбранного бара
   if(flag==false) //условие, для того чтобы не начать искать экстремум на незавершенном тренде
   {
      if(rsi<=low||rsi>=high) //если первые бары в зонах перекупл. или перепрод.,
         continue;            //то переходим к следующему бару
      else flag = true;       //если нет, то продолжаем анализ  
   }
   if(rsi<low) //если найдено пересечение RSI c уровнем low
   {
      if(ext_min==true) //если RSI еще не пересекал уровень high
      {
        if(ext_max==true) //если еще не выставлен запрет на поиск максимального экстремума,
        {
           ext_max=false;     //то запрещаем искать максимальный экстремум
           if(distans>=0) high=high-distans; //изменяем уровень high, по которому потом
        }                                    //будет производиться поиск второго бара
        if(price_min<min) //ищем и запоминаем индекс первого бара
        {                 //сравнивая цены Low свечей
           min=price_min;
           index_bar=i;
        }
      }
      else break; /*Выходим из цикла, поскольку раз искать минимальный экстремум уже запрещено,
                    значит, найден уже максимальный*/
   }
   if(rsi>high) //далее алгоритм тот же, только по поиску максимального экстремума
   {
      if(ext_max==true)
      {
        if(ext_min==true)
        {
           ext_min=false;
           if(distans>=0) low=low+distans;
        }
        if(price_max>max)
        {
           max=price_max;
           index_bar=i;
        }
      }
      else break; /*Выходим из цикла, поскольку раз искать максимальный экстремум уже запрещено,
                    значит, найден уже минимальный*/
   }
}

Общая функция для нахождения всех последующих экстремумов Ext_2

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

  1. Параметр, содержащий ссылку на структуру, в которой будут храниться индексы баров-экстремумов. Необходим для определения индекса бара, с которого будет начинаться поиск экстремума.
  2. Порядковый номер бара-экстремума, который требуется найти (принимает значения от 2 до 4). Нужен для того, чтобы, собственно, выбрать из структуры индекс нужного бара, а также определить, на какой линии (сопротивления или поддержки) будет лежать искомый экстремум.
  3. Параметр типа bool, определяющий, на какой линии (сопротивления или поддержки) лежит первый бар-экстремум. Не зная этого, нельзя по порядковому номеру определить, на какой линии должен лежать искомый бар.
int Ext_2(double low,    //нижний уровень RSI, уровень перепроданности
          double high,   //верхний уровень RSI, уровень перекупленности
          int bars,      //число анализируемых баров, чтобы не копировать в массивы лишние данные
          int h_rsi,     //хэндл индикатора RSI
          string symbol, //символ графика
          st_Bars &bars_ext,//структура, содержащая индексы найденных баров
          char n_bar,    //порядковый номер бара, которвый требуется найти (2, 3 или 4)
          float distans, //дистанция для смещения одного из уровней индикатора
          bool first_ext,//тип первого бара
          ENUM_TIMEFRAMES period_trade)//период графика

На глобальном уровне создаем переменную типа структуры, которая будет содержать индексы всех 4 баров-экстремумов:

struct st_Bars //инициализация структуры
  {
   int               Bar_1;
   int               Bar_2;
   int               Bar_3;
   int               Bar_4;
  };

st_Bars Bars_Ext; //объявление переменной типа структуры

Для определения линии индикатора, на которой будет лежать искомый экстремум, нужно предварительно создать две переменные типа bool:

bool high_level= false; //переменные для определения типа искомого бара
bool low_level = false; //...

Если порядковый номер искомого бара-экстремума равен 2 или 4, а первый бар-экстремум лежит на линии поддержки, то искомый бар должен лежать на линии сопротивления и, соответственно, анализировать нужно бары, значение RSI на которых больше либо равно верхнему уровню (параметр high). Если порядковый номер искомого бара-экстремума равен 3, а первый бар-экстремум лежит на линии поддержки, то искомый бар также должен лежать на этой линии. Если первый бар-экстремум лежит на линии сопротивления, то положение искомого бара определяем по аналогии.

if(n_bar!=3)
{
   if(first_ext==true)//если первая точка была максимальной
   {
      low_level=true;//то эта должна быть минимальной
      if(distans>=0) low=low+distans; //если нужно, смещаем нижний уровень RSI
   }
   else //если минимальной
   {
      high_level = true;
      if(distans>=0) high=high-distans; //если нужно, смещаем верхний уровень RSI
   }
}
else
{
   if(first_ext==false)//если первая точка была минимальной
   {
      low_level=true;//то и эта должна быть минимальной
      if(distans>=0) high=high-distans; //если нужно, смещаем верхний уровень RSI
   }
   else //если максимальной
   {
      high_level = true;
      if(distans>=0) low=low+distans; //если нужно, смещаем нижний уровень RSI
   }
}

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

bool _start = false;    

Значение данной переменной меняем на true, когда в истории находим нужный диапазон баров для анализа. Анализ баров прекращается, если _start = true, и при low_level = true индикаторная линия RSI пересекает уровень high, а при high_level = true индикаторная линия RSI пересекает уровень low.

if(_start==true && ((low_level==true && rsi>=high) || (high_level==true && rsi<=low)))
  break; //выходим из цикла, если второй экстремум уже найден, а RSI уже пересек противоположный уровень

Цикл для поиска бара-экстремума примет вид:

for(int i=bar_1;i<bars;i++) //анализируем оставшиеся бары
{
   rsi=m_rsi[i];
   price_max = NormalizeDouble(m_high[i], digits);
   price_min = NormalizeDouble(m_low[i], digits);
   if(_start==true && ((low_level==true && rsi>=high) || (high_level==true && rsi<=low)))
   {
      break; //выходим из цикла, если второй экстремум уже найден, а RSI уже пересек противоположный уровень
   }
   if(low_level==true) //если ищем минимальный экстремум
   {
      if(rsi<=low)
      {
         if(_start==false) _start=true;
         if(price_min<min)
         {
            min=price_min;
            index_bar=i;
         }
      }
   }
   else //если ищем максимальный экстремум
   {
      if(rsi>=high)
      {
         if(_start==false) _start=true;
         if(price_max>=max)
         {
            max=price_max;
            index_bar=i;
         }
      }
   }
}

Переменная bar_1 содержит индекс предыдущего бара-экстремума, который вычисляется с помощью оператора switch:

switch(n_bar) //находим индекс предыдущего бара
{
   case 2: bar_1 = bars_ext.Bar_1; break;
   case 3: bar_1 = bars_ext.Bar_2; break;
   case 4: bar_1 = bars_ext.Bar_3; break;
}

Чтобы узнать на какой линии индикатора (сопротивления или поддержки) лежит первый бар-экстремум, достаточно получить его индекс и значение индикатора RSI на баре с полученным индексом:

bool One_ext(st_Bars &bars_ext, //переменная типа структуры для получения индекса первого бара
             string symbol,     //символ графика
             int h_rsi,         //хэндл индикатора
             double low,        //заданный уровень перепроданности RSI (можно использовать и high уровень)
             ENUM_TIMEFRAMES period_trade) //период графика
  {
   double m_rsi[];               //инициализация массива данных индикатора
   ArraySetAsSeries(m_rsi,true); //порядок индексации
   CopyBuffer(h_rsi,0,0,bars_ext.Bar_1+1,m_rsi); //заполнение массива данными RSI
   double rsi=m_rsi[bars_ext.Bar_1]; //определяем значение RSI на баре с первым экстремумом
   if(rsi<=low)                      //если значение меньше нижнего уровня,
      return(false);                 //то первый экстремум был минимальным
   else                              //если нет,
   return(true);                     //то максимальным
  }

Обработка полученных результатов

Теперь мы знаем индексы всех четырех баров и цены (low или high) которые им соответствуют. Для того чтобы заполнить массивы, которые будут соответствовать значением индикаторных линий на каждом баре, нужно получить уравнения двух прямых, соответствующих линиям сопротивления и поддержки. Всем известное уравнение прямой имеет вид: y = kx + b. В нашем случае "x" — индекс бара, "y" — цена (для линии поддержки цена low свечи, для линии сопротивления — high).

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

K=(price_2-price_1)/(_bar2-_bar1);  //находим коэффициент K
B=price_1-K*_bar1;                  //находим коэффициент B

где

double K,B;

это глобальные переменные "K" и "B", которые соответствуют значениям коэффициентов "k" и "b" уравнения прямой;

int _bar1,_bar2;

это индексы баров, расположенных на одной линии;

double price_1,price_2;

это цены low соответствующих баров, если нужно определить "K" и "B" для линии поддержки, либо цены high соответствующих баров, если нужно определить "K" и "B" для линии сопротивления.

Функция, представленная ниже, задает значения глобальных переменных "K" и "B" для линии поддержки, если параметр "_line" равен false, и для линии сопротивления, если параметр "_line" равен true:

void Level(bool _line,              //параметр, определяющий линию (сопротивления/поддержки), коэффициенты к которой нужно найти
           bool _first_ext,         //тип первого экстремума (уже знакомый вам параметр)
           st_Bars &bars_ext,       //структура, содержащая индексы баров
           string _symbol,          //символ
           ENUM_TIMEFRAMES _period) //период графика
  {
   int bars=Bars_H;           //количество анализируемых баров
   double m_high[],m_low[];         //инициализация массивов
   ArraySetAsSeries(m_high,true);   //массивы индексируются с первого элемента
   ArraySetAsSeries(m_low,true);    //...
   int h_high = CopyHigh(_symbol, _period, 0, bars, m_high); //заполняем массив максимальных цен свечей
   int h_low = CopyLow(_symbol, _period, 0, bars, m_low);    //заполняем массив минимальных цен свечей
   double price_1,price_2;
   int _bar1,_bar2;
   int digits=(int)SymbolInfoInteger(_symbol,SYMBOL_DIGITS);//количество знаков после запятой текущего символа
   if(_line==true)                                          //если нужна линия сопротивления
     {
      if(_first_ext==true) //если первый экстремум максимальный
        {
         price_1 = NormalizeDouble(m_high[bars_ext.Bar_1], digits);
         price_2 = NormalizeDouble(m_high[bars_ext.Bar_3], digits);
         _bar1 = bars_ext.Bar_1;
         _bar2 = bars_ext.Bar_3;
        }
      else                                                  //если минимальный
        {
         price_1 = NormalizeDouble(m_high[bars_ext.Bar_2], digits);
         price_2 = NormalizeDouble(m_high[bars_ext.Bar_4], digits);
         _bar1 = bars_ext.Bar_2;
         _bar2 = bars_ext.Bar_4;
        }
     }
   else                                                     //если нужна линия поддержки
     {
      if(_first_ext==true) //если первый экстремум максимальный
        {
         price_1 = NormalizeDouble(m_low[bars_ext.Bar_2], digits);
         price_2 = NormalizeDouble(m_low[bars_ext.Bar_4], digits);
         _bar1 = bars_ext.Bar_2;
         _bar2 = bars_ext.Bar_4;
        }
      else                                                  //если минимальный
        {
         price_1 = NormalizeDouble(m_low[bars_ext.Bar_1], digits);
         price_2 = NormalizeDouble(m_low[bars_ext.Bar_3], digits);
         _bar1 = bars_ext.Bar_1;
         _bar2 = bars_ext.Bar_3;
        }
     }
   K=(price_2-price_1)/(_bar2-_bar1);  //находим коэффициент K
   B=price_1-K*_bar1;                  //находим коэффициент B
  }

Уравнение прямой имеет вид: y = kx + b, в качестве значений оси "y" используется цена финансового инструмента, в качестве значений оси "x" — индекс бара. Если использовать в качестве значений оси "x" количество секунд, прошедших с 1 января 1970 года, то в районе выходных дней график прямой будет "скакать", поэтому я использовал индексы баров.

Из функции "OnCalculate" функция "Level" вызывается дважды: первый раз перед тем, как заполнить массив для линии сопротивления, второй раз для заполнения массива со значениями цен для линии поддержки:

for(int i=0;i<Bars_H;i++)
{
   resistanceBuffer[i]=NormalizeDouble(K*i+B,Dig);
}
Level(false,First_Ext,Bars_Ext,Trade_Symbol,Period_Trade); //получим коэффициенты K и B для линии поддержки
for(int i=0;i<Bars_H;i++)
{
   supportBuffer[i]=NormalizeDouble(K*i+B,Dig);
}

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

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

Результат работы индикатора

Рис.7. Результат работы индикатора

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

Полный код индикатора содержится в приложенном к данной статье файле.


Заключение

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