English Deutsch 日本語
preview
Повышение эффективности торговли с использованием Smart Money Concepts (SMC): OB, BOS и FVG

Повышение эффективности торговли с использованием Smart Money Concepts (SMC): OB, BOS и FVG

MetaTrader 5Примеры |
418 12
Hlomohang John Borotho
Hlomohang John Borotho

Введение

Иногда трейдеры сталкиваются с проблемой непоследовательности своих стратегий, часто переключаясь между индикаторами, торговыми сценариями и методами без прочной структуры, которая могла бы направлять их действия. Отсутствие такой структуры может приводить к путанице, упущенным возможностям и растущему разочарованию по мере изменения рыночных условий, когда традиционные инструменты хуже справляются с меняющимися рыночными условиями. В этой статье мы рассмотрим преимущества единого советника, который объединяет несколько стратегий Smart Money Concepts (SMC) в одно мощное решение.

Smart Money Concepts (SMC) предлагают структурированный подход к анализу поведения рынка через ключевые элементы: ордер-блоки (Order Blocks, OB), пробой структуры (Break of Structure, BOS) и разрывы справедливой стоимости (Fair Value Gaps, FVG). При первом упоминании указаны английские названия, поскольку они устоялись в SMC-сообществе; далее в пояснительном тексте используются русские эквиваленты. Объединив эти элементы в одном советнике, трейдеры могут упростить торговый процесс, автоматизировать принятие решений и сосредоточиться на наиболее сильных сигналах ценового действия. Независимо от того, используется ли автоматический режим для непрерывного исполнения стратегии или выбираются отдельные концепции, такой подход снижает элемент угадывания и делает торговлю более эффективной, последовательной и адаптивной к меняющимся рыночным условиям.



Логика советника

Ордер-блок (Order Block):

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

Разрыв справедливой стоимости (Fair Value Gap):

Разрыв справедливой стоимости (Fair Value Gap, FVG) возникает при дисбалансе ценового движения, обычно сформированном сильным импульсом, когда одна или несколько свечей оставляют разрыв между тенями предыдущих и последующих свечей. Такой разрыв отражает рыночную неэффективность, при которой не все ордера были сопоставлены, и цена часто возвращается, чтобы «заполнить» это пространство, прежде чем продолжить движение в предполагаемом направлении. Обнаруживая FVG и торгуя вокруг них, трейдеры могут предвидеть возможные откаты в эти зоны, получая точные возможности входа с четко заданными риском и потенциальной прибылью.

Пробой структуры (Break of Structure):

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

Архитектура системы:



Начало работы

//+------------------------------------------------------------------+
//|                                                 SMC_ALL_IN_1.mq5 |
//|                        GIT under Copyright 2025, MetaQuotes Ltd. |
//|                     https://www.mql5.com/en/users/johnhlomohang/ |
//+------------------------------------------------------------------+
#property copyright "GIT under Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/en/users/johnhlomohang/"
#property version   "1.01"
#property description "Unified SMC: FVG + ордер-блоки + BOS. Detect + Draw + Trade."

#include <Trade/Trade.mqh>
#include <Trade/PositionInfo.mqh>

CTrade         trade;
CPositionInfo  pos;

enum ENUM_STRATEGY
{
   STRAT_OB,         // Использовать только ордер-блоки
   STRAT_FVG,        // Использовать только FVG
   STRAT_BOS,        // Использовать только пробой структуры
   STRAT_AUTO        // Авто (все концепции SMC)
};
enum SWING_TYPE{
   SWING_OB,
   SWING_BOS,
};

Код подключает торговые библиотеки MetaTrader, в частности CTrade и CPositionInfo, которые позволяют советнику открывать сделки и управлять ими, а также получать информацию об открытых позициях. Инициализируя эти классы в начале, советник получает базовые инструменты, необходимые для исполнения ордеров и отслеживания позиций.

Следующая часть определяет гибкость стратегии и классификацию локальных экстремумов с помощью перечислений. ENUM_STRATEGY дает трейдерам возможность выбрать, какому методу должен следовать советник: только ордер-блокам, только FVG, только BOS или полностью автоматическому режиму (STRAT_AUTO), который динамически использует все три концепции. Аналогично, перечисление SWING_TYPE различает точки локальных экстремумов, используемые для ордер-блоков, и точки, используемые для анализа пробоя структуры. Такая структура делает код модульным и адаптивным, позволяя трейдерам экспериментировать с разными подходами SMC либо позволить советнику автоматически выбирать логику работы в зависимости от рыночных условий.

//----------------------------- Входные параметры ------------------------------//
input ENUM_STRATEGY TradeStrategy   = STRAT_AUTO;
input double        In_Lot          = 0.02;
input double        StopLoss        = 3500;   // пункты
input double        TakeProfit      = 7500;   // пункты
input long          MagicNumber     = 76543;

input int           SwingPeriod     = 5;      // бары с каждой стороны для подтверждения локального экстремума
input int           SwingProbeBar   = 5;      // индекс бара, который проверяем на локальный экстремум (>=SwingPeriod)
input double        Fib_Trade_lvls  = 61.8;   // коррекция OB должна достичь этого %
input bool          DrawBOSLines    = true;

input int           FVG_MinPoints   = 3;      // минимальный разрыв в пунктах
input int           FVG_ScanBars    = 20;     // сколько баров сканировать для поиска FVG
input bool          FVG_TradeAtEQ   = true;   // торговать на 50% разрыва (EQ)
input bool          OneTradePerBar  = true;

