English Deutsch 日本語
preview
Знакомство с MQL5 (Часть 19): Автоматизация обнаружения волн Вульфа

Знакомство с MQL5 (Часть 19): Автоматизация обнаружения волн Вульфа

MetaTrader 5Торговые системы |
146 0
Israel Pelumi Abioye
Israel Pelumi Abioye

Введение

И снова приветствуем вас в Части 19 серии "Знакомство с языком MQL5"! В Части 18 я познакомил вас с паттерном волн Вульфа – уникальной пятиволновой структурой, используемой для точного предсказания разворотов цен. Мы обсудили два основных типа волн Вульфа: медвежий сетап, который сигнализирует о потенциальном падении цены, и бычий сетап, который намекает на предстоящий рост. Вы также узнали, как извлекать свечные данные, что является необходимым для анализа ценового действия, и мы изложили логику для выявления действительных торговых возможностей на основе этого паттерна.

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

В этой статье вы узнаете:

  • Как обнаружить классическую пятиволновую структуру волн Вульфа, используя исторические ценовые данные.
  • Программное выявление максимумов и минимумы свинга для определения волновых точек.
  • Как применять и изменять уровни расширения Фибоначчи для валидации волновых формаций, особенно для волн 3 и 5.
  • Как провести три важные трендовые линии (по волнам 1-3, 2-4 и 1-4), которые критически важны для подтверждения паттерна и торговых решений.
  • Как проверить симметрию и пропорциональные отношения между волнами 1-2 и волнами 3-4, чтобы улучшить надежность паттерна.
  • Как размечать волны и отрисовывать визуальные объекты, такие как текст, прямоугольники и трендовые линии, непосредственно на графике с помощью языка MQL5.
  • Реализация логики, которая ждет подтверждения перед размещением сделок, избегая ложных входов.
  • Как программировать автоматические выходы из сделок, когда рынок касается трендовой линии между волнами 1 и 4, что помогает в управлении рисками.


Выявление медвежьих паттернов волн Вульфа

В последней статье мы уже подробно обсудили структуру и правила медвежьего паттерна волн Вульфа. Теперь в этом разделе мы сосредоточимся на том, как реализовать эту логику программно. Как уже упоминалось, медвежья волна Вульфа состоит из пяти волн, которые должны соответствовать определенной последовательности и определенным структурным требованиям. Волна 2 должна быть минимумом свинга, который находится ниже волны 1, а волна 1 должна быть максимумом свинга. Новый максимум свинга, который этот раз выше волны 1 и находится внутри определенного расширения Фибоначчи от отрезков волны 1 и волны 2, формируется волной 3. Затем распознается волна 4 – минимум свинга, который падает ниже волны 3, но остается выше волны 2. Волна 5 завершает паттерн, устанавливая максимум свинга выше волны 3 и падая внутри заранее определенного расширения Фибоначчи от движений в рамках волны 3 и волны 4.

Важно помнить, что отрезки волн 1 и 2 должны быть сопоставимы по размеру с отрезками волн 3 и 4. Волны 3-4 должны составлять минимум 70% от длины волн 1-2. Структура становится надежной благодаря этой симметрии, которая служит еще одним подтверждением действительности паттерна. В этом разделе мы определим пять точек с помощью функций обнаружения свинга и добавим проверки, чтобы убедиться, что расстояния и связи между ними соответствуют требованиям волн Вульфа.

Выявление волн 1 и 2

Точное распознавание волн 1 и 2 является первым шагом в выявлении медвежьего паттерна волн Вульфа. Без этих двух основ точно определить остальные волны будет сложно. В этой части мы сосредоточимся на поиске действительных максимумов и минимумов свинга на графике, которые соответствуют критериям волны 1 и волны 2. Как только эти две волны будут обнаружены, мы явно обозначим их на графике для наглядности.

По волнам 1 и 2 мы также построим объект расширения Фибоначчи. Устанавливая соответствующие уровни цен на основе правил паттерна, это расширение поможет в управлении обнаружением волн 3 и 5. Внедряя этот процесс на раннем этапе, мы создаем прочную основу для оставшейся части процесса идентификации волн.

Пример:
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;
int bars_check  = 500;
datetime time_bar;
double total_symbol_bars;

double open[];
double close[];
double low[];
double high[];
datetime time[];

double wv1;
datetime wv1_time;
string   wv1_txt;

double wv2;
datetime wv2_time;
string   wv2_txt;

string fib_ext_wv1_wv2;

ulong chart_id = ChartID();

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {

   ObjectsDeleteAll(chart_id);

  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   total_symbol_bars = Bars(_Symbol, timeframe);
   time_bar = iTime(_Symbol,timeframe,0);

   CopyOpen(_Symbol, timeframe, time_bar, bars_check, open);
   CopyClose(_Symbol, timeframe, time_bar, bars_check, close);
   CopyLow(_Symbol, timeframe, time_bar, bars_check, low);
   CopyHigh(_Symbol, timeframe, time_bar, bars_check, high);
   CopyTime(_Symbol, timeframe, time_bar, bars_check, time);

   if(total_symbol_bars >= bars_check)
     {
      for(int i = 7; i < bars_check - 7; i++)
        {
         if(IsSwingHigh(high, i, 7))
           {
            wv1 = high[i];
            wv1_time = time[i];
            wv1_txt = StringFormat("WAVE 1 %d", i);

            for(int j = i; j < bars_check - 7; j++)
              {
               if(IsSwingLow(low, j, 7) && low[j] < wv1)
                 {
                  wv2 = low[j];
                  wv2_time = time[j];
                  wv2_txt = StringFormat("WAVE 2 %d", j);

                  ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1);
                  ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
                  ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

                  ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2);
                  ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
                  ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

                  fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);
                  ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2);
                  ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

                  for(int i = 0; i <= 2; i++)
                    {
                     ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);
                    }

                  break;
                 }
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING LOW                                           |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low_price[], int index, int lookback)
  {
   for(int i = 1; i <= lookback; i++)
     {
      if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i])
         return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING HIGH                                          |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high_price[], int index, int lookback)
  {
   for(int i = 1; i <= lookback; i++)
     {
      if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i])
         return false;
     }
   return true;
  }

Вывод:

Figure 1. Wave 1 and 2

Пояснение:

Для сохранения ключевой информации о первых двух волнах, и чтобы эффективно выявлять и описывать на графике такие паттерны, как медвежий паттерн волн Вульфа, объявляются определенные переменные. Для волны 1 добавлены три переменные: wv1 для хранения уровня цены, wv1_time для указания точного момента, когда эта цена была достигнута, и wv1_txt, используемая в качестве текстовой метки для графического представления волны на графике. Переменные wv2, wv2_time и wv2_txt выполняют ту же роль для волны 2. 

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

Строковая переменная с именем fib_ext_wv1_wv2 объявляется для хранения имени объекта расширения Фибоначчи в дополнение к данным волны. Эта переменная гарантирует, что элемент, представляющий расширение между волнами 1 и 2, может быть корректно сгенерирован, изменен или удален при необходимости, поскольку каждый объект на графике должен иметь уникальный идентификатор. Чтобы сделать каждый экземпляр уникальным, в имя обычно включается индекс волны, особенно если советник создает несколько паттернов, просматривая прошлые бары.

Также код использует функцию ChartID() для получения уникального идентификатора текущего графика и сохраняет его в переменной chart_id для поддержки всех операций создания объектов. Это имеет большое значение, поскольку MetaTrader 5 позволяет одновременно открывать несколько графиков. Чтобы предотвратить конфликты или неправильное размещение, приложение гарантирует, что графические элементы на соответствующем графике будут создаваться и изменяться с явной ссылкой на соответствующий идентификатор графика.

Строка ObjectsDeleteAll(chart_id); в функции OnDeinit() вызывает функцию очистки. Эта функция удаляет с графика все объекты, ранее нарисованные советником, при удалении советника или закрытии графика. Это предотвращает загромождение и гарантирует, что после завершения работы советника устаревшие объекты рисования, такие как метки волн или линии Фибоначчи, не останутся на графике.

Перед запуском логики обнаружения паттернов советник проводит критически важную проверку, чтобы убедиться, что доступно достаточно баров. Он сравнивает количество баров, которые он планирует просканировать (bars_check), и общее количество баров на графике (total_symbol_bars). После подтверждения того, что на дисплее загружено достаточно исторических баров (if(total_symbol_bars >= bars_check)), программа начинает сканировать ценовые данные, чтобы найти ранние признаки медвежьей волны Вульфа.

Первым шагом в ее обнаружении является цикл for:

for(int i = 7; i < bars_check - 7; i++)

Для безопасной проверки максимумов и минимумов свинга, цикл начинается с индекса 7, а не с 0. Чтобы определить, действительно ли цена является точкой свинга, функции обнаружения свинга (описанные позже) проверяют семь баров вперед и семь баров назад. Если бы цикл начинался с индекса 0, это привело бы к ошибке выхода за пределы, и не было бы найдено никаких предыдущих баров для сравнения. В аналогичном ключе, остановка цикла на bars_check-7 гарантирует наличие достаточного количества последующих баров для сравнения. 

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

if(IsSwingHigh(high, i, 7))

Если функция возвращает true, указывая на то, что это действительный максимум свинга, советник рассматривает эту свечу как потенциального кандидата на волну 1. Создается строковая метка (wv1_txt) для последующего использования на графике, а связанные с ней цена максимума и время сохраняются в переменные wv1 и wv1_time. 

Как только будет найдена действительная волна 1 (максимум свинга), код инициирует внутренний цикл, чтобы заглянуть вперед во времени в поисках последующего действительного минимума свинга, который будет представлять волну 2:

for(int j = i; j < bars_check - 7; j++)

Этот цикл начинается с того же индекса, что и внешний цикл (i), и продвигается вперед. Он ищет бар, который соответствует двум условиям:

  • Он является минимумом свинга (локальным минимумом).
  • Его цена ниже, чем цена волны 1.
if(IsSwingLow(low, j, 7) && low[j] < wv1)

