English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Трендовые линии на основе фракталов посредством MQL4 и MQL5

Трендовые линии на основе фракталов посредством MQL4 и MQL5

MetaTrader 5Трейдинг | 28 апреля 2015, 12:02
11 617 21
Almat Kaldybay
Almat Kaldybay

Оглавление


Введение

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

Анализ рынков я часто делаю на основной своей работе, где я могу уделить трейдингу немного времени. К тому же недостаточно просто расчертить линии на старшем таймфрейме, линия должна чертиться по экстремумам с точностью до 15 минут. Данная необходимость обусловлена тем, что время фрактала на старшем таймфрейме не всегда соответствует времени этого же экстремума на M15. Словом, автоматизация приходит на помощь. Так получилось, что "кодить" я начал на MQL5, а в последующем на MQL4, так как для MetaTrader 4 мне также нужна была аналогичная программа.

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


1. Входные параметры, функция DeInit() и первоначальное объявление переменных

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

input color Resistance_Color=Red;       // задаем цвет линии сопротивления
input ENUM_LINE_STYLE Resistance_Style; // задаем стиль линии сопротивления
input int Resistance_Width=1;           // задаем ширину линии сопротивления
input color Support_Color=Red;          // задаем цвет линии поддержки
input ENUM_LINE_STYLE Support_Style;    // задаем стиль линии поддержки
input int Support_Width=1;              // задаем ширину линии поддержки

Данные параметры в MQL4 и MQL5 совершенно одинаковы.

В языке MQL5 необходимо заранее создать индикатор:

//--- хендл индикатора iFractals 
int Fractal;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//--- получаем хендл индикатора iFractals
   Fractal=iFractals(Symbol(),PERIOD_D1);
//---
   return(INIT_SUCCEEDED);
  }

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

void OnDeinit(const int reason)
  {
   ObjectDelete(0,"TL_Resistance");
   ObjectDelete(0,"TL_Support");
  }

Для построения двух линий (поддержка и сопротивление) нужно четыре точки. Для определения точки прохождения линии нужно знать время и цену.

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

В функции OnTick() объявляем переменные:

MQL4
//---объявление переменных
int n,UpperFractal_1,UpperFractal_2,LowerFractal_1,LowerFractal_2;
MQL5
//--- объявление переменных
int n,UpperFractal_1,UpperFractal_2,LowerFractal_1,LowerFractal_2;
//--- объявление массивов для записи значений буферов индикатора iFractal
double FractalDown[],FractalUp[];
double UpFractal_1,UpFractal_2,LowFractal_1,LowFractal_2;

Для начала я объявил только переменные для хранения в них индексов баров, на которых сформировались фракталы.

В MQL4:

  1. n - переменная необходима для поиска ближайшего известного фрактала с помощью оператора цикла for;
  2. UpperFractal_1, UpperFractal_2,  LowerFractal_1, LowerFractal_2 - переменные будет хранить индекс бара, который является первым и вторым ближайшим экстремумом с наибольшей/наименьшей ценой (с позиции определения фракталов);

В MQL5 появляются дополнительно переменные:

  1. FractalDown[],FractalUp[]; - объявление массивов с типом данных double, в которые будут записываться значения буфера индикатора iFractals;
  2. Далее переменные с типом double: UpFractal_1,UpFractal_2,LowFractal_1,LowFractal_2. В них будут храниться ценовые значения экстремумов.

2. Поиск ближайших фракталов

Поиск индекса бара, на котором сформировался фрактал, осуществляется через оператор цикла for.

Определим первые два индекса бара, которые соответствуют первому и второму верхнему фракталу:

MQL4
//--- находим индекс бара первого ближайшего верхнего фрактала
   for(n=0; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_UPPER,n)!=NULL)
         break;
      UpperFractal_1=n+1;
     }
//--- находим индекс бара второго ближайшего верхнего фрактала
   for(n=UpperFractal_1+1; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_UPPER,n)!=NULL)
         break;
      UpperFractal_2=n+1;
     }
 MQL5
