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

Sergey Pavlov | 10 апреля, 2013

Введение

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

Существует множество версий этого индикатора: 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). Введём следующие обозначения и формулы:

Всего у нас есть два веера индикаторов: один для вершин и один для впадин (примерно по сто штук в каждом). Для каждого индикатора из веера мы будем рассчитывать отклонения узлов ЗигЗага от основной линии 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()
  {
  }

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

Также используется библиотека 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;
           }
        }
     }
  }
//+------------------------------------------------------------------+

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

Итак, полученный индикатор рисует последние семь узлов индикатора ЗигЗаг и делает расчёт координат всех остальных узлов на заданной истории (рис. 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), то это говорит о том, что присутствует тенденция к росту.

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

 

Заключение