Скачать MetaTrader 5

20 торговых сигналов на MQL5

17 августа 2010, 11:19
Sergey Gritsay
35
10 382

Введение

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

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


1. Какие сигналы мы знаем

Все способы определения моментов входа в рынок можно разделить на несколько типов:

  • пересечение скользящих средних
  • пробой диапазона
  • выход из зоны перекупленности/перепроданности по показаниям стохастиков
  • отскок от границ канала
  • пробой границ канала
  • изменения тренда


2. Как это лучше закодировать

Существует множество способов написания кода, можно написать весь код в функции OnStart(), OnTick()  и других. Более подробно с ними можно ознакомиться в документации на сайте или во встроенной справке MetaEditor, но этот способ неэффективен и порой приходиться писать одни и те же участки кода несколько раз. Поэтому мы пойдем другим путем - с использованием пользовательских функций, которые можно вызвать из любого участка нашей программы.

Для удобства использования созданных функций объединим их в отдельный блок в виде внешнего подключаемого файла и назовем его SignalTrade, который будет храниться в директории ...\MQL5\Include. Это позволит с легкостью подключать данный модуль к любым программам. 

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

  • сигнал на покупку
  • сигнал на продажу
  • отсутствие сигнала

Поэтому, создадим перечисление, соответствующее этим сигналам.

  •  1 - сигнал на покупку
  • -1 - сигнал на продажу
  •  0 - отсутствие сигнала

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

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

int TradeSignal()
  {
   //--- ноль означает отсутствие сигнала
   int sig=0;
   
   //--- проверим хэндлы индикаторов

   //--- если хэндлы невалидные - создадим их

   //--- если хэндлы валидные - копируем значения из индикаторов

   //--- проводим проверку скопированных данных

   //--- если произошла ошибка при копировании, выходим из функции

   //--- производим индексацию в массиве как таймсерия

   //--- если произошла ошибка индексации, выходим из функции

   //--- проводим проверку условия и устанавливаем значение для sig
 
   //--- возвращаем торговый сигнал
   return(sig);
  }


3. Примеры 20 торговых сигналов

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

Эти функции возвращают хэндл соответствующей копии индикатора.  В дальнейшем, используя этот хэндл, можно получать данные, рассчитанные соответствующим индикатором. Данные соответствующего буфера (технические индикаторы содержат рассчитанные данные в своих внутренних буферах, которых, в зависимости от индикатора, может быть от 1 до 5) можно копировать в mql5-программу при помощи функции CopyBuffer().

Нельзя обратиться к данным индикатора сразу после его создания, так как на расчет значений индикатора требуется некоторое время, и поэтому создавать хэндлы индикаторов лучше всего в OnInit(). Функция iCustom() создает соответствующий пользовательский индикатор и при успешном создании возвращает его хэндл. Пользовательские индикаторы могут содержать до 512 индикаторных буферов, содержимое которых также можно получать при помощи функции CopyBuffer(), используя полученный хэндл.

Для каждого сигнала создадим функцию согласно нашему прототипу TradeSignal() и пронумеруем их по порядку TradeSignal_01() - TradeSignal_20(). На примере сигнала пересечения скользящих средних более подробно разберем структуру функции формирования сигнала, затем подобным образом напишем функции для других типов сигналов.

3.1. Пересечение скользящих средних

Рисунок 1. Пересечение двух скользящих средних

В функции TradeSignal_01() мы будем получать сигнал от пересечения двух Moving Average (далее МА): быстрой с периодом 8 и медленной с периодом 16.

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

