English Deutsch 日本語
preview
От новичка до эксперта: Усиление стратегий ликвидности за счет структурного подтверждения на нескольких таймфреймах

От новичка до эксперта: Усиление стратегий ликвидности за счет структурного подтверждения на нескольких таймфреймах

MetaTrader 5 — Примеры |
42 1
Clemence Benjamin
Clemence Benjamin

Вы снова и снова сталкиваетесь с одной и той же проблемой: технически безупречный сетап на M1-M15 почти сразу перестает работать, потому что цена достигает уровня ликвидности на старшем таймфрейме — H1, H4 или дневном. Ручное отслеживание нескольких графиков для подтверждения контекста старшего таймфрейма требует времени, чревато ошибками и приводит к пропуску моментов входа. Нам требуется инженерное решение с четкими критериями приемки: оно должно автоматически обнаруживать зоны ликвидности на выбранном старшем таймфрейме, отображать их на текущем графике и формировать однократный алерт о входе только при возвращении цены в зону и выполнении формализованного условия разворота на младшем таймфрейме. Входные параметры и ограничения должны быть явно заданы (ZoneTimeframe, LookbackBars, RatioMultiplier, ExtendBars, допустимые разворотные паттерны и ограничение частоты алертов).

Для решения этой проблемы мы используем архитектуру из нескольких слоев:

  1. обнаружение зон — поиск паттернов "база → импульс" на выбранном старшем таймфрейме (HTF) с использованием настраиваемого коэффициента для определения зон предложения и спроса;
  2. визуальный слой — отображение зон на текущем графике в виде объектов-прямоугольников, сохраняющихся при смене таймфреймов;
  3. мониторинг реакции — проверка каждого закрытого бара младшего таймфрейма на пересечение с зоной и наличие одного из небольшого набора формализованных разворотных паттернов (паттерн поглощения, пин-бар, пробой внутреннего бара).

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


Содержание


Введение

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

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

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

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

Особую эффективность этому подходу придает концепция зон премии и дисконта. Если построить уровни коррекции Фибоначчи от свингового минимума до свингового максимума, уровень 50% будет обозначать равновесие. Ниже уровня 50% находится зона дисконта (более низкие цены), а выше — зона премии (более высокие цены). В бычьем контексте следует покупать от бычьего блока ордеров, расположенного в зоне дисконта. В медвежьем контексте следует продавать от медвежьего блока ордеров, расположенного в зоне премии.

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

Рассмотрим основную логику исходного индикатора, поскольку она лежит в основе дальнейшего обсуждения:

//+------------------------------------------------------------------+
//|                                         Liquidity Zone Indicator |
//|                                Copyright © 2026 Clemence Benjamin|
//|             https://www.mql5.com/ru/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/ru/users/billionaire2024/seller"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 8
#property indicator_plots   4

//+------------------------------------------------------------------+
//| Input  Variables                                                 |
//+------------------------------------------------------------------+
input int           LookbackBars      = 1000;     //--- Number of bars to look back for zones
input double        RatioMultiplier   = 3.0;      //--- Ratio multiplier (3x)
input int           ExtendBars        = 50;       //--- Bars to extend rectangle right
input bool          FillRectangles    = true;     //--- Fill or just border?
input color         DemandZoneColor   = clrLimeGreen;
input color         SupplyZoneColor   = clrRed;
input uchar         ZoneOpacity       = 40;       //--- 0-255 transparency
input bool          ShowBuffers       = true;     //--- Show buffer lines on chart
input ENUM_LINE_STYLE BufferStyle     = STYLE_DASH; //--- Buffer line style
input int           BufferWidth       = 1;        //--- Buffer line width

//+------------------------------------------------------------------+
//| Global Variables                                                 |
//+------------------------------------------------------------------+
double myPoint;
double Open[], Close[], High[], Low[];
datetime TimeArray[];

//--- Indicator Buffers
double DemandZoneHighBuffer[];
double DemandZoneLowBuffer[];
double SupplyZoneHighBuffer[];
double SupplyZoneLowBuffer[];
double DemandZoneSignalBuffer[];
double SupplyZoneSignalBuffer[];
double ZoneStartTimeBuffer[];
double ZoneEndTimeBuffer[];

//+------------------------------------------------------------------+
//| Candle range helper                                              |
//+------------------------------------------------------------------+
double CandleRange(const int i)
  {
   return(High[i] - Low[i]);
  }

