English 中文 Español Deutsch 日本語 Português 한국어 Français Italiano Türkçe
Реализация автоматического анализа волн Эллиотта на MQL5

Реализация автоматического анализа волн Эллиотта на MQL5

MetaTrader 5Примеры | 8 марта 2011, 16:20
19 320 43
MRoVas
[Удален]


Введение

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

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


1. Волновой принцип Эллиотта

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

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

1. Импульсы:
Импульс
Рисунок 1. Импульс
  • Окончание второй волны никогда не заходит за начало первой волны;
  • Третья волна всегда простирается дальше вершины первой волны;
  • Окончание четвертой волны никогда не заходит за вершину первой волны;
  • Третья волна никогда не бывает самой короткой из всех действующих волн;
  • Третья волна всегда является импульсом;
  • Первая волна может быть или импульсом, или клином;
  • Пятая волна может быть или импульсом, или диагональю;
  • Вторая волна может принять форму любой коррекционной волны за исключением треугольника;
  • Четвертая волна может принять форму любой коррекционной волны;
2. Клинья:
Клин
Рисунок 2. Клин
  • Окончание второй волны никогда не заходит за начало первой волны;
  • Третья волна всегда простирается дальше вершины первой волны;
  • Окончание четвертой волны всегда заходит за вершину первой волны, но никогда не заходит за начало третьей волны;
  • Третья волна никогда не бывает самой короткой из всех действующих волн;
  • Третья волна всегда является импульсом;
  • Первая волна может быть или импульсом, или клином;
  • Пятая волна может быть или импульсом или диагональю;
  • Вторая волна может принять форму любой коррекционной волны за исключением треугольника;
  • Четвертая волна может принять форму любой коррекционной волны;
3. Диагонали:
Диагональ
Рисунок 3. Диагональ
  • Окончание второй волны никогда не заходит за начало первой волны;
  • Третья волна всегда простирается дальше вершины первой волны;
  • Окончание четвертой волны, как правило, заходит за вершину первой волны, но никогда не заходит за начало третьей волны;
  • Третья волна никогда не бывает самой короткой из всех действующих волн;
  • Первая, вторая и третья волны могут принять форму любой коррекционной волны за исключением треугольника;
  • Четвертая и пятая волна могут принять форму любой коррекционный волны;
Коррекционные волны различаются на:
4. Зигзаги:
Зигзаг
Рисунок 4. Зигзаг
  • Волна A может принять форму импульса или клина;
  • Волна C может принять форму импульса или диагонали;
  • Волна B может принять форму любой коррекционной волны;
  • Волна C простирается дальше вершины волны A;
  • Окончание волны B не заходит за начало волны A;
5. Плоскости:
Плоскость
Рисунок 5. Плоскость
  • Волна A может принять форму любой коррекционной волны за исключением треугольника;
  • Волна B может принять форму любой коррекционной волны;
  • Волна C может принять форму импульса или диагонали;
6. Двойные зигзаги:
Двойной зигзаг
Рисунок 6. Двойной зигзаг
  • Волна W и волна Y принимают форму зигзага;
  • Волна X может принять форму любой коррекционной волны;
  • Волна Y простирается дальше вершины волны W;
  • Окончание волны X не заходит за начало волны W;
7. Тройные зигзаги:
Тройной зигзаг
Рисунок 7. Тройной зигзаг
  • Волна W, волна Y и волна Z принимают форму зигзага;
  • Волна X может принять формул любой коррекционной волны за исключением треугольника;
  • Волна XX может принять форму любой коррекционной волны;
  • Волна Y простирается дальше вершины волны W;
  • Волна Z простирается дальше вершины волны Y;
  • Окончание волны X не заходит за начало волны W;
  • Окончание волны XX не заходит за начало волны Y;
8. Двойные тройки:
Двойная тройка
Рисунок 8. Двойная тройка
  • Волна W принимает форму любой коррекционной волны за исключением треугольника;
  • Волна X и волна Y принимают форму любой коррекционной волны;
9. Тройные тройки:
Тройная тройка
Рисунок 9. Тройная тройка
  • Волна W, волна X и волна Y могут принять форму любой коррекционной волны за исключением треугольника;
  • Волна XX и волна Z могут принять форму любой коррекционной волны;
10. Сходящиеся треугольники:
Сходящийся треугольник
Рисунок 10. Сходящийся треугольник
  • Волна C никогда не выходит за ценовые пределы волны B;
  • Волна D никогда не выходит за ценовые пределы волны C;
  • Волна E никогда не выходит за ценовые пределы волны D;
  • Волна A, волна B и волна C могут принять форму любой коррекционной волны за исключением треугольника;
  • Волна D и волна E могут принять форму любой коррекционной волны;
11. Расходящиеся треугольники:
Расходящийся треугольник
Рисунок 11. Расходящийся треугольник
  • Волна C всегда превышает волну B по длине
  • Волна D всегда превышает волну С по длине
  • Волна A, волна B и волна C могут принять форму любой коррекционной волны за исключением треугольника
  • Волна D и волна E могут принять форму любой коррекционной волны

Представленные выше модели волн и правила соответствуют лишь классическому представлению о волновом анализе.

Существует также его современное представление, сформированное при изучении рынка Forex. Найдена, например новая модель наклонного (сдвигающегося) треугольника, выявлены импульсы с треугольником во второй волне и др.

Как видно из рисунков 1-11, каждая импульсная или коррекционная волна состоит из таких же импульсных и коррекционных волн (выделены штриховой линией), но уже меньшей степени. Это так называемая фрактальность (вложенность) волн Эллиотта: волны больших степеней состоят из волн меньших степеней, которые в свою очередь состоят из волн еще меньших степеней и так далее.

На этом можно закончить краткое введение в волновой принцип Эллиотта и перейти к теме автоматической разметки волн.


2. Алгоритм автоматической разметки волн Эллиотта

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

Одним из таких инструментов стал механизм автоматической разметки волн Эллиотта.

Можно выделить два принципа  авторазметки:

  1. Согласно фрактальности волн, анализ ведется "сверху вниз", от больших волн к меньшим;
  2. Анализ ведется методом прямого перебора возможных вариантов.

Блок-схема алгоритма автоматического анализа волн Эллиотта представлена на рисунке 12.

Рисунок 12. Блок-схема алгоритма автоматического анализа волн Эллиотта
Рисунок 12. Блок-схема алгоритма автоматического анализа волн Эллиотта

Рассмотрим алгоритм более подробно на примере автоматической разметки Импульса (см. рисунок 13).

На первом этапе, на требуемом интервале времени графика цены с помощью "Зигзага" выделяется необходимое для разметки количество точек. Количество точек зависит от того, какую волну мы хотим анализировать. Так, для анализа Импульса требуется шесть точек - 5 вершин и одна точка начала. Если бы анализировался Зигзаг, то количество требуемых точек было бы уже 4 - 3 вершины и одна точка начала.

Если "Зигзаг" определил шесть точек на графике цены, тогда сразу можно произвести разметку Импульса: первая точка - точка начала волны 1, вторая точка - вершина волны 1, третья точка - вершина волны 2, четвертая точка - вершина волны 3, пятая точка - вершина волны 4, и шестая точка - вершина волны 5.

Однако на рисунке 13 "Зигзаг" определил 8 точек. В этом случае придется перебрать по этим точкам все возможные варианты разметки волны. А их, если нетрудно посчитать, будет пять (выделены разным цветом). И каждый вариант разметки придется проверять на правила.

Варианты разметки
Рисунок 13. Варианты разметки импульса

После проверки на правила, в случае, если размеченная волна по всем параметрам является Импульсом, таким же образом происходит анализ ее субволн.

То же самое касается анализа всех остальных импульсных и коррекционных волн.

 

3. Виды волн для автоматической разметки

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

Все волны можно разбить на следующие группы:

  1. Неначатые волны:
    1. Волны с неначатой первой волной - 1<-2-3-4-5 (например, Импульс с неначатой волной 1; количество требуемых точек - 5) и 1<-2-3 (например, Зигзаг с неначатой волной A; количество требуемых точек - 3);
    2. Волны с неначатой второй волной - 2<-3-4-5 (например, Диагональ с неначатой волной 2; количество требуемых точек - 4) и 2<-3 (например, Плоскость с неначатой волной B; количество требуемых точек -2);
    3. Волны с неначатой третье волной - 3<-4-5 (например, Тройной зигзаг с неначатой волной Y; количество требуемых точек - 3);
    4. Волны с неначатой четвертой волной - 4<-5 (например, Треугольник с неначатой волной D; количество требуемых точек -2);
    5. Волны с неначатой пятой волной - 5< (например, Импульс с неначатой волной 5; количество требуемых точек - 1);
    6. Волны с неначатой третьей волной - 3< (например Двойная тройка с неначатой волной Z; количество требуемых точек - 1);
  2. Незавершенные волны:
    1. Волны с незавершенной пятой волной - 1-2-3-4-5> (например, Импульс с незавершенной волной 5; количество требуемых точек - 5);
    2. Волны с незавершенной четвертой волной - 1-2-3-4> (например, Тройной зигзаг с незавершенной волной XX; количество требуемых точек - 4);
    3. Волны с незавершенной третьей волной - 1-2-3> (например, Клин с незавершенной волной 3; количество требуемых точек -3);
    4. Волны с незавершенной второй волной - 1-2> (например, Зигзаг с незавершенной волной B; количество требуемых точек -2);
    5. Волны с незавершенной первой волной - 1> (например, Плоскость с незавершенной волной A; количество требуемых точек -1);
  3. Неначатые и незавершенные волны:
    1. Волны с неначатой первой волной и незавершенной второй волной -1<-2> (например, Зигзаг с неначатой волной A и незавершенной волной B; количество требуемых точек - 1);
    2. Волны с неначатой второй волной и незавершенной третьей волной - 2<-3> (например, Зигзаг с неначатой волной B и незавершенной волной C; количество требуемых точек - 1);
    3. Волны с неначатой третьей волной и незавершенной четвертой волной - 3<-4> (например, Импульс с неначатой волной 3 и незавершенной волной 4; количество требуемых точек - 1);
    4. Волны с неначатой четвертой волной и незавершенной пятой волной - 4<-5> (например, Импульс с неначатой волной 4 и незавершенной волной 5; количество требуемых точек - 1);
    5. Волны с неначатой первой и незавершенной третьей волной - 1<-2-3> (например, Тройная тройка с неначатой волной W и незавершенной  волной Y; количество требуемых точек - 2);
    6. Волны с неначатой второй волной и незавершенной четвертой волной - 2<-3-4> (например, Клин с неначатой волной 2 и незавершенной волной 4; количество требуемых точек - 2);
    7. Волны с неначатой третьей волной и незавершенной пятой волной - 3<-4-5> (например, Диагональ с неначатой волной 3 и незавершенной волной 5; количество требуемых точек - 2);
    8. Волны с неначатой первой и незавершенной четвертой волной - 1<-2-3-4> (например, Тройная  тройка с неначатой волной W и незавершенной  волной XX; количество требуемых точек - 3);
    9. Волны с неначатой второй волной и незавершенной пятой волной - 2<-3-4-5> (например, Импульс с неначатой волной 2 и незавершенной волной 5; количество требуемых точек - 3);
    10. Волны с неначатой первой волной и незавершенной пятой волной -1<-2-3-4-5> (например, Тройной зигзаг с неначатой волной W и незавершенной  волной Z; количество требуемых точек - 4);
  4. Завершенные волны - 1-2-3-4-5 (количество требуемых точек - 6) и 1-2-3 (количество требуемых точек - 4).

Знак "<" после номера волны говорит о том, что она не началась. Знак ">" после номера волны говорит, что она незавершена.

На рисунке 14 можно выделить следующие волны:

  1. Волну с неначатой первой волной AA<-B-C;
  2. Волну с неначатой первой W и незавершенной второй X волной - W<-X>;
  3. Завершенные волны B и C;

Неначатые и незавершенные волны 
Рисунок 14. Неначатые и незавершенные волны


4. Описание структур данных автоматического анализатора волн Эллиотта

Для написания автоматического анализатора волн Эллиотта нам понадобятся следующие структуры данных:

4.1. Структура описания анализируемых в программе волн:

// Структура описания анализируемых в программе волн
struct TWaveDescription
  {
   string            NameWave;    // название волны
   int               NumWave;     // количество субволн в волне
   string            Subwaves[6]; // названия возможных субволн в волне
  };

4.2. Класс для хранения параметров конкретной волны: 

// Класс для хранения параметров волны
class TWave
  {
public:
   string            Name;            // имя волны
   string            Formula;         // формула волны (1-2-3-4-5, <1-2-3 и т.п.)
   int               Level;           // уровень (степень) волны
   double            ValueVertex[6]; // значения вершин волны
   int               IndexVertex[6]; // индексы вершин волны
  };

4.3. Класс для хранения значений вершин и индексов вершин зигзага:

// Класс для хранения значений вершин и индексов зигзага
class TZigzag:public CObject
  {
public:
   CArrayInt        *IndexVertex;    // индексы вершин зигзага
   CArrayDouble     *ValueVertex;    // значения вершин зигзага
  };

4.4. Класс для представления дерева волн:

// Класс для представления дерева волн
class TNode:public CObject
  {
public:
   CArrayObj        *Child;    // потомки данного узла дерева
   TWave            *Wave;      // волна, хранимая в узле дерева
   string            Text;       // текст узла дерева
   TNode            *Add(string Text,TWave *Wave=NULL) // функция добавления узла в дерево
     {
      TNode *Node=new TNode;
      Node.Child=new CArrayObj;
      Node.Text =Text;
      Node.Wave=Wave;
      Child.Add(Node);
      return(Node);
     }
  };

