# Dynamic Equilibrium After BOS/MSS Breakout **Дата:** 2026-03-04 **Модуль:** CEquilibrium.mqh **Статус:** Утверждён ## Проблема После пробоя BOS/MSS уровня цена продолжает двигаться, но до формирования нового BOS/MSS линия Equilibrium остаётся на старых подтверждённых уровнях (`in_high`/`in_low`). Equilibrium становится неактуальным на время между пробоями. ## Решение Добавить **динамический режим** в CEquilibrium: после BOS/MSS пробоя отслеживать экстремум цены по направлению пробоя и пересчитывать Equilibrium в реальном времени. ## Подход Вся логика внутри CEquilibrium (Подход 1). Добавляются 2 параметра `price_high`, `price_low` в `Calculate()`. ## Новые поля CEquilibrium ```cpp //--- Динамический режим после пробоя BOS/MSS bool m_dynamic_mode; // Активен ли динамический режим int m_break_dir; // Направление пробоя: 1=вверх, -1=вниз double m_track_high; // Running max(high[]) с момента пробоя double m_track_low; // Running min(low[]) с момента пробоя int m_break_bar; // AS_SERIES индекс бара начала трекинга ``` ## Изменение сигнатуры Calculate() ```cpp void Calculate(int bar, const SStructureSwingState &state, double price_high, double price_low, // НОВЫЕ параметры double &buf_equilibrium[], double &buf_premium2[], double &buf_discount2[], int rates_total); ``` ## Логика динамического режима ### Активация (при BOS/MSS) ```cpp bool is_bos_mss = state.bos_up || state.bos_down || state.mss_up || state.mss_down; if(is_bos_mss) { //--- Предыдущий dynamic mode автоматически завершается m_dynamic_mode = true; m_break_bar = bar; if(state.bos_up || state.mss_up) { m_break_dir = 1; m_track_high = price_high; m_track_low = EMPTY_VALUE; } else { m_break_dir = -1; m_track_low = price_low; m_track_high = EMPTY_VALUE; } } ``` ### Обновление трекинга (каждый бар в dynamic mode) ```cpp if(m_dynamic_mode && !is_bos_mss) { if(m_break_dir == 1) m_track_high = MathMax(m_track_high, price_high); else m_track_low = MathMin(m_track_low, price_low); } ``` ### Расчёт динамического Equilibrium (буферы) ```cpp if(m_dynamic_mode) { double dyn_eq = EMPTY_VALUE; double dyn_prem2 = EMPTY_VALUE; double dyn_disc2 = EMPTY_VALUE; if(m_break_dir == 1 && state.in_low != EMPTY_VALUE && m_track_high != EMPTY_VALUE) { double range = m_track_high - state.in_low; if(range > 0.0) { dyn_eq = m_track_high - (pre_dis_cal * range); // Discount if(m_eq_con) dyn_disc2 = m_track_high - (pre_dis_cal2 * range); } } else if(m_break_dir == -1 && state.in_high != EMPTY_VALUE && m_track_low != EMPTY_VALUE) { double range = state.in_high - m_track_low; if(range > 0.0) { dyn_eq = m_track_low + (pre_dis_cal * range); // Premium if(m_eq_con) dyn_prem2 = m_track_low + (pre_dis_cal2 * range); } } //--- Backfill от бара пробоя до текущего if(dyn_eq != EMPTY_VALUE) { int fill_start = MathMin(m_break_bar, rates_total - 1); for(int i = fill_start; i >= bar && i >= 0; i--) { buf_equilibrium[i] = dyn_eq; if(dyn_prem2 != EMPTY_VALUE) buf_premium2[i] = dyn_prem2; if(dyn_disc2 != EMPTY_VALUE) buf_discount2[i] = dyn_disc2; } } } ``` ### AdjustShift — корректировка при новом баре ```cpp void AdjustShift(int shift) { if(m_bar_eq_prev >= 0) m_bar_eq_prev += shift; if(m_break_bar >= 0) m_break_bar += shift; } ``` ## Деактивация Dynamic mode завершается **только** при следующем BOS/MSS. На том же баре: 1. Старый dynamic mode выключается 2. Обычная логика (`structural_event = ch_in_h || ch_in_l`) обновляет `m_fixed_eq` 3. Новый dynamic mode включается (если это BOS/MSS) Простое подтверждение свинга (`ch_in_h`/`ch_in_l` без BOS/MSS) НЕ завершает dynamic mode. ## Геттеры (для Entry сигналов) В dynamic mode геттеры тоже обновляются: ```cpp if(m_dynamic_mode) { if(m_break_dir == 1 && state.in_low != EMPTY_VALUE && m_track_high != EMPTY_VALUE) { double range = m_track_high - state.in_low; if(range > 0.0) { m_discount = m_track_high - (pre_dis_cal * range); if(m_eq_con) m_discount2 = m_track_high - (pre_dis_cal2 * range); } } else if(m_break_dir == -1 && state.in_high != EMPTY_VALUE && m_track_low != EMPTY_VALUE) { double range = state.in_high - m_track_low; if(range > 0.0) { m_premium = m_track_low + (pre_dis_cal * range); if(m_eq_con) m_premium2 = m_track_low + (pre_dis_cal2 * range); } } } ``` ## Что НЕ меняется - Обычная логика `structural_event` (ch_in_h/ch_in_l) — продолжает работать - `m_fixed_eq` — обновляется как раньше, но в dynamic mode буфер перезаписывается - `FullReset()` — сбрасывает dynamic state - Логика `IsInDiscountZone()` / `IsInPremiumZone()` — без изменений ## Затронутые файлы | Файл | Изменения | |------|-----------| | `CEquilibrium.mqh` | Новые поля, изменённый Calculate(), AdjustShift(), FullReset(), Init() | | `CSwingDetector.mqh` | Обновить вызов equilibrium->Calculate() — добавить high[bar], low[bar] |