Скачать MetaTrader 5

Индикатор "ЗигЗаг": новый взгляд и новые решения

10 апреля 2013, 11:14
Sergey Pavlov
29
13 887

Введение

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

Существует множество версий этого индикатора: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16. Тем не менее, интерес к написанию собственного "идеального" зигзага возникает у многих разработчиков программ на языке MQL5. Главными недостатками ЗигЗага являются: запаздывание, некорректная разметка спорных узлов (внешний бар), неудовлетворительное быстродействие.

На мой взгляд, самая изящная реализация ЗигЗага сделана Юрием Куликовым (Yurich). Кроме того, на MQL4 есть очень хорошие статьи: "Записки дилетанта. ZigZag..." и "Show Must Go On... или очередное возвращение к ZigZag'у". Казалось бы, тема исследована очень подробно и имеется множество публикаций, но что-то притягивает внимание к этому индикатору снова и снова. Вот и меня заинтересовала эта тема, а именно - возможность создания опережающего ЗигЗага.

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

 

Методика создания опережающего ЗигЗага

Поставим перед собой цель: найти координаты двух узлов - текущего и прогнозируемого (рис. 1). Текущий - это тот, который ещё не сформировался и продолжается поиск или корректировка его координат. При этом он всё время находится на текущем (нулевом) баре. А прогнозируемый должен находиться в будущем и показывать предполагаемый уровень следующего узла ЗигЗага.

Прогнозирование новых узлов ЗигЗага

Рис. 1. Прогнозирование новых узлов ЗигЗага - текущего и следующего.

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

Индикаторы ЗигЗаг и конверты скользящей средней (Envelopes)

Рис. 2. Индикаторы ЗигЗаг и конверты скользящей средней (Envelopes).

Для повышения статистической достоверности прогноза следует взять не один индикатор Envelopes и даже не 10, а целый веер, например 100 или более индикаторов с разными входными данными. Отличаться они будут периодом усреднения основной линии индикатора и используемой ценой (для вершин - цены High, а для впадин - цены Low). Введём следующие обозначения и формулы:

  • ZZ - индикатор ЗигЗаг;
  • ENV - основная линия индикатора Envelopes (совпадает с индикатором iMA);
  • Envelopes(i) - значение основной линии индикатора Envelopes на i-ом баре;
  • ZZ(High) - значение вершины индикатора ЗигЗаг;
  • ZZ(Low) - значение впадины индикатора ЗигЗаг;
  • ENV(High) - значение основной линии индикатора Envelopes соответствующее вершине индикатора ЗигЗаг;
  • ENV(Low) - значение основной линии индикатора Envelopes соответствующее впадине индикатора ЗигЗаг;
  • n_high - количество вершин индикатора ЗигЗаг;
  • n_low - количество впадин индикатора ЗигЗаг.

Всего у нас есть два веера индикаторов: один для вершин и один для впадин (примерно по сто штук в каждом). Для каждого индикатора из веера мы будем рассчитывать отклонения узлов ЗигЗага от основной линии Envelopes! И для каждого индикатора из веера мы будем находить среднее арифметическое отклонений по формулам выше. На следующем рисунке показана диаграмма отклонений найденных узлов ZZ от основной линии ENV для одного индикатора.

Диаграмма отклонений узлов ZZ от ENV

Рис. 3. Диаграмма отклонений узлов ZZ от ENV.

Среднее арифметическое отклонений и будет использоваться для определения высоты, на которую нужно сдвигать основную линию индикатора Envelopes для построения линии конвертов. То есть среднее арифметическое отклонений от вершин ЗигЗага используется для построения верхней линии (upper line), а среднее арифметическое отклонений от впадин используется для построения нижней линии (lower line) индикатора Envelopes.

Именно верхнюю и нижнюю линии конвертов мы будем использовать для поиска характерных точек и прогнозирования узлов ЗигЗага. Повторю, нас интересует веер конвертов состоящий из множества индикаторов Envelopes. Для каждого индикатора находим среднее арифметическое отклонений узлов ЗигЗага от основной линии взятого конверта. Если нанести полученные линии (upper line и lower line) веера на график, то мы можем получить следующий результат:

Линии Envelopes на плоскости

Рис. 4. Линии Envelopes на плоскости.

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

Линии Envelopes в пространстве (3D)