int TradeSignal_01()
  {
//--- ноль означает отсутствие сигнала
   int sig=0;

//--- проверим хендлы индикаторов
   if(h_ma1==INVALID_HANDLE)//--- если хэндл невалидный
     {
      //--- создадим его снова                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- выходим из функции
      return(0);
     }
   else //--- если хэндл валидный 
     {
      //--- копируем значения из индикатора в массив
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- и если данных меньше требуемых
         //--- выходим из функции
         return(0);
      //--- зададим индексацию в массиве как таймсерию                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- в случае ошибки индексации выходим из функции
         return(0);
     }

   if(h_ma2==INVALID_HANDLE)//--- если хэндл невалидный
     {
      //--- создадим его снова                                                      
      h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
      //--- выходим из функции
      return(0);
     }
   else //--- если хэндл валидный 
     {
      //--- копируем значения из индикатора в массив
      if(CopyBuffer(h_ma2,0,0,2,ma2_buffer)<2) //--- и если данных меньше требуемых
         //--- выходим из функции
         return(0);
      //--- зададим индексацию в массиве как таймсерию                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- в случае ошибки индексации выходим из функции
         return(0);
     }

//--- проводим проверку условия и устанавливаем значение для sig
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

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

int sig=0;

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

   if(h_ma1==INVALID_HANDLE)//--- если хэндл невалидный
     {
      //--- создадим его снова                                                      
      h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
      //--- выходим из функции
      return(0);
     }

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

int  CopyBuffer(
   int       indicator_handle,     // handle индикатора
   int       buffer_num,           // номер буфера индикатора
   int       start_pos,            // откуда начнем 
   int       count,                // сколько копируем
   double    buffer[]              // массив, куда будут скопированы данные
   );

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

bool  ArraySetAsSeries(
   void  array[],     // массив по ссылке
   bool  set          // true означает обратный порядок индексации
   );

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

else //--- если хэндл валидный 
     {
      //--- копируем значения из индикатора в массив
      if(CopyBuffer(h_ma1,0,0,3,ma1_buffer)<3) //--- и если данных меньше требуемых
         //--- выходим из функции
         return(0);
      //--- зададим индексацию в массиве как таймсерию                                   
      if(!ArraySetAsSeries(ma1_buffer,true))
         //--- в случае ошибки индексации выходим из функции
         return(0);
     }

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

Для исключения дребезга сигнала на текущем баре, будем анализировать только закрытые 1-й и 2-й бары.

Формируем сигнал для покупки. Для этого возьмем значение быстрой МА на 2-ом баре и сравним его со значением медленной МА на 1-ом баре, а также сравним значение быстрой МА на 1-ом баре со значением медленной МА на 1-ом баре. Если быстрая МА на 2-ом баре меньше значения медленной МА на 1-ом баре и если значение быстрой МА на 1-ом баре больше значения медленной МА на 1-ом баре, это значит, что произошло пересечение быстрой МА медленную МА снизу вверх, и это и будет нашим сигналом для покупки. Если наше условие истинно, то запишем значение 1 в переменную sig.

Точно так же формируется сигнал для продажи. Если быстрая МА на 2-ом баре больше медленной МА на 1-ом, и если быстрая МА на 1-ом меньше медленной МА на 1-ом баре, то это соответствует пересечению быстрой МА медленной сверху вниз. Если наше условие истинно, то запишем значение -1 в переменную sig. Если оба наши условия ложны, то это соответствует отсутствию сигнала, и мы записываем в переменную sig значение ноль. Теперь сигналы сформированы, возвращаем полученный тип сигнала нашей функции TradeSignal_01()

//--- проводим проверку условия и устанавливаем значение для sig
   if(ma1_buffer[2]<ma2_buffer[1] && ma1_buffer[1]>ma2_buffer[1])
      sig=1;
   else if(ma1_buffer[2]>ma2_buffer[1] && ma1_buffer[1]<ma2_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);

3.2. Пересечение главной и сигнальной линии MACD

Рисунок 2. Пересечение главной и сигнальной линии индикатора MACD

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

int TradeSignal_02()
  {
   int sig=0;

   if(h_macd==INVALID_HANDLE)
     {
      h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_macd,0,0,2,macd1_buffer)<2)
         return(0);
      if(CopyBuffer(h_macd,1,0,3,macd2_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(macd1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(macd2_buffer,true))
         return(0);
     }

//--- проводим проверку условия и устанавливаем значение для sig
   if(macd2_buffer[2]>macd1_buffer[1] && macd2_buffer[1]<macd1_buffer[1])
      sig=1;
   else if(macd2_buffer[2]<macd1_buffer[1] && macd2_buffer[1]>macd1_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.3. Пробой диапазона Price Channel

Рисунок 3. Пробой канала верхней и нижней границ Price Channel

В функции TradeSignal_03() мы будем получать сигнал от пробоя канала верхней и нижней границ Price Channel.

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

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

int  CopyClose(
   string           symbol_name,       // имя символа
   ENUM_TIMEFRAMES  timeframe,          // период
   int              start_pos,         // откуда начнем 
   int              count,             // сколько копируем
   double           close_array[]      // массив для копирования цен закрытия
   );
int TradeSignal_03()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,3,pc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_pc,1,0,3,pc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[1]>pc1_buffer[2])
      sig=1;
   else if(Close[1]<pc2_buffer[2])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.4. Пробой диапазона адаптивного канала на ADX

Рисунок 4. Пробой канала верхней и нижней границ адаптивного канала ADX

В функции TradeSignal_04() мы будем получать сигнал от пробоя канала верхней и нижней границ адаптивного канала на ADX.

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

int TradeSignal_04()
  {
   int sig=0;

   if(h_acadx==INVALID_HANDLE)
     {
      h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_acadx,0,0,2,acadx1_buffer)<2)
         return(0);
      if(CopyBuffer(h_acadx,1,0,2,acadx2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(acadx1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(acadx2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[1]>acadx1_buffer[1])
      sig=1;
   else if(Close[1]<acadx2_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.5. Выход из перекупленности/перепроданности стохастикa

Рисунок 5. Пересечение стохастиком уровней перепроданности и перекупленности

В функции TradeSignal_05() мы будем получать сигнал из выхода стохастика из зон перекупленности/перепроданности, уровнями этих зон будут служить уровни со значениями 80 и 20.

Покупаем, когда осциллятор (%K или %D) сначала опустится ниже определенного уровня (обычно 20), а затем поднимется выше него. Продаем, когда осциллятор сначала поднимется выше определенного уровня (обычно 80), а потом опустится ниже него.

int TradeSignal_05()
  {
   int sig=0;

   if(h_stoh==INVALID_HANDLE)
     {
      h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_stoh,0,0,3,stoh_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(stoh_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(stoh_buffer[2]<20 && stoh_buffer[1]>20)
      sig=1;
   else if(stoh_buffer[2]>80 && stoh_buffer[1]<80)
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.6. Выход из перекупленности/перепроданности RSI

Рисунок 6. Пересечение индикатором RSI уровней перепроданности и перекупленности

В функции TradeSignal_06() мы будем получать сигнал из выхода RSI из зон перекупленности/перепроданности, уровнями этих зон будут служить уровни со значениями 70 и 30.

Покупаем, когда RSI сначала опустится ниже определенного уровня (обычно 30), а затем поднимется выше него. Продаем, когда RSI сначала поднимется выше определенного уровня (обычно 70), а потом опустится ниже него.

int TradeSignal_06()
  {
   int sig=0;

   if(h_rsi==INVALID_HANDLE)
     {
      h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_rsi,0,0,3,rsi_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(rsi_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(rsi_buffer[2]<30 && rsi_buffer[1]>30)
      sig=1;
   else if(rsi_buffer[2]>70 && rsi_buffer[1]<70)
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
 
3.7. Выход из перекупленности/перепроданности CCI

Рисунок 7. Пересечение индикатором CCI уровней перепроданности и перекупленности

В функции TradeSignal_07() мы будем получать сигнал из выхода CCI из  зон перекупленности/перепроданности, уровнями этих зон будут служить уровни со значениями 100 и -100.

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

int TradeSignal_07()
  {
   int sig=0;

   if(h_cci==INVALID_HANDLE)
     {
      h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_cci,0,0,3,cci_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(cci_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(cci_buffer[2]<-100 && cci_buffer[1]>-100)
      sig=1;
   else if(cci_buffer[2]>100 && cci_buffer[1]<100)
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.8. Выход из перекупленности/перепроданности Williams %

Рисунок 8. Пересечение индикатором Williams % уровней перепроданности и перекупленности

В функции TradeSignal_08() мы будем получать сигнал из выхода Williams % из  зон перекупленности/перепроданности, уровнями этих зон будут служить уровни со значениями -20 и -80.

Покупаем, когда Williams % сначала опустится ниже  уровня -80, а затем поднимется выше него. Продаем, когда Williams % сначала поднимется выше  уровня -20, а потом опустится ниже него.

int TradeSignal_08()
  {
   int sig=0;

   if(h_wpr==INVALID_HANDLE)
     {
      h_wpr=iWPR(Symbol(),Period(),14);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_wpr,0,0,3,wpr_buffer)<3)
         return(0);

      if(!ArraySetAsSeries(wpr_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(wpr_buffer[2]<-80 && wpr_buffer[1]>-80)
      sig=1;
   else if(wpr_buffer[2]>-20 && wpr_buffer[1]<-20)
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }
3.9. Отскок от границ канала Боллинджера

Рисунок 9. Отскок цены от границ канала Боллинджера

В функции TradeSignal_09() мы будем получать сигнал при отскоке цены от границ канала Боллинджера.

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

int TradeSignal_09()
  {
   int sig=0;

   if(h_bb==INVALID_HANDLE)
     {
      h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_bb,1,0,2,bb1_buffer)<2)
         return(0);
      if(CopyBuffer(h_bb,2,0,2,bb2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(bb1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(bb2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[2]<=bb2_buffer[1] && Close[1]>bb2_buffer[1])
      sig=1;
   else if(Close[2]>=bb1_buffer[1] && Close[1]<bb1_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.10. Отскок от границ канала стандартного отклонения

Рисунок 10. Отскок цены от границ канала стандартного отклонения

В функции TradeSignal_10() мы будем получать сигнал при отскоке цены от границ канала стандартного отклонения.

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

int TradeSignal_10()
  {
   int sig=0;

   if(h_sdc==INVALID_HANDLE)
     {
      h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sdc,0,0,2,sdc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sdc,1,0,2,sdc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sdc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sdc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[2]<=sdc2_buffer[1] && Close[1]>sdc2_buffer[1])
      sig=1;
   else if(Close[2]>=sdc1_buffer[1] && Close[1]<sdc1_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.11. Отскок от границ канала Price Channel

Рисунок 11. Отскок цены от границ канала Price Channel

В функции TradeSignal_11() мы будем получать сигнал при отскоке цены от границ Price Channel.

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

int TradeSignal_11()
  {
   int sig=0;

   if(h_pc==INVALID_HANDLE)
     {
      h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_pc,0,0,4,pc1_buffer)<4)
         return(0);
      if(CopyBuffer(h_pc,1,0,4,pc2_buffer)<4)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(pc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(pc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[1]>pc2_buffer[2] && Close[2]<=pc2_buffer[3])
      sig=1;
   else if(Close[1]<pc1_buffer[2] && Close[2]>=pc1_buffer[3])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.12. Отскок от границ канала Convert (Envelopes)

Рисунок 12. Отскок цены от границ конверта Envelopes

В функции TradeSignal_12() мы будем получать сигнал при отскоке цены от границ конверта Envelopes.

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

int TradeSignal_12()
  {
   int sig=0;

   if(h_env==INVALID_HANDLE)
     {
      h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_env,0,0,2,env1_buffer)<2)
         return(0);
      if(CopyBuffer(h_env,1,0,2,env2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(env1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(env2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[2]<=env2_buffer[1] && Close[1]>env2_buffer[1])
      sig=1;
   else if(Close[2]>=env1_buffer[1] && Close[1]<env1_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.13. Пробой канала Дончиана

Рисунок 13. Пробой границ канала Дончиана

В функции TradeSignal_13() мы будем получать сигнал при пробое ценой границ канала Дончиана.

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

int TradeSignal_13()
  {
   int sig=0;

   if(h_dc==INVALID_HANDLE)
     {
      h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_dc,0,0,3,dc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_dc,1,0,3,dc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(dc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(dc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[1]>dc1_buffer[2])
      sig=1;
   else if(Close[1]<dc2_buffer[2])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.14. Пробой канала Silver-Channel

Рисунок 14. Пробой границ Silver-Channel

В функции TradeSignal_14() мы будем получать сигнал при пробое ценой границ канала Silver-Channel.  Индикатор Silver-Channel рисует 8 границ, которые могут также служить уровнями поддержки и сопротивления. Для получения сигнала мы возьмем 2 средние границы. 

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

int TradeSignal_14()
  {
   int sig=0;

   if(h_sc==INVALID_HANDLE)
     {
      h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_sc,0,0,2,sc1_buffer)<2)
         return(0);
      if(CopyBuffer(h_sc,1,0,2,sc2_buffer)<2)
         return(0);
      if(CopyClose(Symbol(),Period(),0,3,Close)<3)
         return(0);
      if(!ArraySetAsSeries(sc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(sc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[2]<sc1_buffer[1] && Close[1]>sc1_buffer[1])
      sig=1;
   else if(Close[2]>sc2_buffer[1] && Close[1]<sc2_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.15. Пробой канала Галахера

Рисунок 15. Пробой границ канала Галахера

В функции TradeSignal_15() мы будем получать сигнал при пробое ценой границ канала Галахера. Индикатор канала Галахера строиться по максимумам и минимумам за 10 дней. 

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

int TradeSignal_15()
  {
   int sig=0;

   if(h_gc==INVALID_HANDLE)
     {
      h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
      return(0);
     }
   else
     {
      if(CopyBuffer(h_gc,0,0,3,gc1_buffer)<3)
         return(0);
      if(CopyBuffer(h_gc,1,0,3,gc2_buffer)<3)
         return(0);
      if(CopyClose(Symbol(),Period(),0,2,Close)<2)
         return(0);
      if(!ArraySetAsSeries(gc1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(gc2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(Close,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(Close[1]>gc1_buffer[2])
      sig=1;
   else if(Close[1]<gc2_buffer[2])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.16. Изменение тренда по NRTR

Рисунок 16. Идентификация смены тренда при помощи индикатора NRTR

В функции TradeSignal_16() мы будем получать сигнал при смене тренда NRTR.

Когда индикатор NRTR показывает тренд вверх - это будет сигналом к покупке, если NRTR показывает тренд вниз - это будет сигналом к продаже.

int TradeSignal_16()
  {
   int sig=0;

   if(h_nrtr==INVALID_HANDLE)
     {
      h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_nrtr,0,0,2,nrtr1_buffer)<2)
         return(0);
      if(CopyBuffer(h_nrtr,1,0,2,nrtr2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(nrtr1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(nrtr2_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(nrtr1_buffer[1]>0)
      sig=1;
   else if(nrtr2_buffer[1]>0)
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.17. Изменение тренда по Аллигатору

Рисунок 17. Изменение тренда по Аллигатору

В функции TradeSignal_17() мы будем получать сигнал при смене тренда Аллигатора.

Когда Челюсть, Зубы и Губы закрыты или переплетены, Аллигатор собирается спать или уже спит. Когда он спит, его голод увеличивается — чем дольше он спит, тем более голодным он будет, когда проснется. Когда он просыпается, первое, что он делает, — это открывает свою Пасть и начинает зевать. Затем он начинает чуять запах пищи: мясо быка или мясо медведя, и начинает за ним охотиться. Когда Аллигатор основательно наестся, он начинает терять интерес к пище-цене (Линии Баланса сходятся) - это время для фиксирования прибыли.

int TradeSignal_17()
  {
   int sig=0;

   if(h_al==INVALID_HANDLE)
     {
      h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_al,0,0,2,al1_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,1,0,2,al2_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,2,0,2,al3_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(al1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al3_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(al3_buffer[1]>al2_buffer[1] && al2_buffer[1]>al1_buffer[1])
      sig=1;
   else if(al3_buffer[1]<al2_buffer[1] && al2_buffer[1]<al1_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.18. Изменение тренда по AMA

Рисунок 18. Изменение тренда по AMA

В функции TradeSignal_18() мы будем получать сигнал при смене тренда AMA.

Когда индикатор AMA направлен вверх, это будет служить сигналом к покупке, если AMA направлен вниз, то продаем.

int TradeSignal_18()
  {
   int sig=0;

   if(h_ama==INVALID_HANDLE)
     {
      h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ama,0,0,3,ama_buffer)<3)
         return(0);
      if(!ArraySetAsSeries(ama_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(ama_buffer[2]<ama_buffer[1])
      sig=1;
   else if(ama_buffer[2]>ama_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.19. Смена цвета Awesome Oscillator

Рисунок 19. Идентификация смены тренда при помощи индикатора Awesome Oscillator

В функции TradeSignal_19() мы будем получать сигнал при смене цвета гистограммы Awesome Oscillator.

Одной из особенностей MQL5 является то, что можно создавать буферы для индикаторов, в которых можно хранить индексы на цвет линий, заданных в свойствах #property indicator_colorN. Когда цвет гистограммы Awesome Oscillator зеленого цвета, это будет служить сигналом к покупке, если цвет гистограммы Awesome Oscillator меняется на красный, то продаем.

int TradeSignal_19()
  {
   int sig=0;

   if(h_ao==INVALID_HANDLE)
     {
      h_ao=iAO(Symbol(),Period());
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ao,1,0,20,ao_buffer)<20)
         return(0);
      if(!ArraySetAsSeries(ao_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(ao_buffer[1]==0)
      sig=1;
   else if(ao_buffer[1]==1)
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }

3.20. Изменение тренда в Ишимоку

Рисунок 20. Идентификация смены тренда при помощи индикатора Ишимоку

В функции TradeSignal_20() мы будем получать сигнал при смене тренда в Ишимоку. Для этого мы будем анализировать пересечение линий Tenkan-sen и Kijun-sen.

Сигнал к покупке генерируется, когда линия Tenkan-sen пересекает Kijun-sen снизу вверх. Пересечение сверху вниз является сигналом к продаже.

int TradeSignal_20()
  {
   int sig=0;

   if(h_ich==INVALID_HANDLE)
     {
      h_ich=iIchimoku(Symbol(),Period(),9,26,52);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_ich,0,0,2,ich1_buffer)<2)
         return(0);
      if(CopyBuffer(h_ich,1,0,2,ich2_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(ich1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(ich2_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(ich1_buffer[1]>ich2_buffer[1])
      sig=1;
   else if(ich1_buffer[1]<ich2_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }


4. Сделаем это в виде индикатора

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

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

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

//--- Подключим необходимые библиотеки функций
#include <SignalTrade.mqh>
//--- Импорт функций из библиотеки LibFunctions
#import "LibFunctions.ex5"
void SetLabel(string nm,string tx,ENUM_BASE_CORNER cn,ENUM_ANCHOR_POINT cr,int xd,int yd,string fn,int fs,double yg,color ct);
string arrow(int sig);
color Colorarrow(int sig);
#import
//+------------------------------------------------------------------+
//| Объявим переменные для хранения сигналов индикаторов             |
//+------------------------------------------------------------------+
int SignalMA;
int SignalMACD;
int SignalPC;
int SignalACADX;
int SignalST;
int SignalRSI;
int SignalCCI;
int SignalWPR;
int SignalBB;
int SignalSDC;
int SignalPC2;
int SignalENV;
int SignalDC;
int SignalSC;
int SignalGC;
int SignalNRTR;
int SignalAL;
int SignalAMA;
int SignalAO;
int SignalICH;

Как я и писал ранее, индикаторы загружаются в терминал один раз и создаются указатели (хэндлы) на эти индикаторы, поэтому в функции OnInit() пропишем их создание, так как эта функция запускается один раз при запуске программы.

int OnInit()
  {
//--- создадим хэндлы индикаторов
   h_ma1=iMA(Symbol(),Period(),8,0,MODE_SMA,PRICE_CLOSE);
   h_ma2=iMA(Symbol(),Period(),16,0,MODE_SMA,PRICE_CLOSE);
   h_macd=iMACD(Symbol(),Period(),12,26,9,PRICE_CLOSE);
   h_pc=iCustom(Symbol(),Period(),"Price Channel",22);
   h_acadx=iCustom(Symbol(),Period(),"AdaptiveChannelADX",14);
   h_stoh=iStochastic(Symbol(),Period(),5,3,3,MODE_SMA,STO_LOWHIGH);
   h_rsi=iRSI(Symbol(),Period(),14,PRICE_CLOSE);
   h_cci=iCCI(Symbol(),Period(),14,PRICE_TYPICAL);
   h_wpr=iWPR(Symbol(),Period(),14);
   h_bb=iBands(Symbol(),Period(),20,0,2,PRICE_CLOSE);
   h_sdc=iCustom(Symbol(),Period(),"StandardDeviationChannel",14,0,MODE_SMA,PRICE_CLOSE,2.0);
   h_env=iEnvelopes(Symbol(),Period(),28,0,MODE_SMA,PRICE_CLOSE,0.1);
   h_dc=iCustom(Symbol(),Period(),"Donchian Channels",24,3,-2);
   h_sc=iCustom(Symbol(),Period(),"Silver-channels",26,38.2,23.6,0,61.8);
   h_gc=iCustom(Symbol(),Period(),"PriceChannelGalaher");
   h_nrtr=iCustom(Symbol(),Period(),"NRTR",40,2.0);
   h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
   h_ama=iAMA(Symbol(),Period(),9,2,30,0,PRICE_CLOSE);
   h_ao=iAO(Symbol(),Period());
   h_ich=iIchimoku(Symbol(),Period(),9,26,52);
   return(0);
  }

Все основные расчеты проходят в функции OnCalculate(), где мы и поместим остальной код индикатора.

int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
//---присваеваем переменной значение сигнала
   SignalMA    = TradeSignal_01();
   SignalMACD  = TradeSignal_02();
   SignalPC    = TradeSignal_03();
   SignalACADX = TradeSignal_04();
   SignalST    = TradeSignal_05();
   SignalRSI   = TradeSignal_06();
   SignalCCI   = TradeSignal_07();
   SignalWPR   = TradeSignal_08();
   SignalBB    = TradeSignal_09();
   SignalSDC   = TradeSignal_10();
   SignalPC2   = TradeSignal_11();
   SignalENV   = TradeSignal_12();
   SignalDC    = TradeSignal_13();
   SignalSC    = TradeSignal_14();
   SignalGC    = TradeSignal_15();
   SignalNRTR  = TradeSignal_16();
   SignalAL    = TradeSignal_17();
   SignalAMA   = TradeSignal_18();
   SignalAO    = TradeSignal_19();
   SignalICH   = TradeSignal_20();

//--- рисуем графические объекты на графике в верхнем левом углу
   int size=((int)ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS)/22);
   int i=0;
   int x=10;
   int y=0;
   int fz=size-4;

   y+=size;
   SetLabel("arrow"+(string)i,arrow(SignalMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMA));
   x+=size;
   SetLabel("label"+(string)i,"Moving Average",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalMACD),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalMACD));
   x+=size;
   SetLabel("label"+(string)i,"MACD",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalACADX),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalACADX));
   x+=size;
   SetLabel("label"+(string)i,"Adaptive Channel ADX",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalST),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalST));
   x+=size;
   SetLabel("label"+(string)i,"Stochastic Oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalRSI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalRSI));
   x+=size;
   SetLabel("label"+(string)i,"RSI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalCCI),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalCCI));
   x+=size;
   SetLabel("label"+(string)i,"CCI",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalWPR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalWPR));
   x+=size;
   SetLabel("label"+(string)i,"WPR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalBB),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalBB));
   x+=size;
   SetLabel("label"+(string)i,"Bollinger Bands",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSDC));
   x+=size;
   SetLabel("label"+(string)i,"StDevChannel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalPC2),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalPC2));
   x+=size;
   SetLabel("label"+(string)i,"Price Channell 2",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalENV),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalENV));
   x+=size;
   SetLabel("label"+(string)i,"Envelopes",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalDC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalDC));
   x+=size;
   SetLabel("label"+(string)i,"Donchian Channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalSC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalSC));
   x+=size;
   SetLabel("label"+(string)i,"Silver-channels",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalGC),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalGC));
   x+=size;
   SetLabel("label"+(string)i,"Galaher Channel",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalNRTR),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalNRTR));
   x+=size;
   SetLabel("label"+(string)i,"NRTR",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAL),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAL));
   x+=size;
   SetLabel("label"+(string)i,"Alligator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAMA),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAMA));
   x+=size;
   SetLabel("label"+(string)i,"AMA",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalAO),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalAO));
   x+=size;
   SetLabel("label"+(string)i,"Awesome oscillator",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);
   i++;y+=size;x=10;
   SetLabel("arrow"+(string)i,arrow(SignalICH),CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y+4,"Wingdings",fz-2,0,Colorarrow(SignalICH));
   x+=size;
   SetLabel("label"+(string)i,"Ichimoku Kinko Hyo",CORNER_RIGHT_UPPER,ANCHOR_RIGHT_UPPER,x,y,"Arial",fz,0,BlueViolet);

   return(rates_total);
  }

Вот и готов наш индикатор, в итоге получаем вот такую картину на графике.



5. Сделаем это в виде эксперта

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


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

Для того чтобы менять настройки индикаторов через наш графический интерфейс, доработаем немного нашу библиотеку SignalTrade.mqh и назовем ее SignalTradeExp.mqh.

Во-первых, нам понадобятся дополнительные переменные для хранения настроек индикаторов.

//--- input parameters Moving Average
int                periodma1=8;
int                periodma2=16;
ENUM_MA_METHOD     MAmethod=MODE_SMA;
ENUM_APPLIED_PRICE MAprice=PRICE_CLOSE;
//--- input parameters MACD
int                FastMACD=12;
int                SlowMACD=26;
int                MACDSMA=9;
ENUM_APPLIED_PRICE MACDprice=PRICE_CLOSE;
//--- input parameters Price Channel
int                PCPeriod=22;
//--- input parameters Adaptive Channel ADX
int                ADXPeriod=14;
//--- input parameters Stochastic Oscillator
int                SOPeriodK=5;
int                SOPeriodD=3;
int                SOslowing=3;
ENUM_MA_METHOD     SOmethod=MODE_SMA;
ENUM_STO_PRICE     SOpricefield=STO_LOWHIGH;
//--- input parameters RSI
int                RSIPeriod=14;
ENUM_APPLIED_PRICE RSIprice=PRICE_CLOSE;
//--- input parameters CCI
int                CCIPeriod=14;
ENUM_APPLIED_PRICE CCIprice=PRICE_TYPICAL;
//--- input parameters WPR
int                WPRPeriod=14;
//--- input parameters Bollinger Bands
int                BBPeriod=20;
double             BBdeviation=2.0;
ENUM_APPLIED_PRICE BBprice=PRICE_CLOSE;
//--- input parameters Standard Deviation Channel
int                SDCPeriod=14;
double             SDCdeviation=2.0;
ENUM_APPLIED_PRICE SDCprice=PRICE_CLOSE;
ENUM_MA_METHOD     SDCmethod=MODE_SMA;
//--- input parameters Price Channel 2
int                PC2Period=22;
//--- input parameters Envelopes
int                ENVPeriod=14;
double             ENVdeviation=0.1;
ENUM_APPLIED_PRICE ENVprice=PRICE_CLOSE;
ENUM_MA_METHOD     ENVmethod=MODE_SMA;
//--- input parameters Donchian Channels
int                DCPeriod=24;
int                DCExtremes=3;
int                DCMargins=-2;
//--- input parameters Silver-channels
int                SCPeriod=26;
double             SCSilvCh=38.2;
double             SCSkyCh=23.6;
double             SCFutCh=61.8;
//--- input parameters NRTR
int                NRTRPeriod   =  40;
double             NRTRK        =  2.0;
//--- input parameters Alligator
int                ALjawperiod=13;
int                ALteethperiod=8;
int                ALlipsperiod=5;
ENUM_MA_METHOD     ALmethod=MODE_SMMA;
ENUM_APPLIED_PRICE ALprice=PRICE_MEDIAN;
//--- input parameters AMA
int                AMAperiod=9;
int                AMAfastperiod=2;
int                AMAslowperiod=30;
ENUM_APPLIED_PRICE AMAprice=PRICE_CLOSE;
//--- input parameters Ichimoku Kinko Hyo
int                IKHtenkansen=9;
int                IKHkijunsen=26;
int                IKHsenkouspanb=52;

Заменим постоянные значения индикаторов переменными. Все остальное остается без изменения.

h_ma1=iMA(Symbol(),Period(),periodma1,0,MAmethod,MAprice);

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

bool  IndicatorRelease(
   int       indicator_handle,     // handle индикатора
   );
   if(id==CHARTEVENT_OBJECT_ENDEDIT && sparam=="PIPSetEditMA2")
     {
      periodma2=(int)ObjectGetString(0,"PIPSetEditMA2",OBJPROP_TEXT);
      ObjectSetString(0,"PIPSetEditMA2",OBJPROP_TEXT,(string)periodma2);
      //--- выгружаем старую копию индикатора
      IndicatorRelease(h_ma2);
      //--- создаем новую копию индикатора
      h_ma2=iMA(Symbol(),Period(),periodma2,0,MAmethod,MAprice);
      ChartRedraw();
     }

Заключение

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

Примечание

  • Файлы индикаторов SignalTrade.mq5, AdaptiveChannelADX.mq5, Donchian Channels.mq5, NRTR.mq5, Price Channel.mq5, PriceChannelGalaher.mq5, Silver-channels.mq5, StandardDeviationChannel.mq5 необходимо поместить в папку ...\MQL5\Indicators.
  • Подключаемый файл SignalTrade.mqh и SignalTradeExp.mqh поместить в папку ...\MQL5\Include.
  • Библиотеку функций LibFunctions.mq5 поместить в папку ...\MQL5\Libraries.
  • Файл эксперта ExpSignalTrade.mq5 в папку ...\MQL5\Experts.
Прикрепленные файлы |
indicators.zip (9.09 KB)
signaltrade.mqh (20.18 KB)
signaltradeexp.mqh (23.24 KB)
libfunctions.mq5 (5.6 KB)
signaltrade.mq5 (10.55 KB)
expsignaltrade.mq5 (165.82 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (35)
Sergey Gritsay
Sergey Gritsay | 7 окт 2010 в 15:27

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

ENUM_ORDER_TYPE ind_01()
  {
   ENUM_ORDER_TYPE sig=WRONG_VALUE;

   if(IND01_handle==INVALID_HANDLE || IND01_handle==0)
     {
      IND01_handle=iAC(_Symbol,TF_01);
      return(WRONG_VALUE);
     }
   else
     {
      if(CopyBuffer(IND01_handle,1,0,AC_shift+3,IND01_buffer1)<AC_shift+3) return(WRONG_VALUE);
      if(!ArraySetAsSeries(IND01_buffer1,true)) return(WRONG_VALUE);

      if(CopyBuffer(IND01_handle,0,0,AC_shift+1,IND01_buffer2)<AC_shift+1) return(WRONG_VALUE);
      if(!ArraySetAsSeries(IND01_buffer2,true)) return(WRONG_VALUE);
     }

   if(IND01_buffer2[AC_shift]>0.0 && IND01_buffer1[AC_shift]==0 && IND01_buffer1[AC_shift+1]==0)sig=ORDER_TYPE_BUY;
   else if(IND01_buffer2[AC_shift]<0.0 && IND01_buffer1[AC_shift]==0 && IND01_buffer1[AC_shift+1]==0 && IND01_buffer1[AC_shift+2]==0)sig=ORDER_TYPE_BUY;
   else if(IND01_buffer2[AC_shift]<0.0 && IND01_buffer1[AC_shift]==1 && IND01_buffer1[AC_shift+1]==1)sig=ORDER_TYPE_SELL;
   else if(IND01_buffer2[AC_shift]>0.0 && IND01_buffer1[AC_shift]==1 && IND01_buffer1[AC_shift+1]==1 && IND01_buffer1[AC_shift+2]==1)sig=ORDER_TYPE_SELL;
   else sig=WRONG_VALUE;

   return(sig);
  }
Alexey Klenov
Alexey Klenov | 7 окт 2010 в 15:53
sergey1294:

Во первых в справке ясно написано

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

ДА верно визуальное но и ориентируемся мы на визуальное значение при входе в сделку а не на то как они лежат в памяти терминала по этому и нужно  это учитывать

Правильно будет так для аллигатора 13 на 8, 8 на 5, 5 на 3 (стандартные значения)

int TradeSignal_17()
  {
   int sig=0;

   if(h_al==INVALID_HANDLE)
     {
      h_al=iAlligator(Symbol(),Period(),13,0,8,0,5,0,MODE_SMMA,PRICE_MEDIAN);
      return(0);
     }
   else
     {
      if(CopyBuffer(h_al,0,8,2,al1_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,1,5,2,al2_buffer)<2)
         return(0);
      if(CopyBuffer(h_al,2,3,2,al3_buffer)<2)
         return(0);
      if(!ArraySetAsSeries(al1_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al2_buffer,true))
         return(0);
      if(!ArraySetAsSeries(al3_buffer,true))
         return(0);
     }
//--- проводим проверку условия и устанавливаем значение для sig
   if(al3_buffer[1]>al2_buffer[1] && al2_buffer[1]>al1_buffer[1])
      sig=1;
   else if(al3_buffer[1]<al2_buffer[1] && al2_buffer[1]<al1_buffer[1])
      sig=-1;
   else sig=0;

//--- возвращаем торговый сигнал
   return(sig);
  }
Sergey Gritsay
Sergey Gritsay | 7 окт 2010 в 16:19
olyakish:

ДА верно визуальное но и ориентируемся мы на визуальное значение при входе в сделку а не на то как они лежат в памяти терминала по этому и нужно  это учитывать

Правильно будет так для аллигатора 13 на 8, 8 на 5, 5 на 3 (стандартные значения)

Не буду утверждать, но я не думаю что все используют алигатора со смещением. Так что ошибок в этом случае нет. Вам понадобилось смещение вы его и добавили, так же могут и стальные сделать кому это необходимо. Повторюсь еще раз, цель стати показать как правильно обращаться к индикаторам и их данным, а сколько и  в каком количестве их получать это дело каждого индивидуально.
Alexey Klenov
Alexey Klenov | 7 окт 2010 в 16:28
sergey1294:
Не буду утверждать, но я не думаю что все используют алигатора со смещением. Так что ошибок в этом случае нет. Вам понадобилось смещение вы его и добавили, так же могут и стальные сделать кому это необходимо. Повторюсь еще раз, цель стати показать как правильно обращаться к индикаторам и их данным, а сколько и  в каком количестве их получать это дело каждого индивидуально.

Справка...

Технический Индикатор Alligator — это комбинация Линий Баланса (Скользящих Средних, Moving Averages) , использующих фрактальную геометрию и нелинейную динамику.

  • Синяя линия (Челюсть Аллигатора) — это Линия Баланса для временного периода, который использовался для построения графика (13-периодное сглаженное скользящее среднее, сдвинутое на 8 баров в будущее);
  • Красная линия (Зубы Аллигатора) — это Линия Баланса для значимого временного периода на порядок ниже (8-периодное сглаженное скользящее среднее, сдвинутое на 5 баров в будущее);
  • Зеленая линия (Губы Аллигатора) — это Линия Баланса для значимого временного периода, который ниже еще на один порядок (5-периодное сглаженное скользящее среднее, сдвинутое на 3 бара в будущее).


Ключевые слова выделены жирным.

Итог Вашего кода это анализ средних

Итог мною предложенного кода это анализ линий аллигатора.

Ovsov_Nikita
Ovsov_Nikita | 22 ноя 2013 в 15:35
Спасибо! Статья супер
Только, нет кнопки Stochastic Osticllator в настройках. По-моему просто две кнопки на одно место настроены.
ObjectSetInteger(0,"PIPIndNameButtonSO",OBJPROP_YDISTANCE,100);
  ObjectSetInteger(0,"PIPIndNameButtonRSI",OBJPROP_YDISTANCE,110);
Прототип торгового робота Прототип торгового робота

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

Применение функции TesterWithdrawal() для моделирования снятия прибыли Применение функции TesterWithdrawal() для моделирования снятия прибыли

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

Интервью с Леонидом Величковским: "Главный миф о нейронных сетях – сверхприбыльность" (ATC 2010) Интервью с Леонидом Величковским: "Главный миф о нейронных сетях – сверхприбыльность" (ATC 2010)

Герой нашего интервью - Леонид Величковский (LeoV) – уже принимал участие в Чемпионатах по автоматическому трейдингу. В 2008 году его мультивалютная нейронная сеть ярко вспыхнула на небосклоне, заработав в определенный момент 110 000 $, но в итоге пала жертвой собственного агрессивного мани-менеджмента. В интервью двухлетней давности Леонид говорил о собственном опыте трейдинга и особенностях работы его советника. В преддверии же Чемпионата ATC 2010 наш герой рассказывает о самых распространенных мифах и заблуждениях, связанных с нейросетями.

Как быстро написать советник для Automated Trading Championship 2010 Как быстро написать советник для Automated Trading Championship 2010

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