//--- сначала нужно записать в массивы значения буферов индиктора Fractal
//--- заполнение данными буфера
   CopyBuffer(Fractal,0,TimeCurrent(),Bars(Symbol(),PERIOD_D1),FractalUp);
   CopyBuffer(Fractal,1,TimeCurrent(),Bars(Symbol(),PERIOD_D1),FractalDown);
//--- индексация как в таймсериях
   ArraySetAsSeries(FractalUp,true);
   ArraySetAsSeries(FractalDown,true);
//--- далее используем оператор цикла for для поиска первого верхнего фрактала
   for(n=0; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      //--- если непустое значение, прерываем цикл
      if(FractalUp[n]!=EMPTY_VALUE)
         break;
     }
//--- запишем ценовое значение первого фрактала в переменную
   UpFractal_1=FractalUp[n];
//--- запишем индекс первого фрактала в переменную
   UpperFractal_1=n;
//--- поиск второго верхнего фрактала 
   for(n=UpperFractal_1+1; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      if(FractalUp[n]!=EMPTY_VALUE) //если непустое значение, прерываем цикл
         break;
     }
//--- запишем ценовое значение первого фрактала в переменную
   UpFractal_2=FractalUp[n];
//--- запишем индекс первого фрактала в переменную
   UpperFractal_2=n;

Тут наглядно представлено одно из принципиальных отличий MQL5 от MQL4 - использование функций для доступа к таймсериям.

Если в MQL4 я сразу перешел к поиску индекса бара, на котором сформировался фрактал, то в MQL5 я сначала через обращение к индикатору iFractals с помощью функции CopyBuffer() указал программе, что в массивах FractalUp[] и FractalDown[] будут храниться ценовые значения верхних и нижних фракталов. Далее я привел индексацию этих массивов в соответствие с индексацией как в таймсериях с помощью функции ArraySetAsSeries().

Теперь если в MQL4 я нашел только индексы баров, по которым мне известны фракталы, то с помощью функции CopyBuffer() MQL5 мне известны индексы баров и ценовые значения фракталов.

Аналогично производим поиск первых двух нижних фракталов:

MQL4
//--- находим индекс бара первого ближайшего нижнего фрактала
   for(n=0; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_LOWER,n)!=NULL)
         break;
      LowerFractal_1=n+1;
     }
//--- находим индекс бара второго ближайшего нижнего фрактала
   for(n=LowerFractal_1+1; n<(Bars-1);n++)
     {
      if(iFractals(NULL,1440,MODE_LOWER,n)!=NULL)
         break;
      LowerFractal_2=n+1;
     }
 MQL5
//--- поиск значений нижних фракталов
//--- поиск первого нижнего фрактала
   for(n=0; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      //--- если непустое значение, прерываем цикл
      if(FractalDown[n]!=EMPTY_VALUE)
         break;
     }
//--- запишем ценовое значение первого фрактала в переменную
   LowFractal_1=FractalDown[n];
//--- запишем индекс первого фрактала в переменную
   LowerFractal_1=n;
//--- поиск второго нижнего фрактала 
   for(n=LowerFractal_1+1; n<Bars(Symbol(),PERIOD_D1); n++)
     {
      if(FractalDown[n]!=EMPTY_VALUE)
         break;
     }
//--- запишем ценовое значение второго фрактала в переменную
   LowFractal_2=FractalDown[n];
//--- запишем индекс второго фрактала в переменную
   LowerFractal_2=n;

Как видно из таблицы, код в MQL4 и MQL5 очень похож. Присутствует разница в синтаксисе.


3. Поиск ценовых и временных значений фракталов

Для того чтобы прочертить линию, нужно найти также время и цену фрактала. С позиции MQL4 можно было бы, конечно, воспользоваться просто предопределенными таймсериями High[] и Low[], а также функцией iTime(), однако дополнительно требуется также уточнение временных координат линии - от этого будет зависеть корректность прорисовки трендовой линии.

На рис. 1-2 представлена разница во временных значениях экстремумов на таймфреймах H4 и M15.

Рис.1. Время экстремума с позиции таймфрейма H4

Рис.1. Время экстремума с позиции таймфрейма H4