//+------------------------------------------------------------------+
//| Draw a liquidity zone                                            |
//+------------------------------------------------------------------+
void DrawZone(const int base_idx, const bool isDemand, const int rates_total)
  {
   datetime start_time = TimeArray[base_idx];
   datetime end_time   = start_time + ExtendBars * PeriodSeconds();

   string obj_name = "LiqZone_" + IntegerToString(start_time);

   //--- Delete existing object if present
   if(ObjectFind(0, obj_name) >= 0)
     ObjectDelete(0, obj_name);

   //--- Determine zone top/bottom
   double top, bottom;
   if(isDemand)
     {
      top    = High[base_idx];
      bottom = Low[base_idx];
     }
   else
     {
      top    = High[base_idx];
      bottom = Low[base_idx];
     }

   //--- Create rectangle
   if(ObjectCreate(0, obj_name, OBJ_RECTANGLE, 0, start_time, top, end_time, bottom))
     {
      color zone_color = isDemand ? DemandZoneColor : SupplyZoneColor;
      ObjectSetInteger(0, obj_name, OBJPROP_COLOR, zone_color);
      ObjectSetInteger(0, obj_name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, obj_name, OBJPROP_FILL, FillRectangles);
      ObjectSetInteger(0, obj_name, OBJPROP_BACK, true);
      ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false);

      if(FillRectangles)
        {
         color fill_clr = (color)ColorToARGB(zone_color, ZoneOpacity);
         ObjectSetInteger(0, obj_name, OBJPROP_BGCOLOR, fill_clr);
        }
     }

   //--- Optional buffers for visualization
   if(ShowBuffers && base_idx < rates_total)
     {
      if(isDemand)
        {
         DemandZoneHighBuffer[base_idx] = top;
         DemandZoneLowBuffer[base_idx]  = bottom;
        }
      else
        {
         SupplyZoneHighBuffer[base_idx] = top;
         SupplyZoneLowBuffer[base_idx]  = bottom;
        }

      ZoneStartTimeBuffer[base_idx] = start_time;
      ZoneEndTimeBuffer[base_idx]   = end_time;
     }
  }

//+------------------------------------------------------------------+
//| Calculates indicator values                                      |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   //--- Not enough bars to calculate
   if(rates_total < 5)
     return(0);

   //--- Copy data into series arrays
   int bars_to_process = MathMin(rates_total, 5000);
   CopyOpen(Symbol(), PERIOD_CURRENT, 0, bars_to_process, Open);
   CopyClose(Symbol(), PERIOD_CURRENT, 0, bars_to_process, Close);
   CopyHigh(Symbol(), PERIOD_CURRENT, 0, bars_to_process, High);
   CopyLow(Symbol(), PERIOD_CURRENT, 0, bars_to_process, Low);
   CopyTime(Symbol(), PERIOD_CURRENT, 0, bars_to_process, TimeArray);

   ArraySetAsSeries(Open, true);
   ArraySetAsSeries(Close, true);
   ArraySetAsSeries(High, true);
   ArraySetAsSeries(Low, true);
   ArraySetAsSeries(TimeArray, true);

   //--- Iterate over bars to detect zones
   for(int i = bars_to_process - 3; i >= 0; i--)
     {
      int base_idx    = i + 2;
      int impulse_idx = i + 1;

      double base_range    = CandleRange(base_idx);
      double impulse_range = CandleRange(impulse_idx);

      //--- Demand zone: both bars bullish, impulse breaks out
      if(Open[base_idx] < Close[base_idx] &&
         Open[impulse_idx] < Close[impulse_idx] &&
         impulse_range >= base_range * RatioMultiplier)
        {
         DrawZone(base_idx, true, rates_total);
        }

      //--- Supply zone: both bars bearish, impulse breaks out
      if(Open[base_idx] > Close[base_idx] &&
         Open[impulse_idx] > Close[impulse_idx] &&
         impulse_range >= base_range * RatioMultiplier)
        {
         DrawZone(base_idx, false, rates_total);
        }
     }

   //--- Return total number of processed bars
   return(rates_total);
  }
//+------------------------------------------------------------------+

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

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

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

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

Эта новая архитектура требует коренного изменения подхода. Исходный индикатор работал изолированно на одном графике. Наша новая система должна быть организована по слоям:

  • Слой обнаружения зон: он будет работать на выбранном пользователем старшем таймфрейме (например, H1) и использовать исходную логику "база → импульс" для выявления и сохранения зон ликвидности.
  • Визуальный слой: зоны должны отображаться на текущем графике в виде сохраняющихся прямоугольников и оставаться видимыми независимо от того, на каком таймфрейме мы торгуем.
  • Слой мониторинга реакции: на текущем младшем таймфрейме (например, M5) он будет анализировать каждый закрытый бар. Он будет проверять, пересекается ли ценовой диапазон бара с какой-либо активной зоной старшего таймфрейма и присутствует ли разворотный паттерн. Если выполняются оба условия, генерируется сигнал.

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