//---------------------------- Цвета -------------------------------//
#define BullOB   clrLime
#define BearOB   clrRed
#define BullFVG  clrPaleGreen
#define BearFVG  clrMistyRose
#define BOSBull  clrDodgerBlue
#define BOSBear  clrTomato

Этот раздел кода определяет входные параметры, которые трейдеры могут настроить под свой стиль торговли и предпочтения по риск-менеджменту. Параметр TradeStrategy позволяет выбрать, должен ли советник торговать только по ордер-блокам, только по FVG, только по BOS или автоматически комбинировать все три подхода. Настройки риска и управления капиталом задаются через In_Lot, StopLoss, TakeProfit и MagicNumber, что обеспечивает корректный размер лота, защитные стопы, цели по прибыли и уникальную идентификацию сделок. Кроме того, параметры обнаружения локальных экстремумов, такие как SwingPeriod и SwingProbeBar, задают количество баров, учитываемых при определении локальных максимумов и минимумов, а Fib_Trade_lvls гарантирует, что сделки по ордер-блокам согласуются с подтверждением коррекции Fibonacci. Опция DrawBOSLines добавляет гибкость визуализации графика, позволяя трейдерам видеть уровни пробоя структуры непосредственно на графике.

Далее идут настройки разрывов справедливой стоимости (FVG), которые дают более тонкий контроль над тем, как разрывы обнаруживаются и торгуются. FVG_MinPoints гарантирует, что учитываются только значимые дисбалансы, а FVG_ScanBars определяет, насколько глубоко в истории советник будет искать подтвержденные разрывы. Опция FVG_TradeAtEQ позволяет торговать на уровне равновесия (50% разрыва), который в теории SMC часто рассматривается как сбалансированная точка входа. Наконец, определения цветов, такие как BullOB, BearOB, BullFVG, BearFVG, BOSBull и BOSBear, делают объекты графика визуально понятными, позволяя трейдерам быстро отличать бычьи сценарии от медвежьих.

//---------------------------- Глобальные переменные ------------------------------//
double   Bid, Ask;
datetime g_lastBarTime = 0;

// состояние OB
class COrderBlock : public CObject
{
public:
   int      direction;   // +1 бычий, -1 медвежий
   datetime time;        // время свечи OB
   double   high;        // high свечи OB
   double   low;         // low свечи OB

   string Key() const { return TimeToString(time, TIME_DATE|TIME_MINUTES); }

   void draw(datetime tmS, datetime tmE, color clr){
      string objOB = " OB REC" + TimeToString(time);
      ObjectCreate( 0, objOB, OBJ_RECTANGLE, 0, time, low, tmS, high);
      ObjectSetInteger( 0, objOB, OBJPROP_FILL, true);
      ObjectSetInteger( 0, objOB, OBJPROP_COLOR, clr);
      
      string objtrade = " OB trade" + TimeToString(time);
      ObjectCreate( 0, objtrade, OBJ_RECTANGLE, 0, tmS, high, tmE, low); // прямоугольник торговой зоны
      ObjectSetInteger( 0, objtrade, OBJPROP_FILL, true);
      ObjectSetInteger( 0, objtrade, OBJPROP_COLOR, clr);
   }
};
COrderBlock* OB = NULL;

// состояние Fibonacci для OB
// Отслеживаем, был ли OB уже отработан
datetime lastTradedOBTime = 0;
bool tradedOB = false;
double fib_low, fib_high;
datetime fib_t1, fib_t2;
bool isBullishOB = false; 
bool isBearishOB = false;
datetime T1;
datetime T2;
color OBClr;
#define FIB_OB_BULL "FIB_OB_BULL"
#define FIB_OB_BEAR "FIB_OB_BEAR"
#define FIBO_OBJ "Fibo Retracement"

// состояние BOS 
datetime lastBOSTradeTime = 0;
bool Bull_BOS_traded, Bear_BOS_traded;
int lastBOSTradeDirection = 0; // 1 для покупки, -1 для продажи
double   swng_High = -1.0, swng_Low = -1.0;
datetime bos_tH = 0, bos_tL = 0;

В этом глобальном разделе кода задаются основные переменные и структуры, которые отслеживают состояние рынка, торговые возможности и объекты графика. Он начинается с простых глобальных значений, таких как Bid, Ask и g_lastBarTime, которые необходимы для отслеживания цены в реальном времени и для того, чтобы советник выполнял логику только один раз на новом баре. Затем создается специализированный класс COrderBlock, инкапсулирующий свойства ордер-блока, включая направление (бычье или медвежье), время и ценовые уровни (high/low). Класс также содержит функцию draw(), которая автоматически создает и раскрашивает прямоугольники на графике, помогая трейдерам визуально определять активные ордер-блоки и их торговые зоны. Благодаря централизации этих функций внутри класса код остается организованным и пригодным для повторного использования.

Помимо базового определения OB, задаются дополнительные переменные для подтверждения ордер-блоков на основе Fibonacci. Например, fib_low, fib_high и временные маркеры fib_t1 и fib_t2 хранят границы уровней коррекции Fibonacci, а флаги isBullishOB, isBearishOB и tradedOB гарантируют, что каждый ордер-блок будет подтвержден и отработан только один раз. Эти переменные позволяют советнику динамически учитывать уровни Fibonacci, чтобы для входов в сделки рассматривались только подтвержденные коррекции, например уровень 61.8%, заданный во входных параметрах. Для более понятной визуализации константы FIB_OB_BULL, FIB_OB_BEAR и FIBO_OBJ задают имена объектов графика, благодаря чему построенные коррекции Fibonacci легко различать.