Рис.2. Время экстремума с позиции таймфрейма M15

Рис.2. Время экстремума с позиции таймфрейма M15

Лично я для себя решил, что уточнение временного значения экстремума до 15 минут вполне достаточно для моих целей.

В целом принцип уточнения экстремума, как для MQL4, так и для MQL5, примерно одинаков, однако в деталях присутствуют некоторые отличия:

MQL4MQL5
  1. Определяется временное значение экстремума на старшем таймфрейме;
  2. На основании полученного временного значения  с помощью функции iBarShift() производится поиск индекса бара - экстремума на младшем таймфрейме;
  3. Так как 24 часа представляют собой массив из 96 15-минутных баров, то соответственно, производится поиск экстремума среди этих 96 элементов (наибольшее или наименьшее значения в массиве из 96 элементов) с помощью функций iHigh()iLow() и iTime(), а также ArrayMaximum() и ArrayMinimum().
  1. Определяется временное значение экстремума на старшем таймфрейме;
  2. На основании полученного временного значения определяется время формирования следующего дневного бара - это нужно для использования в последующем функций CopyHigh(), CopyLow() и CopyTime();
  3. Объявление массивов для хранения ценовых и временных данных по 15 минутному таймфрейму, их заполнение;
  4. С помощью функций ArrayMaximum() и ArrayMinimum() осуществляется поиск наименьших и наибольших ценовых значений, а также временные значения уточненных экстремумов. 

Далее привожу код на каждый из этапов:

 MQL4
// Этап 1. Определение временного значения экстремума на старшем таймфрейме:
//--- определение времени фракталов
   datetime UpFractalTime_1=iTime(NULL, 1440,UpperFractal_1);
   datetime UpFractalTime_2=iTime(NULL, 1440,UpperFractal_2);
   datetime LowFractalTime_1=iTime(NULL, 1440,LowerFractal_1);
   datetime LowFractalTime_2=iTime(NULL, 1440,LowerFractal_2);
// Этап 2.  Нахождение индекса бара - экстремума на младшем таймфрейме:   
//--- находим индекс фрактала на м15
   int UpperFractal_1_m15=iBarShift(NULL, 15, UpFractalTime_1,true);
   int UpperFractal_2_m15=iBarShift(NULL, 15, UpFractalTime_2,true);
   int LowerFractal_1_m15=iBarShift(NULL, 15, LowFractalTime_1,true);
   int LowerFractal_2_m15=iBarShift(NULL, 15, LowFractalTime_2,true);
// Этап 3. Использование массивов для поиска уточненных экстремумов на таймфрейме М15:
//--- использование массивов для поиска уточненных экстремумов
//--- введем переменную i для использорвания ее операторе цикла for
   int i;
//--- 1. Сначала найдем нижние экстремумы
//--- 3.1 Поиск первого нижнего экстремума
//--- объявление массива для хранения значений индексов баров
   int Lower_1_m15[96];
//--- объявление массива для хранения ценовых значений
   double LowerPrice_1_m15[96];
//--- запускаем цикл for:
   for(i=0;i<=95;i++)
     {
      //--- заполнение массива данными индексов баров
      Lower_1_m15[i]=LowerFractal_1_m15-i;
      //--- заполнение массива ценовыми данными
      LowerPrice_1_m15[i]=iLow(NULL,15,LowerFractal_1_m15-i);
     }
//--- определение минимального ценового значения в заданном массиве
   int LowestPrice_1_m15=ArrayMinimum(LowerPrice_1_m15,WHOLE_ARRAY,0);
//--- определние бара, на котором цена является наименьшей в рамках заданного массива
   int LowestBar_1_m15=Lower_1_m15[LowestPrice_1_m15];
//--- определение времени бара, на котором цена является наименьшей
   datetime LowestBarTime_1_m15=iTime(NULL,15,Lower_1_m15[LowestPrice_1_m15]);

//--- 3.2 Поиск второго нижнего экстремума
   int Lower_2_m15[96];
   double LowerPrice_2_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- заполнение массива данными индексов баров
      Lower_2_m15[i]=LowerFractal_2_m15-i;
      //--- заполнение массива ценовыми данными
      LowerPrice_2_m15[i]=iLow(NULL,15,LowerFractal_2_m15-i);
     }
