English 中文 Español Deutsch 日本語
preview
Разработка инструментария для анализа движения цен (Часть 15): Введение в теорию четвертей (II) — советник Intrusion Detector

Разработка инструментария для анализа движения цен (Часть 15): Введение в теорию четвертей (II) — советник Intrusion Detector

MetaTrader 5Торговые системы |
440 0
Christian Benjamin
Christian Benjamin

Введение

В предыдущей статье мы рассмотрели скрипт Quarters Drawer, предназначенный для визуального отображения уровней четвертей на графике, что делает анализ рынка более наглядным. Эта концепция основана на теории четвертей (Quarters Theory), первоначально предложенной Илияном Йотовым (Ilian Yotov). Успешное отображение четвертей оказалось мощным методом упрощения анализа ценового движения. Однако ручной мониторинг этих уровней и их взаимодействия с ценой требует значительного времени и внимания.

Для решения этой задачи я рад представить советник Intrusion Detector — решение, разработанное для автоматизации процесса мониторинга. Этот советник постоянно отслеживает графики, определяя, когда цена достигает любого уровня четверти (маленького, большого, существенного, превышающего или понижающего). Кроме того, он предоставляет мгновенные комментарии и аналитические выводы, основанные на теории четвертей Илияна Йотова, помогая каждому трейдеру предвидеть потенциальные реакции рынка. В этой статье мы начнем с обзора инструмента Quarters Drawer, затем углубимся в концепцию стратегии и ее реализации в MQL5, проанализируем результаты тестирования и завершим ключевыми выводами. Ниже представлено оглавление для структурированного обзора.

Содержание



Обзор предыдущей статьи

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

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

Уровни четвертей

Рис 1. Уровни четвертей

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

Результаты

Рис 2. Результаты


Концепция стратегии и реализация в MQL5

Основная логика

Советник Intrusion Detector (детектор "вторжений") основан на построении карты ключевых психологических ценовых уровней с использованием теории четвертей. Он делит рынок на диапазоны в 1000 пунктов, при этом в качестве основы используются основные целые числа. Внутри этих диапазонов обозначены "Большие четверти" (зоны по 250 пипсов) и, если эта функция включена, "Малые четверти" для еще большей детализации. Советник также отображает зоны превышения и понижения, выявляя расширения цен, которые могут ввести трейдеров в заблуждение. При каждом изменении цены система сканирует текущий уровень, проверяет, приближается ли он к ключевым уровням в пределах заданного допуска (так называемой погрешности), и отправляет оповещение, если что-то происходит.

Главная цель здесь - выявить потенциальные поворотные моменты, прорывы или ложные прорывы, прежде чем они станут очевидны всем остальным. Если цена находится вблизи важного целого числа, советник помечает его как ключевую зону поддержки или сопротивления. Если цена колеблется около уровня "Большой четверти", это сигнализирует о возможности движения на 250 пунктов. Что такое зоны превышения и понижения? Они помогают выявлять ловушки, в которых цена может резко развернуться. Советник следит за тем, чтобы не рассылать оповещения бесконтрольно при обнаружении "вторжений", и мгновенно обновляет панель комментариев, чтобы вы всегда были в курсе происходящего. Вся система построена таким образом, чтобы вы всегда были впереди, делая теорию четвертей практической и применимой на практике.

Реализация

В советнике мы начинаем с заголовка, который содержит метаданные о EA, такие как его название (Intrusion Detector), сведения об авторских правах и ссылка на профиль разработчика. Директивы #property определяют эти детали и обеспечивают соблюдение строгих правил компиляции, гарантируя соответствие кода современным стандартам MQL5.

//+------------------------------------------------------------------+
//|                                             Intrusion Detector   |
//|                             Copyright 2025, Christian Benjamin   |
//|                        https://www.mql5.com/en/users/lynnchris   |
//+------------------------------------------------------------------+
#property copyright "Christian Benjamin"
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

Далее мы определяем набор входных параметров, которые позволяют настраивать поведение советника без изменения кода. Параметры включают числовые значения, такие как MajorStep, который определяет интервал между основными уровнями (фактически определяя диапазон в 1000 пипсов) и AlertTolerance, который устанавливает пороговое значение близости для определения момента, когда цена "касается" определенного уровня. Логические значения определяют, будет ли советник рисовать дополнительные линии, такие как большие и малые четверти, а также зоны выхода (превышения) за пределы этих основных уровней.

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

Параметры конфигурации