Наконец, вводится управление состоянием пробоя структуры (Break of Structure, BOS). Переменные lastBOSTradeTime, Bull_BOS_traded и Bear_BOS_traded предотвращают дублирование сделок при появлении сигнала BOS. Переменная lastBOSTradeDirection отслеживает, была ли последняя сделка BOS бычьей (+1) или медвежьей (–1), а swng_High и swng_Low хранят последние уровни локальных экстремумов для определения структуры. Временные маркеры bos_tH и bos_tL используются для фиксации точных моментов подтверждения локальных экстремумов, обеспечивая соответствие правилам рыночной структуры. Глобальное управление этими состояниями помогает советнику поддерживать согласованность между разными стратегиями (ордер-блоки, BOS, FVG) и предотвращать наложение сделок, создавая структурированную основу для автоматизации торговли по Smart Money Concepts.

//--------------------------- Вспомогательные функции -------------------------------//
double  getHigh(int i)   { return iHigh(_Symbol, _Period, i);  }
double  getLow(int i)    { return iLow(_Symbol, _Period, i);   }
double  getOpen(int i)   { return iOpen(_Symbol, _Period, i);  }
double  getClose(int i)  { return iClose(_Symbol, _Period, i); }
datetime getTimeBar(int i){ return iTime(_Symbol, _Period, i); }

bool IsNewBar()
{
   datetime lastbar_time = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
   if(g_lastBarTime == 0) { g_lastBarTime = lastbar_time; return false; }
   if(g_lastBarTime != lastbar_time) { g_lastBarTime = lastbar_time; return true; }
   return false;
}

void ExecuteTrade(ENUM_ORDER_TYPE type)
{
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   double price = (type==ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK)
                                         : SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double sl = (type==ORDER_TYPE_BUY) ? price - StopLoss*point
                                      : price + StopLoss*point;
   double tp = (type==ORDER_TYPE_BUY) ? price + TakeProfit*point
                                      : price - TakeProfit*point;
   sl = NormalizeDouble(sl, _Digits);
   tp = NormalizeDouble(tp, _Digits);

   trade.SetExpertMagicNumber(MagicNumber);
   trade.PositionOpen(_Symbol, type, In_Lot, price, sl, tp, "SMC");
}

Вспомогательные функции здесь дают чистый и переиспользуемый способ работы с данными графика. Вместо прямых вызовов встроенных функций iHigh, iLow или iClose по всему коду, обертки getHigh(), getLow() и getClose() упрощают доступ к информации о барах и повышают читаемость. Функция IsNewBar() играет ключевую роль: она гарантирует, что советник обрабатывает логику только один раз на свечу, избегая повторных выполнений внутри одного и того же бара. Для этого она хранит время последнего известного бара в g_lastBarTime и сравнивает его с временной меткой текущего бара. Такой эффективный подход гарантирует, что сигналы и сделки рассматриваются только при открытии нового бара, что особенно важно для стратегий, опирающихся на подтвержденные закрытия свечей.

Функция ExecuteTrade() инкапсулирует всю логику исполнения сделки. Она автоматически рассчитывает корректную цену входа, уровни stop loss и take profit на основе типа ордера (buy или sell), при этом нормализуя значения под точность котирования инструмента. Централизация этой логики помогает советнику избежать повторяющегося кода и снизить количество ошибок при открытии сделок. Функция также задает уникальный MagicNumber советника для отслеживания и помечает каждую сделку комментарием "SMC", чтобы трейдеры могли легко определить позиции, открытые этой системой.

//----------------------- Унифицированное обнаружение локальных экстремумов -------------------//
// Определяет, является ли barIndex локальным максимумом и/или минимумом, используя len баров с каждой стороны.
// Если найден локальный максимум -> обновляет fib_high/fib_tH (и swng_High/bos_tH для BOS).
// Если найден локальный минимум -> обновляет fib_low/fib_tL (и swng_Low/bos_tL для BOS).
// Функция обновляет соответствующие переменные, если обнаружен хотя бы один локальный экстремум.
void DetectSwingForBar(int barIndex, SWING_TYPE type)
{
   const int len = 5;
   bool isSwingH = true, isSwingL = true;   

   for(int i = 1; i <= len; i++){
      int right_bars = barIndex - i;
      int left_bars  = barIndex + i;
      
      if(right_bars < 0) {
         isSwingH = false;
         isSwingL = false;
         break;
      }
      
      if((getHigh(barIndex) <= getHigh(right_bars)) || (left_bars < Bars(_Symbol, _Period) && getHigh(barIndex) < getHigh(left_bars)))
         isSwingH = false;
      
      if((getLow(barIndex) >= getLow(right_bars)) || (left_bars < Bars(_Symbol, _Period) && getLow(barIndex) > getLow(left_bars)))
         isSwingL = false;
   }

   // Назначаем значения в зависимости от типа локального экстремума
   if(isSwingH){
      if(type == SWING_OB) {
         fib_high = getHigh(barIndex);
         fib_t1 = getTimeBar(barIndex);
      } else {
         swng_High = getHigh(barIndex);
         bos_tH = getTimeBar(barIndex);
      }
   }
   if(isSwingL){
      if(type == SWING_OB) {
         fib_low = getLow(barIndex);
         fib_t2 = getTimeBar(barIndex);
      } else {
         swng_Low = getLow(barIndex);
         bos_tL = getTimeBar(barIndex);
      }
   }
}