Когда оба требования волны 2 выполнены, советник записывает цену, время и метку в переменные wv2, wv2_time и wv2_txt, соответственно. Как только волны 1 и 2 будут найдены, применяется оператор break для завершения внутреннего цикла, между волнами строится объект расширения Фибоначчи, и на график добавляются текстовые метки в обеих точках. На этом этапе советник прекращает выявлять подходящую пару для текущей волны 1 и искать дополнительные возможные волны 2.

Выявление волн 3 и 4

Чтобы построить паттерн волн Вульфа, имеет смысл выявлять волны 3 и 4 после корректного выявления волн 1 и 2. Расположение и соотношение волн 1 и 2 оказывают значительное влияние на эти последующие волны. Волна 3, в частности, должна выполнить два основных требования: она должна преодолеть максимум волны 1 и завершаться в пределах заранее определенного диапазона расширения Фибоначчи, который рассчитывается на основе волн 1 и 2.

Движение цены от волны 1 до волны 2 используется для построения расширения Фибоначчи, чтобы выявить этот диапазон. На основе величины и моментума первой волновой структуры этот инструмент помогает прогнозировать возможные будущие ценовые уровни. Диапазон, который, как ожидается, будет достигнут ценой перед разворотом, составляет уровни расширения от 127,2% до 161,8%, что обычно является приемлемой областью для волны 3. Цена считается хорошим кандидатом на волну 3, если она ставит максимум свинга в этом диапазоне.

Волна 3 должна соблюдать симметрию паттерна помимо его ценового уровня. Структура и масштаб волн 1 и 2 должны быть соответствующим образом отражены волнами 3 и 4. Именно это геометрическое равновесие отличает действительные волны Вульфа. Для целей этого исследования мы определим симметрию простым, количественно измеримым способом. В частности, общее движение от волны 1 до волны 2 должно составлять как минимум 70% от размера волн 3 и 4. По мере того как мы продолжаем разрабатывать советник, это процентное соотношение обеспечивает надежную систему для проверки паттернов и способствует сохранению структурной и визуальной целостности волны Вульфа.

Определение 70% расстояния между волной 1 и волной 2

Сначала давайте определим 70% расстояния между волной 1 и волной 2.

Пример:

double wv1_wv2_size;
double wv1_wv2_70p;
if(total_symbol_bars >= bars_check)
  {
   for(int i = 7; i < bars_check - 7; i++)
     {
      if(IsSwingHigh(high, i, 7))
        {
         wv1 = high[i];
         wv1_time = time[i];
         wv1_txt = StringFormat("WAVE 1 %d", i);

         for(int j = i; j < bars_check - 7; j++)
           {
            if(IsSwingLow(low, j, 7) && low[j] < wv1)
              {
               wv2 = low[j];
               wv2_time = time[j];
               wv2_txt = StringFormat("WAVE 2 %d", j);

               ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1);
               ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
               ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

               ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2);
               ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
               ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

               fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);
               ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2);
               ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

               for(int i = 0; i <= 2; i++)
                 {
                  ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);
                 }

               wv1_wv2_size = MathAbs(wv1 - wv2);
               wv1_wv2_70p = (wv1_wv2_size / 100) * 70;

               string luh = StringFormat("bb 2 %d", j);
               ObjectCreate(chart_id, luh, OBJ_TREND, 0, wv1_time, wv1 - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p);

               break;
              }
           }
        }
     }
  }

Вывод:

Figure 2. 70% of the distance between Wave 1 and Wave 2.

Пояснение:

Волна 3 должна соблюдать симметрию паттерна помимо его ценового уровня. Структура и масштаб волн 1 и 2 должны быть соответствующим образом отражены волнами 3 и 4. Именно это геометрическое равновесие отличает действительные волны Вульфа. Для целей этого исследования мы определим симметрию простым, количественно измеримым способом. В частности, общее движение от волны 1 до волны 2 должно составлять как минимум 70% от размера волн 3 и 4. По мере того как мы продолжаем разрабатывать советник, это процентное соотношение обеспечивает надежную систему для проверки паттернов и способствует сохранению структурной и визуальной целостности волны Вульфа.

Чтобы помочь визуализировать этот критерий, на графике проводится горизонтальная трендовая линия от времени волны 1 до времени волны 2 на ценовом уровне 70% ниже максимума волны 1. Эта линия служит в качестве контрольной точки, гарантирующей, что последующие волны соответствуют минимальному размеру.

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

Расширение Фибоначчи для волны 1 и волны 2

Используя функцию IsSwingHigh, алгоритм находит волну 3 как максимум свинга, который следует за волной 2 и находится между 127,2% и 161,8% по расширению Фибоначчи для волн 1 и 2. Волна 3 сохраняется, если соответствует этим критериям. Чтобы убедиться, что мы имеем дело с правильным паттерном, мы рисуем на графике только инструменты Фибоначчи, линию 70% и названия волн. Затем алгоритм ищет волну 4, которая должна быть минимумом свинга после третьей волны.

Пример:

input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT;
input double max_fib_ext_wv12 = 161.8;
input double min_fib_ext_wv12 = 127.2;

int bars_check = 500;
datetime time_bar;
double total_symbol_bars;

double open[];
double close[];
double low[];
double high[];
datetime time[];

double wv1;
datetime wv1_time;
string wv1_txt;

double wv2;
datetime wv2_time;
string wv2_txt;

string fib_ext_wv1_wv2;

ulong chart_id = ChartID();

double wv1_wv2_size;
double wv1_wv2_70p;
string perc_70;
double fib_ext_1_2_161_8;
double fib_ext_1_2_127_2;
string fib_ext_range;

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   ObjectsDeleteAll(chart_id);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   total_symbol_bars = Bars(_Symbol, timeframe);
   time_bar = iTime(_Symbol, timeframe, 0);

   CopyOpen(_Symbol, timeframe, time_bar, bars_check, open);
   CopyClose(_Symbol, timeframe, time_bar, bars_check, close);
   CopyLow(_Symbol, timeframe, time_bar, bars_check, low);
   CopyHigh(_Symbol, timeframe, time_bar, bars_check, high);
   CopyTime(_Symbol, timeframe, time_bar, bars_check, time);

   if(total_symbol_bars >= bars_check)
     {
      for(int i = 7; i < bars_check - 7; i++)
        {
         if(IsSwingHigh(high, i, 7))
           {
            wv1 = high[i];
            wv1_time = time[i];
            wv1_txt = StringFormat("WAVE 1 %d", i);

            for(int j = i; j < bars_check - 7; j++)
              {
               if(IsSwingLow(low, j, 7) && low[j] < wv1)
                 {
                  wv2 = low[j];
                  wv2_time = time[j];
                  wv2_txt = StringFormat("WAVE 2 %d", j);

                  ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1);
                  ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
                  ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

                  ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2);
                  ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
                  ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

                  fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);
                  ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2);
                  ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

                  for(int i = 0; i <= 2; i++)
                    {
                     ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);
                    }

                  wv1_wv2_size = MathAbs(wv1 - wv2);
                  wv1_wv2_70p = (wv1_wv2_size / 100) * 70;

                  perc_70 = StringFormat("70 PERCENT %d", j);
                  ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_time, wv1 - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p);

                  fib_ext_1_2_127_2 = MathAbs((((wv1 - wv2) / 100) * (min_fib_ext_wv12 - 100)) + wv1);
                  fib_ext_1_2_161_8 = MathAbs((((wv1 - wv2) / 100) * (max_fib_ext_wv12 - 100)) + wv1);

                  fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j);
                  ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_time, fib_ext_1_2_161_8, wv2_time, fib_ext_1_2_127_2);

                  break;
                 }
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING LOW                                           |
//+------------------------------------------------------------------+
bool IsSwingLow(const double &low_price[], int index, int lookback)
  {
   for(int i = 1; i <= lookback; i++)
     {
      if(low_price[index] > low_price[index - i] || low_price[index] > low_price[index + i])
         return false;
     }
   return true;
  }

//+------------------------------------------------------------------+
//| FUNCTION FOR SWING HIGH                                          |
//+------------------------------------------------------------------+
bool IsSwingHigh(const double &high_price[], int index, int lookback)
  {
   for(int i = 1; i <= lookback; i++)
     {
      if(high_price[index] < high_price[index - i] || high_price[index] < high_price[index + i])
         return false;
     }
   return true;
  }

Вывод:

Figure 3. Fibonacci Expansion of Wave 1 and Wave 2

Выявление волны 3

Код продвигается дальше от волны 2, чтобы выявить максимум свинга, который находится в пределах 127,2% до 161,8% расширения волн 1 и 2, чтобы идентифицировать волну 3 как медвежью волну Вульфа. Эта зона обозначается как волна 3, если там обнаружен действительный свинг. 

Пример:
double wv3;
datetime wv3_time;
string wv3_txt;
if(total_symbol_bars >= bars_check)
     {
      for(int i = 7; i < bars_check - 7; i++)
        {
         if(IsSwingHigh(high, i, 7))
           {
            wv1 = high[i];
            wv1_time = time[i];
            wv1_txt = StringFormat("WAVE 1 %d", i);

            for(int j = i; j < bars_check - 7; j++)
              {
               if(IsSwingLow(low, j, 7) && low[j] < wv1)
                 {
                  wv2 = low[j];
                  wv2_time = time[j];
                  wv2_txt = StringFormat("WAVE 2 %d", j);

                  wv1_wv2_size = MathAbs(wv1 - wv2);
                  wv1_wv2_70p = (wv1_wv2_size / 100) * 70;

                  fib_ext_1_2_127_2 = MathAbs((((wv1 - wv2) / 100) * (min_fib_ext_wv12 - 100)) + wv1);
                  fib_ext_1_2_161_8 = MathAbs((((wv1 - wv2) / 100) * (max_fib_ext_wv12 - 100)) + wv1);

                  for(int k = j; k < bars_check - 7; k++)
                    {
                     if(IsSwingHigh(high, k, 7) && time[k] > wv2_time)
                       {
                        wv3 = high[k];
                        wv3_time = time[k];
                        wv3_txt = StringFormat("WAVE 3 %d", k);

                        if(wv3 >= fib_ext_1_2_127_2 && wv3 <= fib_ext_1_2_161_8)
                          {
                           ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1);
                           ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
                           ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

                           ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2);
                           ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
                           ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

                           fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);
                           ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2);
                           ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

                           for(int i = 0; i <= 2; i++)
                             {
                              ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);
                             }

                           perc_70 = StringFormat("70 PERCENT %d", j);
                           ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_time, wv1 - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p);

                           fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j);
                           ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_time, fib_ext_1_2_161_8, wv2_time, fib_ext_1_2_127_2);

                           ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_time, wv3);
                           ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3");
                           ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue);
                          }

                        break;
                       }
                    }

                  break;
                 }
              }
           }
        }
     }