//--- определение минимального ценового значения в заданном массиве
   int LowestPrice_2_m15=ArrayMinimum(LowerPrice_2_m15,WHOLE_ARRAY,0);
//--- определние бара, на котором цена является наименьшей в рамках заданного массива
   int LowestBar_2_m15=Lower_2_m15[LowestPrice_2_m15];
//--- определение времени бара, на котором цена является наименьшей
   datetime LowestBarTime_2_m15=iTime(NULL,15,Lower_2_m15[LowestPrice_2_m15]);

//--- 3.3 Поиск первого верхнего экстремума
   int Upper_1_m15[96];
   double UpperPrice_1_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- заполнение массива данными индексов баров
      Upper_1_m15[i]=UpperFractal_1_m15-i;
      //--- заполнение массива ценовыми данными
      UpperPrice_1_m15[i]=iHigh(NULL,15,UpperFractal_1_m15-i);
     }
//--- определение максимального ценового значения в заданном массиве
   int HighestPrice_1_m15=ArrayMaximum(UpperPrice_1_m15,WHOLE_ARRAY,0);
//--- определние бара, на котором цена является наибольшей в рамках заданного массива
   int HighestBar_1_m15=Upper_1_m15[HighestPrice_1_m15];
//--- определение времени бара, на котором цена является наибольшей
   datetime HighestBarTime_1_m15=iTime(NULL,15,Upper_1_m15[HighestPrice_1_m15]);

//--- 3.4 Поиск второго верхнего экстремума
   int Upper_2_m15[96];
   double UpperPrice_2_m15[96];
   for(i=0;i<=95;i++)
     {
      //--- заполнение массива данными индексов баров
      Upper_2_m15[i]=UpperFractal_2_m15-i;
      //--- заполнение массива ценовыми данными
      UpperPrice_2_m15[i]=iHigh(NULL,15,UpperFractal_2_m15-i);
     }
 MQL5
// Этап 1. Определение временного значения экстремума на старшем таймфрейме:
//--- объявление массива для хранения времени соответствующего индекса бара на старшем таймфрейме
   datetime UpFractalTime_1[],LowFractalTime_1[],UpFractalTime_2[],LowFractalTime_2[];
//--- определение времени фракталов на старшем таймфрейме
   CopyTime(Symbol(),PERIOD_D1,UpperFractal_1,1,UpFractalTime_1);
   CopyTime(Symbol(),PERIOD_D1,LowerFractal_1,1,LowFractalTime_1);
   CopyTime(Symbol(),PERIOD_D1,UpperFractal_2,1,UpFractalTime_2);
   CopyTime(Symbol(),PERIOD_D1,LowerFractal_2,1,LowFractalTime_2);
// Этап 2. Определение времени формирования следующего дневного бара:
//--- определение времени формирования следующего дневного бара (точка стопа для функций CopyHigh(), CopyLow() и CopyTime())
   datetime UpFractalTime_1_15=UpFractalTime_1[0]+86400;
   datetime UpFractalTime_2_15=UpFractalTime_2[0]+86400;
   datetime LowFractalTime_1_15=LowFractalTime_1[0]+86400;
   datetime LowFractalTime_2_15=LowFractalTime_2[0]+86400;
// Этап 3. Объявление массивов для хранения ценовых и временных данных по 15 минутному таймфрейму, их заполнение:   
//--- объявление массивов для хранения данных о максимальных и минимальных ценовых значениямх
   double High_1_15[],Low_1_15[],High_2_15[],Low_2_15[];
//--- заполнение массивов данными с помощью функций CopyHigh() и CopyLow()
   CopyHigh(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15,High_1_15);
   CopyHigh(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15,High_2_15);
   CopyLow(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15,Low_1_15);
   CopyLow(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15,Low_2_15);
//--- объявляем массивы для хранения временных значений, соответствующих индексам баров ценовых экстремумов  
   datetime High_1_15_time[],High_2_15_time[],Low_1_15_time[],Low_2_15_time[];
