Ind_Aleks_ICT_Entry_V2_TS_I.../docs/plans/2026-03-04-dynamic-equilibrium-design.md

184 lines
6.2 KiB
Markdown
Raw Permalink Normal View History

# 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] |