Унифицированная функция обнаружения локальных экстремумов предназначена для определения потенциальных локальных максимумов и минимумов на графике путем анализа заданного бара и сравнения его с определенным количеством соседних баров. Используя симметричный диапазон (len баров с каждой стороны), функция проверяет, формирует ли текущий бар локальный экстремум: его high должен быть выше баров слева и справа (локальный максимум), либо его low должен быть ниже с обеих сторон (локальный минимум). Если любой бар слева или справа нарушает эти условия, сигнал локального экстремума отклоняется. Такой метод предотвращает ложные срабатывания на незначительных колебаниях и фокусируется на значимых опорных точках рыночной структуры.

После подтверждения локального экстремума функция обновляет ключевые переменные в зависимости от его типа. Для обнаружения ордер-блока (SWING_OB) локальный максимум и локальный минимум назначаются опорными точками Fibonacci (fib_high и fib_low) вместе с их временными метками, которые позднее будут использоваться для проверки коррекции. Для логики пробоя структуры (BOS) те же уровни локальных экстремумов назначаются переменным swng_High или swng_Low вместе с соответствующими временными метками (bos_tH или bos_tL). Обрабатывая OB и BOS в одной функции, советник упрощает обнаружение локальных экстремумов и избегает избыточной логики, обеспечивая единый процесс их идентификации как для подтверждения структуры, так и для сценариев коррекции Fibonacci.

void DetectAndDrawOrderBlocks()
{
   static datetime lastDetect = 0;
   datetime lastBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
   
   // Сбрасываем обнаружение OB на новом баре
   if(lastDetect != lastBar)
   {
      if(OB != NULL) 
      { 
         delete OB; 
         OB = NULL; 
      }
      lastDetect = lastBar;
   }
   
   // Обнаруживаем новый OB только если текущего еще нет
   if(OB == NULL)
   {
      for(int i = 1; i < 100; i++)
      {
         // Кандидат на бычий OB
         if(getOpen(i) < getClose(i) && 
            getOpen(i+2) < getClose(i+2) &&
            getOpen(i+3) > getClose(i+3) && 
            getOpen(i+3) < getClose(i+2))
         {
            OB = new COrderBlock();
            OB.direction = 1;
            OB.time = getTimeBar(i+3);
            OB.high = getHigh(i+3);
            OB.low = getLow(i+3);
            OBClr = BullOB;
            T1 = OB.time;
            Print("Бычий ордер-блок обнаружен в: ", TimeToString(OB.time));
            break;
         }
         
         // Кандидат на медвежий OB
         if(getOpen(i) > getClose(i) && 
            getOpen(i+2) > getClose(i+2) &&
            getOpen(i+3) < getClose(i+3) && 
            getOpen(i+3) > getClose(i+2)) // Исправленное условие
         {
            OB = new COrderBlock();
            OB.direction = -1;
            OB.time = getTimeBar(i+3);
            OB.high = getHigh(i+3);
            OB.low = getLow(i+3);
            OBClr = BearOB;
            T1 = OB.time;
            Print("Медвежий ордер-блок обнаружен в: ", TimeToString(OB.time));
            break;
         }
      }
   }

   if(OB == NULL) return;
   
   // Проверяем, был ли этот OB уже отработан
   if(lastTradedOBTime == OB.time) return;

   // Если цена откатывается внутрь зоны OB
   Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   bool inBullZone = (OB.direction > 0 && Ask <= OB.high && Ask >= OB.low);
   bool inBearZone = (OB.direction < 0 && Bid >= OB.low && Bid <= OB.high);

   if(!inBullZone && !inBearZone) return;

   // Используем функцию DetectSwing для поиска локальных экстремумов
   // Нужно вызвать ее несколько раз, чтобы найти последние локальные экстремумы
   double mostRecentSwingHigh = 0;
   double mostRecentSwingLow = EMPTY_VALUE;
   datetime mostRecentSwingHighTime = 0;
   datetime mostRecentSwingLowTime = 0;
   
   // Сканируем последние бары для поиска актуальных локальных экстремумов
   for(int i = 0; i < 20; i++) // Проверяем последние 20 баров
   {
      // Сбрасываем переменные локальных экстремумов
      fib_high = 0;
      fib_low = 0;
      fib_t1 = 0;
      fib_t2 = 0;
      
      DetectSwingForBar(i, SWING_OB);
      
      if(fib_high > 0 && (mostRecentSwingHighTime == 0 || fib_t1 > mostRecentSwingHighTime))
      {
         mostRecentSwingHigh = fib_high;
         mostRecentSwingHighTime = fib_t1;
      }
      
      if(fib_low < EMPTY_VALUE && (mostRecentSwingLowTime == 0 || fib_t2 > mostRecentSwingLowTime))
      {
         mostRecentSwingLow = fib_low;
         mostRecentSwingLowTime = fib_t2;
      }
   }
   
   // Убеждаемся, что найдены обе точки локального экстремума
   if(mostRecentSwingHighTime == 0 || mostRecentSwingLowTime == 0) return;
   
   // Строим Fibonacci перед торговлей для подтверждения
   if(OB.direction > 0 && inBullZone)
   {
      // Строим Fibonacci от последнего локального минимума к последнему локальному максимуму
      ObjectDelete(0, "FIB_OB_BULL");
      if(ObjectCreate(0, "FIB_OB_BULL", OBJ_FIBO, 0, mostRecentSwingLowTime, mostRecentSwingLow, 
                     mostRecentSwingHighTime, mostRecentSwingHigh))
      {
         // Форматируем Fibonacci
         ObjectSetInteger(0, "FIB_OB_BULL", OBJPROP_COLOR, clrBlack);
         for(int i = 0; i < ObjectGetInteger(0, "FIB_OB_BULL", OBJPROP_LEVELS); i++)
         {
            ObjectSetInteger(0, "FIB_OB_BULL", OBJPROP_LEVELCOLOR, i, clrBlack);
         }
         
         double entLvlBull = mostRecentSwingHigh - (mostRecentSwingHigh - mostRecentSwingLow) * (Fib_Trade_lvls / 100.0);
         
         if(Ask <= entLvlBull)
         {
            T2 = getTimeBar(0);
            OB.draw(T1, T2, BullOB);
            ExecuteTrade(ORDER_TYPE_BUY);
            lastTradedOBTime = OB.time; // Помечаем этот OB как отработанный
            delete OB;
            OB = NULL;
         }
      }
   }
   else if(OB.direction < 0 && inBearZone)
   {
      // Строим Fibonacci от последнего локального максимума к последнему локальному минимуму
      ObjectDelete(0, "FIB_OB_BEAR");
      if(ObjectCreate(0, "FIB_OB_BEAR", OBJ_FIBO, 0, mostRecentSwingHighTime, mostRecentSwingHigh, 
                     mostRecentSwingLowTime, mostRecentSwingLow))
      {
         // Форматируем Fibonacci
         ObjectSetInteger(0, "FIB_OB_BEAR", OBJPROP_COLOR, clrBlack);
         for(int i = 0; i < ObjectGetInteger(0, "FIB_OB_BEAR", OBJPROP_LEVELS); i++)
         {
            ObjectSetInteger(0, "FIB_OB_BEAR", OBJPROP_LEVELCOLOR, i, clrBlack);
         }
         
         double entLvlBear = mostRecentSwingLow + (mostRecentSwingHigh - mostRecentSwingLow) * (Fib_Trade_lvls / 100.0);
         
         if(Bid >= entLvlBear)
         {
            T2 = getTimeBar(0);
            OB.draw(T1, T2, BearOB);
            ExecuteTrade(ORDER_TYPE_SELL);
            lastTradedOBTime = OB.time; // Помечаем этот OB как отработанный
            delete OB;
            OB = NULL;
         }
      }
   }
}

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

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