//--- заполнение массивов данными
   CopyTime(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15,High_1_15_time);
   CopyTime(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15,High_2_15_time);
   CopyTime(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15,Low_1_15_time);
   CopyTime(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15,Low_2_15_time);
// Этап 4. Поиск наименьших и наибольших ценовых значений, а также временные значения уточненных экстремумов:
//--- с помощью функций ArrayMaximum() и ArrayMinimum() находим наибольшие и наименьшие ценовые и временные значения
   int Max_M15_1=ArrayMaximum(High_1_15,0,96);
   int Max_M15_2=ArrayMaximum(High_2_15,0,96);
   int Min_M15_1=ArrayMinimum(Low_1_15,0,96);
   int Min_M15_2=ArrayMinimum(Low_2_15,0,96);

В итоге получилось определить следующие координаты трендовых линий:

1. Для линии поддержки:

MQL4MQL5
  1. Первая временная координата -  LowestBarTime_2_m15;
  2. Первая Ценовая координата  - LowerPrice_2_m15[LowestPrice_2_m15];
  3. Вторая временная координата  - LowestBarTime_1_m15;
  4. Вторая ценовая координата  - LowerPrice_1_m15[LowestPrice_1_m15].
  1. Первая временная координата - Low_2_15_time[Min_M15_2];
  2. Первая ценовая координата - Low_2_15[Min_M15_2];
  3. Вторая временная координата - Low_1_15_time[Min_M15_1];
  4. Вторая ценовая координата - Low_1_15[Min_M15_1].

2. Для линии сопротивления:

MQL4MQL5
  1. Первая временная координата -  HighestBarTime_2_m15;
  2. Первая Ценовая координата  - UpperPrice_2_m15[HighestPrice_2_m15];
  3. Вторая временная координата  - HighestBarTime_1_m15;
  4. Вторая ценовая координата  - UpperPrice_1_m15[HighestPrice_1_m15].
  1. Первая временная координата - High_2_15_time[Max_M15_2];
  2. Первая ценовая координата - High_2_15[Max_M15_2];
  3. Вторая временная координата - High_1_15_time[Max_M15_1];
  4. Вторая ценовая координата - High_1_15[Max_M15_1].


4. Создание объектов и модификация их свойств. Перерисовка линий

Теперь, когда известны координаты линий, осталось создать графические объекты:

MQL4
//--- создание линии поддержки
   ObjectCreate(0,"TL_Support",OBJ_TREND,0,LowestBarTime_2_m15,LowerPrice_2_m15[LowestPrice_2_m15],
                LowestBarTime_1_m15,LowerPrice_1_m15[LowestPrice_1_m15]);
   ObjectSet("TL_Support",OBJPROP_COLOR,Support_Color);
   ObjectSet("TL_Support",OBJPROP_STYLE,Support_Style);
   ObjectSet("TL_Support",OBJPROP_WIDTH,Support_Width);
//--- создание линии сопротивления
   ObjectCreate(0,"TL_Resistance",OBJ_TREND,0,HighestBarTime_2_m15,UpperPrice_2_m15[HighestPrice_2_m15],
                HighestBarTime_1_m15,UpperPrice_1_m15[HighestPrice_1_m15]);
   ObjectSet("TL_Resistance",OBJPROP_COLOR,Resistance_Color);
   ObjectSet("TL_Resistance",OBJPROP_STYLE,Resistance_Style);
   ObjectSet("TL_Resistance",OBJPROP_WIDTH,Resistance_Width);
MQL5
//--- cоздание линии поддержки
   ObjectCreate(0,"TL_Support",OBJ_TREND,0,Low_2_15_time[Min_M15_2],Low_2_15[Min_M15_2],Low_1_15_time[Min_M15_1],Low_1_15[Min_M15_1]);
   ObjectSetInteger(0,"TL_Support",OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,"TL_Support",OBJPROP_COLOR,Support_Color);
   ObjectSetInteger(0,"TL_Support",OBJPROP_STYLE,Support_Style);
   ObjectSetInteger(0,"TL_Support",OBJPROP_WIDTH,Support_Width);