Рис. 5. Линии Envelopes в пространстве (3D).

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

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

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

Поскольку мы отдельно ищем линии Envelopes для вершин и впадин, то и сечений у нас должно получиться два - одно для вершин, другое для впадин.

Для получения прогноза будем использовать ближайшую характерную точку. Например, если идёт поиск вершины ЗигЗага, то берём характерные точки сечения полученного в результате пересечения поверхности верхних линий индикатора Envelopes секущей плоскостью. И наоборот, для поиска впадины берём характерные точки сечения полученного в результате пересечения поверхности нижних линий индикатора Envelopes секущей плоскостью.

 

Тестирование нового индикатора

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

//+------------------------------------------------------------------+
//|                                               AdvancedZigZag.mqh |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/ru/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/ru/users/DC2008"
#property version   "1.00"
//+------------------------------------------------------------------+
//|                                                 GetExtremums.mqh |
//+------------------------------------------------------------------+
#include <GetExtremums.mqh>   // author of the code Yurich
#property copyright "Copyright 2012, Yurich"
#property link      "https://www.mql5.com/ru/users/Yurich"
//+------------------------------------------------------------------+
//| Структура узла ZigZag                                            |
//+------------------------------------------------------------------+
struct MqlZigZag
  {
   double            price;   // Координата узла
   datetime          t;       // Time
  };
//+------------------------------------------------------------------+
//| Класс AdvancedZigZag                                             |
//+------------------------------------------------------------------+
class AdvancedZigZag
  {
private:
   MqlRates          rt[];
   dextremum         zz[];
   int               history;
   double            amplitude;
public:
   dextremum         zHL[];
   MqlZigZag         zzH[],zzL[];
   int               Count(const double range);
   int               Read(const int nodes);
                     AdvancedZigZag(const int bars);
                    ~AdvancedZigZag();
  };
//+------------------------------------------------------------------+
//| Конструктор класса                                               |
//+------------------------------------------------------------------+
AdvancedZigZag::AdvancedZigZag(const int bars)
  {
   history=bars;
   amplitude=0;
  }
//+------------------------------------------------------------------+
//| Метод класса Read                                                |
//+------------------------------------------------------------------+
int AdvancedZigZag::Read(const int nodes)
  {
   CopyRates(NULL,0,TimeCurrent(),history,rt);
   int cnt=GetExtremums(amplitude,rt,zHL,nodes);
   return(cnt);
  }