Реализация

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

Шаг 1 — создание основы индикатора и настройка входных параметров

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

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

//+------------------------------------------------------------------+
//|                                 Liquidity Zone Reaction Indicator|
//|                                Copyright © 2026 Clemence Benjamin|
//|             https://www.mql5.com/ru/users/billionaire2024/seller |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/ru/users/billionaire2024/seller"
#property version   "3.0"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

//--- Inputs for zone detection (higher timeframe)
input ENUM_TIMEFRAMES ZoneTimeframe   = PERIOD_H1;       // Timeframe for zone detection
input int             LookbackBars    = 1000;            // Number of bars on higher TF to look back
input double          RatioMultiplier = 3.0;             // Ratio Multiplier (3x)
input int             ExtendBars      = 50;              // Bars to extend rectangle right (on higher TF)
input bool            FillRectangles  = true;            // Fill or just border?
input color           DemandZoneColor = clrLimeGreen;    // Demand zone rectangle color
input color           SupplyZoneColor = clrTomato;       // Supply zone rectangle color
input uchar           ZoneOpacity     = 40;              // 0-255 transparency

//--- Inputs for reaction signals (current timeframe)
input color           DemandArrowColor = clrLimeGreen;   // Buy arrow color
input color           SupplyArrowColor = clrRed;         // Sell arrow color
input int             ArrowSize        = 1;              // Arrow width

//--- Arrow buffers
double BuyArrow[];
double SellArrow[];
//+------------------------------------------------------------------+

Шаг 2 — определение структуры зоны и глобальных переменных

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

//--- Current timeframe data arrays (for reversal patterns)
double Open[], Close[], High[], Low[];
datetime TimeArray[];

//--- Global variables
double    myPoint;
int       htfPeriodSeconds;               // period seconds of the higher timeframe
int       currentPeriodSeconds;           // current timeframe period in seconds
datetime  lastHtfUpdateTime = 0;          // last time zones were updated
datetime  lastAlertTime = 0;               // for alert throttling

//+------------------------------------------------------------------+
//| Zone structure                                                   |
//+------------------------------------------------------------------+
struct LiquidityZone
  {
   double            high;              // zone top price
   double            low;               // zone bottom price
   datetime          start_time;        // bar time where zone was formed
   datetime          expiry_time;       // time when zone expires (start + ExtendBars * htfPeriod)
   bool              demand;            // true = demand, false = supply
   bool              triggered;         // true if a signal already fired in this zone
  };

LiquidityZone zones[];          // dynamic array of currently active zones
//+------------------------------------------------------------------+

Шаг 3 — инициализация и настройка буферов

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

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   myPoint = Point();
   if(_Digits == 5 || _Digits == 3)
      myPoint *= 10;

//--- Set arrow buffers
   SetIndexBuffer(0, BuyArrow, INDICATOR_DATA);
   SetIndexBuffer(1, SellArrow, INDICATOR_DATA);

//--- Make arrow buffers series so index 0 = newest bar
   ArraySetAsSeries(BuyArrow, true);
   ArraySetAsSeries(SellArrow, true);

   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_ARROW);
   PlotIndexSetInteger(0, PLOT_ARROW, 233);                 // up arrow
   PlotIndexSetString(0, PLOT_LABEL, "Buy");
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, DemandArrowColor);
   PlotIndexSetInteger(0, PLOT_LINE_WIDTH, ArrowSize);

   PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_ARROW);
   PlotIndexSetInteger(1, PLOT_ARROW, 234);                 // down arrow
   PlotIndexSetString(1, PLOT_LABEL, "Sell");
   PlotIndexSetInteger(1, PLOT_LINE_COLOR, SupplyArrowColor);
   PlotIndexSetInteger(1, PLOT_LINE_WIDTH, ArrowSize);

   ArrayInitialize(BuyArrow, EMPTY_VALUE);
   ArrayInitialize(SellArrow, EMPTY_VALUE);

//--- Period seconds for time calculations
   currentPeriodSeconds = PeriodSeconds();
   htfPeriodSeconds = PeriodSeconds(ZoneTimeframe);
   if(htfPeriodSeconds <= 0)
      htfPeriodSeconds = 3600;       // fallback to 1 hour

//--- Initial update of zones (will be done in OnCalculate)
   lastHtfUpdateTime = 0;

   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+

Шаг 4 — очистка и управление объектами

