English Deutsch 日本語
preview
Повторное использование нарушенных ордер-блоков в качестве блоков смягчения (SMC)

Повторное использование нарушенных ордер-блоков в качестве блоков смягчения (SMC)

MetaTrader 5Примеры |
54 0
Hlomohang John Borotho
Hlomohang John Borotho

Содержание:

  1. Введение
  2. Рассмотрение ордер-блока смягчения
  3. Начало
  4. Результаты тестирования на истории
  5. Заключение


Введение

При торговле в рамках Концепции умных денег (SMC) блоки ордеров представляют собой ключевые области, где институциональные трейдеры накапливают или распределяют позиции, прежде чем направить цену в определенном направлении. Однако не все блоки ордеров остаются действительными — некоторые из них нарушаются по мере изменения рыночных условий. Когда ордер-блок перестает выполняться, он не обязательно теряет свою значимость; вместо этого он превращается в потенциальный блок смягчения последствий. Эта концепция фокусирует внимание на том, как цена часто возвращается к этим недействительным зонам, чтобы “смягчить” оставшиеся ордера, прежде чем продолжить движение в направлении нового тренда, предлагая трейдерам более глубокое понимание институциональных намерений и поведения рыночной структуры.

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


Рассмотрение ордер-блока смягчения:

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

Когда рынок начинает снижаться, он обычно формирует паттерн ‘M’, представляющий собой неудачное колебание, которое сигнализирует об исчерпании бычьего импульса вблизи уровня сопротивления. Второй пик ‘M’, как правило, не приводит к созданию нового максимума, показывая, что покупатели теряют силу, в то время как продавцы начинают вступать в дело. Такое поведение цены затем подтверждается изменением структуры рынка, часто называемым сдвигом структуры рынка, когда рынок опускается ниже ключевого минимума. Этот сдвиг подтверждает, что «умные деньги» или институциональные инвесторы действительно занимают позиции, направленные на снижение цен, переводя рынок из бычьего состояния в медвежье.

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


Начинаем

//+------------------------------------------------------------------+
//|                                            OB_&_MitigationOB.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#include <Arrays\ArrayObj.mqh>

CTrade trade;
CArrayObj obList;

#define BullOB clrLime
#define BearOB clrRed
#define ViolatedBullOB clrBlue
#define ViolatedBearOB clrMagenta

//+------------------------------------------------------------------+
//|                           Input parameters                       |
//+------------------------------------------------------------------+
input double Lots = 0.11;
input int takeProfit = 3000;
input double stopLoss = 2000;
input int Time1Hstrt = 1;
input int Time1Hend = 10;

Начнем с определения основ и структуры советника (EA), предназначенного для выявления и управления блоками ордеров и их средствами смягчения в рамках Концепции умных денег. Затем добавляются два важных компонента библиотеки: Trade/Trade.mqh для обработки торговых операций, таких как исполнение ордеров, и Arrays/ArrayObj.mqh для управления динамическими коллекциями объектов, которые в данном случае будут использоваться для эффективного хранения данных таких ордер-блоков. Объявление объекта CTrade trade инициализирует обработчик торговых операций, а CArrayObj obList создает контейнер для хранения всех обнаруженных ордер-блоков во время выполнения.

Далее мы определяем цветовые константы для большей наглядности на графике: зеленый (clrLime) для бычьих ордер-блоков, красный (clrRed) - для медвежьих, а также различимые цвета, такие как синий и пурпурный, для обозначения нарушенных или смягченных ордер-блоков. Эти визуальные подсказки имеют решающее значение для различения действительных и недействительных зон. Приведенные ниже входные параметры позволяют трейдерам настраивать поведение советника: размер позиции (Lots), целевые значения прибыли и убытка (takeProfit и stopLoss), а также временные фильтры (Time1Hstrt и Time1Hend) для ограничения исполнения сделок определенными рыночными сессиями. Эта первоначальная настройка эффективно закладывает визуальную, логическую и операционную основу, на которой остальная часть советника будет обнаруживать, классифицировать и повторно использовать ордер-блоки в качестве зон смягчения последствий.

//+------------------------------------------------------------------+
//|                         OrderBlock Class                         |
//+------------------------------------------------------------------+
class COrderBlock : public CObject
{
public:
   int direction;
   datetime time;
   double high;
   double low;
   bool violated;
   datetime violatedTime;
   bool traded;
   bool inTrade;
   string identifier;
   string tradeComment;

