Построение излучений индикаторов в MQL5

Sergey Pavlov | 23 марта, 2010

Введение

Наверняка, многих трейдеров и разработчиков торговых стратегий (ТС) интересовали подобные вопросы:

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

Рис. 1 Излучение индикатора DCMV

Рис. 2 Излучение индикатора, построенного на конвертах iМА

Здесь изображены излучения от разных индикаторов, но принцип построения у них одинаков. С каждым тиком, на графике появляются всё новые и новые точки с различным цветом и формой. Они образуют многочисленные скопления в виде туманностей, облаков, дорожек, линий, дуг и т.п. Эти характерные области точек помогают обнаружить невидимые пружины и силы, которые влияют на движение рыночных цен. Рассмотрение и анализ излучений индикаторов похожи чем-то даже на хиромантию.

Излучение и его свойства

Излучение - это множество точек, расположенных в местах пересечений характерных линий индикатора.

Свойства излучений пока не все открыты и не достаточно изучены - они ждут ещё своих исследователей. А вот некоторые, уже известные, свойства можно перечислить:

Построение излучения от индикаторов

Рассмотрим принцип построения излучения на примере. Для этого возьмём два индикатора: iBands и iMA. Затем будем искать пересечения линий этих индикаторов и там рисовать соответствующие точки. Для этого нам потребуются графические объекты, алгоритм реализован в эксперте, хотя можно и в индикаторе.

Итак, исходные индикаторы изображаются на графике примерно так.

Рис. 3 Индикаторы iBands (зелёный) и iMA (красный)

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

Рис. 4 Создание шаблона советника с помощью мастера

//+------------------------------------------------------------------+
//|                                        Излучение Bands && MA.mq5 |
//|                                                 Copyright DC2008 |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "DC2008"
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---
   
  }
//+------------------------------------------------------------------+

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

Рис. 5 Вспомогательные построения. Продолжение линий индикаторов лучами

Итак, добавим в код эксперта графические объекты: горизонтальные и трендовые (индикаторные) линии.

input bool     H_line=true;   // разрешаем строить горизонтальные линии
input bool     I_line=true;   // разрешаем строить индикаторные линии
//---
string         name;
//---- indicator buffers
double      MA[];    // массив для индикатора iMA
double      BBH[];   // массив для индикатора iBands - UPPER_BAND 
double      BBL[];   // массив для индикатора iBands - LOWER_BAND
double      BBM[];   // массив для индикатора iBands - BASE_LINE
datetime    T[];     // массив координат времени
//---- handles for indicators
int         MAHandle;   // указатель на индикатор iMA
int         BBHandle;   // указатель на индикатор iBands
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MAHandle=iMA(Symbol(),0,21,0,MODE_EMA,PRICE_CLOSE);
   BBHandle=iBands(Symbol(),0,144,0,2,PRICE_CLOSE);
//---
   if(H_line)     // Горизонтальные линии для индикатора iBands
      {
         //--- iBands - UPPER_BAND
         name="Hi";
         ObjectCreate(0,name,OBJ_HLINE,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,Red);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         //--- iBands - LOWER_BAND
         name="Lo";
         ObjectCreate(0,name,OBJ_HLINE,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,Blue);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         //--- iBands - BASE_LINE
         name="MIDI";
         ObjectCreate(0,name,OBJ_HLINE,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,DarkOrange);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_DOT);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
      }
//---
   if(I_line)     // Индикаторные линии
      {
         //--- iMA
         name="MA";
         ObjectCreate(0,name,OBJ_TREND,0,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,Red);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,2);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,1);
         //--- iBands - UPPER_BAND
         name="BH";
         ObjectCreate(0,name,OBJ_TREND,0,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,MediumSeaGreen);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,1);
         //--- iBands - LOWER_BAND
         name="BL";
         ObjectCreate(0,name,OBJ_TREND,0,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,MediumSeaGreen);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,1);
         //--- iBands - BASE_LINE
         name="BM";
         ObjectCreate(0,name,OBJ_TREND,0,0,0,0);           
         ObjectSetInteger(0,name,OBJPROP_COLOR,MediumSeaGreen);
         ObjectSetInteger(0,name,OBJPROP_STYLE,STYLE_SOLID);
         ObjectSetInteger(0,name,OBJPROP_WIDTH,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_RIGHT,1);
         ObjectSetInteger(0,name,OBJPROP_RAY_LEFT,1);
      }
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- заполнение массивов координат текущими значениями
   CopyBuffer(MAHandle,0,0,2,MA);
   ArraySetAsSeries(MA,true);  
   CopyBuffer(BBHandle,0,0,2,BBM);
   ArraySetAsSeries(BBM,true);  
   CopyBuffer(BBHandle,1,0,2,BBH);
   ArraySetAsSeries(BBH,true);  
   CopyBuffer(BBHandle,2,0,2,BBL);
   ArraySetAsSeries(BBL,true);
   CopyTime(Symbol(),0,0,10,T);
   ArraySetAsSeries(T,true);
     
   //--- Горизонтальные линии для индикатора iBands (корректировка)
   if(H_line)
      {
      name="Hi";
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBH[0]);
      name="Lo";
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBL[0]);
      name="MIDI";
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBM[0]);
      }
   //--- Индикаторные линии (корректировка)
   if(I_line)
      {
      name="MA";  //--- iMA
      ObjectSetInteger(0,name,OBJPROP_TIME,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,MA[1]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,MA[0]);
      name="BH";  //--- iBands - UPPER_BAND
      ObjectSetInteger(0,name,OBJPROP_TIME,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBH[1]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,BBH[0]);
      name="BL";  //--- iBands - LOWER_BAND
      ObjectSetInteger(0,name,OBJPROP_TIME,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBL[1]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,BBL[0]);
      name="BM";  //--- iBands - BASE_LINE
      ObjectSetInteger(0,name,OBJPROP_TIME,T[1]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,BBM[1]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,T[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,BBM[0]);
      }
  }