Корректное удаление созданных объектов необходимо для индикаторов профессионального уровня. При удалении индикатора с графика следует удалять все созданные им объекты — в нашем случае прямоугольники зон, — чтобы они не загромождали график. Функция OnDeinit() выполняет это автоматически, вызывая DeleteAllZones() для удаления всех объектов, имена которых начинаются с "LiqZone_".

//+------------------------------------------------------------------+
//| Deinitialization                                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   DeleteAllZones();
  }

//+------------------------------------------------------------------+
//| Delete all drawn zone rectangles                                 |
//+------------------------------------------------------------------+
void DeleteAllZones()
  {
   for(int i = ObjectsTotal(0, 0, -1) - 1; i >= 0; i--)
     {
      string name = ObjectName(0, i);
      if(StringFind(name, "LiqZone_") == 0)
         ObjectDelete(0, name);
     }
   ChartRedraw();
  }
//+------------------------------------------------------------------+

Шаг 5 — отрисовка прямоугольников зон для наглядности

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

//+------------------------------------------------------------------+
//| Draw a single zone rectangle                                     |
//+------------------------------------------------------------------+
void DrawZone(datetime start_time, datetime expiry_time,
              double price_top, double price_bottom, bool isDemand)
  {
   string obj_name = "LiqZone_" + IntegerToString(start_time) + "_" + IntegerToString(expiry_time);
   if(ObjectFind(0, obj_name) >= 0)
      ObjectDelete(0, obj_name);

   color zone_color = isDemand ? DemandZoneColor : SupplyZoneColor;

   if(ObjectCreate(0, obj_name, OBJ_RECTANGLE, 0,
                   start_time, price_top, expiry_time, price_bottom))
     {
      ObjectSetInteger(0, obj_name, OBJPROP_COLOR, zone_color);
      ObjectSetInteger(0, obj_name, OBJPROP_STYLE, STYLE_SOLID);
      ObjectSetInteger(0, obj_name, OBJPROP_WIDTH, 1);
      ObjectSetInteger(0, obj_name, OBJPROP_FILL, FillRectangles);
      ObjectSetInteger(0, obj_name, OBJPROP_BACK, true);           // behind price
      ObjectSetInteger(0, obj_name, OBJPROP_SELECTABLE, false);

      if(FillRectangles)
        {
         color fill_clr = (color)ColorToARGB(zone_color, ZoneOpacity);
         ObjectSetInteger(0, obj_name, OBJPROP_BGCOLOR, fill_clr);
        }
     }
  }
//+------------------------------------------------------------------+

Шаг 6 — обнаружение зон на старшем таймфрейме

В основе нашего подхода с использованием нескольких таймфреймов лежит UpdateZones(). Эта функция вызывается при появлении каждого нового бара на выбранном старшем таймфрейме. Она копирует необходимые ценовые данные, ищет паттерны "база → импульс", создает структуры LiquidityZone для каждого допустимого паттерна и отображает соответствующие прямоугольники на графике.