Вывод:

Figure 4. Wave 3

Пояснение:

В коде используются три переменные для определения волны 3: wv3 для уровня цены, wv3_time для времени и wv3_txt для метки. Используя функцию IsSwingHigh для нахождения действительных максимумов свинга, которые следуют за волной 2, можно определить волну 3, сканируя свечи, следующие за вершиной волны 2. Ранее все объекты рисовались после обнаружения wv2. Теперь эти объекты будут появляться только тогда, когда wv3 будет находиться в пределах указанного расширения для wv1 и wv2.

Выявление волны 4

Следующим этапом является нахождение волны 4 – минимума свинга, который устанавливается после волны 3. Чтобы подтвердить, что паттерн по-прежнему действителен, мы должны убедиться, что движение цены от волны 3 до волны 4 сохраняет достаточную симметрию в сравнении с движением от волны 1 до волны 2. В этом проекте мы используем простой критерий симметрии, который гласит, что волны 3 и 4 должны составлять как минимум 70% от длины волн 1 и 2. Исключая непропорциональные или мелкие откаты, это требование способствует поддержанию структурного равновесия в паттерне волн Вульфа. В следующей части мы применим это рассуждение на практике, осуществляя поиск действительного минимума свинга после волны 3 и подтверждая, что он соответствует критериям минимального размера.

Пример:
double wv4;
datetime wv4_time;
string wv4_txt;
double wv3_wv4_size;
if(total_symbol_bars >= bars_check)
     {
      for(int i = 7; i < bars_check - 7; i++)
        {
         if(IsSwingHigh(high, i, 7))
           {
            wv1 = high[i];
            wv1_time = time[i];
            wv1_txt = StringFormat("WAVE 1 %d", i);

            for(int j = i; j < bars_check - 7; j++)
              {
               if(IsSwingLow(low, j, 7) && low[j] < wv1)
                 {
                  wv2 = low[j];
                  wv2_time = time[j];
                  wv2_txt = StringFormat("WAVE 2 %d", j);

                  wv1_wv2_size = MathAbs(wv1 - wv2);
                  wv1_wv2_70p = (wv1_wv2_size / 100) * 70;

                  fib_ext_1_2_127_2 = MathAbs((((wv1 - wv2) / 100) * (min_fib_ext_wv12 - 100)) + wv1);
                  fib_ext_1_2_161_8 = MathAbs((((wv1 - wv2) / 100) * (max_fib_ext_wv12 - 100)) + wv1);

                  for(int k = j; k < bars_check - 7; k++)
                    {
                     if(IsSwingHigh(high, k, 7) && time[k] > wv2_time)
                       {
                        wv3 = high[k];
                        wv3_time = time[k];
                        wv3_txt = StringFormat("WAVE 3 %d", k);

                        if(wv3 >= fib_ext_1_2_127_2 && wv3 <= fib_ext_1_2_161_8)
                          {
                           for(int l = k; l < bars_check - 7; l++)
                             {
                              if(IsSwingLow(low, l, 7) && time[l] > wv3_time)
                                {
                                 wv4 = low[l];
                                 wv4_time = time[l];
                                 wv4_txt = StringFormat("WAVE 4 %d", l);

                                 wv3_wv4_size = MathAbs(wv3 - wv4);

                                 if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2 && wv4 < wv3)
                                   {
                                    ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_time, wv1);
                                    ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
                                    ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

                                    ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_time, wv2);
                                    ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
                                    ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

                                    fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);
                                    ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_time, wv2, wv1_time, wv1, wv2_time, wv2);
                                    ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

                                    for(int i = 0; i <= 2; i++)
                                      {
                                       ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);
                                      }

                                    perc_70 = StringFormat("70 PERCENT %d", j);
                                    ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_time, wv1 - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p);

                                    fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j);
                                    ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_time, fib_ext_1_2_161_8, wv2_time, fib_ext_1_2_127_2);

                                    ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_time, wv3);
                                    ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3");
                                    ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue);

                                    ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4);
                                    ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4");
                                    ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue);
                                   }

                                 break;
                                }
                             }
                          }

                        break;
                       }
                    }

                  break;
                 }
              }
           }
        }
     }

Вывод:

Figure 5. Wave 4

Пояснение:

После того как программа выявит wv3, следующим шагом является выявление wv4. Программа найдет расстояние между wv3 и wv4 и сохранит его в wv3_wv4_size. После того как программа обнаружит минимум свинга, она сохранит время бара и цену в переменные wv4_time и wv4. Для поддержания корректной структуры программа обеспечивает, что wv3 и wv4 похожи на wv1 и wv2, а также гарантирует, что волна 4 остается выше волны 2 и ниже волны 3. Программа проверяет, попадает ли wv3 в заданное расширение. Волна 4, которая завершает сетап паттерна с применением логики свингов, фильтров Фибоначчи и критериев симметрии, отмечается на графике после успешного завершения всех проверок.

Figure 6. Wave 3 Highest High

Figure 7. Wave 1 Highest High

Как вы можете видеть на первом изображении (Рисунок 11), волна 3 не является наибольшим максимумом во время перехода от волны 3 к волне 4. Волна 3 должна быть наибольшим пиком перед падением в волну 4, так что в данном случае у нас проблема. Наш механизм обнаружения не смог зафиксировать корректную структуру, поскольку после точки, которую мы обозначили как волна 3, возник более высокий максимум. Второе изображение (Рисунок 12) также показывает, что волна 1 не является наибольшим максимумом в области между волнами 1 и 2.

Это также нарушает правила паттерна, поскольку волна 1 должна быть доминирующим максимумом свинга в этом отрезке. Мы не получим надежные результаты для паттерна волн Вульфа, если наш алгоритм продолжит выбирать фундаментально некорректные волновые точки. Чтобы повысить надежность логики обнаружения, эту проблему необходимо решить.

Теперь мы добавим дополнительный этап проверки для каждой волны, чтобы решить проблемы, продемонстрированные в примерах выше. Чтобы быть уверенными, что установленная волна 3 точно отражает максимум перед снижением, она должна быть являться наибольшим максимумом между вершинами волн 3 и 4. Волна 2 должна быть наименьшим минимумом между волнами 2 и 3, чтобы корректно отражать дно перед следующим восходящим трендом. Волна 1 должна быть наибольшим максимумом между волнами 1 и 2, чтобы ее легко можно было распознать как значимый максимум свинга на этом сегменте паттерна.

Пример:

int wv3_wv4_bars;
int wv3_highest_index;
double wv3_hh;
datetime wv3_hh_t;

int wv2_wv3_bars;
int wv2_lowest_index;
double wv2_ll;
datetime wv2_ll_t;

int wv1_wv2_bars;
int wv1_highest_index;
double wv1_hh;
datetime wv1_hh_t;
for(int l = k; l < bars_check - 7; l++)
  {
   if(IsSwingLow(low, l, 7) && time[l] > wv3_time)
     {
      wv4 = low[l];
      wv4_time = time[l];
      wv4_txt = StringFormat("WAVE 4 %d", l);

      wv3_wv4_bars = Bars(_Symbol, timeframe, wv3_time, wv4_time);
      wv3_highest_index = ArrayMaximum(high, k, wv3_wv4_bars);
      wv3_hh = high[wv3_highest_index];
      wv3_hh_t = time[wv3_highest_index];

      wv2_wv3_bars = Bars(_Symbol, timeframe, wv2_time, wv3_time);
      wv2_lowest_index = ArrayMinimum(low, j, wv2_wv3_bars);
      wv2_ll = low[wv2_lowest_index];
      wv2_ll_t = time[wv2_lowest_index];

      wv1_wv2_bars = Bars(_Symbol, timeframe, wv1_time, wv2_time);
      wv1_highest_index = ArrayMaximum(high, i, wv1_wv2_bars);
      wv1_hh = high[wv1_highest_index];
      wv1_hh_t = time[wv1_highest_index];

      wv1_wv2_size = MathAbs(wv1_hh - wv2_ll);
      wv1_wv2_70p = (wv1_wv2_size / 100) * 70;

      fib_ext_1_2_127_2 = MathAbs((((wv1_hh - wv2_ll) / 100) * (min_fib_ext_wv12 - 100)) + wv1_hh);
      fib_ext_1_2_161_8 = MathAbs((((wv1_hh - wv2_ll) / 100) * (max_fib_ext_wv12 - 100)) + wv1_hh);

      wv3_wv4_size = MathAbs(wv3_hh - wv4);

      if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2_ll && wv4 < wv3_hh && wv3_hh >= fib_ext_1_2_127_2 && wv3_hh <= fib_ext_1_2_161_8)
        {
         ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_hh_t, wv1_hh);
         ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
         ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

         ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_ll_t, wv2_ll);
         ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
         ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

         fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);
         ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_ll_t, wv2_ll, wv1_hh_t, wv1_hh, wv2_ll_t, wv2_ll);
         ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

         for(int i = 0; i <= 2; i++)
            ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);

         perc_70 = StringFormat("70 PERCENT %d", j);
         ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_hh_t, wv1_hh - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p);

         fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j);
         ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_hh_t, fib_ext_1_2_161_8, wv2_ll_t, fib_ext_1_2_127_2);

         ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_hh_t, wv3_hh);
         ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3");
         ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue);

         ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4);
         ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4");
         ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue);
        }
      break;
     }
  }

Вывод:

Figure 8. Highest High

Пояснение:

Мы предлагаем новый набор расчетов для проверки структуры волны Вольфа и уверенности в том, что каждая волновая точка точно отображает самый важный максимум или минимум на соответствующем сегменте. Чтобы выявить экстремальные ценовые точки (наибольший максимум или наименьший минимум) на каждом волновом сегменте, мы сначала объявляем несколько переменных: переменная wv3_wv4_bars содержит количество баров, которые разделяют волны 3 и 4. Затем мы получаем наибольший максимум в этом диапазоне с помощью функции ArrayMaximum, которая проходит по количеству баров wv3_wv4_bars, начиная с индекса k (где находится волна 3). Результат сохраняется в переменной wv3_highest_index. Этот индекс используется для извлечения истинной максимальной цены (wv3_hh) и ее временной метки (wv3_hh_t).

Переменная wv2_wv3_bars охватывает область между волнами 2 и 3, хотя функционирует аналогично. Здесь мы используем функцию ArrayMinimum, которая возвращает индекс минимальной цены, чтобы найти наименьший минимум. Мы извлекаем соответствующую минимальную цену (wv2_ll) и время (wv2_ll_t) после сохранения этого индекса в переменной wv2_lowest_index. Количество баров, разделяющих волны 1 и 2, определяется переменной wv1_wv2_bars. В нашем случае волна 1 должна быть доминирующим пиком; поэтому мы снова используем функцию ArrayMaximum, чтобы получить наибольший максимум в этой области. По полученному индексу, который хранится в wv1_highest_index, мы извлекаем максимальную цену (wv1_hh) и время, которое ему соответствует (wv1_hh_t).

В переменной wv3_wv4_bars фиксируется количество баров между волнами 3 и 4. Чтобы найти наибольший максимум внутри этого диапазона, мы затем используем функцию ArrayMaximum, чтобы пройтись по количеству баров wv3_wv4_bars, начиная с индекса k, который соответствует позиции волны 3. Результат сохраняется в переменной wv3_highest_index. Этот индекс используется для извлечения временной метки (wv3_hh_t) и фактической максимальной цены (wv3_hh). Вводя эти ограничения на программном уровне, мы увеличиваем точность нашего распознавания волн Вульфа и уменьшаем вероятность выявления слабых или некорректных паттернов волн. Эта дополнительная проверка гарантирует соответствие паттерна техническим требованиям и его пригодность для анализа торговли в реальных условиях.

Проверенные максимумы или минимумы теперь будут использоваться для построения объектов на графике вместо волновых точек, которые были выбраны изначально. Для волны 1 мы специально заменим значения wv1_hh_t и wv1_hh значениями wv1_time и wv1. Затем для волны 2 мы заменим значения wv2_ll_t и wv2_ll значениями wv2_time и wv2. Также, для волны 3 мы заменим значения wv3_hh_t и wv3_hh значениями wv3_time и wv3. Внося это изменение, мы можем быть уверены, что ссылаемся на фактические структурные максимумы и минимумы каждого сегмента вместо точек свинга, которые были найдены изначально. Мы значительно увеличиваем точность и надежность визуализации паттерна волн Вульфа, рисуя графические объекты в этих проверенных областях, помогая трейдерам принимать более обоснованные решения на основе более точной структуры паттерна.

Расширение Фибоначчи, элементы графика для волн 1-4 и другие графические элементы будут сгенерированы только после соблюдения нижеследующих критериев:

if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2_ll && wv4 < wv3_hh && wv3_hh >= fib_ext_1_2_127_2 && wv3_hh <= fib_ext_1_2_161_8)

Это условие гарантирует, что выполнены все требования по цене и структуре для действительной медвежьей волны Вульфа. Это подтверждает, что волны 3 и 4 вертикально отделены как минимум на столько же, сколько волны 1 и 2, сохраняя симметрию. Также это условие подтверждает расположение волны 4 выше наименьшего минимума между волнами 2 и 3 и ниже наибольшего максимума между волнами 3 и 4. Кроме того, волна 3 должна находиться в диапазоне расширения Фибоначчи от 127,2% до 200% относительно волн 1 и 2.

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

Выявление волны 5

Следующим этапом в нашей процедуре обнаружения волн Вульфа является нахождение волны 5. Эта волна имеет решающее значение, поскольку она указывает на возможную точку разворота паттерна. Волна 5 является полезным индикатором в техническом анализе, поскольку трейдеры часто ищут ее, чтобы предсказать изменение направления рынка. Однако прежде чем мы сможем корректно ее выявить, сначала мы должны построить три ключевые трендовые линии, которые будут управлять подтверждением волны 5.

Первая трендовая линия будет соединять волны 1 и 3. Эта линия обычно представляет собой верхнюю границу медвежьего паттерна волн Вульфа. Она помогает определить структуру и направление паттерна и служит уровнем сопротивления, в пределах которого цена остается в процессе построения волны 4 и развития волны 5. Вторая трендовая линия соединит волны 2 и 4. Эта линия важна, поскольку уточняет структуру канала и симметрию волны Вульфа. По мере того как цена приближается к точке разворота, эта линия дает общее представление о предполагаемой траектории волны 5.

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

Пример:
string tline_1_3;
string tline_2_4;
string tline_1_4;
if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2_ll && wv4 < wv3_hh && wv3_hh >= fib_ext_1_2_127_2 && wv3_hh <= fib_ext_1_2_161_8)
  {
   ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_hh_t, wv1_hh);
   ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
   ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

   ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_ll_t, wv2_ll);
   ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
   ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

   fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);

   ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_ll_t, wv2_ll, wv1_hh_t, wv1_hh, wv2_ll_t, wv2_ll);
   ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

   for(int i = 0; i <= 2; i++)
     {
      ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);
     }

   perc_70 = StringFormat("70 PERCENT %d", j);
   ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_hh_t, wv1_hh - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p);

   fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j);
   ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_hh_t, fib_ext_1_2_161_8, wv2_ll_t, fib_ext_1_2_127_2);

   ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_hh_t, wv3_hh);
   ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3");
   ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue);

   ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4);
   ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4");
   ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue);

   tline_1_3 = StringFormat("TREND LINE WAVE 1 AND 3 %d", i);
   ObjectCreate(chart_id, tline_1_3, OBJ_TREND, 0, wv1_hh_t, wv1_hh, wv3_hh_t, wv3_hh);
   ObjectSetInteger(chart_id, tline_1_3, OBJPROP_COLOR, clrBlue);

   tline_2_4 = StringFormat("TREND LINE WAVE 2 AND 4 %d", i);
   ObjectCreate(chart_id, tline_2_4, OBJ_TREND, 0, wv2_ll_t, wv2_ll, wv4_time, wv4);
   ObjectSetInteger(chart_id, tline_2_4, OBJPROP_COLOR, clrBlue);

   tline_1_4 = StringFormat("TREND LINE WAVE 1 AND 4 %d", i);
   ObjectCreate(chart_id, tline_1_4, OBJ_TREND, 0, wv1_hh_t, wv1_hh, wv4_time, wv4);
   ObjectSetInteger(chart_id, tline_1_4, OBJPROP_COLOR, clrBlue);
  }

Вывод:


Figure 9. Wave 5

Пояснение:

Целью первоначального объявления строковых переменных tline_1_3, tline_2_4 и tline_1_4 является присвоение трендовым линиям на графике уникальных идентификаторов. В переменной tline_1_3 представлена трендовая линия, соединяющая волны 1 и 3 и являющаяся верхней границей медвежьего паттерна волн Вульфа. Функция ObjectCreate() используется для построения этой линии с использованием подтвержденных максимумом волн 1 и 3, и линия окрашивается в синий цвет для видимости. Вторая трендовая линия, которая соединяет волны 2 и 4, сохраняется в переменной tline_2_4. Эта линия, также оформленная в синем цвете, строится между наименьшим минимумом волны 2 (wv2_ll_t, wv2_ll) и точкой волны 4 (wv4_time, wv4). Третья трендовая линия tline_1_4 соединяет волны 1 и 4.

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

Сохранение симметрии волн Вульфа и предсказание вероятной позиции волны 5 требуют, чтобы трендовая линия между волнами 2 и 4 имела такую же длину и направление, что и линия между волнами 1 и 3. Поскольку цена движется к трендовой линии, которая служит целью для выхода из сделки и указывает, когда завершить транзакцию, она также должна продолжаться за пределы волны 4, чтобы ее можно было соблюсти. Другими словами, все три трендовые линии должны быть соответствующим образом продлены в будущее, чтобы обеспечить точное подтверждение паттерна и подготовку к сделке.

Пример:

tline_1_3 = StringFormat("TREND LINE WAVE 1 AND 3 %d", i);
ObjectCreate(chart_id,tline_1_3,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv3_hh_t,wv3_hh);
ObjectSetInteger(chart_id, tline_1_3, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true);

tline_2_4 = StringFormat("TREND LINE WAVE 2 AND 4 %d", i);
ObjectCreate(chart_id,tline_2_4,OBJ_TREND,0,wv2_ll_t,wv2_ll,wv4_time,wv4);
ObjectSetInteger(chart_id, tline_2_4, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true);

tline_1_4 = StringFormat("TREND LINE WAVE 1 AND 4 %d", i);
ObjectCreate(chart_id,tline_1_4,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv4_time,wv4);
ObjectSetInteger(chart_id, tline_1_4, OBJPROP_COLOR, clrBlue);
ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true);

Figure 10. Trend Lines

Атрибут OBJPROP_RAY_RIGHT гарантирует, что трендовые линии будут продолжаться за пределы своих якорных точек, что также является проблемой. Хотя это может быть полезно для поддержания видимости линии, это не соответствует точному поведению, которое ожидаем при анализе волны Вульфа. Мы не хотим, чтобы трендовые линии продолжались вечно в нашей ситуации. Скорее, мы хотим, чтобы они остановились в определенный момент, особенно когда выполнено определенное условие.

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

Пример:
tline_1_3 = StringFormat("TREND LINE WAVE 1 AND 3 %d", i);
if(ObjectCreate(chart_id,tline_1_3,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv3_hh_t,wv3_hh))
  {
   ObjectSetInteger(chart_id, tline_1_3, OBJPROP_COLOR, clrBlue);
   ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true);
   ObjectSetInteger(chart_id, tline_1_3, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
  }