//+------------------------------------------------------------------+
Поскольку построение излучения происходит и в прошлое, и в будущее, то свойства трендовых линий должны быть следующими:

В результате, график с дополнительными линиями будет выглядеть приблизительно так, как на рис. 6.

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

Рис. 6 Вспомогательные построения. Продолжения индикаторов прямыми линиями

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

void Draw_Point(
                string   P_name,     // имя объекта OBJ_ARROW
                double   P_y1,       // Y-координата 1 прямой на [1] баре
                double   P_y0,       // Y-координата 1 прямой на [0] баре
                double   P_yy1,      // Y-координата 2 прямой на [1] баре 
                double   P_yy0,      // Y-координата 2 прямой на [0] баре
                char     P_code1,    // код символа точки справа от [0] бара
                char     P_code2,    // код символа точки слева  от [0] бара
                color    P_color1,   // цвет точки точки справа от [0] бара
                color    P_color2    // цвет точки точки слева  от [0] бара
                )
  {
   double   P,X;
   datetime P_time;
   if(MathAbs((P_yy0-P_yy1)-(P_y0-P_y1))>0)
     {
      P=P_y1+(P_y0-P_y1)*(P_y1-P_yy1)/((P_yy0-P_yy1)-(P_y0-P_y1));
      X=(P_y1-P_yy1)/((P_yy0-P_yy1)-(P_y0-P_y1));
      if(X>draw_period)
        {
         P_time=T[0]+(int)(X*PeriodSeconds());
         ObjectCreate(0,P_name,OBJ_ARROW,0,0,0);
         ObjectSetDouble(0,P_name,OBJPROP_PRICE,P);
         ObjectSetInteger(0,P_name,OBJPROP_TIME,P_time);
         ObjectSetInteger(0,P_name,OBJPROP_WIDTH,0);
         ObjectSetInteger(0,P_name,OBJPROP_ARROWCODE,P_code1);
         ObjectSetInteger(0,P_name,OBJPROP_COLOR,P_color1);
         if(X<0)
           {
            ObjectSetInteger(0,P_name,OBJPROP_ARROWCODE,P_code2);
            ObjectSetInteger(0,P_name,OBJPROP_COLOR,P_color2);
           }
        }
     }
  }

а в функцию OnTick() советника включим вот этот фрагмент:

//+------------------------------------------------------------------+
   int GTC=GetTickCount();                                                    
//+------------------------------------------------------------------+
   name="H"+(string)GTC;
   Draw_Point(name,BBH[1],BBH[0],MA[1],MA[0],170,178,Red,Red);
   name="L"+(string)GTC;
   Draw_Point(name,BBL[1],BBL[0],MA[1],MA[0],170,178,Blue,Blue);
   name="M"+(string)GTC;
   Draw_Point(name,BBM[1],BBM[0],MA[1],MA[0],170,178,Green,Green);
//---
   ChartRedraw(0);

Запускаем эксперт и смотрим на результат (рис. 7).

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

Рис. 7 Излучение от индикаторов iMA и iBands (3 пересечения)

Давайте попробуем добавить ещё одну серию точек к полученному излучению:

В результате этих пересечений, мы получили бы 3 точки, но они все будут иметь одинаковые координаты. Поэтому, достаточно оставить только одно пересечение, в котором участвует прямая "BH" и прямая "BL".

Добавим в код эксперта вот эти строчки и посмотрим на окончательный результат (рис. 8).

   name="B"+(string)GTC;
   Draw_Point(name,BBH[1],BBH[0],BBL[1],BBL[0],170,178,Magenta,Magenta);

Рис. 8 Излучение от индикаторов iMA и iBands (4 пересечения)

Итак, вроде получили излучение, но остаётся ощущение, что мы что-то очень важное пропустили. А что?

У внимательного читателя давно уже возник вопрос: а почему, собственно говоря, в индикаторах использованы именно такие входные параметры? А что будет, если их изменить? И вообще, какова их роль в излучениях?

Всё правильно. Полученное нами излучение соответствует только одной частоте, обусловленной входными параметрами индикаторов. А чтобы получить наиболее полный многочастотный спектр,  необходимо построить аналогичные излучения и для других частот. Выбор таких частот остаётся за вами. В качестве примера, приведу свой вариант возможного спектра излучения:

//---- handles for indicators
int         MAHandle[5];   // массив указателей на индикатор iMA
int         BBHandle[7];   // массив указателей на индикатор iBands
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   MAHandle[0]=iMA(NULL,0,21,0,MODE_EMA,PRICE_CLOSE);
   MAHandle[1]=iMA(NULL,0,34,0,MODE_EMA,PRICE_CLOSE);
   MAHandle[2]=iMA(NULL,0,55,0,MODE_EMA,PRICE_CLOSE);
   MAHandle[3]=iMA(NULL,0,89,0,MODE_EMA,PRICE_CLOSE);
   MAHandle[4]=iMA(NULL,0,144,0,MODE_EMA,PRICE_CLOSE);
//---
   BBHandle[0]=iBands(NULL,0,55,0,2,PRICE_CLOSE);
   BBHandle[1]=iBands(NULL,0,89,0,2,PRICE_CLOSE);
   BBHandle[2]=iBands(NULL,0,144,0,2,PRICE_CLOSE);
   BBHandle[3]=iBands(NULL,0,233,0,2,PRICE_CLOSE);
   BBHandle[4]=iBands(NULL,0,377,0,2,PRICE_CLOSE);
   BBHandle[5]=iBands(NULL,0,610,0,2,PRICE_CLOSE);
   BBHandle[6]=iBands(NULL,0,987,0,2,PRICE_CLOSE);
//---
   return(0);
  }

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

//+------------------------------------------------------------------+
   CopyTime(NULL,0,0,10,T);
   ArraySetAsSeries(T,true);
   int GTC=GetTickCount();
//+------------------------------------------------------------------+
   int iMax=ArraySize(BBHandle)-1;
   int jMax=ArraySize(MAHandle)-1;
   for(int i=0; i<iMax; i++)
     {
      for(int j=0; j<jMax; j++)
        {
         //--- заполнение массивов координат текущими значениями
         CopyBuffer(MAHandle[j],0,0,2,MA);
         ArraySetAsSeries(MA,true);
         CopyBuffer(BBHandle[i],0,0,2,BBM);
         ArraySetAsSeries(BBM,true);
         CopyBuffer(BBHandle[i],1,0,2,BBH);
         ArraySetAsSeries(BBH,true);
         CopyBuffer(BBHandle[i],2,0,2,BBL);
         ArraySetAsSeries(BBL,true);

         name="H"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBH[1],BBH[0],MA[1],MA[0],250,158,Aqua,Aqua);
         name="L"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBL[1],BBL[0],MA[1],MA[0],250,158,Blue,Blue);
         name="M"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBM[1],BBM[0],MA[1],MA[0],250,158,Green,Green);
         name="B"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBH[1],BBH[0],BBL[1],BBL[0],250,158,Magenta,Magenta);
        }
     }
//---
   ChartRedraw(0);

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

 Рис. 9 Многочастотный спектр излучения

О стилях отображения излучений

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

  1. У каждого человека своё восприятие графических образов, поэтому придётся потратить какое-то время на настройку изображения излучений под себя.
  2. "Каша" на графике (рис. 9) мешает распознаванию образов и каких-либо закономерностей, поэтому он служит примером - "как НЕ надо делать".
  3. Цвета старайтесь подбирать так, чтобы они находились по соседству в спектре радуги.
  4. Коды символов (точек) в прошлом (слева от [0] бара) и будущем (справа от [0] бара) должны обязательно отличаться.
  5. Гармоничное сочетание цветовой гаммы и формы точек способно превратить излучение в шедевры на ваших мониторах, которые будут не только помогать в торговле, но и радовать глаз.

В качестве примера, приведу свой вариант стиля отображения излучения (см. рис. 10-17):

         name="H"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBH[1],BBH[0],MA[1],MA[0],250,158,Aqua,Aqua);
         name="L"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBL[1],BBL[0],MA[1],MA[0],250,158,Blue,Blue);
         name="M"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBM[1],BBM[0],MA[1],MA[0],250,158,Magenta,Magenta);
         name="B"+(string)GTC+(string)i+(string)j;
         Draw_Point(name,BBH[1],BBH[0],BBL[1],BBL[0],250,158,DarkOrchid,DarkOrchid);

Галерея излучения iMA и iBands

В этом разделе размещены рисунки с исследуемым излучением.

Рис. 10

Рис. 11

Рис. 12

Рис. 13

Рис. 14

Рис. 15

Рис. 16

Рис. 17

Анализ излучения

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

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

Заключение

  1. Излучения индикаторов могут заинтересовать трейдеров и разработчиков торговых стратегий, которые ищут новые методы исследования и анализа рынка.
  2. Статья является ознакомительной и готовых решений не содержит. Однако приведённая технология получения излучений подробно рассмотрена и может быть применена для других индикаторов или их комбинаций.
  3. К моменту опубликования статьи у меня накопилось больше вопросов, чем ответов. Вот некоторые из них: как оптимизировать алгоритм построения излучений; как влияют спектрально-частотные характеристики на информативность излучения; как использовать излучения в автотрейдинге?