4.5. Структура для хранения найденных по зигзагу точек:

// Структура для хранения точек, найденных по зигзагу
struct TPoints
  {
   double            ValuePoints[];  // значения найденных точек
   int               IndexPoints[];  // индексы найденных точек
   int               NumPoints;       // количество найденных точек
  };

4.6. Класс для хранения параметров уже проанализированного участка графика:

// Класс для хранения параметров уже проанализированного участка, соответствующему узлу дереву волн
class TNodeInfo:CObject
  {
public:
   int               IndexStart,IndexFinish;  // диапазон уже проанализированного участка
   double            ValueStart,ValueFinish;  // крайние значения уже проанализированного участка
   string            Subwaves;                  // название волны или группы волн
   TNode            *Node;                      // узел, указывающий на проанализированный диапазон графика
  };

4.7. Класс для хранения маркировки волн перед нанесением на график:

// Класс для хранения маркировки волн перед нанесением на график
class TLabel:public CObject
  {
public:
   double            Value;  // значение вершины
   int               Level;  // уровень волны
   string            Text;    // маркировка вершины
  };

 

5. Описание функций автоматического анализатора волн Эллиотта

Для написания автоматического анализатора волн Эллиотта нам понадобятся следующие функции:

5.1. Zigzag

Функция поиска экстремумов "Зигзага":

int Zigzag(int H,int Start,int Finish,CArrayInt *IndexVertex,CArrayDouble *ValueVertex)

Ключевым звеном в автоматическом анализаторе волн Эллиотта является "Зигзаг", по которому и будут строиться волны. При этом расчет "Зигзага" по какому-либо параметру должен быть очень быстрым.

В нашем анализаторе будет использоваться "Зигзаг", взятый из статьи "Как писать быстрые и неперерисовывающиеся зигзаги".

Функция Zigzag рассчитывает "Зигзаг" с параметром H на интервале от Start до Finish и записывает найденные индексы вершин и значения вершин, соответственно, в массивы IndexVertex и ValueVertex, адреса которых передаются в данную функцию.

Функция Zigzag возвращает количество найденных вершин "Зигзага". 

5.2. FillZigZagArray

Функция перебора "Зигзага" и сохранения его параметров: 

void FillZigzagArray(int Start,int Finish)

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

Функция FillZigzagArray рассчитывает "Зигзаги" на интервале графика от Start до Finish со всеми возможными значениями параметра H (пока количество вершин "Зигзага" не станет равно или менее двух), сохраняет информацию о найденных вершинах в объектах класса TZigzag и записывает эти объекты в глобальный массив ZigzagArray, объявление которого выглядит следующим образом:

CArrayObj ZigzagArray;

5.3. FindPoints

Функция поиска на заданном интервале требуемого количества точек на графике цены: 

bool FindPoints(int NumPoints,int IndexStart,int IndexFinish,double ValueStart,double ValueFinish,TPoints &Points)

Функция FindPoints ищет не менее NumPoints точек на графике цены на требуемом диапазоне поиска от IndexStart до IndexFinish с требуемыми значениями первой и последней точек ValueStart и ValueFinish, и сохраняет их (т.е. точки) в структуре Points, ссылка на которую передается в данную функцию.

Функция FindPoints возвращает true, если требуемое количество точек удалось найти, иначе возвращается false.

5.4. NotStartedAndNotFinishedWaves

Функция анализа неначатых и незавершенных волн:

void NotStartedAndNotFinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)

Функция NotStartedAndNotFinishedWaves анализирует все волны третьей группы волн - волны неначатые и незавершенные. Функция анализирует NumWave волну (с волновым уровнем Level) волны с названием ParentWave.Name, которая может принимать форму Subwaves волн (форму Зигзага, Плоскости, Двойного зигзага и (или) др.). Анализируемая волна NumWave будет храниться в узле дерева волн, дочернем узлу Node.

Так, например, если ParentWave.Name="Импульс", NumWave=5, Subwaves="Импульс, Диагональ," и Level=2, то можно сказать, что функция NotStartedAndNotFinishedWaves будет анализировать пятую волну Импульса, которая имеет волновой уровень 2 и может принять форму Импульса или Диагонали.

В качестве примера приведем блок-схему алгоритма анализа неначатой и незавершенной волны 1<-2-3> в функции NotStartedAndNotFinishedWaves:

  <img alt="Рисунок 15. Блок-схема анализа волны с формулой "1"" title="Рисунок 15. Блок-схема анализа волны с формулой "1"" src="https://c.mql5.com/2/2/fig15__1.gif" style="vertical-align:middle;" height="1746" width="750">
Рисунок 15. Блок-схема анализа волны с формулой "1<-2-3>"

При работе функции NotStartedAndNotFinishedWaves вызываются функции NotStartedWaves, NotFinishedWaves и FinishedWaves.

5.5. NotStartedWaves

Функция анализа неначатых волн: 

void NotStartedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)

Функция NotStartedWaves анализирует все волны первой группы волн - волны неначатые. Функция анализирует NumWave волну (с волновым уровнем Level) волны с названием ParentWave.Name, которая может принимать форму Subwaves волн. Анализируемая волна NumWave будет храниться в узле дерева волн, дочернем узлу Node.

При работе функции NotStartedWaves вызываются функции NotStartedWaves и FinishedWaves.

Все волны анализируются аналогично блок-схеме на рисунке 15. 

5.6. NotFinishedWaves

Функция анализа незавершенных волн: 

void NotFinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)

Функция NotFinishedWaves анализирует все волны второй группы волн - волны незавершенные. Функция анализирует NumWave волну (с волновым уровнем Level) волны с названием ParentWave.Name, которая может принимать форму Subwaves волн. Анализируемая волна NumWave будет храниться в узле дерева волн, дочернем узлу Node.

При работе функции NotFinishedWaves вызываются функции NotFinishedWaves и FinishedWaves.   

Все волны анализируются аналогично блок-схеме на рисунке 15.

5.7. FinishedWaves

Функция анализа завершенных (законченных) волн: 

void FinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)

Функция FinishedWaves анализирует все волны четвертой группы волн - волны завершенные. Функция анализирует NumWave волну (с волновым уровнем Level) волны с названием ParentWave.Name, которая может принимать форму Subwaves волн. Анализируемая волна NumWave будет храниться в узле дерева волн, дочернем узлу Node.

При работе функции FinishedWaves вызывается функция FinishedWaves.   

Все волны анализируются аналогично блок-схеме на рисунке 15.

5.8. FindWaveInWaveDescription

Функция поиска волны в структуре данных WaveDescription:

int FindWaveInWaveDescription(string NameWave)

Функция FindWaveInWaveDescription по названию волны NameWave, передаваемую в качестве параметра, ищет ее в массиве структур WaveDescription и возвращает номер индекса, соответствующего данной волне.

Массив структур WaveDescription имеет следующий вид:

TWaveDescription WaveDescription[]=
  {
     {
      "Импульс",5,
        {
         "",
         "Импульс,Клин,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Импульс,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Импульс,Диагональ,"
        }
     }
      ,
     {
      "Клин",5,
        {
         "",
         "Импульс,Клин,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Импульс,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Импульс,Диагональ,"
        }
     }
      ,
     {
      "Диагональ",5,
        {
         "",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,"
        }
     }
      ,
     {
      "Зигзаг",3,
        {
         "",
         "Импульс,Клин,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Импульс,Диагональ,",
         "",
         ""
        }
     }
      ,
     {
      "Плоскость",3,
        {
         "",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Импульс,Диагональ,",
         "",
         ""
        }
     }
      ,
     {
      "Двойной зигзаг",3,
        {
         "",
         "Зигзаг,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Зигзаг,",
         "",
         ""
        }
     }
      ,
     {
      "Тройной зигзаг",5,
        {
         "",
         "Зигзаг,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Зигзаг,"
        }
     }
      ,
     {
      "Двойная тройка",3,
        {
         "",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "",
         ""
        }
     }
      ,
     {
      "Тройная тройка",5,
        {
         "",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,"
        }
     }
      ,
     {
      "Сходящийся треугольник",5,
        {
         "",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,"
        }
     }
      ,
     {
      "Расходящийся треугольник",5,
        {
         "",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,",
         "Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,"
        }
     }
  };

Функция FindWaveInWaveDescription применяется в функциях анализа волн NotStartedAndNotFinishedWaves, NotStartedWaves, NotFinishedWaves и FinishedWaves. 

5.9. Already

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

bool Already(TWave *Wave,int NumWave,TNode *Node,string Subwaves)

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

Функция Already ищет в глобальном массиве NodeInfoArray, в котором хранятся объекты класса TNodeInfo, участок графика, соответствующий волне NumWave волны с именем Wave.Name, имеющую форму Subwaves волн, и записывает в Node адрес узла уже размеченного участка графика. Если данного участка нет, то создается и заполняется новый объект класса TNodeInfo и записывается в массив NodeInfoArray.

Функция возвращает true, если участок графика уже анализировался, иначе возвращается false. 

Массив NodeInfoArray объявляется следующим образом: 

CArrayObj NodeInfoArray;

5.10. Функции проверки волны на правила

Включают в себя функцию VertexAAboveB, WaveAMoreWaveB и собственно WaveRules, из которой и вызываются первые две функции. При проверке следует помнить, что волны бывают неначатые и (или) незавершенные, и, например, у волны с формулой "1<-2-3>" невозможно определить, зашла ли четвертая волна за территорию первой или нет, т.к. четвертой волны еще нет.

5.10.1. WaveRules

Функция проверки волны на правила:

bool WaveRules(TWave *Wave)

Функция WaveRules возвращает true, если волна с названием Wave.Name "правильная", иначе возвращается false. При своей работе функция WaveRules вызывает функции VertexAAboveVertexB и WaveAMoreWaveB.

5.10.2. VertexAAboveVertexB

Функция проверки превышения одной вершины другой вершины: 

int VertexAAboveVertexB(int A,int B,bool InternalPoints)

Функция VertexAAboveVertexB возвращает число >=0, если вершина волны A превысила вершину волны B, иначе возвращается -1. Если InternalPoints=true, то учитываются внутренние точки волн (максимальные и (или) минимальные значения волн).

5.10.3. WaveAMoreWaveB

Функция проверки превышения длины одной волны длины другой волны:

int WaveAMoreWaveB(int A,int B)

Функция WaveAMoreWaveB возвращает число >=0, если волна A больше волны B, иначе возвращается -1.

11. Функции очистки памяти

5.11.1. ClearTree

Функция очистки дерева волн с верхним узлом Node: 

void ClearTree(TNode *Node)

5.11.2. ClearNodeInfoArray

Функция очистки массива ClearNodeInfoArray: 

void ClearNodeInfoArray()

5.11.3. ClearZigzagArray

Функция очистки массива ZigzagArray:

void ClearZigzagArray()

5.12. Функции обхода дерева волн и выдачи результатов анализа на график

После завершения автоматического анализа волн Эллиотта у нас имеется дерево волн.

Его пример можно представить как на рисунке ниже:

Рисунок 16. Пример дерева волн  
Рисунок 16. Пример дерева волн 

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

Можно выделить два типа узлов дерева.

Первый тип - узлы с названием волн ("Импульс", "Зигзаг" и т.п.). Второй тип - узлы с номерами волн ("1", "1<" и т.п.). Вся информация о параметрах волны находится в первом типе узлов. Поэтому при посещении данных узлов мы будем извлекать и записывать информацию о волне, чтобы затем отобразить ее на графике.

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

Пример обхода приведен на рисунке 17 и выделен красной линией.

Рисунок 17. Пример обхода дерева волн
Рисунок 17. Пример обхода дерева волн

5.12.1. FillLabelArray

Функция обхода дерева волн:

void FillLabelArray(TNode *Node)

Функция FillLabelArray обходит дерево волн с корнем Node, посещая только первые варианты волн в дереве, и заполняет глобальный массив LabelArray, в каждом индексе которого будет храниться ссылка на массив вершин (массив объектов класса TLabel), имеющих данный индекс на графике.

Массив LabelArray определен следующим образом: 

CArrayObj *LabelArray[];

5.12.2. CreateLabels

Функция вывода результатов анализа на график: 

void CreateLabels()

Функция CreateLabels создает графические объекты "Текст", соответствующие меткам волн на графике. Метки волн создаются на основе массива LabelArray.

5.12.3. CorrectLabel

Функция обновления (корректировки) вершин волн на графике: 

void CorrectLabel()

Функция CorrectLabel корректирует метки волн на графике при его прокручивании и (или) его сужении.

 

6. Реализация функций автоматической разметки волн Эллиотта 

6.1. Функция Zigzag:

//+------------------------------------------------------------------+
//| Функция Zigzag                                                   |
//+------------------------------------------------------------------+
int Zigzag(int H,int Start,int Finish,CArrayInt *IndexVertex,CArrayDouble *ValueVertex)
  {
   bool Up=true;
   double dH=H*Point();
   int j=0;
   int TempMaxBar = Start;
   int TempMinBar = Start;
   double TempMax = rates[Start].high;
   double TempMin = rates[Start].low;
   for(int i=Start+1;i<=Finish;i++)
     {
      // обработка случая восходящего сегмента
      if(Up==true)
        {
         // проверка, не изменился ли текущий максимум
         if(rates[i].high>TempMax)
           {
            // если да, скорректируем соответствующие переменные
            TempMax=rates[i].high;
            TempMaxBar=i;
           }
         else if(rates[i].low<TempMax-dH)
           {
            // иначе, если пробит отстающий уровень, зафиксируем максимум
            ValueVertex.Add(TempMax);
            IndexVertex.Add(TempMaxBar);
            j++;
            // скорректируем соответствующие переменные
            Up=false;
            TempMin=rates[i].low;
            TempMinBar=i;
           }
        }
      else
        {
         // обработка случая нисходящего сегмента
         // проверка, не изменился ли текущий минимум
         if(rates[i].low<TempMin)
           {
            // если да, скорректируем соответствующие переменные
            TempMin=rates[i].low;
            TempMinBar=i;
           }
         else if(rates[i].high>TempMin+dH)
           {
            // иначе, если пробит отстающий уровень, зафиксируем минимум
            ValueVertex.Add(TempMin);
            IndexVertex.Add(TempMinBar);
            j++;
            // скорректируем соответствующие переменные
            Up=true;
            TempMax=rates[i].high;
            TempMaxBar=i;
           }
        }
     }
   // возвратим количество вершин зигзага
   return(j);
  }