input double MajorStep          = 0.1000;   // Difference between Major Whole Numbers (defines the 1000-PIP Range)
input bool   DrawLargeQuarters  = true;     // Draw intermediate Large Quarter lines.
input bool   DrawSmallQuarters  = false;    // Draw Small Quarter lines.
input bool   DrawOvershootAreas = true;     // Mark overshoot/undershoot areas for Large Quarter lines.
input double AlertTolerance     = 0.0025;   // Tolerance for detecting a "touch"

  • MajorStep - интервал между основными уровнями (например, диапазон в 1000 пипсов).
  • DrawLargeQuarters и DrawSmallQuarters - логические значения, определяющие, должен ли советник рисовать дополнительные линии в пределах заданного диапазона.
  • DrawOvershootAreas определяет, следует ли проводить дополнительные линии "превышения" и "понижения" вблизи уровней крупных четвертей.
  • AlertTolerance указывает, насколько близко цена должна приблизиться к определенному уровню (в пределах 0,0025), чтобы это считалось "касанием".

Настройки цвета

input color  MajorColor         = 0x2F4F4F; // Dark Slate Gray for Major lines.
input color  LargeQuarterColor  = 0x8B0000; // Dark Red for Large Quarter lines.
input color  SmallQuarterColor  = 0x00008B; // Dark Blue for Small Quarter lines.
input color  OvershootColor     = clrRed;   // Red for overshoot/undershoot lines.

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

Настройки стиля и толщины линии

input ENUM_LINE_STYLE MajorLineStyle       = STYLE_SOLID;
input int    MajorLineWidth                 = 4;
input ENUM_LINE_STYLE LargeQuarterLineStyle  = STYLE_DOT;
input int    LargeQuarterLineWidth          = 3;
input ENUM_LINE_STYLE OvershootLineStyle     = STYLE_DASH;
input int    OvershootLineWidth             = 1;
input ENUM_LINE_STYLE SmallQuarterLineStyle  = STYLE_SOLID;
input int    SmallQuarterLineWidth          = 1;

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

Настраиваемые комментарии

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

  • MajorSupportReason указывает на значительный уровень поддержки на рынке. Если цена упадет ниже этого уровня, это может указывать на возможное изменение торгового диапазона, потенциально ведущее к дальнейшему снижению цен.
  • MajorResistanceReason - критический уровень сопротивления. Если цена пробьет этот уровень сопротивления, это может сигнализировать о начале нового торгового диапазона, что потенциально может привести к восходящему движению цены.
  • LargeQuarterReason - указывает, что решительный прорыв цены на этом уровне может привести к существенному изменению цены, потенциально достигающему 250 пунктов. Это означает, что трейдерам следует внимательно следить за этим уровнем, чтобы выявлять потенциальные торговые возможности.
  • OvershootReason - цена тестирует уровень прорыва, и если ей не удастся удержать импульс, вероятен разворот в направлении движения цены. Это означает, что трейдерам следует проявлять осторожность, если цена резко поднимется выше ключевого уровня без сильной поддержки со стороны покупателей.
  • UndershootReason - на рынке не наблюдалось достаточного бычьего интереса, что предполагает возможность разворота (медвежий тренд). Трейдерам следует внимательно следить за этим сигналом, чтобы выявить потенциальные возможности для продаж.
  • SmallQuarterReason - незначительные колебания цен на рынке. Цена движется небольшими шагами и не указывает на какие-либо существенные торговые возможности или изменения тренда.

input string MajorSupportReason    = "Key support level. Break below signals range shift.";
input string MajorResistanceReason = "Pivotal resistance. Breakout above may start new range.";
input string LargeQuarterReason    = "Decisive break could trigger next 250-PIP move.";
input string OvershootReason       = "Test of breakout; reversal likely if momentum fails.";
input string UndershootReason      = "Insufficient bullish force; possible bearish reversal.";
input string SmallQuarterReason    = "Minor fluctuation.";

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

// Global flag to avoid repeated alerts while price lingers at a level
bool intrusionAlerted = false;

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

void DrawHorizontalLine(string name, double price, color lineColor, int width, ENUM_LINE_STYLE style)
{
   if(ObjectFind(0, name) != -1)
      ObjectDelete(0, name);

   if(!ObjectCreate(0, name, OBJ_HLINE, 0, 0, price))
   {
      Print("Failed to create line: ", name);
      return;
   }
   ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
}