//+------------------------------------------------------------------+
//| Update zones from higher timeframe data                          |
//+------------------------------------------------------------------+
void UpdateZones()
  {
//--- Delete old rectangles and clear zone array
   DeleteAllZones();
   ArrayResize(zones, 0);

//--- Copy higher timeframe data
   double htf_open[], htf_close[], htf_high[], htf_low[];
   datetime htf_time[];
   int htf_bars = CopyOpen(Symbol(), ZoneTimeframe, 0, LookbackBars, htf_open);
   if(htf_bars <= 0)
      return;
   CopyClose(Symbol(), ZoneTimeframe, 0, LookbackBars, htf_close);
   CopyHigh(Symbol(), ZoneTimeframe, 0, LookbackBars, htf_high);
   CopyLow(Symbol(), ZoneTimeframe, 0, LookbackBars, htf_low);
   CopyTime(Symbol(), ZoneTimeframe, 0, LookbackBars, htf_time);

//--- Work with series (index 0 = most recent)
   ArraySetAsSeries(htf_open,  true);
   ArraySetAsSeries(htf_close, true);
   ArraySetAsSeries(htf_high,  true);
   ArraySetAsSeries(htf_low,   true);
   ArraySetAsSeries(htf_time,  true);

//--- Scan higher timeframe bars for zones
   for(int i = htf_bars - 5; i >= 2; i--)
     {
      int base_idx    = i + 2;
      int impulse_idx = i + 1;

      if(base_idx >= htf_bars)
         continue;

      double base_range    = htf_high[base_idx] - htf_low[base_idx];
      double impulse_range = htf_high[impulse_idx] - htf_low[impulse_idx];

      if(base_range <= 0)
         continue;
      //+------------------------------------------------------------------+

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

//--- Creating a new liquidity Zone                                            

if(htf_open[base_idx] < htf_close[base_idx] &&
   htf_open[impulse_idx] < htf_close[impulse_idx] &&
   impulse_range >= base_range * RatioMultiplier)
  {
   LiquidityZone z;
   z.high        = htf_high[base_idx];
   z.low         = htf_low[base_idx];
   z.start_time  = htf_time[base_idx];
   z.expiry_time = z.start_time + ExtendBars * htfPeriodSeconds;
   z.demand      = true;
   z.triggered   = false;

   int size = ArraySize(zones);
   ArrayResize(zones, size + 1);
   zones[size] = z;

   DrawZone(z.start_time, z.expiry_time, z.high, z.low, true);
  }
//+------------------------------------------------------------------+

Шаг 7 — определение разворотных паттернов для подтверждения входа в сделку

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

//+------------------------------------------------------------------+
//| Bullish Engulfing Pattern                                        |
//| Detects bullish reversal when current candle engulfs previous    |
//| bearish candle body                                              |
//+------------------------------------------------------------------+
bool BullishEngulfing(int i)
  {
   if(Close[i] > Open[i] &&
      Close[i+1] < Open[i+1] &&
      Close[i] > Open[i+1] &&
      Open[i] < Close[i+1])
      return true;
   return false;
  }

//+------------------------------------------------------------------+
//| Bearish Engulfing Pattern                                        |
//| Detects bearish reversal when current candle engulfs previous    |
//| bullish candle body                                              |
//+------------------------------------------------------------------+
bool BearishEngulfing(int i)
  {
   if(Close[i] < Open[i] &&
      Close[i+1] > Open[i+1] &&
      Open[i] > Close[i+1] &&
      Close[i] < Open[i+1])
      return true;
   return false;
  }

//+------------------------------------------------------------------+
//| Bullish Pin Bar Pattern                                          |
//| Long lower wick indicating rejection of lower prices             |
//+------------------------------------------------------------------+
bool BullishPinBar(int i)
  {
   double body = MathAbs(Close[i] - Open[i]);
   double lower = MathMin(Open[i], Close[i]) - Low[i];
   if(lower > body * 2)
      return true;
   return false;
  }

//+------------------------------------------------------------------+
//| Bearish Pin Bar Pattern                                          |
//| Long upper wick indicating rejection of higher prices            |
//+------------------------------------------------------------------+
bool BearishPinBar(int i)
  {
   double body = MathAbs(Close[i] - Open[i]);
   double upper = High[i] - MathMax(Open[i], Close[i]);
   if(upper > body * 2)
      return true;
   return false;
  }

//+------------------------------------------------------------------+
//| Bullish Inside Bar Breakout                                      |
//| Inside bar structure followed by bullish breakout                |
//+------------------------------------------------------------------+
bool BullishInsideBreak(int i)
  {
   if(High[i] < High[i+1] && Low[i] > Low[i+1])
      if(Close[i] > High[i+1])
         return true;
   return false;
  }

//+------------------------------------------------------------------+
//| Bearish Inside Bar Breakout                                      |
//| Inside bar structure followed by bearish breakout                |
//+------------------------------------------------------------------+
bool BearishInsideBreak(int i)
  {
   if(High[i] < High[i+1] && Low[i] > Low[i+1])
      if(Close[i] < Low[i+1])
         return true;
   return false;
  }

//+------------------------------------------------------------------+
//| Demand Zone Reversal Detector                                    |
//| Combines bullish reversal patterns for demand zones              |
//+------------------------------------------------------------------+
bool DemandReversal(int i)
  {
   if(BullishEngulfing(i))
      return true;
   if(BullishPinBar(i))
      return true;
   if(BullishInsideBreak(i))
      return true;
   return false;
  }

//+------------------------------------------------------------------+
//| Supply Zone Reversal Detector                                    |
//| Combines bearish reversal patterns for supply zones              |
//+------------------------------------------------------------------+
bool SupplyReversal(int i)
  {
   if(BearishEngulfing(i))
      return true;
   if(BearishPinBar(i))
      return true;
   if(BearishInsideBreak(i))
      return true;
   return false;
  }
//+------------------------------------------------------------------+

Шаг 8 — двухпроходная логика OnCalculate

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

//+------------------------------------------------------------------+
//| Calculates indicator values and updates zones                    |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
  {
   //--- Not enough bars to calculate
   if(rates_total < 5)
     return(0);

   //--- Copy current timeframe data into global arrays (as series)
   int barsNeeded = MathMin(rates_total, 5000);  // safe upper limit
   CopyOpen(Symbol(), PERIOD_CURRENT, 0, barsNeeded, Open);
   CopyClose(Symbol(), PERIOD_CURRENT, 0, barsNeeded, Close);
   CopyHigh(Symbol(), PERIOD_CURRENT, 0, barsNeeded, High);
   CopyLow(Symbol(), PERIOD_CURRENT, 0, barsNeeded, Low);
   CopyTime(Symbol(), PERIOD_CURRENT, 0, barsNeeded, TimeArray);

   ArraySetAsSeries(Open,      true);
   ArraySetAsSeries(Close,     true);
   ArraySetAsSeries(High,      true);
   ArraySetAsSeries(Low,       true);
   ArraySetAsSeries(TimeArray, true);

   //--- Update zones if a new higher timeframe bar appears
   datetime latestHtfTime = iTime(Symbol(), ZoneTimeframe, 0);
   if(latestHtfTime != lastHtfUpdateTime)
     {
      UpdateZones();
      lastHtfUpdateTime = latestHtfTime;
     }

   //--- further calculations continue here...

   return(rates_total);
  }
//+------------------------------------------------------------------+

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

//---------------------------------------------------------------
// 1) Loop over ALL completed bars (oldest to newest) and set arrows
//    This makes historical signals appear on the chart.
//---------------------------------------------------------------
for(int i = rates_total - 2; i >= 0; i--)   // i = bar index (0 = newest)
  {
   bool buySignal  = false;
   bool sellSignal = false;

// Check every active zone
   for(int z = 0; z < ArraySize(zones); z++)
     {
      // --- Time validity: bar must be within zone's lifetime ---
      if(TimeArray[i] < zones[z].start_time || TimeArray[i] > zones[z].expiry_time)
         continue;

      // --- Price inside zone? ---
      if(!PriceInsideZone(i, zones[z], High, Low))
         continue;

      // --- Reversal pattern? ---
      if(zones[z].demand && DemandReversal(i))
         buySignal = true;
      if(!zones[z].demand && SupplyReversal(i))
         sellSignal = true;
     }

// Set arrow (or EMPTY_VALUE if no signal)
   BuyArrow[i]  = buySignal  ? Low[i] - 5 * myPoint : EMPTY_VALUE;
   SellArrow[i] = sellSignal ? High[i] + 5 * myPoint : EMPTY_VALUE;
  }
//+------------------------------------------------------------------+

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

//--------------------------------------------------------------------------
//     2)   Real-time alert handling (only for the most recent closed bars |
//   Uses the 'triggered' flag to avoid repeated alerts from the same zone.|
//--------------------------------------------------------------------------
int signal_bar = 1;   // last closed bar
if(signal_bar < rates_total)
  {
   for(int z = 0; z < ArraySize(zones); z++)
     {
      // Skip if zone already triggered
      if(zones[z].triggered)
         continue;

      // Time validity (using the bar's time, not TimeCurrent)
      if(TimeArray[signal_bar] < zones[z].start_time || TimeArray[signal_bar] > zones[z].expiry_time)
         continue;

      // Price inside?
      if(!PriceInsideZone(signal_bar, zones[z], High, Low))
         continue;

      // Reversal?
      if(zones[z].demand && DemandReversal(signal_bar))
        {
         zones[z].triggered = true;
         if(TimeArray[signal_bar] != lastAlertTime)
           {
            Alert("Demand Zone BUY Reaction ", Symbol(), " ", EnumToString(_Period));
            lastAlertTime = TimeArray[signal_bar];
           }
        }
      else
         if(!zones[z].demand && SupplyReversal(signal_bar))
           {
            zones[z].triggered = true;
            if(TimeArray[signal_bar] != lastAlertTime)
              {
               Alert("Supply Zone SELL Reaction ", Symbol(), " ", EnumToString(_Period));
               lastAlertTime = TimeArray[signal_bar];
              }
           }
     }
  }

return(rates_total);
  }
//+------------------------------------------------------------------+

Шаг 9 — проверка нахождения цены внутри зоны

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

//+------------------------------------------------------------------+
//| Check if a bar is inside a zone                                  |
//+------------------------------------------------------------------+
bool PriceInsideZone(int bar, const LiquidityZone &z,
                     const double &high[], const double &low[])
  {
   if(high[bar] >= z.low && low[bar] <= z.high)
      return true;
   return false;
  }
//+------------------------------------------------------------------+

Объединив все эти компоненты, мы создали комплексный индикатор отслеживания реакции на зоны ликвидности на нескольких таймфреймах. Он выявляет институциональные зоны, отслеживает появление в них торговых возможностей и уведомляет трейдера, когда ими можно воспользоваться.


Тестирование и результаты

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

Визуализация работы индикатора на EURUSD M5 в Тестере стратегий

На рисунке 1 показано, как Тестер стратегий позволяет за несколько минут воспроизвести месяцы исторической динамики цены для быстрой оценки эффективности.

Подходы к тестированию

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

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

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

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

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

Процедура тестирования

Часть A — Тестер стратегий

1. Откройте Тестер стратегий MetaTrader 5, выберите символ EURUSD, установите таймфрейм M5 и задайте период исторических данных со значительными ценовыми движениями (например, январь - март 2020 года). Включите "Визуальный режим", чтобы наблюдать за работой индикатора на каждом баре. Выберите индикатор в Тестере стратегий.

2. Настройте входные параметры индикатора: установите для ZoneTimeframe значение PERIOD_H1 (анализ на старшем таймфрейме), для RatioMultiplier — 2,25 (для тестирования на EURUSD), а остальные параметры внешнего вида задайте по своему усмотрению (на рис. 2 показан типичный вариант настройки).

Окно свойств индикатора с настройками входных параметров

Рис. 2. Входные параметры индикатора для обнаружения зон на H1 при работе с EURUSD M5

3. Запустите тестирование на исторических данных в визуальном режиме. Наблюдайте за появлением прямоугольников зон в значимых структурных точках, где за баром консолидации следовал импульсный бар, а их соотношение достигало порога 3× или превышало его. Обратите внимание, что зоны строятся по данным старшего таймфрейма, но отображаются на графике M5 — именно так и должна работать визуализация с использованием нескольких таймфреймов.

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

5. После завершения моделирования прокрутите график назад по истории. Убедитесь, что стрелки отображаются на каждом баре, где выполнялись условия. Это подтверждает, что двухпроходная логика OnCalculate корректно отображает сигналы на исторических данных; на графике в реальном времени такая проверка заняла бы недели.

Часть B — график в реальном времени

1. Добавьте индикатор на график EURUSD M5 в реальном времени с теми же значениями входных параметров. Дайте индикатору поработать несколько часов и проследите за его работой.

2. Наблюдайте за графиком в реальном времени, отслеживая формирование новых зон и появление сигналов реакции. На рис. 3 приведен пример графика в реальном времени, на котором хорошо видны зоны H1, а в точках разворота нанесены стрелки реакции M5.

График в реальном времени с зонами старшего таймфрейма и стрелками реакции на младшем таймфрейме

Рис. 3. Зоны ликвидности H1 (прямоугольники) и стрелки реакции M5 в местах, где внутри зон возникали разворотные паттерны.

3. Следите за сообщениями алертов в окне терминала. Когда новый бар закрывается внутри зоны и формируется разворотный паттерн, должен сработать алерт в формате "Demand Zone BUY Reaction EURUSD PERIOD_M5" или "Supply Zone SELL Reaction EURUSD PERIOD_M5". На рис. 4 представлена подборка таких алертов, зафиксированных при тестировании на графике в реальном времени.

Алерты с сигналами на покупку и продажу, сформированными индикатором

Рис. 4. Уведомления формировались в реальном времени, когда цена входила в зоны и появлялись разворотные паттерны (зафиксированы после длительного наблюдения за графиком в реальном времени).

Полученные результаты

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

Точность обнаружения зон: как в Тестере стратегий, так и в режиме реального времени зоны старшего таймфрейма стабильно совпадали со значимыми структурными уровнями. При возвращении цены в эти зоны часто наблюдалась реакция, что подтверждало их значимость для институциональных участников. Для EURUSD и XAUUSD мы использовали коэффициент 2,25 вместо значения по умолчанию 3,0, а для более волатильных инструментов, таких как Volatility 10 (1s) Index, требовался более низкий коэффициент (2,0), чтобы формировались значимые зоны, — именно на это указывало первоначальное исследование.

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

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

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

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

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


Заключение

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

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

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

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

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

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

Я приложил исходный код как учебный материал и как практический инструмент. Изучите его, разберитесь в нем и адаптируйте под себя. Если у вас есть вопросы, замечания или предложения по улучшению, пожалуйста, делитесь ими в разделе комментариев ниже. Лучшие торговые инструменты совершенствуются благодаря отзывам сообщества и совместной работе.

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

Основные выводы

Основной вывод Описание
Согласование нескольких таймфреймов Старшие таймфреймы задают структурный контекст и показывают направленность действий институциональных участников, а младшие позволяют точно выбрать момент входа. Когда они согласованы, вероятность успешной сделки значительно возрастает.
Зоны ликвидности как структурные уровни Области, где институциональные участники размещают ордера (зоны спроса и предложения), выступают естественными уровнями поддержки и сопротивления. Цена часто возвращается в эти зоны, прежде чем продолжить движение или развернуться.
Разворотные паттерны дают подтверждение Одного нахождения цены внутри зоны недостаточно — дождитесь подтверждающих паттернов (поглощения, пин-баров, пробоев внутреннего бара), которые показывают реакцию институциональных участников на этом уровне.
Двухпроходная обработка сигналов Отделяйте отображение исторических сигналов от формирования алертов в реальном времени. Это позволяет трейдерам анализировать прошлые сигналы и одновременно предотвращает назойливые повторы алертов в реальном времени.
Правильная индексация буферов Для буферов стрелок необходимо установить индексацию как у временных рядов (ArraySetAsSeries), чтобы согласовать ее с индексацией ценовых массивов. Без этого стрелки отображаются не на тех барах, а исторические сигналы исчезают.
Наглядное отображение с помощью объектов Использование OBJ_RECTANGLE для отображения зон обеспечивает их видимость при смене таймфрейма и позволяет настраивать цвет, заливку и прозрачность для лучшей различимости.
Управление динамическими массивами Использование динамического массива структур зон позволяет обрабатывать неограниченное количество активных зон, каждая из которых имеет собственные свойства и признак срабатывания.
Ограничение срока действия Срок действия зон должен истекать через определенное количество баров (ExtendBars), чтобы устаревшие уровни не загромождали график и не приводили к появлению ложных сигналов.
Ограничение частоты алертов Отслеживайте lastAlertTime, чтобы не допускать нескольких алертов по одному и тому же бару и обеспечивать четкие уведомления без повторов.
Отдельные функции для паттернов Вынесите проверку каждого разворотного паттерна в отдельную функцию, а затем объедините эти проверки в DemandReversal/SupplyReversal. Это упрощает добавление новых паттернов и дальнейшее сопровождение кода.

Приложения

Имя файла Версия Тип Описание
Liquidity_Zone_Reaction_Indicator.mq5 3,0 Индикатор на MQL5 Полнофункциональный индикатор реакции на зоны ликвидности с использованием нескольких таймфреймов, отображением зон в виде прямоугольников, сигналами в виде стрелок и алертами


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

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (1)
M7f0OhpK4
M7f0OhpK4 | 17 мая 2026 в 10:25
ОГРОМНОЕ СПАСИБО. С нетерпением жду ваших отзывов в ближайшее время.
От новичка до эксперта: Выявление переворотов зон ликвидности на языке MQL5 От новичка до эксперта: Выявление переворотов зон ликвидности на языке MQL5
В этой статье представлен индикатор на языке MQL5, который обнаруживает перевороты зон ликвидности и управляет ими. Он выявляет зоны предложения и спроса на старших таймфреймах с помощью паттерна "база → импульс", применяет формализованные пороговые значения для пробоя и силы импульса и автоматически переворачивает зоны при изменении структуры. В результате получается динамическая карта поддержки и сопротивления, которая сокращает необходимость ручной перерисовки и дает понятный и практически применимый контекст для оценки сигналов и ретестов.
Память между перезапусками: Сохранение состояния советника с помощью двоичных файлов в MQL5 Память между перезапусками: Сохранение состояния советника с помощью двоичных файлов в MQL5
В статье представлена структурированная программная основа MQL5 для сериализации внутреннего состояния советника в локальные двоичные файлы. Она предотвращает сброс данных при перезапусках платформы, надёжно сохраняя непосредственно на диск хранящиеся в оперативной памяти показатели, такие как количество сделок и множители. Данная архитектура представляет собой более надёжную альтернативу глобальным переменным терминала для обеспечения непрерывности состояния.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Пошаговая реализация локальной системы стоп-лоссов на MQL5 Пошаговая реализация локальной системы стоп-лоссов на MQL5
В этой статье показано, как создать локальную систему стоп-лоссов в советнике MQL5, которая поддерживает уровни стоп-лосса на стороне терминала. В ней подробно рассматривается логика выполнения, обработчики событий, входные данные и объектно-ориентированный подход с использованием объектов CTrade, CPositionInfo, CHashMap/CHashSet, а также графических объектов. Вам предстоит реализовать одновременное отслеживание нескольких позиций, перемещаемые стоп-уровни, визуальные разделители и метки, а также функции очистки и разрыва соединения для создания практичного инструмента управления рисками.