//+------------------------------------------------------------------+ //| CLiquidityLevels.mqh | //| Модуль уровней ликвидности (Liquidity Levels) | //| Sweep detection + con_buy/con_sell для ICT Entry | //+------------------------------------------------------------------+ #ifndef CLIQUIDITY_LEVELS_MQH #define CLIQUIDITY_LEVELS_MQH //--- Требует SLiquiditySwingState и SStructureSwingState из CSwingDetector.mqh //--- Подключать ПОСЛЕ CSwingDetector.mqh //+------------------------------------------------------------------+ //| Структура состояния уровней ликвидности | //+------------------------------------------------------------------+ struct SLiquidityLevelsState { bool break_dem; // Sweep Demand на этом баре bool break_sup; // Sweep Supply на этом баре bool con_buy; // Условие Buy активно (persistent) bool con_sell; // Условие Sell активно (persistent) double value_dem_lid; // Значение последнего swept Demand уровня double value_sup_lid; // Значение последнего swept Supply уровня datetime pre_bar_Dlid_left; // Время создания swept Demand datetime pre_bar_Dlid_right; // Время sweep Demand datetime pre_bar_Slid_left; // Время создания swept Supply datetime pre_bar_Slid_right; // Время sweep Supply //--- Копии для Entry Signal (сохраняются при активации con_buy/con_sell) double pre_dem_lid; double pre_sup_lid; datetime pre2_bar_Dlid_left; datetime pre2_bar_Dlid_right; datetime pre2_bar_Slid_left; datetime pre2_bar_Slid_right; }; //+------------------------------------------------------------------+ //| Класс CLiquidityLevels — уровни ликвидности и sweep | //+------------------------------------------------------------------+ class CLiquidityLevels { public: CLiquidityLevels(); ~CLiquidityLevels(); //--- Инициализация — вызывать из OnInit() bool Init(int len, int max_levels, bool show_lid); //--- Per-bar расчёт — вызывается из цикла CSwingDetector.Calculate void Calculate(int bar, const SLiquiditySwingState &liq, const SStructureSwingState &str, const double &high[], const double &low[], const double &open[], const double &close[], const datetime &time[], double &buf_liq_up1[], double &buf_liq_up2[], double &buf_liq_up3[], double &buf_liq_dn1[], double &buf_liq_dn2[], double &buf_liq_dn3[], int rates_total); //--- Полный сброс — вызывать при prev_calculated == 0 void FullReset(); //--- Геттеры SLiquidityLevelsState GetState() const { return m_state; } bool GetConBuy() const { return m_state.con_buy; } bool GetConSell() const { return m_state.con_sell; } bool GetBreakDem() const { return m_state.break_dem; } bool GetBreakSup() const { return m_state.break_sup; } private: //--- Параметры int m_len; // Сила пивота Liquidity Swings (Len=5) int m_max_levels; // Максимум уровней в массиве bool m_show_lid; // Заполнять буферы отрисовки //--- Массивы уровней (ring buffer: [0] = самый новый) double m_lid_sup[]; // Supply уровни (Swing Highs) double m_lid_dem[]; // Demand уровни (Swing Lows) datetime m_lid_bar_sup[]; // Время создания Supply уровней datetime m_lid_bar_dem[]; // Время создания Demand уровней int m_count_sup; // Количество активных Supply int m_count_dem; // Количество активных Demand //--- Состояние SLiquidityLevelsState m_state; //--- Внутренние методы void AddSupplyLevel(double value, datetime time_created); void AddDemandLevel(double value, datetime time_created); void RemoveSupplyLevel(int index); void RemoveDemandLevel(int index); void CheckSweepDemand(double bar_low, datetime bar_time); void CheckSweepSupply(double bar_high, datetime bar_time); void UpdateConditions(double bar_close, double bar_open, bool up_trend, bool down_trend); void FillBuffers(int bar, double &buf_liq_up1[], double &buf_liq_up2[], double &buf_liq_up3[], double &buf_liq_dn1[], double &buf_liq_dn2[], double &buf_liq_dn3[]); void ResetState(); }; //+------------------------------------------------------------------+ //| Конструктор | //+------------------------------------------------------------------+ CLiquidityLevels::CLiquidityLevels() { m_len = 5; m_max_levels = 200; m_show_lid = true; m_count_sup = 0; m_count_dem = 0; ZeroMemory(m_state); } //+------------------------------------------------------------------+ //| Деструктор | //+------------------------------------------------------------------+ CLiquidityLevels::~CLiquidityLevels() { } //+------------------------------------------------------------------+ //| Инициализация параметров | //+------------------------------------------------------------------+ bool CLiquidityLevels::Init(int len, int max_levels, bool show_lid) { m_len = len; m_max_levels = max_levels; m_show_lid = show_lid; //--- Выделяем массивы фиксированного размера ArrayResize(m_lid_sup, m_max_levels); ArrayResize(m_lid_dem, m_max_levels); ArrayResize(m_lid_bar_sup, m_max_levels); ArrayResize(m_lid_bar_dem, m_max_levels); ResetState(); return true; } //+------------------------------------------------------------------+ //| Полный сброс — при prev_calculated == 0 | //+------------------------------------------------------------------+ void CLiquidityLevels::FullReset() { ResetState(); } //+------------------------------------------------------------------+ //| Сброс всего состояния | //+------------------------------------------------------------------+ void CLiquidityLevels::ResetState() { m_count_sup = 0; m_count_dem = 0; ArrayInitialize(m_lid_sup, 0); ArrayInitialize(m_lid_dem, 0); ArrayInitialize(m_lid_bar_sup, 0); ArrayInitialize(m_lid_bar_dem, 0); ZeroMemory(m_state); } //+------------------------------------------------------------------+ //| Добавить Supply уровень (Swing High → ликвидность сверху) | //| Ring buffer: вставка в начало, последний элемент теряется | //+------------------------------------------------------------------+ void CLiquidityLevels::AddSupplyLevel(double value, datetime time_created) { //--- Сдвиг вправо на 1 позицию (от конца к началу) int last = MathMin(m_count_sup, m_max_levels - 1); for(int i = last; i > 0; i--) { m_lid_sup[i] = m_lid_sup[i - 1]; m_lid_bar_sup[i] = m_lid_bar_sup[i - 1]; } //--- Новый уровень в [0] m_lid_sup[0] = value; m_lid_bar_sup[0] = time_created; m_count_sup = MathMin(m_count_sup + 1, m_max_levels); } //+------------------------------------------------------------------+ //| Добавить Demand уровень (Swing Low → ликвидность снизу) | //+------------------------------------------------------------------+ void CLiquidityLevels::AddDemandLevel(double value, datetime time_created) { int last = MathMin(m_count_dem, m_max_levels - 1); for(int i = last; i > 0; i--) { m_lid_dem[i] = m_lid_dem[i - 1]; m_lid_bar_dem[i] = m_lid_bar_dem[i - 1]; } m_lid_dem[0] = value; m_lid_bar_dem[0] = time_created; m_count_dem = MathMin(m_count_dem + 1, m_max_levels); } //+------------------------------------------------------------------+ //| Удалить Supply уровень по индексу (сдвиг влево) | //+------------------------------------------------------------------+ void CLiquidityLevels::RemoveSupplyLevel(int index) { for(int j = index; j < m_count_sup - 1; j++) { m_lid_sup[j] = m_lid_sup[j + 1]; m_lid_bar_sup[j] = m_lid_bar_sup[j + 1]; } m_count_sup--; } //+------------------------------------------------------------------+ //| Удалить Demand уровень по индексу (сдвиг влево) | //+------------------------------------------------------------------+ void CLiquidityLevels::RemoveDemandLevel(int index) { for(int j = index; j < m_count_dem - 1; j++) { m_lid_dem[j] = m_lid_dem[j + 1]; m_lid_bar_dem[j] = m_lid_bar_dem[j + 1]; } m_count_dem--; } //+------------------------------------------------------------------+ //| Проверка sweep Demand уровней (tz.md строки 608-620) | //| Sweep: цена Low пробивает уровень поддержки | //| Множественный sweep: while-loop пока low <= lid_dem[0] | //+------------------------------------------------------------------+ void CLiquidityLevels::CheckSweepDemand(double bar_low, datetime bar_time) { if(m_count_dem == 0) return; if(bar_low > m_lid_dem[0]) return; //--- Минимум один уровень пробит while(m_count_dem > 0 && bar_low <= m_lid_dem[0]) { //--- Сохраняем данные swept уровня m_state.value_dem_lid = m_lid_dem[0]; m_state.pre_bar_Dlid_left = m_lid_bar_dem[0]; m_state.pre_bar_Dlid_right = bar_time; //--- Удаляем уровень из массива RemoveDemandLevel(0); } m_state.break_dem = true; } //+------------------------------------------------------------------+ //| Проверка sweep Supply уровней (tz.md строки 622-634) | //| Sweep: цена High пробивает уровень сопротивления | //+------------------------------------------------------------------+ void CLiquidityLevels::CheckSweepSupply(double bar_high, datetime bar_time) { if(m_count_sup == 0) return; if(bar_high < m_lid_sup[0]) return; //--- Минимум один уровень пробит while(m_count_sup > 0 && bar_high >= m_lid_sup[0]) { //--- Сохраняем данные swept уровня m_state.value_sup_lid = m_lid_sup[0]; m_state.pre_bar_Slid_left = m_lid_bar_sup[0]; m_state.pre_bar_Slid_right = bar_time; //--- Удаляем уровень из массива RemoveSupplyLevel(0); } m_state.break_sup = true; } //+------------------------------------------------------------------+ //| Обновление торговых условий con_buy/con_sell | //| (tz.md строки 921-933, 965-977) | //+------------------------------------------------------------------+ void CLiquidityLevels::UpdateConditions(double bar_close, double bar_open, bool up_trend, bool down_trend) { //=== Активация con_buy: Demand swept + цена выше swept уровня === if(m_state.break_dem && bar_close > m_state.value_dem_lid && bar_open > m_state.value_dem_lid && up_trend) { m_state.con_buy = true; m_state.pre_dem_lid = m_state.value_dem_lid; m_state.pre2_bar_Dlid_left = m_state.pre_bar_Dlid_left; m_state.pre2_bar_Dlid_right = m_state.pre_bar_Dlid_right; m_state.con_sell = false; // Деактивация противоположного } //=== Активация con_sell: Supply swept + цена ниже swept уровня === if(m_state.break_sup && bar_close < m_state.value_sup_lid && bar_open < m_state.value_sup_lid && down_trend) { m_state.con_sell = true; m_state.pre_sup_lid = m_state.value_sup_lid; m_state.pre2_bar_Slid_left = m_state.pre_bar_Slid_left; m_state.pre2_bar_Slid_right = m_state.pre_bar_Slid_right; m_state.con_buy = false; // Деактивация противоположного } //=== Деактивация con_buy: цена вернулась ниже swept уровня === if(m_state.con_buy && bar_close < m_state.value_dem_lid) m_state.con_buy = false; //=== Деактивация con_sell: цена вернулась выше swept уровня === if(m_state.con_sell && bar_close > m_state.value_sup_lid) m_state.con_sell = false; } //+------------------------------------------------------------------+ //| Заполнение буферов: 3 ближайших уровня на сторону | //+------------------------------------------------------------------+ void CLiquidityLevels::FillBuffers(int bar, double &buf_liq_up1[], double &buf_liq_up2[], double &buf_liq_up3[], double &buf_liq_dn1[], double &buf_liq_dn2[], double &buf_liq_dn3[]) { if(m_show_lid) { buf_liq_up1[bar] = (m_count_sup > 0) ? m_lid_sup[0] : EMPTY_VALUE; buf_liq_up2[bar] = (m_count_sup > 1) ? m_lid_sup[1] : EMPTY_VALUE; buf_liq_up3[bar] = (m_count_sup > 2) ? m_lid_sup[2] : EMPTY_VALUE; buf_liq_dn1[bar] = (m_count_dem > 0) ? m_lid_dem[0] : EMPTY_VALUE; buf_liq_dn2[bar] = (m_count_dem > 1) ? m_lid_dem[1] : EMPTY_VALUE; buf_liq_dn3[bar] = (m_count_dem > 2) ? m_lid_dem[2] : EMPTY_VALUE; } else { buf_liq_up1[bar] = EMPTY_VALUE; buf_liq_up2[bar] = EMPTY_VALUE; buf_liq_up3[bar] = EMPTY_VALUE; buf_liq_dn1[bar] = EMPTY_VALUE; buf_liq_dn2[bar] = EMPTY_VALUE; buf_liq_dn3[bar] = EMPTY_VALUE; } } //+------------------------------------------------------------------+ //| Главный per-bar расчёт | //| Порядок: добавление → sweep → conditions → буферы | //+------------------------------------------------------------------+ void CLiquidityLevels::Calculate(int bar, const SLiquiditySwingState &liq, const SStructureSwingState &str, const double &high[], const double &low[], const double &open[], const double &close[], const datetime &time[], double &buf_liq_up1[], double &buf_liq_up2[], double &buf_liq_up3[], double &buf_liq_dn1[], double &buf_liq_dn2[], double &buf_liq_dn3[], int rates_total) { //--- 1. Сброс per-bar флагов m_state.break_dem = false; m_state.break_sup = false; //--- 2. Добавление уровней при подтверждении свинга //--- Supply: Ch_swh AND sw_high >= ph (ss_high = ph для текущего бара) if(liq.ch_swh && liq.sw_high >= liq.ss_high) { //--- Время создания уровня = время бара свинга int bar_idx = liq.bar_sw_high; if(bar_idx >= 0 && bar_idx < rates_total) AddSupplyLevel(liq.sw_high, time[bar_idx]); } //--- Demand: Ch_swl AND sw_low <= pl (ss_low = pl для текущего бара) if(liq.ch_swl && liq.sw_low <= liq.ss_low) { int bar_idx = liq.bar_sw_low; if(bar_idx >= 0 && bar_idx < rates_total) AddDemandLevel(liq.sw_low, time[bar_idx]); } //--- 3. Sweep detection CheckSweepDemand(low[bar], time[bar]); CheckSweepSupply(high[bar], time[bar]); //--- 4. Обновление торговых условий UpdateConditions(close[bar], open[bar], str.up_trend, str.down_trend); //--- 5. Заполнение буферов FillBuffers(bar, buf_liq_up1, buf_liq_up2, buf_liq_up3, buf_liq_dn1, buf_liq_dn2, buf_liq_dn3); } #endif // CLIQUIDITY_LEVELS_MQH