Ind_Aleks_ICT_Entry_V2_TS_I.../docs/plans/2026-03-04-dynamic-equilibrium.md
Alexandr Nikolaev d32645030d feat(CEquilibrium): динамический Equilibrium после пробоя BOS/MSS
Добавлен динамический режим в CEquilibrium: после пробоя BOS/MSS
линия Equilibrium автоматически обновляется по новым экстремумам цены
(running max/min), пока не появится следующий BOS/MSS.

Изменения:
- Новые поля: m_dynamic_mode, m_break_dir, m_track_high/low, m_break_bar
- Calculate() принимает price_high/price_low для трекинга
- Backfill от бара пробоя до текущего при каждом обновлении экстремума
- AdjustShift корректирует m_break_bar при новом баре (AS_SERIES)
- Геттеры также обновляются в dynamic mode
- CSwingDetector передаёт high[bar]/low[bar] в Calculate()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 13:51:14 +03:00

12 KiB

Dynamic Equilibrium Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: После пробоя BOS/MSS Equilibrium динамически обновляется по новым экстремумам цены до появления следующего BOS/MSS.

Architecture: Вся логика трекинга внутри CEquilibrium. Добавляются 2 параметра (price_high, price_low) в Calculate(). Новый динамический режим (m_dynamic_mode) активируется при BOS/MSS, трекает running extreme, пересчитывает буферы с backfill. Деактивируется при следующем BOS/MSS.

Tech Stack: MQL5, MetaEditor compiler (validate_mql_code)

Design doc: docs/plans/2026-03-04-dynamic-equilibrium-design.md


Task 1: Добавить новые поля в CEquilibrium

Files:

  • Modify: CEquilibrium.mqh:50-65 (private секция, после m_fixed_disc2)

Step 1: Добавить поля динамического режима

В CEquilibrium.mqh, после строки 64 (m_fixed_disc2), добавить:

   //--- Динамический режим после пробоя 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 индекс бара начала трекинга

Step 2: Инициализация в конструкторе

В CEquilibrium::CEquilibrium() (строка ~71-84), перед закрывающей }, добавить:

   m_dynamic_mode = false;
   m_break_dir    = 0;
   m_track_high   = EMPTY_VALUE;
   m_track_low    = EMPTY_VALUE;
   m_break_bar    = -1;

Step 3: Инициализация в Init()

В CEquilibrium::Init() (строка ~96-112), перед return true;, добавить:

   m_dynamic_mode = false;
   m_break_dir    = 0;
   m_track_high   = EMPTY_VALUE;
   m_track_low    = EMPTY_VALUE;
   m_break_bar    = -1;

Step 4: Сброс в FullReset()

В CEquilibrium::FullReset() (строка ~117-133), перед закрывающей }, добавить:

   m_dynamic_mode = false;
   m_break_dir    = 0;
   m_track_high   = EMPTY_VALUE;
   m_track_low    = EMPTY_VALUE;
   m_break_bar    = -1;

Step 5: Обновить AdjustShift()

Заменить AdjustShift (строка 38):

   void              AdjustShift(int shift) { if(m_bar_eq_prev >= 0) m_bar_eq_prev += shift; if(m_break_bar >= 0) m_break_bar += shift; }

Step 6: Компиляция

Run: validate_mql_code(mq_file_path="<full_path>/Ind_Aleks_ICT_Entry_V2_TS_Indie.mq5") Expected: success: true (поля добавлены, но ещё не используются — всё компилируется)

Step 7: Commit

git add CEquilibrium.mqh
git commit -m "feat(CEquilibrium): add dynamic mode fields for post-BOS/MSS tracking"

Task 2: Изменить сигнатуру Calculate() и обновить вызов

Files:

  • Modify: CEquilibrium.mqh:25-29 (объявление Calculate)
  • Modify: CEquilibrium.mqh:161-165 (определение Calculate)
  • Modify: CSwingDetector.mqh:492-493 (вызов equilibrium.Calculate)

Step 1: Обновить объявление Calculate()

В CEquilibrium.mqh, строка 25-29. Заменить:

   void              Calculate(int bar, const SStructureSwingState &state,
                               double &buf_equilibrium[],
                               double &buf_premium2[],
                               double &buf_discount2[],
                               int rates_total);

На:

   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);

Step 2: Обновить определение Calculate()

В CEquilibrium.mqh, строка 161-165. Заменить:

void CEquilibrium::Calculate(int bar, const SStructureSwingState &state,
                             double &buf_equilibrium[],
                             double &buf_premium2[],
                             double &buf_discount2[],
                             int rates_total)

На:

void CEquilibrium::Calculate(int bar, const SStructureSwingState &state,
                             double price_high, double price_low,
                             double &buf_equilibrium[],
                             double &buf_premium2[],
                             double &buf_discount2[],
                             int rates_total)

Step 3: Обновить вызов в CSwingDetector.mqh

В CSwingDetector.mqh, строка 493. Заменить:

         equilibrium.Calculate(bar, m_str, buf_equilibrium, buf_premium2, buf_discount2, rates_total);

На:

         equilibrium.Calculate(bar, m_str, high[bar], low[bar], buf_equilibrium, buf_premium2, buf_discount2, rates_total);

Step 4: Компиляция