//============================== FVG ================================//
// Определение (в стиле ICT):
// Пусть C=i, B=i+1, A=i+2.
// Бычий FVG, если Low(A) > High(C) -> разрыв [High(C), Low(A)]
// Медвежий FVG, если High(A) < Low(C) -> разрыв [High(A), Low(C)]
struct SFVG
{
   int      dir;    // +1 бычий, -1 медвежий
   datetime tLeft;  // левая временная привязка
   double   top;    // верхняя цена зоны
   double   bot;    // нижняя цена зоны

   string Name() const
   {
      string k = TimeToString(tLeft, TIME_DATE|TIME_MINUTES);
      return (dir>0 ? "FVG_B_" : "FVG_S_") + k + "_" + IntegerToString((int)(top*1000.0));
   }
};

bool FVGExistsAt(const string &name){ return ObjectFind(0, name) != -1; }

void DetectAndDrawFVGs()
{
   double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
   int counted = 0;

   for(int i=2; i<MathMin(FVG_ScanBars, Bars(_Symbol, _Period))-2; i++)
   {
      // Формируем A,B,C
      double lowA  = getLow(i+2);
      double highA = getHigh(i+2);
      double highC = getHigh(i);
      double lowC  = getLow(i);

      // Бычий FVG: Low A > High C
      if(lowA > highC && (lowA - highC >= FVG_MinPoints * point))
      {
         SFVG z;
         z.dir   = +1;
         z.tLeft = getTimeBar(i+2);  // Изменено с getTimeBar на getTime
         z.top   = lowA;
         z.bot   = highC;
         DrawFVG(z);
         counted++;
      }
      // Медвежий FVG: High A < Low C
      else if(highA < lowC && (lowC - highA >= FVG_MinPoints * point))
      {
         SFVG z;
         z.dir   = -1;
         z.tLeft = getTimeBar(i+2);  // Изменено с getTimeBar на getTime
         z.top   = lowC;          // Исправлено: для верхней границы медвежьего FVG должен быть lowC
         z.bot   = highA;         // Исправлено: для нижней границы медвежьего FVG должен быть highA
         DrawFVG(z);
         counted++;
      }
      
      if(counted > 15) break; // избегаем перегруженности
   }

   // --- Упрощенная торговля по FVG ---
   Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   // сканируем построенные объекты и торгуем при первом подтвержденном касании EQ (50%)
   int total = ObjectsTotal(0, 0, -1);
   static datetime lastTradeBar = 0;
   
   if(OneTradePerBar)
   {
      datetime barNow = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
      if(lastTradeBar == barNow) return; // на этом баре уже торговали
   }

   for(int idx=0; idx<total; idx++)
   {
      string name = ObjectName(0, idx);
      if(StringFind(name, "FVG_", 0) != 0) continue; // только наши FVG

      // Получаем координаты объекта
      datetime t1 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0);
      double y1 = ObjectGetDouble(0, name, OBJPROP_PRICE, 0);
      datetime t2 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 1);
      double y2 = ObjectGetDouble(0, name, OBJPROP_PRICE, 1);

      double top = MathMax(y1, y2);
      double bot = MathMin(y1, y2);
      bool isBull = (StringFind(name, "FVG_B_", 0) == 0);
      double mid  = (top + bot) * 0.5;

      if(isBull)
      {
         // торговать, когда Ask внутри разрыва и на/ниже EQ
         if(Ask <= top && Ask >= bot && (!FVG_TradeAtEQ || Ask <= mid))
         {
            ExecuteTrade(ORDER_TYPE_BUY);
            lastTradeBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
            break;
         }
      }
      else
      {
         // торговать, когда Bid внутри разрыва и на/выше EQ
         if(Bid <= top && Bid >= bot && (!FVG_TradeAtEQ || Bid >= mid))
         {
            ExecuteTrade(ORDER_TYPE_SELL);
            lastTradeBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
            break;
         }
      }
   }
}