6.2. Функция FillZigzagArray:

CArrayObj ZigzagArray; // объявляем глобальный динамический массив ZigzagArray
//+------------------------------------------------------------------+
//| Функция FillZigzagArray                                          |
//| перебираются значения параметра H зигзага                        |
//| и заполняется массив ZigzagArray                                 |
//+------------------------------------------------------------------+
void FillZigzagArray(int Start,int Finish)
  {
   CArrayInt *IndexVertex=new CArrayInt;         // создаем динамически массив индексов вершин зигзага
   CArrayDouble *ValueVertex=new CArrayDouble;   // создаем динамический массив значений вершин зигзага
   TZigzag *Zigzag;                                 // объявляем класс для хранения индексов и значений вершин зигзага
   int H=1;
   int j=0;
   int n=Zigzag(H,Start,Finish,IndexVertex,ValueVertex);//находим вершины зигзага с параметром H=1
   if(n>0)
     {
      // сохраняем вершины зигзага в массиве ZigzagArray
      Zigzag=new TZigzag; // создаем объект для хранения найденных индексов и вершин зигзага, 
                             // заполняем его и сохраняем в массиве ZigzagArray
      Zigzag.IndexVertex=IndexVertex;
      Zigzag.ValueVertex=ValueVertex;
      ZigzagArray.Add(Zigzag);
      j++;
     }
   H++;
   // в цикле перебираем параметр H зигзага
   while(true)
     {
      IndexVertex=new CArrayInt;                            // создаем динамический массив индексов вершин зигзага
      ValueVertex=new CArrayDouble;                        // создаем динамический массив значений вершин зигзага
      n=Zigzag(H,Start,Finish,IndexVertex,ValueVertex); // находим вершины зигзага
      if(n>0)
        {
         Zigzag=ZigzagArray.At(j-1);
         CArrayInt *PrevIndexVertex=Zigzag.IndexVertex; // получаем массив индексов предыдущего зигзага
         bool b=false;
         // в цикле проверяем, есть ли разница между текущим зигзагом и предыдущим зигзагом
         for(int i=0; i<=n-1;i++)
           {
            if(PrevIndexVertex.At(i)!=IndexVertex.At(i))
              {
               // если разница есть, сохраняем вершины зигзага в массиве ZigzagArray
               Zigzag=new TZigzag;
               Zigzag.IndexVertex=IndexVertex;
               Zigzag.ValueVertex=ValueVertex;
               ZigzagArray.Add(Zigzag);
               j++;
               b=true;
               break;
              }
           }
         if(b==false)
           {
            // иначе, если разницы нет, освобождаем память
            delete IndexVertex;
            delete ValueVertex;
           }
        }
      // ищем вершины зигзага до тех пор, пока их станет меньше или равно двум
      if(n<=2)
         break;
      H++;
     }
  }

6.3. Функция FindPoints:

//+------------------------------------------------------------------+
//| Функция FindPoints                                               |
//| Заполняет массивы ValuePoints и IndexPoints структуры Points     |
//+------------------------------------------------------------------+
bool FindPoints(int NumPoints,int IndexStart,int IndexFinish,double ValueStart,double ValueFinish,TPoints &Points)
  {
   int n=0;
   // в цикле перебираем массив ZigzagArray
   for(int i=ZigzagArray.Total()-1; i>=0;i--)
     {
      TZigzag *Zigzag=ZigzagArray.At(i);             // получаемый i-ый зигзаг в массиве ZigzagArray
      CArrayInt *IndexVertex=Zigzag.IndexVertex;    // получаем массив индексов вершин i-го зигзага
      CArrayDouble *ValueVertex=Zigzag.ValueVertex; // получаем массив значений вершин i-го зигзага
      int Index1=-1,Index2=-1;
      // ищем индекс массива IndexVertex, соответствующий первой точке
      for(int j=0;j<IndexVertex.Total();j++)
        {
         if(IndexVertex.At(j)>=IndexStart)
           {
            Index1=j;
            break;
           }
        }
      // ищем индекс массива IndexVertex, соответствующий последней точке
      for(int j=IndexVertex.Total()-1;j>=0;j--)
        {
         if(IndexVertex.At(j)<=IndexFinish)
           {
            Index2=j;
            break;
           }
        }
      // если нашли первую и последнюю точки
      if((Index1!=-1) && (Index2!=-1))
        {
         n=Index2-Index1+1; // узнаем, сколько точек нашли
        }
      // если нашли требуемое количество точек (равное или большее)
      if(n>=NumPoints)
        {
         // проверяем, чтобы первая и последняя вершины совпадали с требуемыми значениями вершин
         if(((ValueStart!=0) && (ValueVertex.At(Index1)!=ValueStart)) || 
            ((ValueFinish!=0) && (ValueVertex.At(Index1+n-1)!=ValueFinish)))continue;
         // заполняем структуру Points, передаваемую в качестве параметров
         Points.NumPoints=n;
         ArrayResize(Points.ValuePoints, n);
         ArrayResize(Points.IndexPoints, n);
         int k=0;
         // заполняем массивы ValuePoints и IndexPoints структуры Points
         for(int j=Index1; j<Index1+n;j++)
           {
            Points.ValuePoints[k]=ValueVertex.At(j);
            Points.IndexPoints[k]=IndexVertex.At(j);
            k++;
           }
         return(true);
        };
     };
   return(false);
  };

6.4. Функция NotStartedAndNotFinishedWaves:

//+------------------------------------------------------------------+
//| Функция NotStartedAndNotFinishedWaves                            |
//+------------------------------------------------------------------+
void NotStartedAndNotFinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)
  {
   int v1,v2,v3,v4,I;
   TPoints Points;
   TNode *ParentNode,*ChildNode;
   int IndexWave;
   string NameWave;
   TWave *Wave;
   int i=0,pos=0,start=0;
   // в массив ListNameWave записываем волны, которые будем анализировать
   string ListNameWave[];
   ArrayResize(ListNameWave,ArrayRange(WaveDescription,0));
   while(pos!=StringLen(Subwaves)-1)
     {
      pos=StringFind(Subwaves,",",start);
      NameWave=StringSubstr(Subwaves,start,pos-start);
      ListNameWave[i++]=NameWave;
      start=pos+1;
     }
   int IndexStart=ParentWave.IndexVertex[NumWave-1];
   int IndexFinish=ParentWave.IndexVertex[NumWave];
   double ValueStart = ParentWave.ValueVertex[NumWave - 1];
   double ValueFinish= ParentWave.ValueVertex[NumWave];
   // находим не менее двух точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(2,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора неначатых и незавершенных волн с формулой "1<-2-3>"
   v1=0;
   while(v1<=Points.NumPoints-2)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // по очереди из ListNameWave извлекаем название волны для анализа
            NameWave=ListNameWave[j++];
            // находим индекс волны в структуре WaveDescription для того,
              // чтобы знать количество ее субволн и их названия
            IndexWave=FindWaveInWaveDescription(NameWave);
            if((WaveDescription[IndexWave].NumWave==5) || (WaveDescription[IndexWave].NumWave==3))
              {
               // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="1<-2-3>";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = Points.ValuePoints[v1];
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = 0;
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = IndexStart;
               Wave.IndexVertex[1] = Points.IndexPoints[v1];
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = IndexFinish;
               Wave.IndexVertex[4] = 0;
               Wave.IndexVertex[5] = 0;
               // проверяем волну на правила
               if(WaveRules(Wave)==true)
                 {
                  // если волна прошла проверку на правила, добавляем ее в дерево волн
                  ParentNode=Node.Add(NameWave,Wave);
                  I=1;
                  // создаем первую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем вторую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем третью субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // иначе, если волна не прошла на правила, освобождаем память
               else delete Wave;
              }
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // циклы перебора неначатых и незавершенных волн с формулой "2<-3-4>"
   v2=0;
   while(v2<=Points.NumPoints-2)
     {
      v3=v2+1;
      while(v3<=Points.NumPoints-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // по очереди из ListNameWave извлекаем название волны для анализа
            NameWave=ListNameWave[j++];
            // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==5)
              {
               // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="2<-3-4>";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = 0;
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = 0;
               Wave.IndexVertex[1] = IndexStart;
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = IndexFinish;
               Wave.IndexVertex[5] = 0;
               // проверяем волну на правила
               if(WaveRules(Wave)==true)
                 {
                  // если волна прошла проверку на правила, добавляем ее в дерево волн
                  ParentNode=Node.Add(NameWave,Wave);
                  I=2;
                  // создаем вторую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем третью субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  //создаем четвертую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // иначе, если волна не прошла на правила, освобождаем память
               else delete Wave;
              }
           }
         v3=v3+2;
        }
      v2=v2+2;
     }
   // циклы перебора неначатых и незавершенных волн с формулой "3<-4-5>"
   v3=0;
   while(v3<=Points.NumPoints-2)
     {
      v4=v3+1;
      while(v4<=Points.NumPoints-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // по очереди из ListNameWave извлекаем название волны для анализа
            NameWave=ListNameWave[j++];
            // находим индекс волны в структуре WaveDescription для того,
              // чтобы знать количество ее субволн и их названия
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==5)
              {
               // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="3<-4-5>";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = 0;
               Wave.ValueVertex[2] = 0;
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = Points.ValuePoints[v4];
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = 0;
               Wave.IndexVertex[1] = 0;
               Wave.IndexVertex[2] = IndexStart;
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = Points.IndexPoints[v4];
               Wave.IndexVertex[5] = IndexFinish;
               // проверяем волну на правила
               if(WaveRules(Wave)==true)
                 {
                  // если волна прошла проверку на правила, добавляем ее в дерево волн
                  ParentNode=Node.Add(NameWave,Wave);
                  I=3;
                  // создаем третью субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем четвертую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем пятую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // иначе, если волна не прошла на правила, освобождаем память
               else delete Wave;
              }
           }
         v4=v4+2;
        }
      v3=v3+2;
     }
   // находим не менее трех точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(3,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false) return;
   // циклы перебора неначатых и незавершенных волн с формулой "1<-2-3-4>"
   v1=0;
   while(v1<=Points.NumPoints-3)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-2)
        {
         v3=v2+1;
         while(v3<=Points.NumPoints-1)
           {
            int j=0;
            while(j<=i-1)
              {
               // по очереди из ListNameWave извлекаем название волны для анализа
               NameWave=ListNameWave[j++];
               // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
               IndexWave=FindWaveInWaveDescription(NameWave);
               if(WaveDescription[IndexWave].NumWave==5)
                 {
                  // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
                  Wave=new TWave;
                  Wave.Name=NameWave;
                  Wave.Level=Level;
                  Wave.Formula="1<-2-3-4>";
                  Wave.ValueVertex[0] = 0;
                  Wave.ValueVertex[1] = Points.ValuePoints[v1];
                  Wave.ValueVertex[2] = Points.ValuePoints[v2];
                  Wave.ValueVertex[3] = Points.ValuePoints[v3];
                  Wave.ValueVertex[4] = 0;
                  Wave.ValueVertex[5] = 0;
                  Wave.IndexVertex[0] = IndexStart;
                  Wave.IndexVertex[1] = Points.IndexPoints[v1];
                  Wave.IndexVertex[2] = Points.IndexPoints[v2];
                  Wave.IndexVertex[3] = Points.IndexPoints[v3];
                  Wave.IndexVertex[4] = IndexFinish;
                  Wave.IndexVertex[5] = 0;
                  // проверяем волну на правила
                  if(WaveRules(Wave)==true)
                    {
                     // если волна прошла проверку на правила, добавляем ее в дерево волн
                     ParentNode=Node.Add(NameWave,Wave);
                     I=1;
                     // создаем первую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем вторую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем третью субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем четвертую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                    }
                  //иначе, если волна не прошла на правила, освобождаем память
                  else delete Wave;
                 }
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // циклы перебора неначатых и незавершенных волн с формулой "2<-3-4-5>"
   v2=0;
   while(v2<=Points.NumPoints-3)
     {
      v3=v2+1;
      while(v3<=Points.NumPoints-2)
        {
         v4=v3+1;
         while(v4<=Points.NumPoints-1)
           {
            int j=0;
            while(j<=i-1)
              {
               // по очереди из ListNameWave извлекаем название волны для анализа
               NameWave=ListNameWave[j++];
               // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
               IndexWave=FindWaveInWaveDescription(NameWave);
               if(WaveDescription[IndexWave].NumWave==5)
                 {
                  // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
                  Wave=new TWave;
                  Wave.Name=NameWave;
                  Wave.Level=Level;
                  Wave.Formula="2<-3-4-5>";
                  Wave.ValueVertex[0] = 0;
                  Wave.ValueVertex[1] = 0;
                  Wave.ValueVertex[2] = Points.ValuePoints[v2];
                  Wave.ValueVertex[3] = Points.ValuePoints[v3];
                  Wave.ValueVertex[4] = Points.ValuePoints[v4];
                  Wave.ValueVertex[5] = 0;
                  Wave.IndexVertex[0] = 0;
                  Wave.IndexVertex[1] = IndexStart;
                  Wave.IndexVertex[2] = Points.IndexPoints[v2];
                  Wave.IndexVertex[3] = Points.IndexPoints[v3];
                  Wave.IndexVertex[4] = Points.IndexPoints[v4];
                  Wave.IndexVertex[5] = IndexFinish;
                  // проверяем волну на правила
                  if(WaveRules(Wave)==true)
                    {
                     // если волна прошла проверку на правила, добавляем ее в дерево волн
                     ParentNode=Node.Add(NameWave,Wave);
                     I=2;
                     // создаем вторую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем третью субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     //создаем четвертуюю субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     //если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем пятую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                    }
                  // иначе, если волна не прошла на правила, освобождаем память
                  else delete Wave;
                 }
              }
            v4=v4+2;
           }
         v3=v3+2;
        }
      v2=v2+2;
     }
   // находим не менее четырех точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(4,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false) return;
   // циклы перебора неначатых и незавершенных волн с формулой "1<-2-3-4-5>"
   v1=0;
   while(v1<=Points.NumPoints-4)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-3)
        {
         v3=v2+1;
         while(v3<=Points.NumPoints-2)
           {
            v4=v3+1;
            while(v4<=Points.NumPoints-1)
              {
               int j=0;
               while(j<=i-1)
                 {
                  // по очереди из ListNameWave извлекаем название волны для анализа
                  NameWave=ListNameWave[j++];
                  // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
                  IndexWave=FindWaveInWaveDescription(NameWave);
                  if(WaveDescription[IndexWave].NumWave==5)
                    {
                     // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
                     Wave=new TWave;
                     Wave.Name=NameWave;
                     Wave.Level=Level;
                     Wave.Formula="1<-2-3-4-5>";
                     Wave.ValueVertex[0] = 0;
                     Wave.ValueVertex[1] = Points.ValuePoints[v1];
                     Wave.ValueVertex[2] = Points.ValuePoints[v2];
                     Wave.ValueVertex[3] = Points.ValuePoints[v3];
                     Wave.ValueVertex[4] = Points.ValuePoints[v4];
                     Wave.ValueVertex[5] = 0;
                     Wave.IndexVertex[0] = IndexStart;
                     Wave.IndexVertex[1] = Points.IndexPoints[v1];
                     Wave.IndexVertex[2] = Points.IndexPoints[v2];
                     Wave.IndexVertex[3] = Points.IndexPoints[v3];
                     Wave.IndexVertex[4] = Points.IndexPoints[v4];
                     Wave.IndexVertex[5] = IndexFinish;
                     // проверяем волну на правила
                     if(WaveRules(Wave)==true)
                       {
                        // если волна прошла проверку на правила, добавляем ее в дерево волн
                        ParentNode=Node.Add(NameWave,Wave);
                        I=1;
                        // создаем первую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем вторую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем третью субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем четвертую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем пятую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                       }
                     // иначе, если волна не прошла на правила, освобождаем память
                     else delete Wave;
                    }
                 }
               v4=v4+2;
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // находим не менее одной точки на графике цены и записываем ее в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(1,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // цикл перебора неначатых и незавершенных волн с формулой "1<-2>"
   v1=0;
   while(v1<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // по очереди из ListNameWave извлекаем название волны для анализа
         NameWave=ListNameWave[j++];
         // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5 || WaveDescription[IndexWave].NumWave==3)
           {
            // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="1<-2>";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = Points.ValuePoints[v1];
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = IndexStart;
            Wave.IndexVertex[1] = Points.IndexPoints[v1];
            Wave.IndexVertex[2] = IndexFinish;
            Wave.IndexVertex[3] = 0;
            Wave.IndexVertex[4] = 0;
            Wave.IndexVertex[5] = 0;
            // проверяем волну на правила
            if(WaveRules(Wave)==true)
              {
               // если волна прошла проверку на правила, добавляем ее в дерево волн
               ParentNode=Node.Add(NameWave,Wave);
               I=1;
               // создаем первую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // создаем вторую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // иначе, если волна не прошла на правила, освобождаем память
            else delete Wave;
           }
        }
      v1=v1+1;
     }
   // цикл перебора неначатых и незавершенных волн с формулой "2<-3>"
   v2=0;
   while(v2<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // по очереди из ListNameWave извлекаем название волны для анализа
         NameWave=ListNameWave[j++];
         // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5 || WaveDescription[IndexWave].NumWave==3)
           {
            // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="2<-3>";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = Points.ValuePoints[v2];
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = IndexStart;
            Wave.IndexVertex[2] = Points.IndexPoints[v2];
            Wave.IndexVertex[3] = IndexFinish;
            Wave.IndexVertex[4] = 0;
            Wave.IndexVertex[5] = 0;
            // проверяем волну на правила
            if(WaveRules(Wave)==true)
              {
               // если волна прошла проверку на правила, добавляем ее в дерево волн
               ParentNode=Node.Add(NameWave,Wave);
               I=2;
               // создаем вторую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // создаем третью субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // иначе, если волна не прошла на правила, освобождаем память
            else delete Wave;
           }
        }
      v2=v2+1;
     }
   // цикл перебора неначатых и незавершенных волн с формулой "3<-4>"
   v3=0;
   while(v3<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // по очереди из ListNameWave извлекаем название волны для анализа
         NameWave=ListNameWave[j++];
         // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5)
           {
            // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="3<-4>";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = Points.ValuePoints[v3];
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = 0;
            Wave.IndexVertex[2] = IndexStart;
            Wave.IndexVertex[3] = Points.IndexPoints[v3];
            Wave.IndexVertex[4] = IndexFinish;
            Wave.IndexVertex[5] = 0;
            // проверяем волну на правила
            if(WaveRules(Wave)==true)
              {
               // если волна прошла проверку на правила, добавляем ее в дерево волн
               ParentNode=Node.Add(NameWave,Wave);
               I=3;
               // создаем третью субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // создаем четвертую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // иначе, если волна не прошла на правила, освобождаем память
            else delete Wave;
           }
        }
      v3=v3+1;
     }
   // цикл перебора неначатых и незавершенных волн с формулой "4<-5>"
   v4=0;
   while(v4<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // по очереди из ListNameWave извлекаем название волны для анализа
         NameWave=ListNameWave[j++];
         // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5)
           {
            //создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="4<-5>";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = Points.ValuePoints[v4];
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = 0;
            Wave.IndexVertex[2] = 0;
            Wave.IndexVertex[3] = IndexStart;
            Wave.IndexVertex[4] = Points.IndexPoints[v4];
            Wave.IndexVertex[5] = IndexFinish;
            // проверяем волну на правила
            if(WaveRules(Wave)==true)
              {
               // если волна прошла проверку на правила, добавляем ее в дерево волн
               ParentNode=Node.Add(NameWave,Wave);
               I=4;
               // создаем четвертую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // создаем пятую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            //иначе, если волна не прошла на правила, освобождаем память
            else delete Wave;
           }
        }
      v4=v4+1;
     }
  }

6.5. Функция NotStartedWaves:

//+------------------------------------------------------------------+
//| Функция NotStartedWaves                                          |
//+------------------------------------------------------------------+
void NotStartedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)
  {
   int v1,v2,v3,v4,v5,I;
   TPoints Points;
   TNode *ParentNode,*ChildNode;
   int IndexWave;
   string NameWave;
   TWave *Wave;
   int i=0,Pos=0,Start=0;
   // в массив ListNameWave записываем волны, которые будем анализировать
   string ListNameWave[];
   ArrayResize(ListNameWave,ArrayRange(WaveDescription,0));
   while(Pos!=StringLen(Subwaves)-1)
     {
      Pos=StringFind(Subwaves,",",Start);
      NameWave=StringSubstr(Subwaves,Start,Pos-Start);
      ListNameWave[i++]=NameWave;
      Start=Pos+1;
     }
   int IndexStart=ParentWave.IndexVertex[NumWave-1];
   int IndexFinish=ParentWave.IndexVertex[NumWave];
   double ValueStart = ParentWave.ValueVertex[NumWave - 1];
   double ValueFinish= ParentWave.ValueVertex[NumWave];
   // находим не менее двух точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(2,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора неначатых волн с формулой "4<-5"
   v5=Points.NumPoints-1;
   v4=v5-1;
   while(v4>=0)
     {
      int j=0;
      while(j<=i-1)
        {
         // по очереди из ListNameWave извлекаем название волны для анализа
         NameWave=ListNameWave[j++];
         // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==5)
           {
            // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="4<-5";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = Points.ValuePoints[v4];
            Wave.ValueVertex[5] = Points.ValuePoints[v5];
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = 0;
            Wave.IndexVertex[2] = 0;
            Wave.IndexVertex[3] = IndexStart;
            Wave.IndexVertex[4] = Points.IndexPoints[v4];
            Wave.IndexVertex[5] = Points.IndexPoints[v5];
            // проверяем волну на правила
            if(WaveRules(Wave)==true)
              {
               // если волна прошла проверку на правила, добавляем ее в дерево волн
               ParentNode=Node.Add(NameWave,Wave);
               I=4;
               // создаем четвертую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // создаем пятую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // иначе, если волна не прошла на правила, освобождаем память
            else delete Wave;
           }
        }
      v4=v4-2;
     }
   // циклы перебора неначатых волн с формулой "2<-3"
   v3=Points.NumPoints-1;
   v2=v3-1;
   while(v2>=0)
     {
      int j=0;
      while(j<=i-1)
        {
         // по очереди из ListNameWave извлекаем название волны для анализа
         NameWave=ListNameWave[j++];
         // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
         IndexWave=FindWaveInWaveDescription(NameWave);
         if(WaveDescription[IndexWave].NumWave==3)
           {
            // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="2<-3";
            Wave.ValueVertex[0] = 0;
            Wave.ValueVertex[1] = 0;
            Wave.ValueVertex[2] = Points.ValuePoints[v2];
            Wave.ValueVertex[3] = Points.ValuePoints[v3];
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = 0;
            Wave.IndexVertex[1] = IndexStart;
            Wave.IndexVertex[2] = Points.IndexPoints[v2];
            Wave.IndexVertex[3] = Points.IndexPoints[v3];
            Wave.IndexVertex[4] = 0;
            Wave.IndexVertex[5] = 0;
            // проверяем волну на правила
            if(WaveRules(Wave)==true)
              {
               // если волна прошла проверку на правила, добавляем ее в дерево волн
               ParentNode=Node.Add(NameWave,Wave);
               I=2;
               // создаем вторую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // создаем третью субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // иначе, если волна не прошла на правила, освобождаем память
            else delete Wave;
           }
        }
      v2=v2-2;
     }
   // находим не менее трех точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(3,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора неначатых волн с формулой "3<-4-5"
   v5=Points.NumPoints-1;
   v4=v5-1;
   while(v4>=1)
     {
      v3=v4-1;
      while(v3>=0)
        {
         int j=0;
         while(j<=i-1)
           {
            // по очереди из ListNameWave извлекаем название волны для анализа
            NameWave=ListNameWave[j++];
            // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==5)
              {
               // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="3<-4-5";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = 0;
               Wave.ValueVertex[2] = 0;
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = Points.ValuePoints[v4];
               Wave.ValueVertex[5] = Points.ValuePoints[v5];
               Wave.IndexVertex[0] = 0;
               Wave.IndexVertex[1] = 0;
               Wave.IndexVertex[2] = IndexStart;
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = Points.IndexPoints[v4];
               Wave.IndexVertex[5] = Points.IndexPoints[v5];
               // проверяем волну на правила
               if(WaveRules(Wave)==true)
                 {
                  // если волна прошла проверку на правила, добавляем ее в дерево волн
                  ParentNode=Node.Add(NameWave,Wave);
                  I=3;
                  // создаем третью субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем четвертую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем пятую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // иначе, если волна не прошла на правила, освобождаем память
               else delete Wave;
              }
           }
         v3=v3-2;
        }
      v4=v4-2;
     }
   // циклы перебора неначатых волн с формулой "1<-2-3"
   v3=Points.NumPoints-1;
   v2=v3-1;
   while(v2>=1)
     {
      v1=v2-1;
      while(v1>=0)
        {
         int j=0;
         while(j<=i-1)
           {
            // по очереди из ListNameWave извлекаем название волны для анализа
            NameWave=ListNameWave[j++];
            // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==3)
              {
               // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="1<-2-3";
               Wave.ValueVertex[0] = 0;
               Wave.ValueVertex[1] = Points.ValuePoints[v1];
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = IndexStart;
               Wave.IndexVertex[1] = Points.IndexPoints[v1];
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = 0;
               Wave.IndexVertex[5] = 0;
               // проверяем волну на правила
               if(WaveRules(Wave)==true)
                 {
                  // если волна прошла проверку на правила, добавляем ее в дерево волн
                  ParentNode=Node.Add(NameWave,Wave);
                  I=1;
                  // создаем первую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  //если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем вторую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  //если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем третью субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // иначе, если волна не прошла на правила, освобождаем память
               else delete Wave;
              }
           }
         v1=v1-2;
        }
      v2=v2-2;
     }
   // находим не менее четырех точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(4,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора неначатых волн с формулой "2<-3-4-5"
   v5=Points.NumPoints-1;
   v4=v5-1;
   while(v4>=2)
     {
      v3=v4-1;
      while(v3>=1)
        {
         v2=v3-1;
         while(v2>=0)
           {
            int j=0;
            while(j<=i-1)
              {
               // по очереди из ListNameWave извлекаем название волны для анализа
               NameWave=ListNameWave[j++];
               // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
               IndexWave=FindWaveInWaveDescription(NameWave);
               if(WaveDescription[IndexWave].NumWave==5)
                 {
                  // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
                  Wave=new TWave;
                  Wave.Name=NameWave;
                  Wave.Level=Level;
                  Wave.Formula="2<-3-4-5";
                  Wave.ValueVertex[0] = 0;
                  Wave.ValueVertex[1] = 0;
                  Wave.ValueVertex[2] = Points.ValuePoints[v2];
                  Wave.ValueVertex[3] = Points.ValuePoints[v3];
                  Wave.ValueVertex[4] = Points.ValuePoints[v4];
                  Wave.ValueVertex[5] = Points.ValuePoints[v5];
                  Wave.IndexVertex[0] = 0;
                  Wave.IndexVertex[1] = IndexStart;
                  Wave.IndexVertex[2] = Points.IndexPoints[v2];
                  Wave.IndexVertex[3] = Points.IndexPoints[v3];
                  Wave.IndexVertex[4] = Points.IndexPoints[v4];
                  Wave.IndexVertex[5] = Points.IndexPoints[v5];
                  // проверяем волну на правила
                  if(WaveRules(Wave)==true)
                    {
                     // если волна прошла проверку на правила, добавляем ее в дерево волн
                     ParentNode=Node.Add(NameWave,Wave);
                     I=2;
                     // создаем вторую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем третью субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем четвертую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем пятую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                    }
                  // иначе, если волна не прошла на правила, освобождаем память
                  else delete Wave;
                 }
              }
            v2=v2-2;
           }
         v3=v3-2;
        }
      v4=v4-2;
     }
   // находим не менее пяти точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(5,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора неначатых волн с формулой "1<-2-3-4-5"
   v5=Points.NumPoints-1;
   v4=v5-1;
   while(v4>=3)
     {
      v3=v4-1;
      while(v3>=2)
        {
         v2=v3-1;
         while(v2>=1)
           {
            v1=v2-1;
            while(v1>=0)
              {
               int j=0;
               while(j<=i-1)
                 {
                  // по очереди из ListNameWave извлекаем название волны для анализа
                  NameWave=ListNameWave[j++];
                  // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
                  IndexWave=FindWaveInWaveDescription(NameWave);
                  if(WaveDescription[IndexWave].NumWave==5)
                    {
                     // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
                     Wave=new TWave;
                     Wave.Name=NameWave;
                     Wave.Level=Level;
                     Wave.Formula="1<-2-3-4-5";
                     Wave.ValueVertex[0] = 0;
                     Wave.ValueVertex[1] = Points.ValuePoints[v1];
                     Wave.ValueVertex[2] = Points.ValuePoints[v2];
                     Wave.ValueVertex[3] = Points.ValuePoints[v3];
                     Wave.ValueVertex[4] = Points.ValuePoints[v4];
                     Wave.ValueVertex[5] = Points.ValuePoints[v5];
                     Wave.IndexVertex[0] = IndexStart;
                     Wave.IndexVertex[1] = Points.IndexPoints[v1];
                     Wave.IndexVertex[2] = Points.IndexPoints[v2];
                     Wave.IndexVertex[3] = Points.IndexPoints[v3];
                     Wave.IndexVertex[4] = Points.IndexPoints[v4];
                     Wave.IndexVertex[5] = Points.IndexPoints[v5];
                     // проверяем волну на правила
                     if(WaveRules(Wave)==true)
                       {
                        // если волна прошла проверку на правила, добавляем ее в дерево волн
                        ParentNode=Node.Add(NameWave,Wave);
                        I=1;
                        // создаем первую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           NotStartedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем вторую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем третью субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем четвертую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем пятую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                       }
                     // иначе, если волна не прошла на правила, освобождаем память
                     else delete Wave;
                    }
                 }
               v1=v1-2;
              }
            v2=v2-2;
           }
         v3=v3-2;
        }
      v4=v4-2;
     }
  }

6.6. Функция NotFinishedWaves:

//+------------------------------------------------------------------+
//| Функция NotFinishedWaves                                         |
//+------------------------------------------------------------------+
void NotFinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)
  {
   int v0,v1,v2,v3,v4,I;
   TPoints Points;
   TNode *ParentNode,*ChildNode;
   int IndexWave;
   string NameWave;
   TWave *Wave;
   int i=0,Pos=0,Start=0;
   //в массив ListNameWave записываем волны, которые будем анализировать
   string ListNameWave[];
   ArrayResize(ListNameWave,ArrayRange(WaveDescription,0));
   while(Pos!=StringLen(Subwaves)-1)
     {
      Pos=StringFind(Subwaves,",",Start);
      NameWave=StringSubstr(Subwaves,Start,Pos-Start);
      ListNameWave[i++]=NameWave;
      Start=Pos+1;
     }
   int IndexStart=ParentWave.IndexVertex[NumWave-1];
   int IndexFinish=ParentWave.IndexVertex[NumWave];
   double ValueStart = ParentWave.ValueVertex[NumWave - 1];
   double ValueFinish= ParentWave.ValueVertex[NumWave];
   // находим не менее двух точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(2,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора незавершенных волн с формулой "1-2>"
   v0=0;
   v1=v0+1;
   while(v1<=Points.NumPoints-1)
     {
      int j=0;
      while(j<=i-1)
        {
         // по очереди из ListNameWave извлекаем название волны для анализа
         NameWave=ListNameWave[j++];
         // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
         IndexWave=FindWaveInWaveDescription(NameWave);
         if((WaveDescription[IndexWave].NumWave==5) || (WaveDescription[IndexWave].NumWave==3))
           {
            // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
            Wave=new TWave;
            Wave.Name=NameWave;
            Wave.Level=Level;
            Wave.Formula="1-2>";
            Wave.ValueVertex[0] = Points.ValuePoints[v0];
            Wave.ValueVertex[1] = Points.ValuePoints[v1];
            Wave.ValueVertex[2] = 0;
            Wave.ValueVertex[3] = 0;
            Wave.ValueVertex[4] = 0;
            Wave.ValueVertex[5] = 0;
            Wave.IndexVertex[0] = Points.IndexPoints[v0];
            Wave.IndexVertex[1] = Points.IndexPoints[v1];
            Wave.IndexVertex[2] = IndexFinish;
            Wave.IndexVertex[3] = 0;
            Wave.IndexVertex[4] = 0;
            Wave.IndexVertex[5] = 0;
            // проверяем волну на правила
            if(WaveRules(Wave)==true)
              {
               // если волна прошла проверку на правила, добавляем ее в дерево волн
               ParentNode=Node.Add(NameWave,Wave);
               I=1;
               // создаем первую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
               I++;
               // создаем вторую субволну в дереве волн
               ChildNode=ParentNode.Add(IntegerToString(I));
               // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
               if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                  NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
              }
            // иначе, если волна не прошла на правила, освобождаем память
            else delete Wave;
           }
        }
      v1=v1+2;
     }
   // находим не менее трех точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(3,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора незавершенных волн с формулой "1-2-3>"
   v0=0;
   v1=v0+1;
   while(v1<=Points.NumPoints-2)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // по очереди из ListNameWave извлекаем название волны для анализа
            NameWave=ListNameWave[j++];
            // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
            IndexWave=FindWaveInWaveDescription(NameWave);
            if((WaveDescription[IndexWave].NumWave==5) || (WaveDescription[IndexWave].NumWave==3))
              {
               // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
               Wave=new TWave;
               Wave.Name=NameWave;
               Wave.Level=Level;
               Wave.Formula="1-2-3>";
               Wave.ValueVertex[0] = Points.ValuePoints[v0];
               Wave.ValueVertex[1] = Points.ValuePoints[v1];
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = 0;
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = Points.IndexPoints[v0];
               Wave.IndexVertex[1] = Points.IndexPoints[v1];
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = IndexFinish;
               Wave.IndexVertex[4] = 0;
               Wave.IndexVertex[5] = 0;
               // проверяем волну на правила
               if(WaveRules(Wave)==true)
                 {
                  // если волна прошла проверку на правила, добавляем ее в дерево волн
                  ParentNode=Node.Add(NameWave,Wave);
                  I=1;
                  // создаем первую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем вторую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем третью субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // иначе, если волна не прошла на правила, освобождаем память
               else delete Wave;
              }
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // находим не менее четырех точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(4,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false) return;
   // циклы перебора незавершенных волн с формулой "1-2-3-4>"
   v0=0;
   v1=v0+1;
   while(v1<=Points.NumPoints-3)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-2)
        {
         v3=v2+1;
         while(v3<=Points.NumPoints-1)
           {
            int j=0;
            while(j<=i-1)
              {
               // по очереди из ListNameWave извлекаем название волны для анализа
               NameWave=ListNameWave[j++];
               // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
               IndexWave=FindWaveInWaveDescription(NameWave);
               if(WaveDescription[IndexWave].NumWave==5)
                 {
                  // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
                  Wave=new TWave;
                  Wave.Name=NameWave;
                  Wave.Level=Level;
                  Wave.Formula="1-2-3-4>";
                  Wave.ValueVertex[0] = Points.ValuePoints[v0];
                  Wave.ValueVertex[1] = Points.ValuePoints[v1];
                  Wave.ValueVertex[2] = Points.ValuePoints[v2];
                  Wave.ValueVertex[3] = Points.ValuePoints[v3];
                  Wave.ValueVertex[4] = 0;
                  Wave.ValueVertex[5] = 0;
                  Wave.IndexVertex[0] = Points.IndexPoints[v0];
                  Wave.IndexVertex[1] = Points.IndexPoints[v1];
                  Wave.IndexVertex[2] = Points.IndexPoints[v2];
                  Wave.IndexVertex[3] = Points.IndexPoints[v3];
                  Wave.IndexVertex[4] = IndexFinish;
                  Wave.IndexVertex[5] = 0;
                  // проверяем волну на правила
                  if(WaveRules(Wave)==true)
                    {
                     // если волна прошла проверку на правила, добавляем ее в дерево волн
                     ParentNode=Node.Add(NameWave,Wave);
                     I=1;
                     // создаем первую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем вторую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     //если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем третью субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                     I++;
                     // создаем четвертую субволну в дереве волн
                     ChildNode=ParentNode.Add(IntegerToString(I));
                     // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                     if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                        NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                    }
                  // иначе, если волна не прошла на правила, освобождаем память
                  else delete Wave;
                 }
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // находим не менее пяти точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(5,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора незавершенных волн с формулой "1-2-3-4-5>"
   v0=0;
   v1=v0+1;
   while(v1<=Points.NumPoints-4)
     {
      v2=v1+1;
      while(v2<=Points.NumPoints-3)
        {
         v3=v2+1;
         while(v3<=Points.NumPoints-2)
           {
            v4=v3+1;
            while(v4<=Points.NumPoints-1)
              {
               int j=0;
               while(j<=i-1)
                 {
                  // по очереди из ListNameWave извлекаем название волны для анализа
                  NameWave=ListNameWave[j++];
                  // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
                  IndexWave=FindWaveInWaveDescription(NameWave);
                  if(WaveDescription[IndexWave].NumWave==5)
                    {
                     // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
                     Wave=new TWave;
                     Wave.Name=NameWave;
                     Wave.Level=Level;
                     Wave.Formula="1-2-3-4-5>";
                     Wave.ValueVertex[0] = Points.ValuePoints[v0];
                     Wave.ValueVertex[1] = Points.ValuePoints[v1];
                     Wave.ValueVertex[2] = Points.ValuePoints[v2];
                     Wave.ValueVertex[3] = Points.ValuePoints[v3];
                     Wave.ValueVertex[4] = Points.ValuePoints[v4];
                     Wave.ValueVertex[5] = 0;
                     Wave.IndexVertex[0] = Points.IndexPoints[v0];
                     Wave.IndexVertex[1] = Points.IndexPoints[v1];
                     Wave.IndexVertex[2] = Points.IndexPoints[v2];
                     Wave.IndexVertex[3] = Points.IndexPoints[v3];
                     Wave.IndexVertex[4] = Points.IndexPoints[v4];
                     Wave.IndexVertex[5] = IndexFinish;
                     // проверяем волну на правила
                     if(WaveRules(Wave)==true)
                       {
                        // если волна прошла проверку на правила, добавляем ее в дерево волн
                        ParentNode=Node.Add(NameWave,Wave);
                        I=1;
                        // создаем первую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем вторую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем третью субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем четвертую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем пятую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           NotFinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                       }
                     // иначе, если волна не прошла на правила, освобождаем память
                     else delete Wave;
                    }
                 }
               v4=v4+2;
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
  }

6.7. Функция FinishedWaves:

//+------------------------------------------------------------------+
//| Функция FinishedWaves                                            |
//+------------------------------------------------------------------+
void FinishedWaves(TWave *ParentWave,int NumWave,TNode *Node,string Subwaves,int Level)
  {
   int v0,v1,v2,v3,v4,v5,I;
   TPoints Points;
   TNode *ParentNode,*ChildNode;
   int IndexWave;
   string NameWave;
   TWave *Wave;
   int i=0,Pos=0,Start=0;
   // в массив ListNameWave записываем волны, которые будем анализировать
   string ListNameWave[];
   ArrayResize(ListNameWave,ArrayRange(WaveDescription,0));
   while(Pos!=StringLen(Subwaves)-1)
     {
      Pos=StringFind(Subwaves,",",Start);
      NameWave=StringSubstr(Subwaves,Start,Pos-Start);
      ListNameWave[i++]=NameWave;
      Start=Pos+1;
     }
   int IndexStart=ParentWave.IndexVertex[NumWave-1];
   int IndexFinish=ParentWave.IndexVertex[NumWave];
   double ValueStart = ParentWave.ValueVertex[NumWave - 1];
   double ValueFinish= ParentWave.ValueVertex[NumWave];
   // находим не менее четырех точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(4,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false) return;
   // циклы перебора завершенных волн с формулой "1-2-3"
   v0 = 0;
   v1 = 1;
   v3 = Points.NumPoints - 1;
   while(v1<=v3-2)
     {
      v2=v1+1;
      while(v2<=v3-1)
        {
         int j=0;
         while(j<=i-1)
           {
            // по очереди из ListNameWave извлекаем название волны для анализа
            NameWave=ListNameWave[j++];
            // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
            IndexWave=FindWaveInWaveDescription(NameWave);
            if(WaveDescription[IndexWave].NumWave==3)
              {
               // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
               Wave=new TWave;;
               Wave.Name=NameWave;
               Wave.Formula="1-2-3";
               Wave.Level=Level;
               Wave.ValueVertex[0] = Points.ValuePoints[v0];
               Wave.ValueVertex[1] = Points.ValuePoints[v1];
               Wave.ValueVertex[2] = Points.ValuePoints[v2];
               Wave.ValueVertex[3] = Points.ValuePoints[v3];
               Wave.ValueVertex[4] = 0;
               Wave.ValueVertex[5] = 0;
               Wave.IndexVertex[0] = Points.IndexPoints[v0];
               Wave.IndexVertex[1] = Points.IndexPoints[v1];
               Wave.IndexVertex[2] = Points.IndexPoints[v2];
               Wave.IndexVertex[3] = Points.IndexPoints[v3];
               Wave.IndexVertex[4] = 0;
               Wave.IndexVertex[5] = 0;
               // проверяем волну на правила
               if(WaveRules(Wave)==true)
                 {
                  // если волна прошла проверку на правила, добавляем ее в дерево волн
                  ParentNode=Node.Add(NameWave,Wave);
                  I=1;
                  // создаем первую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(i));
                  // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем вторую субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(i));
                  // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                  I++;
                  // создаем третью субволну в дереве волн
                  ChildNode=ParentNode.Add(IntegerToString(I));
                  // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                  if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                     FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                 }
               // иначе, если волна не прошла на правила, освобождаем память
               else delete Wave;
              }
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
   // находим не менее шести точек на графике цены и записываем их в структуру Points
   // если не нашли, то выходим из функции
   if(FindPoints(6,IndexStart,IndexFinish,ValueStart,ValueFinish,Points)==false)return;
   // циклы перебора завершенных волн с формулой "1-2-3-4-5"
   v0 = 0;
   v1 = 1;
   v5 = Points.NumPoints - 1;
   while(v1<=v5-4)
     {
      v2=v1+1;
      while(v2<=v5-3)
        {
         v3=v2+1;
         while(v3<=v5-2)
           {
            v4=v3+1;
            while(v4<=v5-1)
              {
               int j=0;
               while(j<=i-1)
                 {
                  // по очереди из ListNameWave извлекаем название волны для анализа
                  NameWave=ListNameWave[j++];
                  // находим индекс волны в структуре WaveDescription для того, чтобы знать количество ее субволн и их названия
                  IndexWave=FindWaveInWaveDescription(NameWave);
                  if(WaveDescription[IndexWave].NumWave==5)
                    {
                     // создаем объект класса TWave и заполняем его поля - параметры анализируемой волны
                     Wave=new TWave;
                     Wave.Name=NameWave;
                     Wave.Level=Level;
                     Wave.Formula="1-2-3-4-5";
                     Wave.ValueVertex[0] = Points.ValuePoints[v0];
                     Wave.ValueVertex[1] = Points.ValuePoints[v1];
                     Wave.ValueVertex[2] = Points.ValuePoints[v2];
                     Wave.ValueVertex[3] = Points.ValuePoints[v3];
                     Wave.ValueVertex[4] = Points.ValuePoints[v4];
                     Wave.ValueVertex[5] = Points.ValuePoints[v5];
                     Wave.IndexVertex[0] = Points.IndexPoints[v0];
                     Wave.IndexVertex[1] = Points.IndexPoints[v1];
                     Wave.IndexVertex[2] = Points.IndexPoints[v2];
                     Wave.IndexVertex[3] = Points.IndexPoints[v3];
                     Wave.IndexVertex[4] = Points.IndexPoints[v4];
                     Wave.IndexVertex[5] = Points.IndexPoints[v5];
                     // проверяем волну на правила
                     if(WaveRules(Wave)==true)
                       {
                        // если волна прошла проверку на правила, добавляем ее в дерево волн
                        ParentNode=Node.Add(NameWave,Wave);
                        I=1;
                        // создаем первую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий первой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем вторую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий второй субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем третью субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий третьей субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем четвертую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий четвертой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                        I++;
                        // создаем пятую субволну в дереве волн
                        ChildNode=ParentNode.Add(IntegerToString(I));
                        // если участок графика, соответствующий пятой субволне еще не анализировался, то анализируем его
                        if(Already(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I])==false)
                           FinishedWaves(Wave,I,ChildNode,WaveDescription[IndexWave].Subwaves[I],Level+1);
                       }
                     // иначе, если волна не прошла на правила, освобождаем память
                     else delete Wave;
                    }
                 }
               v4=v4+2;
              }
            v3=v3+2;
           }
         v2=v2+2;
        }
      v1=v1+2;
     }
  }

6.8. Функция FindWaveInWaveDescription:

//+------------------------------------------------------------------+
//| Функция FindWaveInWaveDescription                                |
//+------------------------------------------------------------------+
int FindWaveInWaveDescription(string NameWave)
  {
   for(int i=0;i<ArrayRange(WaveDescription,0);i++)
      if(WaveDescription[i].NameWave==NameWave)return(i);
   return(-1);
  }

6.9. Функция Already:

//+------------------------------------------------------------------+
//| Функция Already                                                  |
//+------------------------------------------------------------------+
bool Already(TWave *Wave,int NumWave,TNode *Node,string Subwaves)
  {
   // получаем требуемые параметры волны или группы волн
   int IndexStart=Wave.IndexVertex[NumWave-1];
   int IndexFinish=Wave.IndexVertex[NumWave];
   double ValueStart = Wave.ValueVertex[NumWave - 1];
   double ValueFinish= Wave.ValueVertex[NumWave];
   // в цикле перебираем массив NodeInfoArray для поиска уже размеченного участка графика
   for(int i=NodeInfoArray.Total()-1; i>=0;i--)
     {
      TNodeInfo *NodeInfo=NodeInfoArray.At(i);
      // если требуемый участок уже ранее размечался
      if(NodeInfo.Subwaves==Subwaves && (NodeInfo.ValueStart==ValueStart) && 
         (NodeInfo.ValueFinish==ValueFinish) && (NodeInfo.IndexStart==IndexStart) &&
         (NodeInfo.IndexFinish==IndexFinish))
        {
         // добавляем дочерние узлы найденного узла в дочерние узлы нового узла
         for(int j=0;j<NodeInfo.Node.Child.Total();j++)
            Node.Child.Add(NodeInfo.Node.Child.At(j));
         return(true); // выходим из функции
        }
     }
   // если участок ранее не размечался, то записываем данные о нем в массив NodeInfoArray
   TNodeInfo *NodeInfo=new TNodeInfo;
   NodeInfo.IndexStart=IndexStart;
   NodeInfo.IndexFinish=IndexFinish;
   NodeInfo.ValueStart=ValueStart;
   NodeInfo.ValueFinish=ValueFinish;
   NodeInfo.Subwaves=Subwaves;
   NodeInfo.Node=Node;
   NodeInfoArray.Add(NodeInfo);
   return(false);
  }

6.10. Функция WaveRules:

int IndexVertex[6];                         // индексы вершин волны
double ValueVertex[6],Maximum[6],Minimum[6]; // значения вершин волны, а также максимальные и минимальные значения волны
string Trend;                                    // направление тренда - "Up" или "Down"
string Formula;                                  // формула волны - "1<2-3>" или "1-2-3>" и т.п.
int FixedVertex[6];                             // информация о вершинах волны, зафиксированы они или нет

//+------------------------------------------------------------------+
//| Функция WaveRules                                                |
//+------------------------------------------------------------------+
bool WaveRules(TWave *Wave)
  {
   Formula=Wave.Formula;
   bool Result=false;
   // заполняем массив IndexVertex и ValueVertex - индексы вершин и значения вершин волны
   for(int i=0;i<=5;i++)
     {
      IndexVertex[i]=Wave.IndexVertex[i];
      ValueVertex[i]=Wave.ValueVertex[i];
      FixedVertex[i]=-1;
     }
   // заполняем массив FixedVertex, значения которого указывают, является ли вершина фиксированной или нет
   int Pos1=StringFind(Formula,"<");
   string Str;
   if(Pos1>0)
     {
      Str=ShortToString(StringGetCharacter(Formula,Pos1-1));
      FixedVertex[StringToInteger(Str)]=1;
      FixedVertex[StringToInteger(Str)-1]=0;
      Pos1=StringToInteger(Str)+1;
     }
   else Pos1=0;
   int Pos2=StringFind(Formula,">");
   if(Pos2>0)
     {
      Str=ShortToString(StringGetCharacter(Formula,Pos2-1));
      FixedVertex[StringToInteger(Str)]=0;
      Pos2=StringToInteger(Str)-1;
     }
   else
     {
      Pos2=StringLen(Formula);
      Str=ShortToString(StringGetCharacter(Formula,Pos2-1));
      Pos2=StringToInteger(Str);
     }
   for(int i=Pos1;i<=Pos2;i++)
      FixedVertex[i]=1;
   double High[],Low[];
   ArrayResize(High,ArrayRange(rates,0));
   ArrayResize(Low,ArrayRange(rates,0));
     // находим максимумы и минимумы волн
     for(int i=1; i<=5; i++)
     {
      Maximum[i]=rates[IndexVertex[i]].high;
      Minimum[i]=rates[IndexVertex[i-1]].low;
      for(int j=IndexVertex[i-1];j<=IndexVertex[i];j++)
        {
         if(rates[j].high>Maximum[i])Maximum[i]=rates[j].high;
         if(rates[j].low<Minimum[i])Minimum[i]=rates[j].low;
        }
     }
   // узнаем тренд
   if((FixedVertex[0]==1 && ValueVertex[0]==rates[IndexVertex[0]].low) ||
      (FixedVertex[1]==1 && ValueVertex[1]==rates[IndexVertex[1]].high) ||
      (FixedVertex[2]==1 && ValueVertex[2]==rates[IndexVertex[2]].low) ||
      (FixedVertex[3]==1 && ValueVertex[3]==rates[IndexVertex[3]].high) ||
      (FixedVertex[4]==1 && ValueVertex[4]==rates[IndexVertex[4]].low) ||
      (FixedVertex[5]==1 && ValueVertex[5]==rates[IndexVertex[5]].high))
      Trend="Up";
   else Trend="Down";
   // проверяем требуемую волну на правила
   if(Wave.Name=="Импульс")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0 && VertexAAboveVertexB(4,1,true)>=0 &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,true)>=0 &&
         (WaveAMoreWaveB(3,1)>=0 || WaveAMoreWaveB(3,5)>=0))
         Result=true;
     }
   else if(Wave.Name=="Клин")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0 && VertexAAboveVertexB(4,2,true)>=0 &&
         VertexAAboveVertexB(1,4,false)>=0 &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,true)>=0&&
         (WaveAMoreWaveB(3,1)>=0 || WaveAMoreWaveB(3,5)>=0))
         Result=true;
     }
   else if(Wave.Name=="Диагональ")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0 && VertexAAboveVertexB(4,2,true)>=0 &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,true)>=0&&
         (WaveAMoreWaveB(3,1)>=0 || WaveAMoreWaveB(3,5)>=0))
         Result=true;
     }
   else if(Wave.Name=="Зигзаг")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0)
         Result=true;
     }
   else if(Wave.Name=="Плоскость")
     {
      if(VertexAAboveVertexB(1,0,false)>=0 &&
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0)
         Result=true;
     }
   else if(Wave.Name=="Двойной зигзаг")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0)
         Result=true;
     }
   else if(Wave.Name=="Двойная тройка")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,false)>=0)
         Result=true;
     }
   else if(Wave.Name=="Тройной зигзаг")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && VertexAAboveVertexB(2,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,true)>=0 &&
         VertexAAboveVertexB(3,1,false)>=0 && VertexAAboveVertexB(5,3,false) &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,true)>=0)
         Result=true;
     }
   else if(Wave.Name=="Тройная тройка")
     {
      if(VertexAAboveVertexB(1,0,true)>=0 && 
         VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,false)>=0 &&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,false)>=0)
         Result=true;
     }
   else if(Wave.Name=="Сходящийся треугольник")
     {
      if(VertexAAboveVertexB(1,0,false)>=0 && VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,false)>= 0&&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,false)>=0 &&
         WaveAMoreWaveB(2,3)>=0 && WaveAMoreWaveB(3,4)>=0 && WaveAMoreWaveB(4,5)>=0)
         Result=true;
     }
   else if(Wave.Name=="Расходящийся треугольник")
     {
      if(VertexAAboveVertexB(1,0,false)>=0 && VertexAAboveVertexB(1,2,false)>=0 && VertexAAboveVertexB(3,2,false)>= 0&&
         VertexAAboveVertexB(3,4,false)>=0 && VertexAAboveVertexB(5,4,false)>=0 &&
         WaveAMoreWaveB(3,2)>=0 && WaveAMoreWaveB(3,2)>=0)
         Result=true;
     }
   return(Result);
  }