Функция DrawQuarters использует DrawHorizontalLine, чтобы обозначить основные границы диапазона в 1000 пипсов (рассчитанного на основе текущей цены), а также дополнительные линии внутри этого диапазона. Если эта функция включена, советник рисует линии "большой четверти", разделяя диапазон на четыре сегмента. Для каждой из этих линий, если включены зоны перерегулирования, функция также рисует линии немного выше и ниже основного уровня, чтобы обозначить потенциальные зоны превышения или понижения. При включении этой опции советник подразделяет диапазон на еще более мелкие части - линии "малых четвертей", дающие более детальные визуальные подсказки о структуре цены.

void DrawQuarters(double currentPrice)
{
   // Calculate the boundaries of the current 1000-PIP Range
   double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
   double upperMajor = lowerMajor + MajorStep;

   // Draw Major Whole Number lines (defining the 1000-PIP Range)
   DrawHorizontalLine("MajorLower", lowerMajor, MajorColor, MajorLineWidth, MajorLineStyle);
   DrawHorizontalLine("MajorUpper", upperMajor, MajorColor, MajorLineWidth, MajorLineStyle);

   // Draw Large Quarter lines and their overshoot/undershoot areas if enabled
   if(DrawLargeQuarters)
   {
      double LQIncrement = MajorStep / 4.0;
      for(int i = 1; i < 4; i++)
      {
         double level = lowerMajor + i * LQIncrement;
         string objName = "LargeQuarter_" + IntegerToString(i);
         DrawHorizontalLine(objName, level, LargeQuarterColor, LargeQuarterLineWidth, LargeQuarterLineStyle);

         if(DrawOvershootAreas)
         {
            double offset = MajorStep / 40.0; // approximately 25 pips if MajorStep=0.1000
            DrawHorizontalLine("Overshoot_" + IntegerToString(i) + "_up", level + offset, OvershootColor, OvershootLineWidth, OvershootLineStyle);
            DrawHorizontalLine("Undershoot_" + IntegerToString(i) + "_down", level - offset, OvershootColor, OvershootLineWidth, OvershootLineStyle);
         }
      }
   }

   // Draw Small Quarter lines if enabled (optional, finer divisions)
   if(DrawSmallQuarters)
   {
      double segStep = MajorStep / 10.0;
      double smallQuarter = segStep / 4.0;
      for(int seg = 0; seg < 10; seg++)
      {
         double segStart = lowerMajor + seg * segStep;
         for(int j = 1; j < 4; j++)
         {
            double level = segStart + j * smallQuarter;
            string objName = "SmallQuarter_" + IntegerToString(seg) + "_" + IntegerToString(j);
            DrawHorizontalLine(objName, level, SmallQuarterColor, SmallQuarterLineWidth, SmallQuarterLineStyle);
         }
      }
   }
}

Ещё одной важной функцией является CreateOrUpdateLabel, которая отвечает за отображение текста на графике. Эта функция проверяет, существует ли уже метка и, если нет, создает ее. Затем функция обновляет текст, цвет, размер шрифта и другие свойства метки, используя моноширинный шрифт (Courier New), чтобы обеспечить аккуратное выравнивание табличных данных. Эта функция особенно важна для обновления комментариев, разъясняющих рыночную ситуацию.

void CreateOrUpdateLabel(string name, string text, int corner, int xdist, int ydist, color txtColor, int fontSize)
{
   if(ObjectFind(0, name) == -1)
   {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xdist);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, ydist);
      // Set a monospaced font for tabular display (Courier New)
      ObjectSetString(0, name, OBJPROP_FONT, "Courier New");
   }
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, txtColor);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
}

Когда советник инициализируется (в функции OnInit), он создает постоянную метку в верхнем левом углу с сообщением "Intrusion Detector Initialized" (система обнаружения вторжений инициализирована). И наоборот, когда советник удаляется (с помощью функции OnDeinit), происходит очистка, чтобы не загромождать график.

int OnInit()
{
   // Create a persistent commentary label in the top-left corner
   CreateOrUpdateLabel("IntrusionCommentary", "Intrusion Detector Initialized", CORNER_LEFT_UPPER, 10, 10, clrWhite, 14);
   return(INIT_SUCCEEDED);
}

Центральной частью советника является функция OnTick, которая выполняется при каждом новом тике на рынке. Когда советник получает новый тик, срабатывает функция OnTick. Первым шагом в этой функции является установка флага с именем intrusionDetected на false и получение текущей рыночной цены, используя SymbolInfoDouble(_Symbol, SYMBOL_BID). Если возвращаемая цена равна 0 (что указывает на недопустимое или недоступное значение), функция немедленно завершает работу.