//+------------------------------------------------------------------+
//| Метод класса Count                                               |
//+------------------------------------------------------------------+
int AdvancedZigZag::Count(const double range)
  {
   amplitude=range;
   CopyRates(NULL,0,TimeCurrent(),history,rt);
   int cnt=GetExtremums(amplitude,rt,zz);
   ArrayResize(zzH,cnt);
   ArrayResize(zzL,cnt);
   int h=0;
   int l=0;
   for(int i=0; i<cnt; i++)
     {
      if(zz[i].type>0)
        {
         zzH[h]=(MqlZigZag)zz[i];
         h++;
        }
      else
        {
         zzL[l]=(MqlZigZag)zz[i];
         l++;
        }
     }
   ArrayResize(zzH,h);
   ArrayResize(zzL,l);
   return(cnt);
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
AdvancedZigZag::~AdvancedZigZag()
  {
  }

Здесь всего два метода:

  • Count - находит все узлы Зигзага на заданном временном интервале (количестве баров) и сохраняет их в разных массивах, отдельно вершины и отдельно впадины. Так будет удобней проводить анализ и расчёт конвертов;
  • Read - находит последние узлы и сохраняет их в один массив. Этот метод нам нужен для визуализации индикатора ЗигЗаг;

Также используется библиотека GetExtremums (автор Yury Kulikov) при помощи которой производится поиск узлов.

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

//+------------------------------------------------------------------+
//|                                                   two_Comets.mq5 |
//|                                           Copyright 2013, DC2008 |
//|                           https://www.mql5.com/ru/users/DC2008 |
//+------------------------------------------------------------------+
#property copyright "Copyright 2013, DC2008"
#property link      "https://www.mql5.com/ru/users/DC2008"
#property version   "1.00"
#include <AdvancedZigZag.mqh>
//--- Глубина истории для расчёта индикатора
input int      depth_stories=5000;  // Depth stories for calculating the indicator [bars]
//--- Минимальное значение амплитуды зигзага
input int      amplitude=100;        // The minimum value of the amplitude of the indicator [points]
//--- Объявление класса
AdvancedZigZag Azz(depth_stories);
//---
#define NUMBER_MA   227
#define START_MA    5
//--- макросы
#define SIZE(i)                     (double)i*0.3<1?1:(int)(i*0.25)
#define ObjF1                       ObjectSetString(0,name,OBJPROP_FONT,"Wingdings")
#define ObjF2                       ObjectSetInteger(0,name,OBJPROP_ANCHOR,ANCHOR_CENTER)
#define ObjF3(T)                    ObjectSetInteger(0,name,OBJPROP_TIME,T)
#define ObjF4(P)                    ObjectSetDouble(0,name,OBJPROP_PRICE,P)
#define ObjF5(size)                 ObjectSetInteger(0,name,OBJPROP_FONTSIZE,size)
#define ObjF6(code)                 ObjectSetString(0,name,OBJPROP_TEXT,CharToString(code))
#define ObjF7(clr)                  ObjectSetInteger(0,name,OBJPROP_COLOR,clr)
#define ObjF8                       ObjectSetInteger(0,name,OBJPROP_COLOR,clrMagenta)
#define ObjF9                       ObjectSetInteger(0,name,OBJPROP_WIDTH,3)
#define ObjF10                      ObjectSetInteger(0,name,OBJPROP_BACK,true) 
#define ObjFont                     ObjF1;ObjF2;
#define ObjCoordinates(T,P)         ObjF3(T);ObjF4(P);
#define ObjProperty(size,code,clr)  ObjF5(size);ObjF6(code);ObjF7(clr);
#define ObjZZ                       ObjF8;ObjF9;ObjF10;
//---
double      MA[1],sumHi[NUMBER_MA],sumLo[NUMBER_MA];
int         handle_MA_H[NUMBER_MA],handle_MA_L[NUMBER_MA];
datetime    t[1];
int         H,L;
int         t_min,t_max;
int         err=-1;
double      sumH[2],maxH[2],minH[2];
double      sumL[2],maxL[2],minL[2];
string      name;
int         count;
int         shift;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   shift=PeriodSeconds()/30;
//--- расчёт узлов зигзага на исторических данных
   Azz.Count(amplitude*Point());
   H=ArraySize(Azz.zzH);
   L=ArraySize(Azz.zzL);
   if(H<30 || L<30)
     {
      Print("Недостаточно данных для расчёта узлов ЗигЗага: "+
            "либо увеличьте глубину истории; "+
            "либо уменьшите значение амплитуды.");
      return(-1);
     }
//---
   for(int i=0; i<NUMBER_MA; i++)
     {
      handle_MA_H[i]=iMA(NULL,0,i+START_MA,0,MODE_SMA,PRICE_HIGH);
      handle_MA_L[i]=iMA(NULL,0,i+START_MA,0,MODE_SMA,PRICE_LOW);
     }
//---
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(0,-1,-1);
   for(int i=0; i<NUMBER_MA; i++)
     {
      IndicatorRelease(handle_MA_H[i]);
      IndicatorRelease(handle_MA_L[i]);
     }
//---
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+

void OnTick()
  {
//--- получим значение времени открытия текущего бара
   CopyTime(NULL,0,0,1,t);
//--- зигзаг: 7 последних узлов
   count=Azz.Read(7);
   for(int i=1; i<count; i++)
     {
      name="ZZ"+(string)i;
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjectSetInteger(0,name,OBJPROP_COLOR,clrRed);
      ObjectSetInteger(0,name,OBJPROP_WIDTH,10);
      ObjectSetInteger(0,name,OBJPROP_BACK,true);
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[i-1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[i-1].time);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,Azz.zHL[i].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,Azz.zHL[i].time);
     }
//--- проверка целостности предварительных расчётов
   if(err<0)
     {
      //--- расчитаем суммы отклонений узлов от МА для вершин зигзага
      ArrayInitialize(sumHi,0.0);
      for(int j=H-1; j>=0; j--)
        {
         for(int i=0; i<NUMBER_MA; i++)
           {
            err=CopyBuffer(handle_MA_H[i],0,Azz.zzH[j].t,1,MA);
            if(err<0) return;
            sumHi[i]+=Azz.zzH[j].price-MA[0];
           }
        }
      //--- рассчитаем суммы отклонений узлов от МА для впадин зигзага
      ArrayInitialize(sumLo,0.0);
      for(int j=L-1; j>=0; j--)
        {
         for(int i=0; i<NUMBER_MA; i++)
           {
            err=CopyBuffer(handle_MA_L[i],0,Azz.zzL[j].t,1,MA);
            if(err<0) return;
            sumLo[i]+=MA[0]-Azz.zzL[j].price;
           }
        }
     }
  }