6.11. Функция VertexAAboveVertexB:

//+-------------------------------------------------------------------------------------+
//| Функция VertexAAboveVertexB проверяет, является ли вершина A выше вершины B,        |
//| передаваемые в качестве параметров данной функции                                   |
//| данную проверку можно совершить только если вершина A и B - фиксированные,          |
//| либо вершина A - нефиксированная и четная, а вершина B - фиксированная,             |
//| либо вершина A - фиксированная, а вершина B - нефиксированная и нечетная,           |
//| либо вершина A - нефиксированная и четная, и вершина B - нефиксированная и нечетная |
//+-------------------------------------------------------------------------------------+
int VertexAAboveVertexB(int A,int B,bool InternalPoints)
  {
   double VA=0,VB=0,VC=0;
   int IA=0,IB=0;
   int Result=0;
   if(A>=B)
     {
      IA = A;
      IB = B;
     }
   else if(A<B)
     {
      IA = B;
      IB = A;
     }
   // если должны учитываться внутренние точки волны
   if(InternalPoints==true)
     {
      if((Trend=="Up") && ((IA%2==0) || ((IA-IB==1) && (IB%2==0))))
        {
         VA=Minimum[IA];
         IA=IA-IA%2;
        }
      else if((Trend=="Down") && ((IA%2==0) || ((IA-IB==1) && (IB%2==0))))
        {
         VA=Maximum[IA];
         IA=IA-IA%2;
        }
      else if((Trend=="Up") && ((IA%2==1) || ((IA-IB==1) && (IB%2==1))))
        {
         VA=Maximum[IA];
         IA=IA -(1-IA%2);
        }
      else if((Trend=="Down") && (IA%2==1) || ((IA-IB==1) && (IB%2==1)))
        {
         VA=Minimum[IA];
         IA=IA -(1-IA%2);
        }
      VB=ValueVertex[IB];
     }
   else
     {
      VA = ValueVertex[IA];
      VB = ValueVertex[IB];
     }
   if(A>B)
     {
      A = IA;
      B = IB;
     }
   else if(A<B)
     {
      A = IB;
      B = IA;
      VC = VA;
      VA = VB;
      VB = VC;
     }
   if(((FixedVertex[A]==1) && (FixedVertex[B]==1)) || 
      ((FixedVertex[A] == 0) &&(A % 2 == 0) && (FixedVertex[B] == 1)) ||
      ((FixedVertex[A] == 1) && (FixedVertex[B] == 0) && (B %2 == 1)) ||
      ((FixedVertex[A] == 0) & (A %2 == 0) && (FixedVertex[B] == 0) && (B % 2== 1)))
     {
      if(((Trend=="Up") && (VA>=VB)) || ((Trend=="Down") && (VA<=VB)))
         Result=1;
      else
         Result=-1;
     }
   return(Result);
  }