   COrderBlock()
   {
      traded = false;
      violated = false;
      inTrade = false;
      tradeComment = "";
   }

   void UpdateIdentifier()
   {
      identifier = IntegerToString(time) + IntegerToString(direction) + IntegerToString(violated);
   }

   void draw(datetime tmS, datetime tmE, color clr)
   {
      UpdateIdentifier();
      string objOB = "OB_REC_" + identifier;
      if(ObjectFind(0, objOB) == -1)
      {
         ObjectCreate(0, objOB, OBJ_RECTANGLE, 0, time, low, tmS, high);
         ObjectSetInteger(0, objOB, OBJPROP_FILL, true);
         ObjectSetInteger(0, objOB, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objOB, OBJPROP_BACK, true);
      }

      string objtrade = "OB_TRADE_" + identifier;
      if(ObjectFind(0, objtrade) == -1)
      {
         ObjectCreate(0, objtrade, OBJ_RECTANGLE, 0, tmS, high, tmE, low);
         ObjectSetInteger(0, objtrade, OBJPROP_FILL, true);
         ObjectSetInteger(0, objtrade, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objtrade, OBJPROP_BACK, true);
      }
   }

   void RemoveObjects()
   {
      UpdateIdentifier();
      string objOB = "OB_REC_" + identifier;
      string objtrade = "OB_TRADE_" + identifier;
      if(ObjectFind(0, objOB) >= 0) ObjectDelete(0, objOB);
      if(ObjectFind(0, objtrade) >= 0) ObjectDelete(0, objtrade);
   }
};

//+------------------------------------------------------------------+
//|                           Global variables                       |
//+------------------------------------------------------------------+
COrderBlock *activeMitigationOB = NULL;
int activeMitigationDirection = 0;

Затем мы определяем класс COrderBlock, который служит основной структурой данных для представления ордер-блоков и управления ими внутри советника. Этот класс наследует от CObject, что позволяет хранить экземпляры внутри массивов объектов (CArrayObj) и работать с ними. Каждый ордер-блок хранит важные атрибуты, такие как направление (бычье или медвежье), время, ценовые уровни минимума и максимума, а также состояния, например, violated, traded и inTrade. Дополнительные идентификаторы, такие как identifier и tradeComment, помогают однозначно идентифицировать каждый ордер-блок на графике и отслеживать связанную с ним торговую логику. Конструктор инициализирует состояния по умолчанию, чтобы гарантировать, что вновь обнаруженные ордер-блоки изначально не были активны и не были нарушены, устанавливая чистую базовую линию до того, как произойдут взаимодействия с графиком или сделками.

В состав этого класса также входят несколько мощных методов, которые обеспечивают как визуализацию, так и управление жизненным циклом. Метод UpdateIdentifier() генерирует уникальную строку на основе времени, направления и состояния нарушения — это необходимо для различения нескольких ордер-блоков. Функция draw() отвечает за отображение зон ордер-блоков и связанных с ними торговых регионов в виде цветных прямоугольников на графике, обеспечивая визуальное выделение каждого блока и отсутствие перекрытий. Между тем, функция RemoveObjects() удаляет эти графические объекты, когда они перестают быть действительными, поддерживая читаемость графика и эффективность. Наконец, объявляются две глобальные переменные — activeMitigationOB и activeMitigationDirection — для отслеживания текущего активного ордер-блока смягчения последствий и его направленного смещения, образуя мост между обнаружением ордер-блоков, нарушением и торговой логикой на более поздних этапах работы советника.

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

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   for(int i = obList.Total()-1; i >= 0; i--)
   {
      COrderBlock* ob = obList.At(i);
      ob.RemoveObjects();
      delete ob;
   }
   obList.Clear();
}

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

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(isNewBar())
   {
      CheckForClosedTradesAndCleanup();
      CheckForViolations();
      getOrderB();
      CheckForTradeEntries();
   }
}

//+------------------------------------------------------------------+
//| Helpers to find bars and swings safely                           |
//+------------------------------------------------------------------+
int BarIndexFromTimeSafely(datetime t)
{
   int idx = iBarShift(_Symbol, _Period, t, true);
   if(idx < 0) idx = Bars(_Symbol, _Period) - 1;
   return(idx);
}

int FindHighestAfter(datetime t, int lookbackBars)
{
   int obIdx = BarIndexFromTimeSafely(t);
   int start = MathMax(1, obIdx - lookbackBars);
   int count = obIdx - start;
   if(count <= 0) return(-1);
   int idx = iHighest(_Symbol, _Period, MODE_HIGH, count, start);
   return(idx);
}