//+------------------------------------------------------------------+

Здесь есть несколько моментов, которые требуют пояснения:

  • Вместо индикатора iEnvelopes используется индикатор iMA. Это не подлог и не обман. Дело в том, что основная линия iEnvelopes совпадает с iMA! Поэтому удобней использовать именно индикатор скользящих средних - Moving Average.
  • Используются два веера скользящих средних по 227 линий в каждом, итого 454 индикатора iMA! Много это или мало? В общем-то да, много. Но, во-первых, можно изменить количество индикаторов при необходимости, а во-вторых - нам нужна статистика. Что толку от того, что будем искать конверты для десятка узлов? Нам как минимум нужна сотня.
  • Значения индикаторов загружаются в блоке OnTick(), а не в OnInit(). Если расположить блок загрузки данных в OnInit(), то очень высока вероятность того, что какие-либо данные не успеют подгрузиться и индикаторы не будут рассчитаны точно и в полном объёме. После того, как будут получены все данные для расчетов, значение переменной err станет положительным, и этот блок будет исключен из работы.

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

Индикатор ЗигЗаг (7 узлов)

Рис. 6. Индикатор ЗигЗаг (7 узлов).

Далее, построим сечения поверхностей индикаторов Envelopes. Для этого добавим в метод OnTick() следующие строки кода:

//--- ВЕРШИНЫ
   sumH[0]=0.0;
   maxH[0]=0.0;
   minH[0]=0.0;
   for(int i=0; i<NUMBER_MA; i++)
     {
      CopyBuffer(handle_MA_H[i],0,t[0],1,MA);
      double envelope=MA[0]+sumHi[i]/H;
      if(i==0 || envelope<minH[0])
        {
         minH[0]=envelope;
         t_min=SIZE(i);
        }
      if(envelope>maxH[0])
        {
         maxH[0]=envelope;
         t_max=SIZE(i);
        }
      sumH[0]+=envelope;
      name="H"+(string)i;
      ObjectCreate(0,name,OBJ_TEXT,0,0,0);
      ObjFont
      ObjCoordinates(t[0]-(NUMBER_MA-i*2)*shift,envelope)
      ObjProperty(SIZE(i),158,clrBlue)
     }
//--- ВПАДИНЫ
   sumL[0]=0.0;
   maxL[0]=0.0;
   minL[0]=0.0;
   for(int i=0; i<NUMBER_MA; i++)
     {
      CopyBuffer(handle_MA_L[i],0,t[0],1,MA);
      double envelope=MA[0]-sumLo[i]/L;
      if(i==0 || envelope<minL[0])
        {
         minL[0]=envelope;
         t_min=SIZE(i);
        }
      if(envelope>maxL[0])
        {
         maxL[0]=envelope;
         t_max=SIZE(i);
        }
      sumL[0]+=envelope;
      name="L"+(string)i;
      ObjectCreate(0,name,OBJ_TEXT,0,0,0);
      ObjFont
      ObjCoordinates(t[0]+(NUMBER_MA-i*2)*shift,envelope)
      ObjProperty(SIZE(i),158,clrGold)
     }
Примечание для начинающих программистов: операторы в конце блоков "Вершины" и "Впадины" не имеют в конце строки символа ';'. Это не ошибка и не опечатка. Это макросы (см. раздел данных, там где они объявлены) - очень удобная вещь! Рекомендую использовать в своих программах.

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

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

Сечение веера индикатора Envelopes

Рис. 7. Сечение веера индикатора Envelopes.

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

//--- ВЕРШИНЫ

...

//--- midi
   string str=(string)t[0];
   name="Hmidi"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],sumH[0]/NUMBER_MA)
   ObjProperty(10,119,clrBlue)
//--- max
   name="Hmax"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],maxH[0])
   ObjProperty(t_max,158,clrBlue)