//--- cоздание линии сопротивления
   ObjectCreate(0,"TL_Resistance",OBJ_TREND,0,High_2_15_time[Max_M15_2],High_2_15[Max_M15_2],High_1_15_time[Max_M15_1],High_1_15[Max_M15_1]);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_RAY_RIGHT,true);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_COLOR,Resistance_Color);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_STYLE,Resistance_Style);
   ObjectSetInteger(0,"TL_Resistance",OBJPROP_WIDTH,Resistance_Width);

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

Теперь необходимо предусмотреть перерисовку трендовых линий.

Если меняется ситуация на рынке, например, появляется новый экстремум, достаточно удалить существующую линию:

MQL4
//--- перерисовка линии поддержки
//--- записываем значения временных координат линии поддержки в переменные
   datetime TL_TimeLow2=ObjectGet("TL_Support",OBJPROP_TIME2);
   datetime TL_TimeLow1=ObjectGet("TL_Support",OBJPROP_TIME1);
//---если координаты линии не совпадают с текущими
   if(TL_TimeLow2!=LowestBarTime_1_m15 && TL_TimeLow1!=LowestBarTime_2_m15)
     {
      //---удаляем линию
      ObjectDelete(0,"TL_Support");
     }
//--- перерисовка линии сопротивления
//--- записываем значения временных координат линии сопротивления в переменные
   datetime TL_TimeUp2=ObjectGet("TL_Resistance",OBJPROP_TIME2);
   datetime TL_TimeUp1=ObjectGet("TL_Resistance",OBJPROP_TIME1);
//--- если координаты линии не совпадают с текущими
   if(TL_TimeUp2!=HighestBarTime_1_m15 && TL_TimeUp1!=HighestBarTime_2_m15)
     {
      //--- удаляем линию
      ObjectDelete(0,"TL_Resistance");
     }
MQL5
//--- перерисовка линии поддержки
//--- записываем значения временных координат линии поддержки в переменные
   datetime TL_TimeLow2=(datetime)ObjectGetInteger(0,"TL_Support",OBJPROP_TIME,0);
   datetime TL_TimeLow1=(datetime)ObjectGetInteger(0,"TL_Support",OBJPROP_TIME,1);
//--- если координаты линии не совпадают с текущими
   if(TL_TimeLow2!=Low_2_15_time[Min_M15_2] && TL_TimeLow1!=Low_1_15_time[Min_M15_1])
     {
      //--- удаляем линию
      ObjectDelete(0,"TL_Support");
     }
//--- перерисовка линии сопротивления
//--- записываем значения временных координат линии сопротивления в переменные
   datetime TL_TimeUp2=(datetime)ObjectGetInteger(0,"TL_Resistance",OBJPROP_TIME,0);
   datetime TL_TimeUp1=(datetime)ObjectGetInteger(0,"TL_Resistance",OBJPROP_TIME,1);
//--- если координаты линии не совпадают с текущими
   if(TL_TimeUp2!=High_2_15_time[Max_M15_2] && TL_TimeUp1!=High_1_15_time[Max_M15_1])
     {
      //--- удаляем линию
      ObjectDelete(0,"TL_Resistance");
     }


5. Контроль загруженности истории баров

При тестировании я столкнулся с проблемой того, что линии не всегда корректно чертились.

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

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

В случае когда бар не найден, функция iBarShift() возвращает значение -1. Значит, можно вывести, например, такое предупреждение:

MQL4
//--- контроль загруженности баров в истории
//--- если на M15 не найден хотя бы один бар
   if(UpperFractal_1_m15==-1 || UpperFractal_2_m15==-1
      || LowerFractal_1_m15==-1 || LowerFractal_2_m15==-1)
     {
      Alert("Для корректной работы недостаточно загружена история!");
     }