Эта функция обнаруживает, отрисовывает и торгует по разрывам справедливой стоимости (Fair Value Gaps, FVG) на основе логики ICT. Сначала она сканирует последние бары, чтобы определить бычьи разрывы, где low свечи A выше high свечи C, и медвежьи разрывы, где high свечи A ниже low свечи C, при условии что разрыв соответствует минимальному размеру в пунктах. После обнаружения FVG он сохраняется в структуре, визуально отрисовывается на графике и отслеживается для торговли. Затем система контролирует активные зоны FVG, проверяя, когда цена входит в разрыв и, если включено, совпадает с уровнем равновесия (50% зоны). При выполнении условий функция открывает сделку: покупает от бычьих FVG и продает от медвежьих FVG, при этом обеспечивая не более одной сделки на бар для предотвращения переторговки. Такое сочетание обнаружения, визуализации и исполнения делает инструмент FVG не только аналитическим, но и применимым в торговле.

//=============================== BOS ===============================//
// Используем унифицированные локальные экстремумы (без RSI). Торговая логика повторяет ваш предыдущий код:
// - Продавать, когда цена пробивает последний локальный максимум вверх
// - Покупать, когда цена пробивает последний локальный минимум вниз
void DetectAndDrawBOS()
{
   // Используем DetectSwingForBar для поиска последних локальных экстремумов
   double mostRecentSwingHigh = 0;
   double mostRecentSwingLow = EMPTY_VALUE;
   datetime mostRecentSwingHighTime = 0;
   datetime mostRecentSwingLowTime = 0;
   
   // Сканируем последние бары для поиска актуальных локальных экстремумов для BOS
   for(int i = 0; i < 20; i++) // Проверяем последние 20 баров
   {
      // Сбрасываем переменные локальных экстремумов
      swng_High = 0;
      swng_Low = 0;
      bos_tH = 0;
      bos_tL = 0;
      
      // Обнаруживаем локальный экстремум на этом баре для BOS
      DetectSwingForBar(i, SWING_BOS);
      
      if(swng_High > 0 && (mostRecentSwingHighTime == 0 || bos_tH > mostRecentSwingHighTime))
      {
         mostRecentSwingHigh = swng_High;
         mostRecentSwingHighTime = bos_tH;
      }
      
      if(swng_Low < EMPTY_VALUE && (mostRecentSwingLowTime == 0 || bos_tL > mostRecentSwingLowTime))
      {
         mostRecentSwingLow = swng_Low;
         mostRecentSwingLowTime = bos_tL;
      }
   }
   
   // Обновляем глобальные переменные BOS последними локальными экстремумами
   if(mostRecentSwingHighTime > 0)
   {
      if(mostRecentSwingHighTime != bos_tH)
         Bull_BOS_traded = false;
      swng_High = mostRecentSwingHigh;
      bos_tH = mostRecentSwingHighTime;
   }
   
   if(mostRecentSwingLowTime > 0)
   {
      if(mostRecentSwingLowTime != bos_tL)
         Bear_BOS_traded = false;
      swng_Low = mostRecentSwingLow;
      bos_tL = mostRecentSwingLowTime;
   }
   
   // Теперь проверяем пробой структуры
   Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   
   // Получаем время текущего бара, чтобы предотвратить несколько сделок на одном баре
   datetime currentBarTime = iTime(_Symbol, _Period, 0);
   
   // ПОКУПКА при пробое локального максимума вверх
   if(swng_High > 0 && Ask > swng_High && Bull_BOS_traded == false)
   {
      // Проверяем, что этот пробой еще не торговался
      if(lastBOSTradeTime != currentBarTime || lastBOSTradeDirection != -1)
      {
         if(DrawBOSLines)
            DrawBOS("BOS_H_" + TimeToString(bos_tH), bos_tH, swng_High,
                    TimeCurrent(), swng_High, BOSBear, -1);
         
         ExecuteTrade(ORDER_TYPE_BUY);
         
         // Обновляем отслеживание сделок
         lastBOSTradeTime = currentBarTime;
         lastBOSTradeDirection = -1;
         Bull_BOS_traded = true;
         
         // Сбрасываем локальный максимум, чтобы предотвратить немедленную повторную торговлю
         swng_High = -1.0;
      }
   }
   
   // ПРОДАЖА при пробое локального минимума вниз
   if(swng_Low > 0 && Bid < swng_Low && Bear_BOS_traded == false)
   {
      // Проверяем, что этот пробой еще не торговался
      if(lastBOSTradeTime != currentBarTime || lastBOSTradeDirection != 1)
      {
         if(DrawBOSLines)
            DrawBOS("BOS_L_" + TimeToString(bos_tL), bos_tL, swng_Low,
                    TimeCurrent(), swng_Low, BOSBull, +1);
         
         ExecuteTrade(ORDER_TYPE_SELL);
         
         // Обновляем отслеживание сделок
         Bear_BOS_traded = true;
         lastBOSTradeTime = currentBarTime;
         lastBOSTradeDirection = 1;
         
         // Сбрасываем локальный минимум, чтобы предотвратить немедленную повторную торговлю
         swng_Low = -1.0;
      }
   }   
}
Функция BOS обнаруживает и торгует событиями пробоя структуры (Break of Structure, BOS), используя унифицированную логику локальных экстремумов. Она сканирует последние 20 баров, чтобы определить последние локальные максимумы и минимумы, обновляет глобальные переменные BOS и предотвращает дублирование сделок на одном и том же баре. В комментарии к функции указано, что торговая логика повторяет предыдущий код: продажа при пробое последнего локального максимума вверх и покупка при пробое последнего локального минимума вниз. При этом непосредственно в последующих условиях кода реализована противоположная логика — покупка при пробое локального максимума вверх и продажа при пробое локального минимума вниз, поэтому это место сохранено как внутреннее несоответствие исходного материала, а не исправлено молча в переводе. Для визуализации могут быть построены дополнительные линии BOS, а внутренние флаги предотвращают немедленную повторную торговлю по тому же пробою.
//---------------------------- Интерфейс BOS -------------------------------//
void DrawBOS(const string name, datetime t1, double p1, datetime t2, double p2, color col, int dir)
{
   if(ObjectFind(0, name) == -1)
   {
      ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2);
      ObjectSetInteger(0, name, OBJPROP_COLOR, col);
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 2);

      string lbl = name + "_lbl";
      ObjectCreate(0, lbl, OBJ_TEXT, 0, t2, p2);
      ObjectSetInteger(0, lbl, OBJPROP_COLOR, col);
      ObjectSetInteger(0, lbl, OBJPROP_FONTSIZE, 10);
      ObjectSetString(0,  lbl, OBJPROP_TEXT, "Пробой");
      ObjectSetInteger(0, lbl, OBJPROP_ANCHOR, (dir>0)?ANCHOR_RIGHT_UPPER:ANCHOR_RIGHT_LOWER);
   }
}