6.12. Функция WaveAMoreWaveB:

//+-----------------------------------------------------------------------+
//| Функция WaveAMoreWaveB проверяет, является ли волна A больше волны B, |
//| передаваемые в качестве параметров данной функции                     |
//| данную проверку можно совершить только если волна A - завершенная,    |
//| а волна B - завершенная или незавершенная или неначатая               |
//+-----------------------------------------------------------------------+
int WaveAMoreWaveB(int A,int B)
  {
   int Result=0;
   double LengthWaveA=0,LengthWaveB=0;
   if(FixedVertex[A]==1 && FixedVertex[A-1]==1 && (FixedVertex[B]==1 || FixedVertex[B-1]==1))
     {
      LengthWaveA=MathAbs(ValueVertex[A]-ValueVertex[A-1]);
      if(FixedVertex[B]==1 && FixedVertex[B-1]==1) LengthWaveB=MathAbs(ValueVertex[B]-ValueVertex[B-1]);
      else if(FixedVertex[B]==1 && FixedVertex[B-1]==0)
        {
         if(Trend=="Up") LengthWaveB=MathAbs(ValueVertex[B]-Minimum[B]);
         else LengthWaveB=MathAbs(ValueVertex[B]-Maximum[B]);
        }
      else if(FixedVertex[B]==0 && FixedVertex[B-1]==1)
        {
         if(Trend=="Up")LengthWaveB=MathAbs(ValueVertex[B-1]-Minimum[B-1]);
         else LengthWaveB=MathAbs(ValueVertex[B-1]-Maximum[B-1]);
        }
      if(LengthWaveA>LengthWaveB) Result=1;
      else Result=-1;
     }
   return(Result);
  }