tline_2_4 = StringFormat("TREND LINE WAVE 2 AND 4 %d", i);
if(ObjectCreate(chart_id,tline_2_4,OBJ_TREND,0,wv2_ll_t,wv2_ll,wv4_time,wv4))
  {
   ObjectSetInteger(chart_id, tline_2_4, OBJPROP_COLOR, clrBlue);
   ObjectSetInteger(chart_id, tline_2_4, OBJPROP_RAY_RIGHT, true);
   ObjectSetInteger(chart_id, tline_2_4, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
  }

tline_1_4 = StringFormat("TREND LINE WAVE 1 AND 4 %d", i);
if(ObjectCreate(chart_id,tline_1_4,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv4_time,wv4))
  {
   ObjectSetInteger(chart_id, tline_1_4, OBJPROP_COLOR, clrBlue);
   ObjectSetInteger(chart_id, tline_1_4, OBJPROP_RAY_RIGHT, true);
   ObjectSetInteger(chart_id, tline_1_4, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
  }


Figure 11. Hiddent Trend Lines

Пояснение:

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

Пример:
double t_1_3_values;
double t_2_4_values;
string tline_1_3_visible;
string tline_2_4_visible;
for(int m = l; m < bars_check - 2; m++)
  {

   t_1_3_values = ObjectGetValueByTime(chart_id, tline_1_3, time[m], 0);
   t_2_4_values = ObjectGetValueByTime(chart_id, tline_2_4, time[m], 0);

   if(close[m] > open[m] && open[m] < t_1_3_values && close[m] > t_1_3_values && time[m] > time[l+4])
     {

      tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i);
      ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[m],t_1_3_values);
      ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue);

      tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i);
      ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[m],t_2_4_values);
      ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue);

      break;
     }
  }

Вывод:

Figure 12. Visible Trend Lines

Пояснение:

Этот фрагмент кода обнаруживает, когда цена пробивает трендовую линию, соединяющую волны 1 и 3, проверяя бары, следующие после волны 4. Здесь используются переменные для получения ценового уровня трендовой линии во время каждого бара, и осуществляется поиск бычьей свечи, которая открывается ниже, но закрывается выше трендовой линии, сигнализируя о пробое, который должен произойти спустя как минимум четыре бара после волны 4. При обнаружении пробоя генерируются две дополнительные видимые трендовые линии, которые динамически именуются с помощью функции StringFormat. Затем эти трендовые линии отображаются на графике: одна связывает волну 1 с точкой пробоя, а другая связывает волну 2 с ней же. Это помогает вам лучше видеть и оценивать развитие паттерна волн Вульфа, давая четкое визуальное указание на точную свечу, в которой цена проходит выше трендовой линии 1-3.

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

Пример:
double t_1_3_values_2;
for(int m = l; m < bars_check - 2; m++)
  {

   t_1_3_values = ObjectGetValueByTime(chart_id, tline_1_3, time[m], 0);

   if(close[m] > open[m] && open[m] < t_1_3_values && close[m] > t_1_3_values && time[m] > time[l+4])
     {

      for(int n = m; n < bars_check - 1; n++)
        {

         t_1_3_values_2 = ObjectGetValueByTime(chart_id, tline_1_3, time[n], 0);
         t_2_4_values = ObjectGetValueByTime(chart_id, tline_2_4, time[n], 0);

         if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2)
           {

            tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i);
            ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n],t_1_3_values_2);
            ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue);

            tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i);
            ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[n],t_2_4_values);
            ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue);

            break;
           }
        }
      break;
     }
  }

Вывод:

Figure 13. Break in Trend Line

Пояснение:

Но наша цель не в этом. Хотя они помогают обозначить пробой, видимые трендовые линии, которые образуются при пересечении рынком трендовой линии 1-3 и закрытии выше нее, еще не завершают структуру волны Вульфа. Настоящая цель заключается в том, чтобы оставлять продление этих линий до тех пор, пока рынок не пересечет трендовую линию 1-3 и не закроется ниже нее. В частности, внутренний цикл for использует переменную t_1_3_values_2 для хранения ценового уровня трендовой линии 1-3 на более поздний момент времени. Этот цикл начинает сканирование вперед, начиная с бара (m), на котором был зафиксирован прорыв выше трендовой линии. Он использует функцию ObjectGetValueByTime для получения значения трендовой линии в этот момент для каждого бара n, сохраняя его в переменную t_1_3_values_2.

Медвежья свеча, которая открылась выше трендовой линии и закрылась ниже нее, проверяется по выражению if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2, что указывает на то, что цена теперь снова пересекла трендовую линию вниз. Тот факт, что эта точка часто обозначает завершение волны 5 и начало ожидаемого разворота цены, делает ее примечательной. Как только будет найдено это второе пересечение, образуются две различимые трендовые линии; одна соединяет волну 1 с этой новой точкой на графике, а вторая соединяет волну 2 с той же точкой. Это дает более полное и точное представление о волне Вульфа от начала до пробоя и разворота, эффективно заменяя предыдущие трендовые линии. Теперь трендовые линии продолжаются от своих начальных точек до точного момента, в котором ценовое действие подтверждает весь паттерн.

Следующий этап – определить ценовой уровень волны 5. Чтобы достичь этого, мы должны определить наибольший максимум между двумя значимыми точками: первый раз – когда рынок пересекает трендовую линию 1-3 и закрывается выше нее, а второй раз – когда он разворачивается и пересекает ту же трендовую линию вниз. Обычно этот диапазон обозначает заключительную фазу паттерна волн Вульфа. Перед началом ожидаемого разворота цена в этой области часто делает еще один толчок вверх. Нахождение наибольшего максимума в этом окне позволит нам точно выявить пик волны 5.

Пример:
int cross_bars;
int cross_bars_highest;
string wv5_txt;
if(close[m] > open[m] && open[m] < t_1_3_values && close[m] > t_1_3_values && time[m] > time[l+4])
  {

   for(int n = m; n < bars_check - 1; n++)
     {

      t_1_3_values_2 = ObjectGetValueByTime(chart_id, tline_1_3, time[n], 0);
      t_2_4_values = ObjectGetValueByTime(chart_id, tline_2_4, time[n], 0);

      if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2)
        {

         tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i);
         ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n],t_1_3_values_2);
         ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue);

         tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i);
         ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[n],t_2_4_values);
         ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue);

         cross_bars = Bars(_Symbol,timeframe,time[n], time[m]);
         cross_bars_highest = ArrayMaximum(high,m,cross_bars);

         wv5_txt = StringFormat("WAVE 5 %d", i);
         ObjectCreate(chart_id, wv5_txt, OBJ_TEXT, 0, time[cross_bars_highest], high[cross_bars_highest]);
         ObjectSetString(chart_id, wv5_txt, OBJPROP_TEXT, "WV5");
         ObjectSetInteger(chart_id, wv5_txt, OBJPROP_COLOR, clrBlue);

         break;
        }
     }

Пояснение:

В этом фрагменте кода выявляется и отмечается пятая волна в паттерне волн Вульфа. Как только цена пробьет трендовую линию волны 1-3, а затем развернется обратно ниже нее, алгоритм определяет количество баров между точками пробоя и разворота, чтобы определить ожидаемое время появления волны 5. Затем код, начиная с индекса m, проходит количество баров, указанное в переменной cross_bars, с помощью функции ArrayMaximum() для поиска массива цен high[]. Эта функция возвращает наибольший максимум в этом диапазоне, который обозначает пик волны 5. Этот индекс хранится в переменной cross_bars_highest.

Чтобы обозначить этот распознанный максимум как "WV5", код в конечном итоге создает текстовый объект на графике. Метка размещается с использованием значения даты и цены по индексу cross_bars_highest и имеет синий цвет (blue), чтобы напоминать метки предыдущих волн. Это визуально завершает структуру волны Вульфа на графике, гарантируя, что волна 5 правильно отмечена в максимальной точке между пробоем и разворотом.

Расширение Фибоначчи

Как вы, возможно, помните, мы уже установили, что волна 5 должна находиться в пределах определенного диапазона расширения, произведенного волнами 3 и 4. Между волнами 3 и 4 этот диапазон обычно составляет от 127,2% до 161,8% от ценового изменения.

Пример:
input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // TIMEFRAME
input double max_fib_ext_wv12 = 161.8; // WAVE 1 AND 2 FIBO EXTENSION MAX LEVEL
input double min_fib_ext_wv12 = 127.2; // WAVE 1 AND 2 FIBO EXTENSION MIN LEVEL
input double max_fib_ext_wv34 = 120.0; // WAVE 3 AND 4 FIBO EXTENSION MAX LEVEL
input double min_fib_ext_wv34 = 200.0; // WAVE 3 AND 4 FIBO EXTENSION MIN LEVEL