В MQL5 я воспользовался функцией Bars(), которая возвращает пустое значение, если данные по таймсерии еще не сформированы в терминале:

 
//--- контроль загруженности баров в истории
//--- 1. определяем количество баров на заданном промежутке времени
   int High_M15_1=Bars(Symbol(),PERIOD_M15,UpFractalTime_1[0],UpFractalTime_1_15);
   int High_M15_2=Bars(Symbol(),PERIOD_M15,UpFractalTime_2[0],UpFractalTime_2_15);
   int Low_M15_1=Bars(Symbol(),PERIOD_M15,LowFractalTime_1[0],LowFractalTime_1_15);
   int Low_M15_2=Bars(Symbol(),PERIOD_M15,LowFractalTime_2[0],LowFractalTime_2_15);
//--- 2. условие на случай, если загруженной истории недостаточно для корректной отрисовки линии
//--- если хотя бы один бар не найден
   if(High_M15_1==0 || High_M15_2==0 || Low_M15_1==0 || Low_M15_2==0)
     {
      Alert("Для корректной работы недостаточно загружена история!");
     }


6. Сигналы о пробое трендовых линий, push - уведомления

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

В целом процесс можно разделить на три шага:

  1. Определение цены закрытия бара и цены трендовой линии;
  2. Определение условий пробития ценой трендовой линии;
  3. Рассылка push - уведомления о пробитии ценой трендовой линии.
MQL4
// 1. Получение параметров цены трендовой линии 
//--- определение цены закрытия бара с индексом 1
   double Price_Close_H4=iClose(NULL,240,1);
//--- определение времени бара с индексом 1
   datetime Time_Close_H4=iTime(NULL,240,1);
//--- определение индекса бара на 4 часах
   int Bar_Close_H4=iBarShift(NULL,240,Time_Close_H4);
//--- определение цены линии на 4 часах
   double Price_Resistance_H4=ObjectGetValueByShift("TL_Resistance",Bar_Close_H4);
//--- определение цены линии на 4 часах   
   double Price_Support_H4=ObjectGetValueByShift("TL_Support",Bar_Close_H4);
// 2. Условия пробоя трендовых линий
//--- для пробоя поддержки
   bool breakdown=(Price_Close_H4<Price_Support_H4);
//--- для пробоя сопротивления
   bool breakup=(Price_Close_H4>Price_Resistance_H4);
// 3. Рассылка push - уведомления
   if(breakdown==true)
     {
      //--- отсылать не чаще, чем раз в 4 часа
      int SleepMinutes=240;
      static int LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=TimeCurrent();
         SendNotification(Symbol()+"Пробой линии поддержки");
        }
     }
   if(breakup==true)
     {
      //--- отсылать не чаще, чем раз в 4 часа
      SleepMinutes=240;
      LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=TimeCurrent();
         SendNotification(Symbol()+"Пробой линии сопротивления");
        }
     }
MQL5
// 1. Получение параметров цены трендовой линии
   double Close[];
   CopyClose(Symbol(),PERIOD_H4,TimeCurrent(),10,Close);
//--- задаем порядок индексации массива
   ArraySetAsSeries(Close,true);
//---
   datetime Close_time[];
   CopyTime(Symbol(),PERIOD_H4,TimeCurrent(),10,Close_time);
//--- задаем порядок индексации массива
   ArraySetAsSeries(Close_time,true);
//---
   double Price_Support_H4=ObjectGetValueByTime(0,"TL_Support",Close_time[1]);
   double Price_Resistance_H4=ObjectGetValueByTime(0,"TL_Resistance",Close_time[1]);
// 2. Условия пробоя трендовых линий
   bool breakdown=(Close[1]<Price_Support_H4);
   bool breakup=(Close[1]>Price_Resistance_H4);
// 3. Рассылка push - уведомления
   if(breakdown==true)
     {
      //--- отсылать не чаще, чем раз в 4 часа
      int SleepMinutes=240;
      static int LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=(int)TimeCurrent();
         SendNotification(Symbol()+"Пробой линии поддержки");
        }
     }
   if(breakup==true)
     {
      //--- отсылать не чаще, чем раз в 4 часа
      int SleepMinutes=240;
      static int LastTime=0;
      if(TimeCurrent()>LastTime+SleepMinutes*60)
        {
         LastTime=(int)TimeCurrent();
         SendNotification(Symbol()+"Пробой линии сопротивления");
        }
     }