void OnTick()
{
   bool intrusionDetected = false;
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(currentPrice == 0)
      return;

Далее советник вызывает функцию DrawQuarters с текущей ценой. Этот вызов отвечает за отображение всех ключевых уровней на графике, включая основные уровни, линии больших четвертей и, а также (при их включении) линии малых четвертей, обеспечивая визуальную структуру, определяющую наш диапазон. Сразу после этого советник пересчитывает границы текущего диапазона в 1000 пипсов, определяя уровень lowerMajor с помощью MathFloor(currentPrice / MajorStep) * MajorStep, а затем добавляя MajorStep, чтобы найти уровень upperMajor.

   // Draw the quarter lines first
   DrawQuarters(currentPrice);

Для наглядного представления того, что обнаруживает советник, создается табличная строка. Таблица начинается с заголовка, который определяет три столбца: Zone (зона), Price (цена) и Reason (причина). В этих столбцах указывается значимость каждого уровня, когда цена приближается к нему.

   double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
   double upperMajor = lowerMajor + MajorStep;

Следующий шаг включает проверку того, находится ли цена вблизи ключевых уровней. Советник сначала проверяет, находится ли цена в пределах заданного допуска нижней (Major Support) или верхней (Major Resistance) границы. Если выполняется хотя бы одно из этих условий, функция добавляет строку в таблицу комментариев с соответствующим сообщением (используя предопределенные сообщения, такие как "Key support level.). Break below signals range shift" (Ключевой уровень поддержки. Пробой ниже уровня сигнала указывает на сдвиг диапазона) для поддержки и похожее сообщение для сопротивления) и устанавливает intrusionDetected на true.

// Check for Major Support
if(MathAbs(currentPrice - lowerMajor) <= AlertTolerance)
{
   table += StringFormat("%-18s | %-8s | %s\n", "Major Support", DoubleToString(lowerMajor,4), MajorSupportReason);
   intrusionDetected = true;
}
// Check for Major Resistance
if(MathAbs(currentPrice - upperMajor) <= AlertTolerance)
{
   table += StringFormat("%-18s | %-8s | %s\n", "Major Resistance", DoubleToString(upperMajor,4), MajorResistanceReason);
   intrusionDetected = true;
}

Если включено отображение линий больших четвертей, советник делит диапазон на четверти и проходит по этим промежуточным уровням. Для каждой крупной четверти система проверяет, находится ли цена в пределах допустимого отклонения; если да, то в таблицу добавляется соответствующая строка с сообщением вида "Decisive break could trigger next 250-PIP move" (решающий прорыв может спровоцировать следующее движение на 250 пунктов). Кроме того, если включены зоны превышения, функция вычисляет небольшое смещение выше и ниже каждого крупного уровня четверти и проверяет, касается ли цена этих зон превышения или понижения, снова добавляя строку в таблицу, если условие выполнено.

if(DrawLargeQuarters)
{
   double LQIncrement = MajorStep / 4.0;
   for(int i = 1; i < 4; i++)
   {
      double level = lowerMajor + i * LQIncrement;
      if(MathAbs(currentPrice - level) <= AlertTolerance)
      {
         table += StringFormat("%-18s | %-8s | %s\n", "Large Quarter", DoubleToString(level,4), LargeQuarterReason);
         intrusionDetected = true;
      }
      if(DrawOvershootAreas)
      {
         double offset = MajorStep / 40.0; // ~25 pips
         double overshootUp = level + offset;
         double undershootDown = level - offset;
         if(MathAbs(currentPrice - overshootUp) <= AlertTolerance)
         {
            table += StringFormat("%-18s | %-8s | %s\n", "Overshoot", DoubleToString(overshootUp,4), OvershootReason);
            intrusionDetected = true;
         }
         if(MathAbs(currentPrice - undershootDown) <= AlertTolerance)
         {
            table += StringFormat("%-18s | %-8s | %s\n", "Undershoot", DoubleToString(undershootDown,4), UndershootReason);
            intrusionDetected = true;
         }
      }
   }
}

При желании, если советник настроен на построение линий небольших четвертей, диапазон подразделяется еще дальше. Функция перебирает эти более мелкие деления и выполняет аналогичные проверки близости, добавляя строки сообщением "Minor fluctuation" (незначительное колебание) всякий раз, когда цена приближается к одному из этих малых уровней четвертей.

if(DrawSmallQuarters)
{
   double segStep = MajorStep / 10.0;
   double smallQuarter = segStep / 4.0;
   for(int seg = 0; seg < 10; seg++)
   {
      double segStart = lowerMajor + seg * segStep;
      for(int j = 1; j < 4; j++)
      {
         double level = segStart + j * smallQuarter;
         if(MathAbs(currentPrice - level) <= AlertTolerance)
         {
            table += StringFormat("%-18s | %-8s | %s\n", "Small Quarter", DoubleToString(level,4), SmallQuarterReason);
            intrusionDetected = true;
         }
      }
   }
}

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

   // If no zones were triggered, still provide full information
   if(!intrusionDetected)
   {
      table = StringFormat("No significant intrusion detected.\nCurrent Price: %s\nMarket consolidating within established quarters.\n", DoubleToString(currentPrice,4));
   }

После создания таблицы комментариев советник обновляет метку графика, используя функцию CreateOrUpdateLabel, что обеспечивает четкое отображение результатов последнего анализа на графике. Наконец, если обнаружено "вторжение" и ранее не было отправлено ни одного оповещения (отслеживается флагом intrusionAlerted), советник отправляет оповещение с содержимым таблицы и устанавливает флаг на true, чтобы предотвратить повторные уведомления. Чтобы все новые объекты и обновления отображались немедленно, функция завершается вызовом метода ChartRedraw.

   // Update the label with the commentary table.
   CreateOrUpdateLabel("IntrusionCommentary", table, CORNER_LEFT_UPPER, 10, 10, clrWhite, 14);

   // Trigger an alert only once per intrusion event.
   if(intrusionDetected && !intrusionAlerted)
   {
      Alert(table);
      intrusionAlerted = true;
   }
   if(!intrusionDetected)
      intrusionAlerted = false;

   ChartRedraw();
}