string fib_ext_3_4;
double fib_ext_3_4_161_8;
double fib_ext_3_4_127_2;
string fib_ext_3_4_168_127;
string fib_ext_range_3_4;
int no_bars;
for(int l = k; l < bars_check - 7; l++)
  {
   if(IsSwingLow(low, l, 7) && time[l] > wv3_time)
     {
      wv4 = low[l];
      wv4_time = time[l];
      wv4_txt = StringFormat("WAVE 4 %d", l);

      wv3_wv4_bars = Bars(_Symbol, timeframe, wv3_time, wv4_time);
      wv3_highest_index = ArrayMaximum(high, k, wv3_wv4_bars);
      wv3_hh = high[wv3_highest_index];
      wv3_hh_t = time[wv3_highest_index];

      wv2_wv3_bars = Bars(_Symbol, timeframe, wv2_time, wv3_time);
      wv2_lowest_index = ArrayMinimum(low, j, wv2_wv3_bars);
      wv2_ll = low[wv2_lowest_index];
      wv2_ll_t = time[wv2_lowest_index];

      wv1_wv2_bars = Bars(_Symbol, timeframe, wv1_time, wv2_time);
      wv1_highest_index = ArrayMaximum(high, i, wv1_wv2_bars);
      wv1_hh = high[wv1_highest_index];
      wv1_hh_t = time[wv1_highest_index];

      wv1_wv2_size = MathAbs(wv1_hh - wv2_ll);
      wv1_wv2_70p = (wv1_wv2_size / 100) * 70;

      fib_ext_1_2_127_2 = MathAbs((((wv1_hh - wv2_ll) / 100) * (min_fib_ext_wv12 - 100)) + wv1_hh);
      fib_ext_1_2_161_8 = MathAbs((((wv1_hh - wv2_ll) / 100) * (max_fib_ext_wv12 - 100)) + wv1_hh);

      wv3_wv4_size = MathAbs(wv3_hh - wv4);

      if(wv3_wv4_size >= wv1_wv2_size && wv4 > wv2_ll && wv4 < wv3_hh && wv3_hh >= fib_ext_1_2_127_2 && wv3_hh <= fib_ext_1_2_161_8)
        {
         tline_1_3 = StringFormat("TREND LINE WAVE 1 AND 3 %d", i);
         if(ObjectCreate(chart_id,tline_1_3,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv3_hh_t,wv3_hh))
           {
            ObjectSetInteger(chart_id, tline_1_3, OBJPROP_COLOR, clrBlue);
            ObjectSetInteger(chart_id, tline_1_3, OBJPROP_RAY_RIGHT, true);
            ObjectSetInteger(chart_id, tline_1_3, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
           }

         tline_2_4 = StringFormat("TREND LINE WAVE 2 AND 4 %d", i);
         if(ObjectCreate(chart_id,tline_2_4,OBJ_TREND,0,wv2_ll_t,wv2_ll,wv4_time,wv4))
           {
            ObjectSetInteger(chart_id, tline_2_4, OBJPROP_COLOR, clrBlue);
            ObjectSetInteger(chart_id, tline_2_4, OBJPROP_RAY_RIGHT, true);
            ObjectSetInteger(chart_id, tline_2_4, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
           }

         tline_1_4 = StringFormat("TREND LINE WAVE 1 AND 4 %d", i);
         if(ObjectCreate(chart_id,tline_1_4,OBJ_TREND,0,wv1_hh_t,wv1_hh,wv4_time,wv4))
           {
            ObjectSetInteger(chart_id, tline_1_4, OBJPROP_COLOR, clrBlue);
            ObjectSetInteger(chart_id, tline_1_4, OBJPROP_RAY_RIGHT, true);
            ObjectSetInteger(chart_id, tline_1_4, OBJPROP_TIMEFRAMES, OBJ_NO_PERIODS);
           }

         fib_ext_3_4 = StringFormat("FIB EXTENSION WAVE 3 AND 4 %d", i);
         fib_ext_3_4_127_2 = MathAbs((((wv3_hh - wv4) / 100) * (min_fib_ext_wv34 - 100)) + wv3_hh);
         fib_ext_3_4_161_8 = MathAbs((((wv3_hh - wv4) / 100) * (max_fib_ext_wv34 - 100)) + wv3_hh);
         fib_ext_3_4_168_127 = StringFormat("FIB EXTENSION wv3 wv4 %d", i);

         for(int m = l; m < bars_check - 2; m++)
           {
            t_1_3_values = ObjectGetValueByTime(chart_id, tline_1_3, time[m], 0);

            if(close[m] > open[m] && open[m] < t_1_3_values && close[m] > t_1_3_values && time[m] > time[l+4])
              {
               for(int n = m; n < bars_check - 1; n++)
                 {
                  t_1_3_values_2 = ObjectGetValueByTime(chart_id, tline_1_3, time[n], 0);
                  t_2_4_values = ObjectGetValueByTime(chart_id, tline_2_4, time[n], 0);

                  no_bars = Bars(_Symbol, timeframe, wv3_hh_t, time[n]);
                  cross_bars = Bars(_Symbol,timeframe,time[n], time[m]);
                  cross_bars_highest = ArrayMaximum(high,m,cross_bars);

                  if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2 && no_bars < 100
                     && time[n] > time[m]
                     && high[cross_bars_highest] >= fib_ext_3_4_127_2 &&  high[cross_bars_highest] <= fib_ext_3_4_161_8)
                    {
                     ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_hh_t, wv1_hh);
                     ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
                     ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

                     ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_ll_t, wv2_ll);
                     ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
                     ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

                     fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);

                     ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_ll_t, wv2_ll, wv1_hh_t, wv1_hh, wv2_ll_t, wv2_ll);

                     ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

                     for(int i = 0; i <= 2; i++)
                       {
                        ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);
                       }

                     perc_70 = StringFormat("70 PERCENT %d", j);
                     ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_hh_t, wv1_hh - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p);

                     fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j);
                     ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_hh_t, fib_ext_1_2_161_8, wv2_ll_t, fib_ext_1_2_127_2);

                     ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_hh_t, wv3_hh);
                     ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3");
                     ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue);

                     ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4);
                     ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4");
                     ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue);

                     tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i);
                     ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n],t_1_3_values_2);
                     ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue);

                     tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i);
                     ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[n],t_2_4_values);
                     ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue);

                     wv5_txt = StringFormat("WAVE 5 %d", i);
                     ObjectCreate(chart_id, wv5_txt, OBJ_TEXT, 0, time[cross_bars_highest], high[cross_bars_highest]);
                     ObjectSetString(chart_id, wv5_txt, OBJPROP_TEXT, "WV5");
                     ObjectSetInteger(chart_id, wv5_txt, OBJPROP_COLOR, clrBlue);

                     ObjectCreate(chart_id, fib_ext_3_4,OBJ_EXPANSION, 0,wv4_time, wv4,wv3_hh_t,wv3_hh,wv4_time,wv4);
                     for(int i = 0; i <= 2; i++)
                       {
                        ObjectSetInteger(chart_id,fib_ext_3_4,OBJPROP_LEVELCOLOR,i,clrBlue);
                        ObjectSetInteger(chart_id,fib_ext_3_4,OBJPROP_COLOR,clrBlue);
                       }

                     fib_ext_range_3_4 =  StringFormat("Fibo EXPENSION RANGE WV3 WV4 %d", i);
                     ObjectCreate(chart_id,fib_ext_range_3_4,OBJ_RECTANGLE,0,wv3_hh_t,fib_ext_3_4_127_2,time[cross_bars_highest],fib_ext_3_4_161_8);

                     break;
                    }
                 }
               break;
              }
           }
        }
      break;
     }
  }

Вывод:

Figure 14. Wave 4 and 5 Extension

Пояснение:

Здесь используется расширение Фибоначчи для волн 3 и 4. Вычисляя уровни Фибоначчи и подсчитывая бары между волной 3 и закрытием цены ниже трендовой линии волны 1-3, данный алгоритм гарантирует, что паттерн будет структурно корректен и компактен. С помощью условного блока советник ищет определенную медвежью свечу, которая пробивает трендовую линию 1-3, а затем закрывается ниже нее. Разворот должен происходить в пределах подходящего количества баров (менее 100), а максимальная цена между пробоем и разворотом должна находиться в пределах допустимого диапазона расширения Фибоначчи, в соответствии с дальнейшими параметрами. Волна 5 может быть действительной, если будут выполнены все эти требования.

Затем волны 3 и 4 используются в качестве базовых отрезков для построения объекта расширения Фибоначчи. Для наглядности все три уровня расширения (0, 127,2 и 161,8) отмечены синим. Чтобы визуально подчеркнуть целевую зону Фибоначчи, также рисуется прямоугольник. Критически важно, чтобы этот блок условий являлся единственным, который используется для создания всех соответствующих графических объектов, таких как зоны расширения, метки и трендовые линии. Это снижает количество ложных сигналов и увеличивает надежность логики обнаружения волнового паттерна, гарантируя, что полный паттерн будет отмечен только после выполнения всех правил.

Проведение трендовой линии от волны 1 до волны 4