//--- min
   name="Hmin"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],minH[0])
   ObjProperty(t_min,158,clrBlue)

...

//--- ВПАДИНЫ

...

//--- midi
   name="Lmidi"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],sumL[0]/NUMBER_MA)
   ObjProperty(10,119,clrGold)
//--- max
   name="Lmax"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],maxL[0])
   ObjProperty(t_max,158,clrGold)
//--- min
   name="Lmin"+str;
   ObjectCreate(0,name,OBJ_TEXT,0,0,0);
   ObjFont
   ObjCoordinates(t[0],minL[0])
   ObjProperty(t_min,158,clrGold)

И посмотрим как это выглядит графически:

Характеристики сечений

Рис. 8. Характеристики сечений: максимум, минимум и центр тяжести для вершин и впадин отдельно.

Осталось сделать последний штрих - найти и нарисовать опережающие узлы ЗигЗага. Добавим в код такие строки:

//--- зигзаг: опережающие узлы
   if(Azz.zHL[0].type>0) // вершина
     {
      ObjectDelete(0,"MIN");
      ObjectDelete(0,"MINfuture");
      name="MAX";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[1].time);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]);
      double price=minH[0];
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
      if(Azz.zHL[0].value>minH[0])
        {
         price=sumH[0]/NUMBER_MA;
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      if(Azz.zHL[0].value>sumH[0]/NUMBER_MA)
        {
         price=maxH[0];
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      //--- в будущее
      name="MAXfuture";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,price);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,t[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,maxL[0]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]+NUMBER_MA*shift);
      if(price<maxL[0])
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,sumL[0]/NUMBER_MA);
      if(price<sumL[0]/NUMBER_MA)
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,minL[0]);
     }
   if(Azz.zHL[0].type<0) // впадина
     {
      ObjectDelete(0,"MAX");
      ObjectDelete(0,"MAXfuture");
      name="MIN";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,Azz.zHL[1].value);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,Azz.zHL[1].time);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]);
      double price=maxL[0];
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
      if(Azz.zHL[0].value<maxL[0])
        {
         price=sumL[0]/NUMBER_MA;
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      if(Azz.zHL[0].value<sumL[0]/NUMBER_MA)
        {
         price=minL[0];
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,price);
        }
      //--- в будущее
      name="MINfuture";
      ObjectCreate(0,name,OBJ_TREND,0,0,0);
      ObjZZ
      ObjectSetDouble(0,name,OBJPROP_PRICE,0,price);
      ObjectSetInteger(0,name,OBJPROP_TIME,0,t[0]);
      ObjectSetDouble(0,name,OBJPROP_PRICE,1,minH[0]);
      ObjectSetInteger(0,name,OBJPROP_TIME,1,t[0]+NUMBER_MA*shift);
      if(price>minH[0])
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,sumH[0]/NUMBER_MA);
      if(price>sumH[0]/NUMBER_MA)
         ObjectSetDouble(0,name,OBJPROP_PRICE,1,maxH[0]);
     }

Итак, получили новый индикатор - опережающий ЗигЗаг, который прогнозирует расположение новых узлов (рис. 9). Сами узлы находятся в характерных точках сечений: максимуме, минимуме и центре тяжести. Рабочее название индикатора - "Две кометы".

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

Прогнозируемые узлы индикатора ЗигЗаг

Рис. 9. Опережающий индикатор ЗигЗаг прогнозирует узлы: текущий и следующий.

 

Анализ результатов и рекомендации для разработчиков

Наблюдения за индикатором показали:

  1. Отклонения координат узлов ЗигЗага от прогнозируемых находятся в зоне допустимых значений. Подавляющее количество узлов расположено в тени соответствующего сечения. Это конечно качественная оценка, т.к. более точные результаты будут рассмотрены в следующих работах.
  2. Сечения линий конвертов очень наглядно показывают настроения рынка и предполагаемые движения! Обратите внимание на хвост кометы, там где находятся точки с наименьшими периодами усреднения (самые маленькие по размеру). Куда он направлен, туда и будет двигаться цена. Хвост кометы делает самые замысловатые изгибы и чем больше он развёрнут в противоположную сторону, тем с большей вероятностью произойдёт смена тренда. А вообще, понаблюдайте за поведением индикатора на разных таймфреймах и с разными амплитудами. Очень интересно!
  3. Характерные точки сечений формируют линии, которые могут оказать сильное сопротивление движению цены. Поэтому их можно рассматривать как линии поддержки и сопротивления.
  4. Когда точки центра тяжести сечения обгоняют его (например, как для вершин на рис. 9), то это говорит о том, что присутствует тенденция к росту.

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

 