int FindLowestAfter(datetime t, int lookbackBars)
{
   int obIdx = BarIndexFromTimeSafely(t);
   int start = obIdx + 1;
   int maxPossible = Bars(_Symbol, _Period) - start;
   int count = MathMin(lookbackBars, maxPossible);
   if(count <= 0) return(-1);
   int idx = iLowest(_Symbol, _Period, MODE_LOW, count, start);
   return(idx);
}

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

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

//+------------------------------------------------------------------+
//| Check for violations of existing orderblocks                     |
//+------------------------------------------------------------------+
void CheckForViolations()
{
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   for(int i = 0; i < obList.Total(); i++)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.violated || ob.traded) continue;

      if(ob.direction == 1 && currentBid < ob.low)
      {
         if(activeMitigationOB != NULL)
         {
            activeMitigationOB = NULL;
            activeMitigationDirection = 0;
         }
         
         ob.violated = true;
         ob.violatedTime = iTime(_Symbol, PERIOD_CURRENT, 0);
         ob.traded = false;
         ob.UpdateIdentifier();
         activeMitigationOB = ob;
         activeMitigationDirection = -1;
         Print("Bullish OB violated and becomes BEARISH mitigation: ", TimeToString(ob.time));
      }
      else if(ob.direction == -1 && currentAsk > ob.high)
      {
         if(activeMitigationOB != NULL)
         {
            activeMitigationOB = NULL;
            activeMitigationDirection = 0;
         }
         
         ob.violated = true;
         ob.violatedTime = iTime(_Symbol, PERIOD_CURRENT, 0);
         ob.traded = false;
         ob.UpdateIdentifier();
         activeMitigationOB = ob;
         activeMitigationDirection = 1;
         Print("Bearish OB violated and becomes BULLISH mitigation: ", TimeToString(ob.time));
      }
   }
}

Функция CheckForViolations() отвечает за непрерывный мониторинг всех существующих ордер-блоков, чтобы определить, не были ли они нарушены текущим рыночным движением цены. Она получает текущие цены bid и ask, затем перебирает все хранящиеся ордер-блоки, при этом пропуская те, которые уже помечены как traded или violated.

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

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

//+------------------------------------------------------------------+
//| Check for trade entries in orderblocks                           |
//+------------------------------------------------------------------+
void CheckForTradeEntries()
{
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   for(int i = 0; i < obList.Total(); i++)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.traded) continue;

      bool priceInZone = false;
      bool isBullishTrade = false;
      bool isMitigation = ob.violated;

      if(!ob.violated)
      {
         if(ob.direction == 1 && currentAsk < ob.high && currentAsk > ob.low)
         {
            priceInZone = true;
            isBullishTrade = true;
         }
         else if(ob.direction == -1 && currentBid > ob.low && currentBid < ob.high)
         {
            priceInZone = true;
            isBullishTrade = false;
         }
      }
      else
      {
         if(ob.direction == 1 && currentBid > ob.low && currentBid < ob.high)
         {
            priceInZone = true;
            isBullishTrade = false;
         }
         else if(ob.direction == -1 && currentAsk < ob.high && currentAsk > ob.low)
         {
            priceInZone = true;
            isBullishTrade = true;
         }
      }

      if(priceInZone)
      {
         if(isMitigation)
         {
            if(activeMitigationOB == NULL || activeMitigationOB != ob)
            {
               continue;
            }
         }

         if(isBullishTrade)
         {
            ExecuteBuyTrade(ob);
         }
         else
         {
            ExecuteSellTrade(ob);
         }
         ob.traded = true;
         break;
      }
   }
}

Функция CheckForTradeEntries() обрабатывает основную логику, определяющую, когда и где советник должен совершать сделки на основе взаимодействия цены с активными ордер-блоками или блоками смягчения последствий. Процесс начинается с получения текущих цен bid и ask и перебора каждого сохраненного ордер-блока, пропуская те, которые уже использованы для торговли. Затем логика проверяет, находится ли текущая цена внутри допустимой зоны ордер-блоков, и определяет направление сделки на основе характера блока — бычье или медвежье. В случае ненарушенных ордер-блоков, бычьи сделки срабатывают, когда цена ask опускается в бычью зону, а медвежьи сделки — когда цена bid поднимается в медвежью зону.