Прежде чем мы перейдем к тому, чтобы позволить советнику исполнять сделки, нам нужно провести трендовую линию, соединяющую волну 1 и волну 4. Эта трендовая линия играет критическую роль в паттерне волн Вульфа, особенно для планирования выхода.
int no_wv1_n_bars;
int no_n_c_bars;
string tline_1_4_visible;
double t_1_4_values;
string tline_1_4_visible_2;
if(close[n] < open[n] && open[n] > t_1_3_values_2 && close[n] < t_1_3_values_2 && no_bars < 100
   && time[n] > time[m]
   && high[cross_bars_highest] >= fib_ext_3_4_127_2 &&  high[cross_bars_highest] <= fib_ext_3_4_161_8)
{

 ObjectCreate(chart_id, wv1_txt, OBJ_TEXT, 0, wv1_hh_t, wv1_hh);
 ObjectSetString(chart_id, wv1_txt, OBJPROP_TEXT, "WV1");
 ObjectSetInteger(chart_id, wv1_txt, OBJPROP_COLOR, clrBlue);

 ObjectCreate(chart_id, wv2_txt, OBJ_TEXT, 0, wv2_ll_t, wv2_ll);
 ObjectSetString(chart_id, wv2_txt, OBJPROP_TEXT, "WV2");
 ObjectSetInteger(chart_id, wv2_txt, OBJPROP_COLOR, clrBlue);

 fib_ext_wv1_wv2 = StringFormat("FIBO EXTENSION WAVE 1 AND 2 %d", i);

 ObjectCreate(chart_id, fib_ext_wv1_wv2, OBJ_EXPANSION, 0, wv2_ll_t, wv2_ll, wv1_hh_t, wv1_hh, wv2_ll_t, wv2_ll);

 ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_COLOR, clrBlue);

 for(int i = 0; i <= 2; i++)
 {
  ObjectSetInteger(chart_id, fib_ext_wv1_wv2, OBJPROP_LEVELCOLOR, i, clrBlue);
 }

 perc_70 = StringFormat("70 PERCENT %d", j);
 ObjectCreate(chart_id, perc_70, OBJ_TREND, 0, wv1_hh_t, wv1_hh - wv1_wv2_70p, wv2_time, wv1 - wv1_wv2_70p);

 fib_ext_range = StringFormat("Fibo EXPENSION RANGE%d", j);
 ObjectCreate(chart_id, fib_ext_range, OBJ_RECTANGLE, 0, wv1_hh_t, fib_ext_1_2_161_8, wv2_ll_t, fib_ext_1_2_127_2);

 ObjectCreate(chart_id, wv3_txt, OBJ_TEXT, 0, wv3_hh_t, wv3_hh);
 ObjectSetString(chart_id, wv3_txt, OBJPROP_TEXT, "WV3");
 ObjectSetInteger(chart_id, wv3_txt, OBJPROP_COLOR, clrBlue);

 ObjectCreate(chart_id, wv4_txt, OBJ_TEXT, 0, wv4_time, wv4);
 ObjectSetString(chart_id, wv4_txt, OBJPROP_TEXT, "WV4");
 ObjectSetInteger(chart_id, wv4_txt, OBJPROP_COLOR, clrBlue);

 tline_1_3_visible = StringFormat("TREND LINE WAVE 1 AND 3 V %d", i);
 ObjectCreate(chart_id,tline_1_3_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n],t_1_3_values_2);
 ObjectSetInteger(chart_id, tline_1_3_visible, OBJPROP_COLOR, clrBlue);

 tline_2_4_visible = StringFormat("TREND LINE WAVE 2 AND 4 V %d", i);
 ObjectCreate(chart_id,tline_2_4_visible,OBJ_TREND,0,wv2_ll_t,wv2_ll,time[n],t_2_4_values);
 ObjectSetInteger(chart_id, tline_2_4_visible, OBJPROP_COLOR, clrBlue);

 wv5_txt = StringFormat("WAVE 5 %d", i);
 ObjectCreate(chart_id, wv5_txt, OBJ_TEXT, 0, time[cross_bars_highest], high[cross_bars_highest]);
 ObjectSetString(chart_id, wv5_txt, OBJPROP_TEXT, "WV5");
 ObjectSetInteger(chart_id, wv5_txt, OBJPROP_COLOR, clrBlue);

 ObjectCreate(chart_id, fib_ext_3_4,OBJ_EXPANSION, 0,wv4_time, wv4,wv3_hh_t,wv3_hh,wv4_time,wv4);
 for(int i = 0; i <= 2; i++)
 {
  ObjectSetInteger(chart_id,fib_ext_3_4,OBJPROP_LEVELCOLOR,i,clrBlue);
  ObjectSetInteger(chart_id,fib_ext_3_4,OBJPROP_COLOR,clrBlue);
 }

 fib_ext_range_3_4 =  StringFormat("Fibo EXPENSION RANGE WV3 WV4 %d", i);
 ObjectCreate(chart_id,fib_ext_range_3_4,OBJ_RECTANGLE,0,wv3_hh_t,fib_ext_3_4_127_2,time[cross_bars_highest],fib_ext_3_4_161_8);

 no_wv1_n_bars =  Bars(_Symbol, timeframe, wv1_hh_t, time[n]);
 no_n_c_bars = Bars(_Symbol, timeframe, time[n], TimeCurrent());

 if(no_n_c_bars <= no_wv1_n_bars)
 {
  t_1_4_values =  ObjectGetValueByTime(chart_id, tline_1_4, TimeCurrent(), 0);
  tline_1_4_visible = "TL WAVE 1 AND 4 Visible";

  ObjectCreate(chart_id,tline_1_4_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,TimeCurrent(),t_1_4_values);
  ObjectSetInteger(chart_id,tline_1_4_visible,OBJPROP_STYLE,STYLE_DASH);
  ObjectSetInteger(chart_id,tline_1_4_visible,OBJPROP_COLOR,clrBlue);
 }

 if(no_n_c_bars > no_wv1_n_bars)
 {
  ObjectDelete(chart_id,tline_1_4_visible);

  t_1_4_values =  ObjectGetValueByTime(chart_id, tline_1_4, time[n + no_wv1_n_bars], 0);
  tline_1_4_visible_2 = StringFormat("TL WAVE 1 AND 4 DISPLAY %d", i);

  ObjectCreate(chart_id,tline_1_4_visible_2,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n+no_wv1_n_bars],t_1_4_values);
  ObjectSetInteger(chart_id,tline_1_4_visible_2,OBJPROP_STYLE,STYLE_DASH);
  ObjectSetInteger(chart_id,tline_1_4_visible_2,OBJPROP_COLOR,clrBlue);
}

break;
}

Пояснение:

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

Трендовая линия с меткой "TL WAVE 1 AND 4 Visible" формируется между волной 1 и текущим рыночным временем, если текущий бар все еще находится в диапазоне, который определяют волна 1 и точка разворота (no_n_c_bars <= no_wv1_n_bars). Функция ObjectGetValueByTime используется для получения текущего значения трендовой линии, а для улучшения видимости линия отображается в синем цвете и пунктиром.

При этом ранее проведенная линия удаляется функцией ObjectDelete, если количество баров после точки разворота больше диапазона между волной 1 и разворотом. Чтобы гарантировать, что она покрывает то же расстояние, что и первоначальная проекция, в данном случае строится новая трендовая линия, растягиваемая от волны 1 до определенной точки в будущем (time[n + no_wv1_n_bars]). Эта новая линия имеет динамическую метку и оформлена синим цветом и пунктиром. В то время как алгоритм явно подстраивается под обновления графика в реальном времени, он гарантирует, что трендовая линия от волны 1 до волны 4 остается постоянной и направленной.

Исполнение сделок

На данный момент мы успешно распознали все пять волн паттерна Вульфа, от волны 1 до волны 5, а также три ключевые трендовые линии, которые соединяют волну 1 с волной 3, волну 2 с волной 4 и волну 1 с волной 4. В дополнение к предоставлению визуального подтверждения паттерна, эти трендовые линии также служат ориентиром для наших решений по исполнению сделок.

Следующим шагом является исполнение сделки в соответствии с заранее установленной структурой. Условие входа для медвежьего паттерна волн Вульфа тесно связано с тем, как цена движется вблизи трендовой линии 1-3. В частности, свеча должна сначала пересечь эту трендовую линию вверх, что может свидетельствовать о том, что восходящий тренд подходит к концу, а затем снова пересечь ее вниз. Это второе движение – хороший момент, чтобы открыть короткую позицию, потому что он подтверждает, что рынок может начать разворот вниз. Советник не должен инициировать сделку, пока не будет завершено это двухступенчатое подтверждение.  

Пример:

#include <Trade/Trade.mqh>
CTrade trade;
input int MagicNumber = 6160;
input double lot_size = 0.01;

input ENUM_TIMEFRAMES timeframe = PERIOD_CURRENT; // TIMEFRAME
input double max_fib_ext_wv12 = 161.8; // WAVE 1 AND 2 FIBO EXTENSION MAX LEVEL
input double min_fib_ext_wv12 = 127.2; // WAVE 1 AND 2 FIBO EXTENSION MIN LEVEL
input double max_fib_ext_wv34 = 200.0; // WAVE 3 AND 4 FIBO EXTENSION MAX LEVEL
input double min_fib_ext_wv34 = 120.0; // WAVE 3 AND 4 FIBO EXTENSION MIN LEVEL
datetime time_exe[];
datetime lastTradeBarTime = 0;
double ask_price;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   trade.SetExpertMagicNumber(MagicNumber);
   ArraySetAsSeries(time_exe,true);

//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
//---

   total_symbol_bars = Bars(_Symbol, timeframe);


   time_bar = iTime(_Symbol,timeframe,0);


   CopyOpen(_Symbol, timeframe, time_bar, bars_check, open);
   CopyClose(_Symbol, timeframe, time_bar, bars_check, close);
   CopyLow(_Symbol, timeframe, time_bar, bars_check, low);
   CopyHigh(_Symbol, timeframe, time_bar, bars_check, high);
   CopyTime(_Symbol, timeframe, time_bar, bars_check, time);

   CopyTime(_Symbol, timeframe, 0, 2, time_exe);
   datetime currentBarTime = iTime(_Symbol, timeframe, 0);
   ask_price = SymbolInfoDouble(_Symbol,SYMBOL_ASK);

                                              
if(time[n] == time_exe[1]  && currentBarTime != lastTradeBarTime)
  {

   trade.Sell(lot_size,_Symbol,ask_price, high[cross_bars_highest],wv2_ll);

   lastTradeBarTime = currentBarTime;

  }

Вывод:

Figure 15. Trade Execution

Пояснение:

В первой строке (#include) импортируется встроенная торговая библиотека MQL5, предоставляя классу CTrade доступ к функциям управления торговлей. После подключения этой библиотеки создается экземпляр класса CTrade с именем trade. Все торговые команды, такие как покупка, продажа, установка тейк-профита и стоп-лосса, а также поддержание ордеров, выполняются этим объектом. Далее определяются две входные переменные – MagicNumber и lot_size. Транзакции этого советника уникально идентифицируются по их магическому числу (MagicNumber), что позволяет легко отличать их от других сделок, которые могут быть совершены на том же счете. Размер лота (lot_size), который в данном случае составляет 0,01 лота, указывает на объем или размер сделки, которая будет осуществлена.

Затем объявляется несколько переменных: time_exe[], lastTradeBarTime и ask_price. Массив time_exe будет хранить данные о времени с недавних баров. Переменная lastTradeBarTime инициализируется нулем и используется для предотвращения исполнения советником нескольких сделок на одной и той же свече. Переменная ask_price используется для хранения текущей цены Ask символа, которая является ценой, по которой может быть исполнена сделка на продажу. Строка trade.SetExpertMagicNumber(MagicNumber); присваивает магическое число объекту торговли, чтобы каждую сделку, которую он совершает, можно было позже идентифицировать. Функция ArraySetAsSeries(time_exe, true); задает обработку массива time_exe как временного ряда, что означает, что индекс 0 соответствует самой последней свече.

Затем используется функция CopyTime(_Symbol, timeframe, 0, 2, time_exe); для копирования значений времени последних двух баров в массив time_exe. Затем переменная datetime currentBarTime = iTime(_Symbol, timeframe, 0); получает время открытия текущей формирующейся свечи. Строка ask_price = SymbolInfoDouble(_Symbol, SYMBOL_ASK); получает текущую цену Ask торгового инструмента, которая необходима для размещения ордера на продажу.

Теперь условие if (time[n] == time_exe[1] && currentBarTime != lastTradeBarTime) проверяет, совпадает ли время, хранящееся в time[n], со временем открытия второго по времени бара (time_exe[1]) и путем сравнения с lastTradeBarTime гарантирует, что на текущем баре сделка еще не исполнялась. Если это условие истинно, советник перейдет к размещению сделки на продажу с помощью trade.Sell(). Он открывает сделку по текущей цене ask_price со стоп-лоссом, установленным на уровень high[cross_bars_highest] (который, вероятно, представляет собой вершину волны 5), и тейк-профитом, установленным на уровень wv2_ll (минимум волны 2, потенциальный целевой уровень). Наконец, переменная lastTradeBarTime обновляется временем текущего бара, чтобы предотвратить множественные входы в пределах одной свечи.

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

Пример:
double low_m[];
double high_m[];
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   trade.SetExpertMagicNumber(MagicNumber);
   ArraySetAsSeries(time_exe,true);

   ArraySetAsSeries(low_m,true);
   ArraySetAsSeries(high_m,true);

//---
   return(INIT_SUCCEEDED);
  }