6.13. Функция ClearTree:

//+------------------------------------------------------------------+
//| Функция очистки дерева волн с верхним узлом Node                 |
//+------------------------------------------------------------------+
void ClearTree(TNode *Node)
  {
   if(CheckPointer(Node)!=POINTER_INVALID)
     {
      for(int i=0; i<Node.Child.Total();i++)
         ClearTree(Node.Child.At(i));
      delete Node.Child;
      if(CheckPointer(Node.Wave)!=POINTER_INVALID)delete Node.Wave;
      delete Node;
     }
  }

 6.14. Функция ClearNodeInfoArray:

//+------------------------------------------------------------------+
//| Функция очистки массива NodeInfoArray                            |
//+------------------------------------------------------------------+
void ClearNodeInfoArray()
  {
   for(int i=NodeInfoArray.Total()-1; i>=0;i--)
     {
      TNodeInfo *NodeInfo=NodeInfoArray.At(i);
      if(CheckPointer(NodeInfo.Node)!=POINTER_INVALID)delete NodeInfo.Node;
      delete NodeInfo;
     }
   NodeInfoArray.Clear();
  }

6.15. Функция ClearZigzagArray:

//+------------------------------------------------------------------+
//| Функция очистки массива ZigzagArray                              |
//+------------------------------------------------------------------+
void ClearZigzagArray()
  {
   for(int i=0;i<ZigzagArray.Total();i++)
     {
      TZigzag *Zigzag=ZigzagArray.At(i);
      delete Zigzag.IndexVertex;
      delete Zigzag.ValueVertex;
      delete Zigzag;
     }
   ZigzagArray.Clear();
  }

 6.16. Функция FillLabelArray:

CArrayObj *LabelArray[];
int LevelMax=0;
//+------------------------------------------------------------------+
//| Функция FillLabelArray                                           |
//+------------------------------------------------------------------+
void FillLabelArray(TNode *Node)
  {
   if(Node.Child.Total()>0)
     {
      // выделяем первый узел
      TNode *ChildNode=Node.Child.At(0);
      // получаем структуру, в которой хранится информация о волне
      TWave *Wave=ChildNode.Wave;
      string Text;
      // если есть первая вершина
      if(Wave.ValueVertex[1]>0)
        {
         // маркируем вершину в зависимости от волны
         if(Wave.Name=="Импульс" || Wave.Name=="Клин" || Wave.Name=="Диагональ")
            Text="1";
         else if(Wave.Name=="Зигзаг" || Wave.Name=="Плоскость" || Wave.Name=="Расходящийся треугольник" || 
            Wave.Name=="Сходящийся треугольник")
            Text="A";
         else if(Wave.Name=="Двойной зигзаг" || Wave.Name=="Двойная тройка" || 
            Wave.Name=="Тройной зигзаг" || Wave.Name=="Тройная тройка")
            Text="W";
         // получаем массив вершин ArrayObj, которые имеют индекс Wave.IndexVertex[1] на графике цены 
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[1]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[1]]=ArrayObj;
           }
         // записываем информацию о вершине с индексом Wave.IndexVertex[1] в массива ArrayObj
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[1];
         ArrayObj.Add(Label);
        }
      if(Wave.ValueVertex[2]>0)
        {
         if(Wave.Name=="Импульс" || Wave.Name=="Клин" || Wave.Name=="Диагональ")
            Text="2";
         else if(Wave.Name=="Зигзаг" || Wave.Name=="Плоскость" || Wave.Name=="Расходящийся треугольник" || 
            Wave.Name=="Сходящийся треугольник")
            Text="B";
         else if(Wave.Name=="Двойной зигзаг" || Wave.Name=="Двойная тройка" || 
            Wave.Name=="Тройной зигзаг" || Wave.Name=="Тройная тройка")
            Text="X";
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[2]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[2]]=ArrayObj;
           }
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[2];
         ArrayObj.Add(Label);
        }
      if(Wave.ValueVertex[3]>0)
        {
         if(Wave.Name=="Импульс" || Wave.Name=="Клин" || Wave.Name=="Диагональ")
            Text="3";
         else if(Wave.Name=="Зигзаг" || Wave.Name=="Плоскость" || 
            Wave.Name=="Расходящийся треугольник" || Wave.Name=="Сходящийся треугольник")
            Text="C";
         else if(Wave.Name=="Двойной зигзаг" || Wave.Name=="Двойная тройка" || 
            Wave.Name=="Тройной зигзаг" || Wave.Name=="Тройная тройка")
            Text="Y";
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[3]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[3]]=ArrayObj;
           }
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[3];
         ArrayObj.Add(Label);
        }
      if(Wave.ValueVertex[4]>0)
        {
         if(Wave.Name=="Импульс" || Wave.Name=="Клин" || Wave.Name=="Диагональ")
            Text="4";
         else if(Wave.Name=="Расходящийся треугольник" || Wave.Name=="Сходящийся треугольник")
            Text="D";
         else if(Wave.Name=="Тройной зигзаг" || Wave.Name=="Тройная тройка")
            Text="XX";
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[4]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[4]]=ArrayObj;
           }
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[4];
         ArrayObj.Add(Label);
        }
      if(Wave.ValueVertex[5]>0)
        {
         if(Wave.Name=="Импульс" || Wave.Name=="Клин" || Wave.Name=="Диагональ")
            Text="5";
         else if(Wave.Name=="Расходящийся треугольник" || Wave.Name=="Сходящийся треугольник")
            Text="E";
         else if(Wave.Name=="Тройной зигзаг" || Wave.Name=="Тройная тройка")
            Text="Z";
         CArrayObj *ArrayObj=LabelArray[Wave.IndexVertex[5]];
         if(CheckPointer(ArrayObj)==POINTER_INVALID)
           {
            ArrayObj=new CArrayObj;
            LabelArray[Wave.IndexVertex[5]]=ArrayObj;
           }
         TLabel *Label=new TLabel;
         Label.Text=Text;
         Label.Level=Wave.Level;
         if(Wave.Level>LevelMax)LevelMax=Wave.Level;
         Label.Value=Wave.ValueVertex[5];
         ArrayObj.Add(Label);
        }
      // посещаем дочерние узлы текущего узла
      for(int j=0;j<ChildNode.Child.Total();j++)
         FillLabelArray(ChildNode.Child.At(j));
     }
  }

 6.17. Функция CreateLabels:

double PriceInPixels;
CArrayObj ObjTextArray; // объявляем массив, в котором будут храниться графические объекты "Текст"
//+------------------------------------------------------------------+
//| Функция CreateLabels                                             |
//+------------------------------------------------------------------+
void CreateLabels()
  {
   double PriceMax =ChartGetDouble(0,CHART_PRICE_MAX,0);
   double PriceMin = ChartGetDouble(0,CHART_PRICE_MIN);
   int WindowHeight=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
   PriceInPixels=(PriceMax-PriceMin)/WindowHeight;
   int n=0;
   // перебираем массив LabelArray
   for(int i=0;i<ArrayRange(LabelArray,0);i++)
     {
      // если имеются вершины с одинаковым индексом i
      if(CheckPointer(LabelArray[i])!=POINTER_INVALID)
        {
         // получаем вершины с одинаковым индексом i
         CArrayObj *ArrayObj=LabelArray[i];
         // перебираем вершины и отображаем на графике
         for(int j=ArrayObj.Total()-1;j>=0;j--)
           {
            TLabel *Label=ArrayObj.At(j);
            int Level=LevelMax-Label.Level;
            string Text=Label.Text;
            double Value=Label.Value;
            color Color;
            int Size=8;
            if((Level/3)%2==0)
              {
               if(Text=="1") Text="i";
               else if(Text == "2") Text = "ii";
               else if(Text == "3") Text = "iii";
               else if(Text == "4") Text = "iv";
               else if(Text == "5") Text = "v";
               else if(Text == "A") Text = "a";
               else if(Text == "B") Text = "b";
               else if(Text == "C") Text = "c";
               else if(Text == "D") Text = "d";
               else if(Text == "E") Text = "e";
               else if(Text == "W") Text = "w";
               else if(Text=="X") Text="x";
               else if(Text == "XX") Text = "xx";
               else if(Text == "Y") Text = "y";
               else if(Text == "Z") Text = "z";
              }
            if(Level%3==2)
              {
               Color=Green;
               Text="["+Text+"]";
              }
            if(Level%3==1)
              {
               Color=Blue;
               Text="("+Text+")";
              }
            if(Level%3==0)
               Color=Red;
            int Anchor;
            if(Value==rates[i].high)
              {
               for(int k=ArrayObj.Total()-j-1;k>=0;k--)
                  Value=Value+15*PriceInPixels;
               Anchor=ANCHOR_UPPER;
              }
            else if(Value==rates[i].low)
              {
               for(int k=ArrayObj.Total()-j-1;k>=0;k--)
                  Value=Value-15*PriceInPixels;
               Anchor=ANCHOR_LOWER;
              }
            CChartObjectText *ObjText=new CChartObjectText;
            ObjText.Create(0,"wave"+IntegerToString(n),0,rates[i].time,Value);
            ObjText.Description(Text);
            ObjText.Color(Color);
            ObjText.SetInteger(OBJPROP_ANCHOR,Anchor);
            ObjText.FontSize(8);
            ObjText.Selectable(true);
            ObjTextArray.Add(ObjText);
            n++;
           }
        }
     }
   ChartRedraw();
  }

 6.18. Функция CorrectLabel:

//+------------------------------------------------------------------+
//| Функция CorrectLabel                                             |
//+------------------------------------------------------------------+
void CorrectLabel()
  {
   double PriceMax=ChartGetDouble(0,CHART_PRICE_MAX,0);
   double PriceMin = ChartGetDouble(0,CHART_PRICE_MIN);
   int WindowHeight=ChartGetInteger(0,CHART_HEIGHT_IN_PIXELS);
   double CurrentPriceInPixels=(PriceMax-PriceMin)/WindowHeight;
   // перебираем все текстовые объекты (вершины волн) и изменяем их величину цены
   for(int i=0;i<ObjTextArray.Total();i++)
     {
      CChartObjectText *ObjText=ObjTextArray.At(i);
      double PriceValue=ObjText.Price(0);
      datetime PriceTime=ObjText.Time(0);
      int j;
      for(j=0;j<ArrayRange(rates,0);j++)
        {
         if(rates[j].time==PriceTime)
            break;
        }
      double OffsetInPixels;
      if(rates[j].low>=PriceValue)
        {
         OffsetInPixels=(rates[j].low-PriceValue)/PriceInPixels;
         ObjText.Price(0,rates[j].low-OffsetInPixels*CurrentPriceInPixels);
        }
      else if(rates[j].high<=PriceValue)
        {
         OffsetInPixels=(PriceValue-rates[j].high)/PriceInPixels;
         ObjText.Price(0,rates[j].high+OffsetInPixels*CurrentPriceInPixels);
        }
     }
   PriceInPixels=CurrentPriceInPixels;
  }


7. Функции инициализации, деинициализации и обработки событий

В функции OnInit происходит создание кнопок управления автоматического анализатора волн Эллиотта.

Создаются кнопки:

  1. "Начать анализ" - происходит автоматический анализ волн,
  2. "Показать результаты" - происходит вывод меток волн на график,
  3. "Очистить график" - происходит очистка памяти и удаление меток волн с графика,
  4. "Скорректировать метки" - происходит корректировка меток волн на графике.

Обработка нажатия на эти кнопки происходит в функции обработки событий OnChartEvent.

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

#include <Object.mqh>
#include <Arrays\List.mqh>
#include <Arrays\ArrayObj.mqh>
#include <Arrays\ArrayInt.mqh>
#include <Arrays\ArrayDouble.mqh>
#include <Arrays\ArrayString.mqh>
#include <ChartObjects\ChartObjectsTxtControls.mqh>
#include <Elliott wave\Data structures.mqh>
#include <Elliott wave\Analysis functions.mqh>
#include <Elliott wave\Rules functions.mqh>
CChartObjectButton *ButtonStart,*ButtonShow,*ButtonClear,*ButtonCorrect;
int State;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   State=0;
   //создаем кнопки управления
   ButtonStart=new CChartObjectButton;
   ButtonStart.Create(0,"Начать анализ",0,0,0,150,20);
   ButtonStart.Description("Начать анализ");
   ButtonShow=new CChartObjectButton;
   ButtonShow.Create(0,"Показать результаты",0,150,0,150,20);
   ButtonShow.Description("Показать результаты");
   ButtonClear=new CChartObjectButton;
   ButtonClear.Create(0,"Очистить график",0,300,0,150,20);
   ButtonClear.Description("Очистить график");
   ButtonCorrect=new CChartObjectButton;
   ButtonCorrect.Create(0,"Скорректировать метки",0,450,0,150,20);
   ButtonCorrect.Description("Скорректировать метки");
   ChartRedraw();
   return(0);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   //очищаем дерево волн
   ClearTree(FirstNode);
   //очищаем массив NodeInfoArray
   ClearNodeInfoArray();
   //очищаем массив ZigzagArray
   ClearZigzagArray();
   //очищаем массив LabelArray
   for(int i=0;i<ArrayRange(LabelArray,0);i++)
     {
      CArrayObj *ArrayObj=LabelArray[i];
      if(CheckPointer(ArrayObj)!=POINTER_INVALID)
        {
         for(int j=0;j<ArrayObj.Total();j++)
           {
            TLabel *Label=ArrayObj.At(j);
            delete Label;
           }
         ArrayObj.Clear();
         delete ArrayObj;
        }
     }
   //удаляем все графические элементы с графика
   for(int i=ObjTextArray.Total()-1;i>=0;i--)
     {
      CChartObjectText *ObjText=ObjTextArray.At(i);
      delete ObjText;
     }
   ObjTextArray.Clear();
   delete ButtonStart;
   delete ButtonShow;
   delete ButtonClear;
   delete ButtonCorrect;
   ChartRedraw();
  }
MqlRates rates[];
TNode *FirstNode;
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
  {
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Начать анализ" && State!=0)
      MessageBox("Сначала нажмите кнопку \"Очистить график\"");
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Показать результаты" && State!=1)
      MessageBox("Сначала нажмите кнопку \"Начать анализ\"");
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Очистить график" && State!=2)
      MessageBox("Сначала нажмите кнопку \"Показать результаты\"");
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Скорректировать метки" && State!=2)
      MessageBox("Сначала нажмите кнопку \"Показать результаты\"");
   //если нажата кнопка "Начать анализ"
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Начать анализ" && State==0)
     {
      //заполняем массив rates
      CopyRates(NULL,0,0,Bars(_Symbol,_Period),rates);
      //заполняем массив ZigzagArray
      FillZigzagArray(0,Bars(_Symbol,_Period)-1);
      //создаем первый узел
      TWave *Wave=new TWave;
      Wave.IndexVertex[0] = 0;
      Wave.IndexVertex[1] = Bars(_Symbol,_Period)-1;
      Wave.ValueVertex[0] = 0;
      Wave.ValueVertex[1] = 0;
      FirstNode=new TNode;
      FirstNode.Child=new CArrayObj;
      FirstNode.Wave=Wave;
      FirstNode.Text="Первый узел";
      string NameWaves="Импульс,Клин,Диагональ,Зигзаг,Плоскость,Двойной зигзаг,Тройной зигзаг,Двойная тройка,Тройная тройка,Сходящийся треугольник,Расходящийся треугольник,";
      //вызываем функцию поиска неначатых и незавершенных волн
      NotStartedAndNotFinishedWaves(Wave,1,FirstNode,NameWaves,0);
      MessageBox("Анализ завершен");
      State=1;
      ButtonStart.State(false);
      ChartRedraw();
     }
   //если нажата кнопка "Показать результаты"
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Показать результаты" && State==1)
     {
      ArrayResize(LabelArray,ArrayRange(rates,0));
      //заполняем массив LabelArray
      FillLabelArray(FirstNode);
      //показываем маркировку волн на графике
      CreateLabels();
      State=2;
      ButtonShow.State(false);
      ChartRedraw();
     }
   //если нажата кнопка "Очистить график"
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Очистить график" && State==2)
     {
      //очищаем дерево волн
      ClearTree(FirstNode);
      //очищаем массив NodeInfoArray
      ClearNodeInfoArray();
      //очищаем массив ZigzagArray
      ClearZigzagArray();
      //очищаем массив LabelArray
      for(int i=0;i<ArrayRange(LabelArray,0);i++)
        {
         CArrayObj *ArrayObj=LabelArray[i];
         if(CheckPointer(ArrayObj)!=POINTER_INVALID)
           {
            for(int j=0;j<ArrayObj.Total();j++)
              {
               TLabel *Label=ArrayObj.At(j);
               delete Label;
              }
            ArrayObj.Clear();
            delete ArrayObj;
           }
        }
      //удаляем разметку с графика
      for(int i=ObjTextArray.Total()-1;i>=0;i--)
        {
         CChartObjectText *ObjText=ObjTextArray.At(i);
         ObjText.Delete();
        }
      ObjTextArray.Clear();
      State=0;
      ButtonClear.State(false);
      ChartRedraw();
     }
   if(id==CHARTEVENT_OBJECT_CLICK && sparam=="Скорректировать метки" && State==2)
     {
      CorrectLabel();
      ButtonCorrect.State(false);
      ChartRedraw();
     }
  }

 На этом мы рассмотрели все функции автоматического анализатора волн Эллиотта.

 

8. Пути совершенствования программы

Написанная на MQL5 программа автоматической разметки волн Эллиотта обладает рядом недостатков:

  1. Несовершенная система проверки правил разметки. Так например, при проверке на правила не учитываются соотношения Фибоначчи между волнами, как временные, так и ценовые.
  2. Наличие неразмеченных областей на графике (пробелов в разметке). Это значит, что по найденным точкам на заданном временном интервале невозможно построить правильную волну. Выходом из данной ситуации является увеличение количества точек для определения какой-либо волны. Например, для нахождения импульса искать не 6, а 8 точек или более.
  3. По результатам разметки не выводится какая-либо дополнительная информация, например, автоматически не строятся каналы, не вычисляются цели и др.
  4. Реализация работы с деревом волн в статье не предусмотрена (нельзя выбрать конкретный вариант разметки), поэтому на график выводится только один из множества вариантов разметки (самый первый вариант разметки).
  5. Вне зависимости от того, что на график выводится только один вариант волн, все остальные варианты находятся в памяти и занимают ее.
  6. Программа ориентирована на разметку графиков от месячных до дневных, т.к. наблюдается очень медленная работа при большом количестве баров (могут уйти часы для разметки часового графика). Пример разметки месячного графика EURUSD приведен на рисунке 18.

Разметка волн автоматическим анализатором на MQL5
Рисунок 18. Разметка волн автоматическим анализатором на MQL5

Заключение

В данной статье был рассмотрен алгоритм автоматического анализа волн Эллиотта. Данный алгоритм был реализован на языке MQL5.

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


Прикрепленные файлы |
elliott_wave.zip (151 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (43)
Alex
Alex | 28 июл. 2016 в 07:31

Перестал работать:

Пишет предупреждение о рекомпиляции. В компиле ошибка мне неизвестная. Посмотрите пожалуйста знающие участники. Спасибо. 

Журнал: 2016.07.28 11:27:17.858 MQL5 'Elliott wave.ex5' version is too old, it should be recompiled

При компиле: 'NodeInfo' - conversion is not accessible because of inheritance access Elliott wave.mq5 658 22

PinkPanteRus
PinkPanteRus | 10 июл. 2018 в 06:50

Добрый день!

Почему-то не могу скомпилировать.



abpavel
abpavel | 20 февр. 2019 в 18:46
Проблема с установкой есть. Если сначала закинуть в Includ без папки то все успешно компилируется но на чарт не ставится ни в какую. Если закинуть в Includ в месте с папкой из архива - при компиляции начинает ругаться ошибками.
oivantcov
oivantcov | 8 мар. 2024 в 14:13

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

В функции WaveRules на этапе проверки "Расходящихся треугольников" есть следующая строка:

WaveAMoreWaveB(3,2)>=0 && WaveAMoreWaveB(3,2)>=0

Очевидно, что здесь есть ошибка, т.к. я не могу представить ситуации, когда одно и то же надо проверять 2 раза подряд. Что хотел сказать автор второй проверкой WaveAMoreWaveB(3,2)?
Возможно, нужно сделать проверку лишь один раз, а возможно, во второй проверке нужно будет подставить какие-то другие числа?

Заранее благодарю за ответ.

oivantcov
oivantcov | 8 мар. 2024 в 14:19

Ещё прошу заметить, что в функции WaveRules массивы High и Low ни разу не использовались. Скорее всего, они не нужны. Скажите, если я не прав.

double High[],Low[];
   ArrayResize(High,ArrayRange(rates,0));
   ArrayResize(Low,ArrayRange(rates,0));
Использование ресурсов в MQL5 Использование ресурсов в MQL5
Программы на MQL5 позволяют не только автоматизировать рутинные вычисления, но и создавать полноценную графическую оболочку. Возможности по созданию по-настоящему интерактивных элементов управления стали практически такими же широкими, как и в классических языках программирования. Если вы хотите писать полноценные самостоятельные программы на MQL5, используйте в них ресурсы. Такие программы легче поддерживать и распространять.
Фильтрация сигналов на основе статистических данных о корреляции цен Фильтрация сигналов на основе статистических данных о корреляции цен
Есть ли связь между поведением цены в прошлом и будущими трендами? Почему сегодня цена повторяет характер движения вчерашнего дня? Возможно ли использовать статистические данные как метод прогнозирования динамики цен? Ответ есть, и он положительный. Если вы ещё сомневаетесь, эта статья для вас. Я расскажу о том, как создать рабочий фильтр для торговой системы на MQL5, обнаружив интересную закономерность в изменении цен.
Статистические оценки Статистические оценки
Оценка статистических параметров последовательности очень важна, так как большинство математических моделей и методов строятся исходя из различного рода предположений, например, о нормальности закона распределения, или требуют знания значения дисперсии или других параметров. В статье кратко рассматриваются простейшие статистические параметры случайной последовательности и некоторые методы ее визуального анализа. Предлагается реализация этих методов на MQL5 и способ визуализации результатов расчета при помощи программы Gnuplot.
Электронные таблицы на MQL5 Электронные таблицы на MQL5
В статье описан класс двумерного динамического массива, имеющий в своем составе разнотипные данные первого измерения. Хранение данных в виде таблиц удобно при решении большого класса задач по упорядочиванию, хранению и оперированию связными данными разных типов. К статье приложен код класса, реализующего функционал работы с таблицами.