Заключение

  • В статье рассмотрена технология прогнозирования узлов индикатора ЗигЗаг, которая позволила создать новый индикатор "Две кометы".
  • Опережающий ЗигЗаг показывает возможные координаты новых узлов, но это только прогноз.
  • Рассмотренный алгоритм можно использовать для построения аналогичных опережающих индикаторов и не только для ЗигЗага. Например, для фракталов или семафорных индикаторов.
  • Для начинающих программистов на языке MQL5 будет интересно посмотреть как создавать макросы в своих программах для сокращения повторов в коде.
Прикрепленные файлы |
advancedzigzag.mqh (3.54 KB)
getextremums.mqh (5.24 KB)
two_comets.mq5 (10.14 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (29)
manya
manya | 15 апр 2013 в 16:30

У меня ошибка "Недостаточно данных для расчёта узлов. Либо увеличьте глубину истории, либо уменьшите значение амплитуды"

Значения эксперта по умолчанию Depth 5000, Min amplitude 100

Поменяла на Depth 50000, Min amplitude 10, заработало


Какие значения вы считаете оптимальными?

Sergey Pavlov
Sergey Pavlov | 15 апр 2013 в 17:05
manya:

У меня ошибка "Недостаточно данных для расчёта узлов. Либо увеличьте глубину истории, либо уменьшите значение амплитуды"

Значения эксперта по умолчанию Depth 5000, Min amplitude 100

Поменяла на Depth 50000, Min amplitude 10, заработало

Какие значения вы считаете оптимальными?

Дело в том, что амплитуда указывается в пунктах! У Вас котировки 4-х значные, а в статье приводятся для 5-ти значных. Поэтому, Вы сделали всё правильно - уменьшили амплитуду в 10 раз.

Оптимальные зависят от вашей стратегии и таймфрейма.

manya
manya | 15 апр 2013 в 17:48
DC2008:

Дело в том, что амплитуда указывается в пунктах! У Вас котировки 4-х значные, а в статье приводятся для 5-ти значных. Поэтому, Вы сделали всё правильно - уменьшили амплитуду в 10 раз.

Оптимальные зависят от вашей стратегии и таймфрейма.

скальпинг на 1м 5м
Sergey Pavlov
Sergey Pavlov | 15 апр 2013 в 19:03
manya:
скальпинг на 1м 5м

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

Youri Tarshecki
Youri Tarshecki | 27 апр 2013 в 07:02
Попробовал визуализировать в тестере - выдает ту же ошибку про глубин и амплитуду. Попробовал поменять переменные в эксперте, но тестер все равно берет все те же значения по умолчанию с той же ошибкой. Открыть код в редакторе, чтобы поменять переменные не удалось почему-то.-((
Переход на новые рельсы: пользовательские индикаторы в MQL5 Переход на новые рельсы: пользовательские индикаторы в MQL5

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

Вот мы и получили долгожданные MetaTrader 5 и MQL5 Вот мы и получили долгожданные MetaTrader 5 и MQL5

Это очень краткий обзор MetaTrader 5. Я не могу описать все новшества системы за столь короткий период времени - тестирование стартовало 09-09-2009. Это символическая дата, и я уверен, что это будет счастливым числом. Всего несколько дней у меня на руках бета-версия терминала MetaTrader 5 и MQL5. Я не успел опробовать все, что в нем есть нового, но то, что есть, уже впечатляет.

Портфельная торговля в MetaTrader 4 Портфельная торговля в MetaTrader 4

В статье обсуждаются принципы портфельной торговли и особенности применения к валютному рынку. Рассматриваются несколько простых математических моделей для формирования портфеля. Приводятся примеры практической реализации портфельной торговли в MetaTrader 4: портфельный индикатор и советник для полуавтоматической торговли. Описываются элементы торговых стратегий, их достоинства и "подводные камни".

Работа с корзинами валютных пар на рынке Форекс Работа с корзинами валютных пар на рынке Форекс

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