CopyLow(_Symbol, timeframe, 0, 3, low_m);
CopyHigh(_Symbol, timeframe, 0, 3, high_m);
if(no_n_c_bars <= no_wv1_n_bars)
  {

   t_1_4_values =  ObjectGetValueByTime(chart_id, tline_1_4, TimeCurrent(), 0);
   tline_1_4_visible = "TL WAVE 1 AND 4 Visible";

   ObjectCreate(chart_id,tline_1_4_visible,OBJ_TREND,0,wv1_hh_t,wv1_hh,TimeCurrent(),t_1_4_values);
   ObjectSetInteger(chart_id,tline_1_4_visible,OBJPROP_STYLE,STYLE_DASH);
   ObjectSetInteger(chart_id,tline_1_4_visible,OBJPROP_COLOR,clrBlue);

   if(time[n] == time_exe[1]  && currentBarTime != lastTradeBarTime)
     {

      trade.Sell(lot_size,_Symbol,ask_price, high[cross_bars_highest],wv2_ll);

      lastTradeBarTime = currentBarTime;

     }

   for(int i = 0; i < PositionsTotal(); i++)
     {
      ulong ticket = PositionGetTicket(i);
      datetime positionTime = 0;

      if(PositionSelectByTicket(ticket))
        {

         if(PositionGetInteger(POSITION_MAGIC) == MagicNumber && PositionGetString(POSITION_SYMBOL) == ChartSymbol(chart_id))
           {

            positionTime = (datetime)PositionGetInteger(POSITION_TIME);


            if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL)
              {

               double pos_bars = Bars(_Symbol, timeframe, positionTime, TimeCurrent());


               if((ask_price < t_1_4_values || low_m[1] < t_1_4_values || no_n_c_bars == no_wv1_n_bars) && no_n_c_bars == pos_bars+1)
                 {

                  trade.PositionClose(ticket);

                 }
              }
           }
        }
     }
  }

if(no_n_c_bars > no_wv1_n_bars)
  {

   ObjectDelete(chart_id,tline_1_4_visible);

   t_1_4_values =  ObjectGetValueByTime(chart_id, tline_1_4, time[n + no_wv1_n_bars], 0);
   tline_1_4_visible_2 = StringFormat("TL WAVE 1 AND 4 DISPLAY %d", i);

   ObjectCreate(chart_id,tline_1_4_visible_2,OBJ_TREND,0,wv1_hh_t,wv1_hh,time[n+no_wv1_n_bars],t_1_4_values);
   ObjectSetInteger(chart_id,tline_1_4_visible_2,OBJPROP_STYLE,STYLE_DASH);
   ObjectSetInteger(chart_id,tline_1_4_visible_2,OBJPROP_COLOR,clrBlue);

  }

Вывод:

Figure 16. Closing Trade

Пояснение:

В первом разделе кода объявлены два массива: double low_m[]; и double high_m[];. В этих массивах хранятся самые последние минимумы и максимумы графика. Подобно типичному поведению графика MetaTrader 5, строки ArraySetAsSeries(low_m, true); и ArraySetAsSeries(high_m, true); изменяют индексацию массивов так, чтобы индекс 0 всегда соответствовал самой последней свече. Это требуется для сравнения недавних или текущих ценовых уровней с важными значениями трендовой линии.

Затем последние три минимальные и максимальные цены с графика затем добавляются в массивы low_m и high_m, соответственно, с помощью методов CopyLow(_Symbol, timeframe, 0, 3, low_m); и CopyHigh(_Symbol, timeframe, 0, 3, high_m);. Затем в этих точках цена анализируется, чтобы определить, коснулась ли она определенной трендовой линии, такой как линия 1-4, или пересекла ее вниз.

Если позиция является сделкой на продажу, что соответствует паттерну волн Вульфа в медвежьей конфигурации, код вычисляет, сколько баров прошло с момента открытия сделки с помощью Bars(_Symbol, timeframe, positionTime, TimeCurrent()). Наконец, условие проверяет, достиг ли рынок точки выхода. В частности, он оценивает следующие критерии:

  • текущая цена Ask ниже трендовой линии 1-4 (t_1_4_values);
  • минимум предыдущей свечи (low_m[1]) пробил эту трендовую линию;
  • количество баров с момента входа равен количеству баров, необходимых для достижения трендовой линии 1-4 (no_n_c_bars == no_wv1_n_bars); и
  • позиция была открыта в течение ровно такого количества баров (no_n_c_bars == pos_bars + 1).

Советник использует это условие для закрытия позиции в случае выполнения всех этих критериев, поскольку это указывает на то, что сделка либо достигла своей ожидаемой точки выхода, либо удерживалась слишком долго. Ticket; PositionClose. Чтобы убедиться, что советник корректно связывает открытую сделку с самым последним паттерном волн Вульфа, используется условие no_n_c_bars == pos_bars + 1. В рамках логики этого советника осуществляется поиск нескольких паттернов волн Вульфа за заранее определенное количество баров. Однако советник совершает сделку только тогда, когда time[n] == time_exe[1]. Это гарантирует, что сделка произойдет в тот же момент, когда последний паттерн будет подтвержден.

Количество баров, которые прошли с момента открытия сделки, представлено переменной pos_bars, которая вычисляется для сохранения этого соответствия между сигналом и исполнением. В свою очередь, переменная no_n_c_bars определяет количество баров, которые прошли между текущим временем и точкой подтверждения волновой модели Вульфа (time[n]). Советник получает информацию о том, что сделка была инициирована именно этим паттерном, а не предыдущим, если эти два значения совпадают, поскольку no_n_c_bars == pos_bars + 1.

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


Выявление бычьих паттернов волн Вульфа

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

Волна 1, которая формирует первую основу в бычьей волне Вульфа, должна быть минимумом свинга. Не пробивая уровень волны 1, волна 3 должна создать еще один минимум свинга после волны 2, которая должна быть максимумом свинга. Бычий сетап должен быть завершен формированием минимума свинга 5 и максимума свинга 4, который обычно ниже, чем максимум свинга 2.

Вершины волн 1-3 должны быть минимальными или максимальными точками между соседними волнами, в то время как волна 5 должна находиться в диапазоне 127,2% - 161,8% расширения Фибоначчи для волн 3 и 4. Чувствительность обнаружения паттернов может регулироваться пользователем путем изменения этих диапазонов. Чтобы повысить надежность входа, цена должна сначала опуститься ниже трендовой линии между волнами 1 и 3, а затем подняться выше нее. Линия между волнами 1-4 часто используется в качестве цели для фиксации прибыли. Важные трендовые линии, связывающие волны 1-3, 2-4 и 1-4, автоматически рисуются для помощи в распознавании паттернов и планировании выхода из сделок.

Figure 17. Long Position


Заключение

В этой статье описывается, как мы создали советник на языке MQL5, который может распознавать как бычьи, так и медвежьи формации волн Вульфа. Мы рассмотрели, как использовать максимумы и минимумы свинга для выявления пятиволновой структуры, как применять уровни расширения Фибоначчи, как создавать важные трендовые линии и входить в сделки только тогда, когда все требования выполнены. Хотя диапазон Фибоначчи от 127,2% до 161,8% является стандартным, пользователи могут изменять эти числа в соответствии со своими предпочтениями. Этот проект предлагает надежную основу для разработки более сложных торговых систем, основанных на паттернах, в дополнение к автоматизации сложного графического паттерна. 

Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/18884

Прикрепленные файлы |
Нейросети в трейдинге: Двусторонняя адаптивная временная корреляция (Окончание) Нейросети в трейдинге: Двусторонняя адаптивная временная корреляция (Окончание)
Фреймворк BAT превращает хаотичный поток рыночных данных в точные прогнозы и взвешенные торговые решения. Тесты на исторических данных показывают стабильный рост капитала при контролируемых рисках. Архитектура модели проста, масштабируема и готова к дальнейшей оптимизации.
Детерминированный алгоритм дендритных клеток — Deterministic Dendritic Cell Algorithm (dDCA) Детерминированный алгоритм дендритных клеток — Deterministic Dendritic Cell Algorithm (dDCA)
Представлена адаптация детерминированного алгоритма дендритных клеток (dDCA) для задач непрерывной оптимизации. Алгоритм, вдохновлённый Теорией Опасности иммунной системы, использует механизм накопления сигналов для автоматического баланса между исследованием и эксплуатацией пространства поиска.
Алгоритм дендритных клеток — Dendritic Cell Algorithm (DCA) Алгоритм дендритных клеток — Dendritic Cell Algorithm (DCA)
Алгоритм дендритных клеток (DCA) — метаэвристика, вдохновлённая механизмами врождённого иммунитета. Дендритные клетки патрулируют пространство поиска, накапливают сигналы о качестве позиций и выносят коллективный вердикт: эксплуатировать найденное или продолжать исследование. Разберём, как биологическая модель обнаружения патогенов превращается в алгоритм оптимизации.
Нейросети в трейдинге: Двусторонняя адаптивная временная корреляция (Основные компоненты) Нейросети в трейдинге: Двусторонняя адаптивная временная корреляция (Основные компоненты)
В этой статье мы продолжаем реализацию фреймворка BAT средствами MQL5, показывая, как двунаправленная корреляция и модуль SATMA позволяют анализировать динамику рынка в контексте текущего состояния. Представлены ключевые архитектурных решения, позволяющие адаптировать фреймворк к анализу финансовых данных.