Run: validate_mql_code(mq_file_path="<full_path>/Ind_Aleks_ICT_Entry_V2_TS_Indie.mq5") Expected: success: true (сигнатура обновлена, новые параметры передаются но не используются)

Step 5: Commit

git add CEquilibrium.mqh CSwingDetector.mqh
git commit -m "refactor(CEquilibrium): add price_high/price_low params to Calculate()"

Task 3: Реализовать логику динамического режима

Files:

  • Modify: CEquilibrium.mqh:161-308 (метод Calculate)

Step 1: Добавить динамическую логику в Calculate()

В метод CEquilibrium::Calculate(), ПОСЛЕ блока геттеров (строка ~215, перед комментарием //=== Стабильные значения для буферов) вставить:

   //=== Динамический режим: трекинг после BOS/MSS ===
   bool is_bos_mss = state.bos_up || state.bos_down || state.mss_up || state.mss_down;

   if(is_bos_mss)
   {
      //--- Новый BOS/MSS → начать (или перезапустить) динамический режим
      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;
      }
   }
   else if(m_dynamic_mode)
   {
      //--- Обновление трекинга (без 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);
   }

Step 2: Добавить динамический пересчёт геттеров

Сразу после блока из Step 1, ПЕРЕД //=== Стабильные значения для буферов, добавить:

   //=== Динамический пересчёт геттеров в dynamic mode ===
   if(m_dynamic_mode)
   {
      if(m_break_dir == 1 && state.in_low != EMPTY_VALUE && m_track_high != EMPTY_VALUE)
      {
         double dyn_range = m_track_high - state.in_low;
         if(dyn_range > 0.0)
         {
            m_discount = m_track_high - (pre_dis_cal * dyn_range);
            m_premium  = EMPTY_VALUE;
            if(m_eq_con)
            {
               m_discount2 = m_track_high - (pre_dis_cal2 * dyn_range);
               m_premium2  = EMPTY_VALUE;
            }
         }
      }
      else if(m_break_dir == -1 && state.in_high != EMPTY_VALUE && m_track_low != EMPTY_VALUE)
      {
         double dyn_range = state.in_high - m_track_low;
         if(dyn_range > 0.0)
         {
            m_premium  = m_track_low + (pre_dis_cal * dyn_range);
            m_discount = EMPTY_VALUE;
            if(m_eq_con)
            {
               m_premium2  = m_track_low + (pre_dis_cal2 * dyn_range);
               m_discount2 = EMPTY_VALUE;
            }
         }
      }
   }

Step 3: Добавить динамическую отрисовку буферов

В блок //=== Отрисовка === (строка ~259), ПОСЛЕ текущей логики записи буферов (строка ~304, buf_discount2[bar] = m_fixed_disc2;), добавить блок перезаписи буферов в динамическом режиме:

      //=== Динамическая отрисовка: перезаписать буфер, если dynamic mode активен ===
      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 dyn_range = m_track_high - state.in_low;
            if(dyn_range > 0.0)
            {
               dyn_eq = m_track_high - (pre_dis_cal * dyn_range);
               if(m_eq_con)
                  dyn_disc2 = m_track_high - (pre_dis_cal2 * dyn_range);
            }
         }
         else if(m_break_dir == -1 && state.in_high != EMPTY_VALUE && m_track_low != EMPTY_VALUE)
         {
            double dyn_range = state.in_high - m_track_low;
            if(dyn_range > 0.0)
            {
               dyn_eq = m_track_low + (pre_dis_cal * dyn_range);
               if(m_eq_con)
                  dyn_prem2 = m_track_low + (pre_dis_cal2 * dyn_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;
               buf_premium2[i]    = (dyn_prem2 != EMPTY_VALUE) ? dyn_prem2 : EMPTY_VALUE;
               buf_discount2[i]   = (dyn_disc2 != EMPTY_VALUE) ? dyn_disc2 : EMPTY_VALUE;
            }
         }
      }

Step 4: Компиляция

Run: validate_mql_code(mq_file_path="<full_path>/Ind_Aleks_ICT_Entry_V2_TS_Indie.mq5") Expected: success: true

Step 5: Commit

git add CEquilibrium.mqh
git commit -m "feat(CEquilibrium): implement dynamic equilibrium tracking after BOS/MSS"

Task 4: Визуальная проверка на графике

Step 1: Открыть MetaTrader 5, наложить индикатор на график

Проверить визуально:

  1. До пробоя BOS/MSS — Equilibrium рисуется как раньше (без изменений)
  2. После пробоя BOS Up — линия Equilibrium динамически сдвигается вверх при новых максимумах
  3. После пробоя BOS Down — линия динамически сдвигается вниз при новых минимумах
  4. При появлении нового BOS/MSS — dynamic mode перезапускается с новыми уровнями
  5. Backfill корректный — вся линия от точки пробоя обновляется, а не только текущий бар

Step 2: Проверить в тестере стратегий

Запустить визуальный тестер на исторических данных, чтобы убедиться что:

  • AdjustShift корректно сдвигает m_break_bar
  • Нет вылетов за границы буфера
  • Линия не "улетает" на ноль

Step 3: Если всё ОК — финальный commit

git add -A
git commit -m "verified: dynamic equilibrium visual test passed"