Скачать MetaTrader 5

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

28 апреля 2015, 12:02
Almat Kaldybay
10
6 922

Оглавление


Введение

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

Анализ рынков я часто делаю на основной своей работе, где я могу уделить трейдингу немного времени. К тому же недостаточно просто расчертить линии на старшем таймфрейме, линия должна чертиться по экстремумам с точностью до 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)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (10)
Roman Kornev
Roman Kornev | 18 окт 2015 в 13:03
Приветствую! Подскажите как поставить советник в мт4. Я скачал файл trendlines.mq4  скомпилировал его и вместе с исходником отправил в папку mql4/Indicators, перезагрузил терминал, пытаюсь присоединить индикатор к графику - ноль реакции. Что делаю не так?
Vladimir Karputov
Vladimir Karputov | 18 окт 2015 в 13:08
Roman Kornev:
Приветствую! Подскажите как поставить советник в мт4. Я скачал файл trendlines.mq4  скомпилировал его и вместе с исходником отправил в папку mql4/Indicators, перезагрузил терминал, пытаюсь присоединить индикатор к графику - ноль реакции. Что делаю не так?

Ошибка: "... отправил в папку mql4/Indicators...", так после скачивания автоматически открывается редактор кода MetaEditor, который сам автоматически размещает в нужном каталоге программу. И после компиляции не нужно никого никуда "отправлять".

Добавлено: и вообще - это не индикатор, а советник!

Roman Kornev
Roman Kornev | 18 окт 2015 в 13:40
Karputov Vladimir:

Ошибка: "... отправил в папку mql4/Indicators...", так после скачивания автоматически открывается редактор кода MetaEditor, который сам автоматически размещает в нужном каталоге программу. И после компиляции не нужно никого никуда "отправлять".

Добавлено: и вообще - это не индикатор, а советник!

Спасибо. Я открыл его свойства как советника и увидел там только настройки стиля линий. Поэтому и решил что это индикатор. Более того, в тестере на евро за месяц он не открыл ни одной сделки. Может я не особо внимательно читал статью конечно, просто решил включить посмотреть...
Almat Kaldybay
Almat Kaldybay | 18 окт 2015 в 19:19
Roman Kornev:
Спасибо. Я открыл его свойства как советника и увидел там только настройки стиля линий. Поэтому и решил что это индикатор. Более того, в тестере на евро за месяц он не открыл ни одной сделки. Может я не особо внимательно читал статью конечно, просто решил включить посмотреть...
Здравствуйте, он (советник) не открывает сделок. Целью статьи не было написание советника, который бы открывал сделки. Я лишь привел решение по автоматизации черчения трендовых линий.  Спасибо, что уделили статье время и внимание. 
MaksGroup
MaksGroup | 1 апр 2017 в 12:37

Добрый день.

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

В коде вреде-как предусмотрена проверка обновления всех экстремумов. Не пойму где проблема.

Создание интерактивного приложения для отображения RSS-каналов в MetaTrader 5 Создание интерактивного приложения для отображения RSS-каналов в MetaTrader 5

В данной статье рассматривается создание приложения, отображающего RSS-каналы. Мы также рассмотрим аспекты применения Стандартной библиотеки при создании интерактивных программ для MetaTrader 5.

Рецепты MQL5 - Реализуем ассоциативный массив или словарь для быстрого доступа к данным Рецепты MQL5 - Реализуем ассоциативный массив или словарь для быстрого доступа к данным

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

Как правильно выбрать продукт в Маркете для покупки. Пошаговое руководство Как правильно выбрать продукт в Маркете для покупки. Пошаговое руководство

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

Как правильно выбрать торговый сигнал для подписки. Пошаговое руководство Как правильно выбрать торговый сигнал для подписки. Пошаговое руководство

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