При работе с нарушенными ордер-блоками (теперь выступающими в качестве блоков смягчения) логика торговли инвертируется, отражая противоположную реакцию в структуре рынка. Система гарантирует, что только активный в данный момент блок смягчения может инициировать сделки, избегая ненужных повторных входов или конфликтов. Как только условия выполнены, заключается сделку на покупку или продажу с помощью соответствующих параметров ордер-блока, а блок помечается как “traded”, чтобы предотвратить дублирование позиций. Эта двухуровневая логика - обработка как действительных, так и смягченных ордер-блоков — обеспечивает динамичную и адаптивную структуру для торговли разумными деньгами, позволяя алгоритму разумно реагировать на изменение рыночных условий.

//+------------------------------------------------------------------+
//| Execute a buy trade                                              |
//+------------------------------------------------------------------+
void ExecuteBuyTrade(COrderBlock* ob)
{
   double entry = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   int swingHighIdx = FindHighestAfter(ob.time, 20);
   double tp = (swingHighIdx>0) ? getHigh(swingHighIdx) : entry + takeProfit * _Point;
   double sl = NormalizeDouble(ob.low - stopLoss * _Point, _Digits);

   string comment = "Bull_OB_" + ob.identifier;
   ob.tradeComment = comment;

   if(trade.Buy(Lots, _Symbol, entry, sl, tp, comment))
   {
      color obColor = ob.violated ? ViolatedBullOB : BullOB;
      ob.draw(ob.time, iTime(_Symbol, PERIOD_CURRENT, 0), obColor);
      ob.inTrade = true;
      Print("Buy trade executed. Type: ", ob.violated ? "Mitigation" : "Regular", 
            " OB Time: ", TimeToString(ob.time), " Entry: ", entry, " SL: ", sl, " TP: ", tp);
   }
}

//+------------------------------------------------------------------+
//| Execute a sell trade                                             |
//+------------------------------------------------------------------+
void ExecuteSellTrade(COrderBlock* ob)
{
   double entry = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   int swingLowIdx = FindLowestAfter(ob.time, 20);
   double tp = (swingLowIdx>0) ? getLow(swingLowIdx) : entry - takeProfit * _Point;
   double sl = NormalizeDouble(ob.high + stopLoss * _Point, _Digits);

   string comment = "Bear_OB_" + ob.identifier;
   ob.tradeComment = comment;

   if(trade.Sell(Lots, _Symbol, entry, sl, tp, comment))
   {
      color obColor = ob.violated ? ViolatedBearOB : BearOB;
      ob.draw(ob.time, iTime(_Symbol, PERIOD_CURRENT, 0), obColor);
      ob.inTrade = true;
      Print("Sell trade executed. Type: ", ob.violated ? "Mitigation" : "Regular", 
            " OB Time: ", TimeToString(ob.time), " Entry: ", entry, " SL: ", sl, " TP: ", tp);
   }
}

Функции ExecuteBuyTrade() и ExecuteSellTrade() обеспечивают точное исполнение сделок после того, как ордер-блок или блок смягчения последствий соответствуют условиям входа. В сценарии покупки система использует текущую цену ask в качестве уровня входа и рассчитывает целевой уровень тейк-профита на основе следующего обнаруженного максимума колебаний. Если не найдено допустимое значение максимума колебаний, по умолчанию используется фиксированное расстояние в пипсах, заданное входным параметром takeProfit. Стоп-лосс располагается чуть ниже минимума ордер-блока, что обеспечивает настройку управляемых рисков. Затем функция добавляет к сделке описательный комментарий для отслеживания и визуально обновляет график, используя корректный цвет — зеленый для действительного бычьего блока или синий для смягченного. Эта структура позволяет четко различать обычные сделки и сделки, основанные на смягчении, как с точки зрения логики, так и визуализации графика.

Логика сделки на продажу повторяет зеркальный процесс, используя текущую цену bid в качестве начального уровня, а тейк-профит определяется ближайшим минимумом колебания или фиксированным резервным значением. Стоп-лосс устанавливается чуть выше максимума ордер-блока, что обеспечивает постоянную симметрию между риском и прибылью при настройках покупки. Для идентификации генерируется четкий торговый комментарий, а на графике применяется соответствующий медвежий или смягченный медвежий цвет (красный или пурпурный) для выделения активных торговых зон. Обе функции завершаются установкой флага inTrade в значение true, гарантируя, что каждый ордер-блок запускает только одну позицию. Эта модульная конструкция эффективно разделяет логику исполнения покупок и продаж, обеспечивая структурированное, визуально отслеживаемое и основанное на правилах торговое поведение, соответствующее концепции умных денег.