Для определения пробоя в MQL4 я воспользовался функцией ObjectGetValueByShift(), в MQL5 - ObjectGetValueByTime().

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


7. Практическое применение трендовых линий в торговле

Самый простой способ: идентифицируем пробой, ждем отката цены и входим после соответствующего отката.

В идеале должно получиться нечто следующее:

Рис. 3. Пробой трендовой линии

Рис. 3. Пробой трендовой линии

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

Рис.4. Фигура "Треугольник"

Рис.4. Фигура "Треугольник"

На изображениях линии не уточнены по младшему таймфрейму.


Заключение

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

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

Спасибо за внимание, любая критика приветствуется.


Прикрепленные файлы |
trendlines.mq5 (10.68 KB)
trendlines.mq4 (10.14 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (21)
aleger
aleger | 25 дек. 2018 в 15:43
Fedot:
Но ведь зиг-заг строится от минимума к максимуму и, опять, к минимуму. Да и вопрос не в том, как найти экстремумы, а как построить ТЛ, не пересекающую промежуточные бары.

Назначение трендовых линий чисто утилитарное - показать весьма приблизительно а) Направление (вверх/вниз) и б) Размер (в пипсах) нужного тренда относительно окна графика, имеющего оцифровку по горизонтали и вертикали.

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

Сергей Дыбленко
Сергей Дыбленко | 17 нояб. 2019 в 16:17
Ни как советник и ни как индикатор на MQL4он не подает вообще никаких признаков!!!!!!!!!!!!!!!!!!! Это такой прикол грузить абы что и очень много лишь бы поднять свой рейтинг?
Vladimir Karputov
Vladimir Karputov | 17 нояб. 2019 в 16:28
Сергей Дыбленко:
Ни как советник и ни как индикатор на MQL4он не подает вообще никаких признаков!!!!!!!!!!!!!!!!!!! Это такой прикол грузить абы что и очень много лишь бы поднять свой рейтинг?

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


Konstantin Seredkin
Konstantin Seredkin | 24 мая 2020 в 08:38

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


Sergei Naumov
Sergei Naumov | 21 мая 2022 в 07:24
Сергей Дыбленко #:
Ни как советник и ни как индикатор на MQL4он не подает вообще никаких признаков!!!!!!!!!!!!!!!!!!! Это такой прикол грузить абы что и очень много лишь бы поднять свой рейтинг?

Добавляем функцию OnTick по окончанию инициализации чтобы работал в выходные да и вообще сразу как прилепляешь на график, а не когда придут новые цены

Как правильно выбрать продукт в Маркете для покупки. Пошаговое руководство Как правильно выбрать продукт в Маркете для покупки. Пошаговое руководство
В данном пошаговом руководстве описываются рекомендации и советы, помогающие быстрее разобраться и найти нужный продукт для покупки. В этой статье мы попытаемся разобраться, как найти подходящий продукт, отсортировать ненужное, определить эффективность продукта и насколько он будет востребован вами.
Создание интерактивного приложения для отображения RSS-каналов в MetaTrader 5 Создание интерактивного приложения для отображения RSS-каналов в MetaTrader 5
В данной статье рассматривается создание приложения, отображающего RSS-каналы. Мы также рассмотрим аспекты применения Стандартной библиотеки при создании интерактивных программ для MetaTrader 5.
Статистическая проверка системы управления капиталом Лябушера Статистическая проверка системы управления капиталом Лябушера
В статье приводится проверка статистических свойств системы управления капиталом Лябушера, являющейся менее агрессивной разновидностью Мартингейла и предполагающей повышение ставок не в 2 раза, а на определенную величину.
Торговые идеи на основе направления и скорости движения цен Торговые идеи на основе направления и скорости движения цен
В данной статье описывается идея, основанная на анализе направления движения цен и их скорости. Производится ее формализация на языке MQL4 в виде тестового торгового советника с целью выяснения жизнеспособности рассматриваемой стратегии. Также с помощью проверки, исследования и оптимизации определяются наилучшие параметры на предоставленном в статье примере.