Функция DrawBOS отвечает за визуальную отметку пробоя структуры (Break of Structure, BOS) на графике: она создает трендовую линию между двумя точками (t1, p1) и (t2, p2) с заданными цветом и шириной. Если объект с указанным именем еще не существует, функция сначала создает трендовую линию, задает ее цвет и толщину, а затем добавляет текстовую метку в конечной точке с надписью "Break". Положение метки привязывается в зависимости от направления dir: в правом верхнем углу для бычьего варианта или в правом нижнем для медвежьего, что дает понятный визуальный сигнал изменения рыночной структуры прямо на графике.

void DrawFVG(const SFVG &z)
{
   string name = z.Name();
   datetime tNow = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE);
   
   // Удаляем существующий объект, если он есть
   if(ObjectFind(0, name) != -1) 
      ObjectDelete(0, name);
   
   // Создаем прямоугольный объект для FVG
   if(!ObjectCreate(0, name, OBJ_RECTANGLE, 0, z.tLeft, z.bot, tNow, z.top))
   {
      Print("Ошибка создания объекта FVG: ", GetLastError());
      return;
   }
   
   // Задаем свойства объекта
   ObjectSetInteger(0, name, OBJPROP_COLOR, z.dir>0 ? BullFVG : BearFVG);
   ObjectSetInteger(0, name, OBJPROP_FILL, true);
   ObjectSetInteger(0, name, OBJPROP_BACK, true);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);
   ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);
   
   // Задаем Z-order, чтобы объект был видимым
   ObjectSetInteger(0, name, OBJPROP_ZORDER, 0);
}

Функция DrawFVG визуализирует разрыв справедливой стоимости (Fair Value Gap, FVG) на графике: сначала она проверяет, существует ли объект с именем этого FVG, и удаляет его, чтобы избежать дубликатов. Затем она создает прямоугольник от левого времени разрыва tLeft до текущего бара и от нижней границы bot до верхней границы top разрыва. Прямоугольник оформляется цветом в зависимости от направления разрыва, заливается, отправляется на задний план графика и получает сплошную границу. Установка Z-order в 0 оставляет FVG видимым позади других элементов графика, предоставляя трейдерам понятный визуальный ориентир ценовых неэффективностей в реальном времени.

void OnTick()
{
   if(!IsNewBar()) return;

   // Переключатель стратегии
   if(TradeStrategy == STRAT_FVG || TradeStrategy == STRAT_AUTO)
      DetectAndDrawFVGs();

   if(TradeStrategy == STRAT_OB  || TradeStrategy == STRAT_AUTO)
      DetectAndDrawOrderBlocks();

   if(TradeStrategy == STRAT_BOS || TradeStrategy == STRAT_AUTO)
      DetectAndDrawBOS();
}

Функция OnTick выполняется на каждом рыночном тике, но продолжает работу только при формировании нового бара, обеспечивая выполнение расчетов один раз на свечу. Затем она проверяет выбранную торговую стратегию: если активны разрывы справедливой стоимости (Fair Value Gaps, FVG) или автоматический режим, вызывается DetectAndDrawFVGs; если активны ордер-блоки (Order Blocks, OB) или автоматический режим, вызывается DetectAndDrawOrderBlocks; если активны пробой структуры (Break of Structure, BOS) или автоматический режим, вызывается DetectAndDrawBOS. Такая структура позволяет советнику или индикатору динамически обнаруживать и визуализировать разные рыночные структуры в реальном времени в зависимости от выбранной стратегии.


Результаты бэктеста