Полный код MQL5

//+------------------------------------------------------------------+
//|                                               Intrusion Detector |
//|                               Copyright 2025, Christian Benjamin |
//|                          https://www.mql5.com/en/users/lynnchris |
//+------------------------------------------------------------------+
#property copyright "Christian Benjamin"
#property link      "https://www.mql5.com/en/users/lynnchris"
#property version   "1.0"
#property strict

//---- Input parameters -------------------------------------------------
input double MajorStep          = 0.1000;   // Difference between Major Whole Numbers (defines the 1000-PIP Range)
input bool   DrawLargeQuarters  = true;     // Draw intermediate Large Quarter lines.
input bool   DrawSmallQuarters  = false;    // Draw Small Quarter lines.
input bool   DrawOvershootAreas = true;     // Mark overshoot/undershoot areas for Large Quarter lines.
input double AlertTolerance     = 0.0025;   // Tolerance for detecting a "touch" (e.g., ~25 pips for a pair where 1 pip=0.0001)

//---- Color settings ---------------------------------------------------
input color  MajorColor         = 0x2F4F4F; // Dark Slate Gray for Major lines.
input color  LargeQuarterColor  = 0x8B0000; // Dark Red for Large Quarter lines.
input color  SmallQuarterColor  = 0x00008B; // Dark Blue for Small Quarter lines.
input color  OvershootColor     = clrRed;   // Red for overshoot/undershoot lines.

//---- Line style and thickness settings -------------------------------
input ENUM_LINE_STYLE MajorLineStyle       = STYLE_SOLID;
input int    MajorLineWidth                 = 4;
input ENUM_LINE_STYLE LargeQuarterLineStyle  = STYLE_DOT;
input int    LargeQuarterLineWidth          = 3;
input ENUM_LINE_STYLE OvershootLineStyle     = STYLE_DASH;
input int    OvershootLineWidth             = 1;
input ENUM_LINE_STYLE SmallQuarterLineStyle  = STYLE_SOLID;
input int    SmallQuarterLineWidth          = 1;

//---- Commentary Messages (customizable) -----------------------------
input string MajorSupportReason    = "Key support level. Break below signals range shift.";
input string MajorResistanceReason = "Pivotal resistance. Breakout above may start new range.";
input string LargeQuarterReason    = "Decisive break could trigger next 250-PIP move.";
input string OvershootReason       = "Test of breakout; reversal likely if momentum fails.";
input string UndershootReason      = "Insufficient bullish force; possible bearish reversal.";
input string SmallQuarterReason    = "Minor fluctuation.";

// Global flag to avoid repeated alerts while price lingers at a level
bool intrusionAlerted = false;

//+------------------------------------------------------------------+
//| DrawHorizontalLine: Creates or replaces a horizontal line        |
//+------------------------------------------------------------------+
void DrawHorizontalLine(string name, double price, color lineColor, int width, ENUM_LINE_STYLE style)
  {
   if(ObjectFind(0, name) != -1)
      ObjectDelete(0, name);

   if(!ObjectCreate(0, name, OBJ_HLINE, 0, 0, price))
     {
      Print("Failed to create line: ", name);
      return;
     }
   ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
  }

