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

Sergey Gritsay | 17 августа, 2010

Введение

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

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


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

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


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

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

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

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

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

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

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

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();
     }

Заключение

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

Примечание