//+------------------------------------------------------------------+
//| Detect new orderblocks                                           |
//+------------------------------------------------------------------+
void getOrderB()
{
   MqlDateTime structTime;
   TimeCurrent(structTime);
   static int prevDay = 0;

   if(structTime.hour >= Time1Hstrt && structTime.hour < Time1Hend)
   {
      if(prevDay != structTime.day)
      {
         prevDay = structTime.day;

         for(int i = 3; i < 100; i++)
         {
            if(i + 3 >= Bars(_Symbol, _Period)) continue;

            if(getOpen(i+2) > getClose(i+2) &&
               getClose(i+1) > getOpen(i+1) &&
               getClose(i) > getOpen(i))
            {
               COrderBlock* ob = new COrderBlock();
               ob.direction = 1;
               ob.time = getTime(i+2);
               ob.high = getHigh(i+2);
               ob.low = getLow(i+2);
               ob.violated = false;
               ob.traded = false;
               ob.UpdateIdentifier();
               obList.Add(ob);
               Print("Bullish Order Block detected at: ", TimeToString(ob.time));
            }
            else if(getOpen(i+2) < getClose(i+2) &&
                    getClose(i+1) < getOpen(i+1) &&
                    getClose(i) < getOpen(i))
            {
               COrderBlock* ob = new COrderBlock();
               ob.direction = -1;
               ob.time = getTime(i+2);
               ob.high = getHigh(i+2);
               ob.low = getLow(i+2);
               ob.violated = false;
               ob.traded = false;
               ob.UpdateIdentifier();
               obList.Add(ob);
               Print("Bearish Order Block detected at: ", TimeToString(ob.time));
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Check for closed trades and cleanup objects after closure        |
//+------------------------------------------------------------------+
void CheckForClosedTradesAndCleanup()
{
   for(int i = obList.Total()-1; i >= 0; i--)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.inTrade)
      {
         if(!PositionWithCommentExists(ob.tradeComment))
         {
            Print("Trade closed for OB: ", ob.identifier, " -> cleaning up drawings");
            ob.RemoveObjects();
            ob.inTrade = false;
            ob.traded = false;
            ob.tradeComment = "";

            if(activeMitigationOB == ob)
            {
               activeMitigationOB = NULL;
               activeMitigationDirection = 0;
               Print("Active mitigation OB cleared after trade closure");
            }

            obList.Delete(i);
            delete ob;
         }
      }
   }
}

Функция getOrderB() отвечает за обнаружение новых ордер-блоков на основе простой логики паттерна из трех свечей. Сканирование осуществляется только в пределах определенного временного окна, заданного пользовательскими входными параметрами Time1Hstrt и Time1Hend. Это гарантирует, что обнаружение происходит в контролируемые торговые часы. Функция сначала проверяет наличие условий для бычьих ордер-блоков, когда за медвежьей свечой следуют две последовательные бычьи свечи. Это указывает на потенциальное накопление и изменение рыночных настроений. Когда этот паттерн идентифицируется, создается новый объект COrderBlock, инициализируются его атрибуты (такие как направление, время, максимум и минимум), и он добавляется в глобальный список.

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

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

//+------------------------------------------------------------------+
//| Check if position with specific comment exists                   |
//+------------------------------------------------------------------+
bool PositionWithCommentExists(string comment)
{
   if(StringLen(comment) == 0) return(false);
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0 && PositionSelectByTicket(ticket))
      {
         string c = PositionGetString(POSITION_COMMENT);
         if(c == comment) return(true);
      }
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Helper functions                                                 |
//+------------------------------------------------------------------+
bool isNewBar()
{
   static datetime lastBar;
   datetime currentBar = iTime(_Symbol, _Period, 0);
   if(lastBar != currentBar)
   {
      lastBar = currentBar;
      return true;
   }
   return false;
}

double getHigh(int index) { return iHigh(_Symbol, _Period, index); }
double getLow(int index) { return iLow(_Symbol, _Period, index); }
double getOpen(int index) { return iOpen(_Symbol, _Period, index); }
double getClose(int index) { return iClose(_Symbol, _Period, index); }
datetime getTime(int index) { return iTime(_Symbol, _Period, index); }

Функция PositionWithCommentExists() играет решающую роль в поддержании целостности состояния сделки, проверяя, существует ли еще позиция с определенным идентифицирующим комментарием. Поскольку каждая сделка с ордер-блоком помечена уникальным комментарием, эта функция просматривает все открытые позиции в цикле, извлекает их комментарии и сравнивает их с комментарием, предоставленным в качестве входного параметра. Если найдено совпадение, то она возвращает значение true, указывающее на то, что сделка все еще активна; в противном случае возвращается значение false. Этот процесс проверки необходим для таких функций, как логика очистки сделок и смягчения последствий, гарантируя, что такие действия, как удаление объекта или сброс состояния, будут выполняться только после того, как соответствующая сделка действительно будет закрыта. Используя этот метод, советник позволяет избежать двойной обработки и обеспечивает точное отображение текущих и завершенных сделок.

Приведенные ниже вспомогательные функции служат вспомогательными утилитами для поиска данных и управления барами. Функция isNewBar() определяет, когда сформировался новый бар, позволяя выполнять ключевые операции, такие как обнаружение ордер-блоков или проверка нарушений, только один раз на каждом баре, что предотвращает избыточную обработку в течение каждого тика. Между тем, другие вспомогательные функции — getHigh(), getLow(), getOpen(), getClose() и getTime() — выступают в качестве удобных абстракций для получения конкретных атрибутов свечей с графика. Это упрощает читаемость кода и обеспечивает согласованность в обработке данных во всем советнике. В совокупности эти небольшие, но необходимые инструменты обеспечивают основу, позволяющую более сложным процедурам, таким как идентификация ордер-блоков и исполнение сделок, функционировать плавно и эффективно.


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

Тестирование на истории оценивалось  на таймфрейме 1H  в течение приблизительно 2-месячного окна тестирования (с 02 июня 2025 г. по 29 июля 2025 г.) с настройками по умолчанию.



Заключение

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

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

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

Прикрепленные файлы |
Создание самооптимизирующихся советников на MQL5 (Часть 17): Ансамблевый интеллект Создание самооптимизирующихся советников на MQL5 (Часть 17): Ансамблевый интеллект
Все стратегии алгоритмической торговли сложны в настройке и обслуживании, независимо от их сложности — эта проблема актуальна как для новичков, так и для экспертов. В данной статье представлен коллективный интеллект, в которой модели с учителем и человеческая интуиция взаимодействуют друг с другом, чтобы преодолеть свои общие ограничения. Совместив стратегию на основе канала скользящих средних с моделью регрессии Риджа на тех же индикаторах, мы добиваемся централизованного управления, более быстрой самокорректировки и прибыльности систем, которые в противном случае были бы убыточными.
Разработка инструментария для анализа Price Action (Часть 34): Построение прогнозных моделей на основе необработанных рыночных данных с помощью усовершенствованного пайплайна загрузки данных Разработка инструментария для анализа Price Action (Часть 34): Построение прогнозных моделей на основе необработанных рыночных данных с помощью усовершенствованного пайплайна загрузки данных
Случалось ли вам пропустить внезапный рыночный всплеск или оказаться застигнутым врасплох, когда такой всплеск происходил? Лучший способ заранее распознавать события в реальном времени – учиться на исторических паттернах. Если вы хотите обучить модель машинного обучения, в этой статье сначала показано, как создать скрипт для MetaTrader 5, который собирает исторические данные и отправляет их в Python для хранения, закладывая основу системы обнаружения всплесков. Читайте дальше, чтобы увидеть каждый шаг на практике.
Особенности написания экспертов Особенности написания экспертов
Написание и тестирование экспертов в торговой системе MetaTrader 4.
Статистический арбитраж на основе коинтегрированных акций (Часть 7): Система оценки 2 Статистический арбитраж на основе коинтегрированных акций (Часть 7): Система оценки 2
В данной статье описываются два дополнительных критерия оценки, используемых при отборе корзин акций для торговли в стратегиях возврата к среднему, а точнее — в статистическом арбитраже на основе коинтеграции. Данная статья дополняет предыдущую публикацию, в которой были представлены показатели ликвидности и силы векторов коинтеграции, а также стратегические критерии — временной интервал и период ретроспективы, — за счет включения показателей стабильности векторов коинтеграции и времени возврата к среднему значению (полупериод). В статье приведены результаты бэктеста с применением новых фильтров с комментариями, а также предоставлены файлы, необходимые для его воспроизведения.