//+------------------------------------------------------------------+
//| DrawQuarters: Draws all quarter lines based on the current price |
//+------------------------------------------------------------------+
void DrawQuarters(double currentPrice)
  {
// Calculate the boundaries of the current 1000-PIP Range
   double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
   double upperMajor = lowerMajor + MajorStep;

// Draw Major Whole Number lines (defining the 1000-PIP Range)
   DrawHorizontalLine("MajorLower", lowerMajor, MajorColor, MajorLineWidth, MajorLineStyle);
   DrawHorizontalLine("MajorUpper", upperMajor, MajorColor, MajorLineWidth, MajorLineStyle);

// Draw Large Quarter lines and their overshoot/undershoot areas if enabled
   if(DrawLargeQuarters)
     {
      double LQIncrement = MajorStep / 4.0;
      for(int i = 1; i < 4; i++)
        {
         double level = lowerMajor + i * LQIncrement;
         string objName = "LargeQuarter_" + IntegerToString(i);
         DrawHorizontalLine(objName, level, LargeQuarterColor, LargeQuarterLineWidth, LargeQuarterLineStyle);

         if(DrawOvershootAreas)
           {
            double offset = MajorStep / 40.0; // approximately 25 pips if MajorStep=0.1000
            DrawHorizontalLine("Overshoot_" + IntegerToString(i) + "_up", level + offset, OvershootColor, OvershootLineWidth, OvershootLineStyle);
            DrawHorizontalLine("Undershoot_" + IntegerToString(i) + "_down", level - offset, OvershootColor, OvershootLineWidth, OvershootLineStyle);
           }
        }
     }

// Draw Small Quarter lines if enabled (optional, finer divisions)
   if(DrawSmallQuarters)
     {
      double segStep = MajorStep / 10.0;
      double smallQuarter = segStep / 4.0;
      for(int seg = 0; seg < 10; seg++)
        {
         double segStart = lowerMajor + seg * segStep;
         for(int j = 1; j < 4; j++)
           {
            double level = segStart + j * smallQuarter;
            string objName = "SmallQuarter_" + IntegerToString(seg) + "_" + IntegerToString(j);
            DrawHorizontalLine(objName, level, SmallQuarterColor, SmallQuarterLineWidth, SmallQuarterLineStyle);
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| CreateOrUpdateLabel: Creates or updates a label with given text  |
//+------------------------------------------------------------------+
void CreateOrUpdateLabel(string name, string text, int corner, int xdist, int ydist, color txtColor, int fontSize)
  {
   if(ObjectFind(0, name) == -1)
     {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xdist);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, ydist);
      // Set a monospaced font for tabular display (Courier New)
      ObjectSetString(0, name, OBJPROP_FONT, "Courier New");
     }
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, txtColor);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
  }

//+------------------------------------------------------------------+
//| OnInit: Initialization function for the EA                       |
//+------------------------------------------------------------------+
int OnInit()
  {
// Create a persistent commentary label in the top-left corner
   CreateOrUpdateLabel("IntrusionCommentary", "Intrusion Detector Initialized", CORNER_LEFT_UPPER, 10, 10, clrWhite, 14);
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| OnDeinit: Deinitialization function for the EA                   |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// Remove the commentary label on exit
   ObjectDelete(0, "IntrusionCommentary");
  }

//+------------------------------------------------------------------+
//| OnTick: Main function called on every tick                       |
//+------------------------------------------------------------------+
void OnTick()
  {
   bool intrusionDetected = true;
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(currentPrice == 0)
      return;

// Draw the quarter lines first
   DrawQuarters(currentPrice);

// Calculate boundaries of the current 1000-PIP Range
   double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
   double upperMajor = lowerMajor + MajorStep;

// Build a tabular commentary string with a header.
   string header = StringFormat("%-18s | %-8s | %s\n", "Zone", "Price", "Reason");
   string separator = "----------------------------------------------\n";
   string table = header + separator;

// Check for Major Support
   if(MathAbs(currentPrice - lowerMajor) <= AlertTolerance)
     {
      table += StringFormat("%-18s | %-8s | %s\n", "Major Support", DoubleToString(lowerMajor,4), MajorSupportReason);
      intrusionDetected = true;
     }
// Check for Major Resistance
   if(MathAbs(currentPrice - upperMajor) <= AlertTolerance)
     {
      table += StringFormat("%-18s | %-8s | %s\n", "Major Resistance", DoubleToString(upperMajor,4), MajorResistanceReason);
      intrusionDetected = true;
     }

// Check Large Quarter Levels and Overshoot/Undershoot Zones
   if(DrawLargeQuarters)
     {
      double LQIncrement = MajorStep / 4.0;
      for(int i = 1; i < 4; i++)
        {
         double level = lowerMajor + i * LQIncrement;
         if(MathAbs(currentPrice - level) <= AlertTolerance)
           {
            table += StringFormat("%-18s | %-8s | %s\n", "Large Quarter", DoubleToString(level,4), LargeQuarterReason);
            intrusionDetected = true;
           }
         if(DrawOvershootAreas)
           {
            double offset = MajorStep / 40.0; // ~25 pips
            double overshootUp = level + offset;
            double undershootDown = level - offset;
            if(MathAbs(currentPrice - overshootUp) <= AlertTolerance)
              {
               table += StringFormat("%-18s | %-8s | %s\n", "Overshoot", DoubleToString(overshootUp,4), OvershootReason);
               intrusionDetected = true;
              }
            if(MathAbs(currentPrice - undershootDown) <= AlertTolerance)
              {
               table += StringFormat("%-18s | %-8s | %s\n", "Undershoot", DoubleToString(undershootDown,4), UndershootReason);
               intrusionDetected = true;
              }
           }
        }
     }

// Check Small Quarter Levels (if enabled)
   if(DrawSmallQuarters)
     {
      double segStep = MajorStep / 10.0;
      double smallQuarter = segStep / 4.0;
      for(int seg = 0; seg < 10; seg++)
        {
         double segStart = lowerMajor + seg * segStep;
         for(int j = 1; j < 4; j++)
           {
            double level = segStart + j * smallQuarter;
            if(MathAbs(currentPrice - level) <= AlertTolerance)
              {
               table += StringFormat("%-18s | %-8s | %s\n", "Small Quarter", DoubleToString(level,4), SmallQuarterReason);
               intrusionDetected = true;
              }
           }
        }
     }

// If no zones were triggered, still provide full information
   if(!intrusionDetected)
     {
      table = StringFormat("No significant intrusion detected.\nCurrent Price: %s\nMarket consolidating within established quarters.\n", DoubleToString(currentPrice,4));
     }

// Update the label with the commentary table.
   CreateOrUpdateLabel("IntrusionCommentary", table, CORNER_LEFT_UPPER, 10, 10, clrWhite, 14);

// Trigger an alert only once per intrusion event.
   if(intrusionDetected && !intrusionAlerted)
     {
      Alert(table);
      // Alternatively, you could use: PlaySound("alert.wav");
      intrusionAlerted = true;
     }
   if(!intrusionDetected)
      intrusionAlerted = false;

   ChartRedraw();
  }
//+------------------------------------------------------------------+


Результаты

Здесь я представлю результаты, полученные после тестирования инструмента в реальных рыночных условиях, хотя для этой цели я использовал демо-счет. Ниже представлен график, отображающий курс новозеландского доллара (NZD) по отношению к доллару США (USD). Цена приблизилась к уровню понижения, что вызвало срабатывание алерта. 

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

NZDUSD

Рис. 3. NZD vs USD

Ниже представлена информация, зарегистрированная во вкладке "Эксперты" в MetaTrader 5.

2025.02.25 16:55:37.188 Intrusion Detector EA (NZDUSD,H1)       Alert: Zone               | Price    | Reason
2025.02.25 16:55:37.188 Intrusion Detector EA (NZDUSD,H1)       ----------------------------------------------
2025.02.25 16:55:37.188 Intrusion Detector EA (NZDUSD,H1)       Undershoot         | 0.5725   | Insufficient bullish force; possible bearish reversal.
2025.02.25 16:55:37.188 Intrusion Detector EA (NZDUSD,H1)       

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

Размещенные сделки

Рис. 4. Торговый тест

Ниже представлена окончательная позиция рынка, несмотря на то, что я быстро закрыл свои сделки.

Рыночное движение

Рис. 5. Рыночное движение

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

USDCAD

Рис. 6. USDCAD



Заключение

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

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

Дата Название инструмента  Описание Версия  Обновления  Примечания
01/10/24 Chart Projector Скрипт для наложения эффекта призрака на движение цены за предыдущий день. 1.0 Первоначальная версия Инструмент номер 1
18/11/24 Analytical Comment Предоставляет информацию за предыдущий день в табличном формате, а также прогнозирует будущее направление рынка. 1.0 Первоначальная версия Инструмент номер 2
27/11/24 Analytics Master Регулярное обновление рыночных показателей каждые два часа  1.01 Вторая версия Инструмент номер 3
02/12/24 Analytics Forecaster  Регулярное обновление рыночных показателей каждые два часа с интеграцией с Telegram 1.1 Третья версия Инструмент номер 4
09/12/24 Volatility Navigator Советник анализирует рыночные условия с помощью полос Боллинджера, RSI и ATR. 1.0 Первоначальная версия Инструмент номер 5
19/12/24 Mean Reversion Signal Reaper  Анализирует рынок и генерирует сигналы, используя стратегию возврата к среднему  1.0  Первоначальная версия  Инструмент номер 6 
9/01/25  Signal Pulse  Анализирует несколько таймфреймов 1.0  Первоначальная версия  Инструмент номер 7 
17/01/25  Metrics Board  Панель с кнопками для анализа  1.0  Первоначальная версия Инструмент номер 8 
21/01/25 External Flow Аналитика с помощью внешних библиотек 1.0  Первоначальная версия Инструмент номер 9 
27/01/25 VWAP Взвешенная по объему средняя цена   1.3  Первоначальная версия  Инструмент номер 10 
02/02/25  Heikin Ashi  Сглаживание тренда и идентификация сигналов разворота  1.0  Первоначальная версия  Инструмент номер 11
04/02/25  FibVWAP  Генерация сигнала с помощью анализа Python  1.0  Первоначальная версия  Инструмент номер 12
14/02/25  RSI DIVERGENCE  Дивергенция цены и RSI  1.0  Первоначальная версия  Инструмент номер 13 
17/02/25  Parabolic Stop and Reverse (PSAR)  Автоматизация стратегии PSAR 1.0 Первоначальная версия  Инструмент номер 14
20/02/25  Скрипт Quarters Drawer  Нанесение уровней четвертей на график  1.0  Первоначальная версия  Инструмент номер 15 
27/02/25 Intrusion Detector Обнаружение и оповещение о достижении ценой уровней четвертей 1.0 Первоначальная версия Инструмент номер 16

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

Прикрепленные файлы |
Разработка инструментария для анализа движения цен (Часть 17): Советник TrendLoom Разработка инструментария для анализа движения цен (Часть 17): Советник TrendLoom
Как ценовой аналитик и трейдер, я заметил, что когда тренд подтверждается на нескольких таймфреймах, он обычно продолжается в этом направлении. Продолжительность тренда может варьироваться в зависимости от стратегии трейдера: держит ли он позиции на долгосрочную перспективу или занимается скальпингом. Выбранные вами таймфреймы играют решающую роль. Статья знакомит с быстрой автоматизированной системой, которая помогает увидеть общий тренд сквозь разные тймфреймы всего одним нажатием кнопки или с помощью регулярных обновлений.
Нейросети в трейдинге: Сеточная аппроксимация событийного потока как инструмент анализа ценовых паттернов (EEMFlow) Нейросети в трейдинге: Сеточная аппроксимация событийного потока как инструмент анализа ценовых паттернов (EEMFlow)
Статья знакомит с архитектурой фреймворка EEMFlow, ориентированного на работу с событийными потоками данных. Особое внимание уделяется адаптивным и многоуровневым модулям, которые обеспечивают гибкую обработку как глобальных, так и локальных изменений. Архитектура фреймворка позволяет сохранять ключевую информацию, минимизировать влияние шума и эффективно формировать признаки для дальнейшего анализа, делая EEMFlow перспективным инструментом для прогнозирования динамики финансовых рынков.
Алгоритм оптимизации одуванчика — Dandelion Optimizer (DO) Алгоритм оптимизации одуванчика — Dandelion Optimizer (DO)
Алгоритм оптимизации одуванчика DO превращает простой полёт семени по ветру в стратегию математического поиска. Три фазы - вихревой подъём, дрейф к центру популяции и приземление по траектории Леви - формируют изящную метафору, которая на практике показывает интересные результаты.
Возможности Мастера MQL5, которые вам нужно знать (Часть 56): Фракталы Билла Вильямса Возможности Мастера MQL5, которые вам нужно знать (Часть 56): Фракталы Билла Вильямса
Фракталы Билла Вильямса — это мощный индикатор, который легко упустить из виду, когда впервые замечаешь его на ценовом графике. Он кажется слишком перегруженным и, вероятно, недостаточно точным. Моя цель - приоткрыть завесу тайны над этим индикатором, рассмотрев различные его паттерны на форвард-тестах применительно к советникам, собранным в Мастере.