Бэктестирование проводилось на таймфрейме 1H примерно на 2-месячном тестовом периоде (с 01 июля 2025 года по 01 сентября 2025 года); по умолчанию использовалась стратегия BOS.



Заключение

В итоге мы разработали единую торговую структуру Smart Money Concepts (SMC), которая объединяет три ключевых элемента: ордер-блоки (Order Blocks, OB), пробой структуры (Break of Structure, BOS) и разрывы справедливой стоимости (Fair Value Gaps, FVG). Каждая концепция была реализована с логикой обнаружения, отрисовки и торговли. OB определялись как институциональные зоны с подтверждением через коррекцию Fibonacci, BOS фиксировал сбор ликвидности, когда цена снимала локальные максимумы или минимумы, а FVG выделяли ценовые неэффективности, которые часто выступают сильными зонами реакции. В одной системе эти элементы обеспечивают динамическое выявление высоковероятных торговых сценариев прямо на графике, дополняя анализ визуальными подсказками и автоматическим исполнением.

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

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

Прикрепленные файлы |
SMC_ALL_IN_1.mq5 (21.41 KB)
Последние комментарии | Перейти к обсуждению на форуме трейдеров (12)
Hlomohang John Borotho
Hlomohang John Borotho | 16 сент. 2025 в 15:59
Stanislav Korotky #:

Разрыв справедливой стоимости описан и изображен неверно, пожалуйста, посмотрите на правильное изображение в этой статье.

Вы ошибаетесь, это одно и то же, я изобразил FVG с ценой/свечами, уже отступившими внутрь зоны FVG.
Hlomohang John Borotho
Hlomohang John Borotho | 16 сент. 2025 в 16:00
Yata Tema Gea #:

Вау, ваша статья великолепна. Я просто знаю, что в статье есть индонезийцы. Наша концепция совпадает, но разница в том, что я использую Python для SMC+GPT.


Я вижу это :)
Stanislav Korotky
Stanislav Korotky | 16 сент. 2025 в 17:37
Hlomohang John Borotho #:
Вы ошибаетесь, это одно и то же, я изобразил FVG с ценой/свечами, которые уже отступили внутрь зоны FVG.

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

Если вы поищете в Интернете, что такое FVG, то обнаружите, что эта формация представляет собой большой однонаправленный скачок/разрыв в цене, а не зигзаг скачков, который затуманивал бы весь эффект для возможных будущих откатов, потому что вы не знаете, какой из 3 разрывов закроется первым (на вашем рисунке это последний разрыв вдоль 3-й свечи, а не 2-й - он закрыт 4-й свечой).
Bao Thuan Thai
Bao Thuan Thai | 28 сент. 2025 в 11:06
Мне не нужно знать, как это работает, но большое спасибо за то, что поделились и проявили доброту.
ritik pathak
ritik pathak | 4 окт. 2025 в 11:01
Отличная работа, она может стать более хорошей, если стоп будет ниже/выше FVG или блок ордеров не будет фиксированной точкой стоп-лосса.
Моделирование рынка: Первые шаги на SQL в MQL5 (IV) Моделирование рынка: Первые шаги на SQL в MQL5 (IV)
Многие люди склонны недооценивать SQL или даже вообще не использовать его, потому что не до конца понимают, как он на самом деле работает. При выполнении запросов к базе данных SQL мы не всегда ищем универсальный ответ, а в некоторых случаях нам нужен очень конкретный и практичный ответ. Если создать базу данных с надлежащей структурой и моделью данных, в неё можно будет интегрировать практически любые типы информации.
MetaTrader и Google Таблицы через PythonAnywhere: Руководство по безопасному потоку данных MetaTrader и Google Таблицы через PythonAnywhere: Руководство по безопасному потоку данных
В этой статье показан безопасный способ экспорта данных MetaTrader в Google Таблицы. Google Таблицы — очень ценное решение, поскольку оно работает в облаке, а сохраненные там данные доступны в любое время и из любого места. Поэтому трейдеры могут получать доступ к торговым и связанным с торговлей данным, экспортированным в Google Таблицы, и выполнять дальнейший анализ для будущей торговли в любое время, где бы они ни находились.
Разработка инструментария для анализа Price Action (Часть 62): Создание адаптивной системы обнаружения параллельных каналов и пробоев на языке MQL5 Разработка инструментария для анализа Price Action (Часть 62): Создание адаптивной системы обнаружения параллельных каналов и пробоев на языке MQL5
В этой статье представлена адаптивная система обнаружения параллельных каналов и пробоев на языке MQL5. В ней показано, как определяются точки свинга, как строятся и динамически пересчитываются каналы, а также как пробои подтверждаются и визуализируются с помощью сигналов, сохраняющихся на графике. Этот подход объединяет геометрию трендовых линий, фильтрацию на основе ATR и проверку ретеста, обеспечивая надежный анализ Price Action в реальном времени для профессиональной работы с графиками и принятия торговых решений.
CRUD-операции в Firebase с использованием MQL CRUD-операции в Firebase с использованием MQL
В этой статье представлено пошаговое руководство по освоению CRUD-операций (Create, Read, Update, Delete — создание, чтение, обновление и удаление) в Firebase с акцентом на Realtime Database и Firestore. Вы узнаете, как использовать методы Firebase SDK для эффективного управления данными в веб- и мобильных приложениях: от добавления новых записей до запросов, изменения и удаления элементов. Также рассмотрены практические примеры кода и лучшие подходы к структурированию и обработке данных в реальном времени, что помогает разработчикам создавать динамические и масштабируемые приложения на гибкой NoSQL-архитектуре Firebase.