# ПЛАН УЛУЧШЕНИЯ MoE v12 → v13
## Анализ архитектуры, значимости входных параметров и целевых параметров

---

## 1. Текущее состояние (v12) — Результаты анализа

### 1.1 Производительность по тикерам (val_acc)

| Класс | Тикеры | val_acc | Проблема |
|-------|--------|---------|----------|
| ✅ Отлично (≥0.75) | LKOH 0.873, X5 0.855, PHOR 0.850, ROSN 0.834, NSVZ 0.828, ASTR 0.824 | | Работают хорошо |
| 👍 Хорошо (0.65-0.75) | SBER 0.753, PLZL 0.739, MOEX 0.738, VTBR 0.711, SNGSP 0.666, NVTK 0.631, ZCASH 0.646 | | Сносно |
| ⚠️ Средне (0.55-0.65) | GAZP 0.578, EURUSD 0.545 | | Ниже случайного к уверенности |
| ❌ Плохо (<0.55) | **MTSS 0.435, BITCOINC 0.479, BITCOIN 0.504** | | **Хуже случайного — модель не работает** |

**Средний val_acc: 0.694** · **5 тикеров с val_acc < 0.60** · **Средний long_sr: 0.199**

### 1.2 Ключевые наблюдения из feature importance

**SBER (val_acc 0.753):**
- Суммарная важность: base=0.267, expert=**0.733** → XGBoost сильно зависит от экспертов
- Топ экспертов: vol (0.096), trend (0.072), vol_dynamics (0.049), osob_rsi (0.048), microstructure (0.045)
- Слабые эксперты: reversal (0.021), volume_profile (0.020), market_regime (0.023) — почти не влияют
- Топ base-фичи: dist_from_ll, atr_pct, parkinson_vol, bars_since_ll

**GAZP (val_acc 0.578 — плохой):**
- Суммарная важность: base=**0.650**, expert=0.350 → XGBoost почти не использует экспертов!
- Отобрано только 5 экспертов: microstructure, skewness, trend, vol, hurst
- Остальные 11 экспертов бесполезны для GAZP

**ASTR (val_acc 0.824 — хороший):**
- Суммарная важность: base=0.391, expert=0.609
- Топ экспертов: hurst (0.059), vol_dynamics (0.040), osob_rsi (0.040), market_regime (0.040), microstructure (0.039)

**BITCOIN (val_acc 0.504 — плохой):**
- Суммарная важность: base=0.443, expert=0.557
- Топ экспертов: breakout (0.103), volume (0.096), pullback_ma (0.094), osob_rsi (0.092), volume_profile (0.092)
- Топ base: atr_pct, month, w1_d1_alignment

### 1.3 Выводы из анализа важности

1. **Распределение base/expert важности сильно варьирует по тикерам**: SBER 27/73, GAZP 65/35 — один фиксированный набор экспертов не оптимален для всех
2. **Слабые эксперты не отсекаются** там, где отбор не включился (selected=16), засоряя XGBoost
3. **Тикеры с плохим val_acc** (MTSS, BITCOIN, BITCOINC) имеют либо низкую важность экспертов, либо экспертную базу не помогающую разделять классы
4. **MTSS 0.435** — val_acc ХУЖЕ случайного угадывания: вероятно, модель систематически инвертирована (класс-дисбаланс + порог)

---

## 2. Проблемы целевых параметров экспертов (v12)

### 2.1 Дескриптивные эксперты не "horizon-aware" по-настоящему

Из 16 экспертов только 7 имеют horizon-aware таргеты (сдвиг на h баров):

| Эксперт | Тип | Target сейчас | Horizon-aware |
|---------|:---:|---------------|:-------------:|
| Trend | reg | `(pdi-mdi)/max(adx,1)` на i+h | ✅ |
| Vol | bin | ATR[i+h]/ATR[i] > 1.05 | ✅ |
| Momentum | reg | Z-score future_return на h | ✅ |
| Reversal | bin | цена вернулась к EMA20 за h | ✅ |
| Volume | reg | volume_ratio (ТЕКУЩИЙ!) | ❌ |
| SR | bin | цена у уровней S/R (ТЕКУЩИЙ) | ❌ |
| Breakout | reg | Close/max(High[N:]) (ТЕКУЩИЙ) | ❌ |
| PullbackMA | reg | откат к EMA + продолжение (ТЕКУЩИЙ) | ❌ |
| OSOBRSI | reg | RSI reversion (ТЕКУЩИЙ) | ❌ |
| TrendFollow | reg | ADX+EMA alignment (ТЕКУЩИЙ) | ❌ |
| VolAtExtremes | reg | объём на пиках (ТЕКУЩИЙ) | ❌ |
| Microstructure | bin | OFI direction на i+h | ✅ |
| VolumeProfile | bin | close[i+h] > POC[i+h] | ✅ |
| OrderFlow | reg | buy/sell delta (ТЕКУЩИЙ) | ❌ |
| VolDynamics | reg | Parkinson/GK/YZ направление (ТЕКУЩИЙ) | ❌ |
| MarketRegime | bin | торгуемый режим (ТЕКУЩИЙ) | ❌ |

**Проблема:** дескриптивные эксперты оценивают ТЕКУЩЕЕ состояние рынка, а не будущий исход. Это делает их сигналы статичными и слабо коррелированными с целевой переменной (TP/SL исход). XGBoost вынужден учиться интерпретировать эти описания без явной связи с исходом.

### 2.2 Tаргеты не связаны с финальным исходом (TP/SL)

Экспертные таргеты предсказывают "структуру рынка" (тренд, волатильность, импульс), а НЕ "выживет ли сделка с TP=8×ATR до SL=4×ATR". Нет прямой связи между:
- Таргетом эксперта (например, "тренд усилится") 
- Финальным исходом (outcome_long = 1 если TP достигнут первым)

Это фундаментальный gap: эксперт может идеально предсказывать тренд, но тренд не гарантирует TP-hit при таком RR.

### 2.3 MTSS: модель хуже случайного — нужно расследование

val_acc=0.435 при long_sr=0.238. При long_sr < 50% даже "правильные" предсказания не дают прибыль. Возможно:
- Сильный класс-дисбаланс (long_sr 0.238 → 76% классов = 0)
- Порог 0.55 слишком низкий/высокий для этого тикера
- Модель "залипла" на предсказании большинства (всегда 0)

---

## 3. ПЛАН УЛУЧШЕНИЙ v13

### Фаза A: Улучшение целевых параметров КАЖДОГО эксперта

#### A1. Сделать все 9 дескриптивных экспертов horizon-aware

Каждый из Volume, SR, Breakout, PullbackMA, OSOBRSI, TrendFollow, VolAtExtremes, OrderFlow, VolDynamics должен иметь таргет, сдвинутый на h баров:

| Эксперт | Новый target (horizon-aware) |
|---------|------------------------------|
| Volume | volume_ratio[i+h] (будущий объёмный режим) вместо текущего |
| SR | 1 если цена отбилась от S/R уровня за h баров |
| Breakout | 1 если пробой N-bar диапазона привёл к движению ≥ 1×ATR за h |
| PullbackMA | 1 если откат к EMA завершился продолжением тренда за h |
| OSOBRSI | 1 если RSI вернулся от экстремума ≥ 0.5×ATR за h |
| TrendFollow | сила тренда на i+h (ADX[i+h] + EMA alignment на i+h) |
| VolAtExtremes | объёмный всплеск НА будущем пике/впадине (за h баров) |
| OrderFlow | cum delta divergence на i+h |
| VolDynamics | направление волатильности на i+h |

#### A2. Привязать экспертные таргеты к финальному исходу (TP/SL)

Самый важный шаг. Для каждого эксперта добавить **"TP/SL-aware" вариант таргета**:

```
outcome_expert_long = 1 если (TP=8×ATR достигнут первым) И (экспертный сигнал > 0)
```

Или мягче — использовать **экспертный сигнал × outcome** как взвешенный таргет:
```
target = expert_signal[i+h] × outcome_long[i]
```

Это создаст прямую связь: эксперт учится предсказывать НЕ просто "тренд", а "тренд, который приведёт к TP".

**Вариант реализации:** добавить 3 дополнительных "outcome-aligned" эксперта (или модифицировать существующие):
1. `TrendOutcomeExpert` — тренд, взвешенный TP/SL исходом
2. `MomentumOutcomeExpert` — импульс, взвешенный TP/SL исходом
3. `VolumeOutcomeExpert` — объём, взвешенный TP/SL исходом

#### A3. Ребалансировка классов для binary экспертов

VolExpert ~85% contraction, SR ~90% no-level → сильный дисбаланс.
- Использовать **Focal Loss** для binary экспертов (уже обсуждался, не реализован)
- Или увеличить `scale_pos_weight` для редкого класса
- Или использовать `class_weight='balanced'` в BCEWithLogitsLoss

### Фаза B: Анализ значимости входных параметров каждого эксперта

#### B1. Permutation-importance входных фич каждого эксперта

> **✅ РЕАЛИЗОВАНО (2026-08-14).** Вместо SHAP (медленный для LSTM) используется
> **permutation importance** — перемешивание колонки и рост loss эксперта.
> `ExpertEnsemble.compute_permutation_importance()`:
> - Оптимизация v13.1: `data_ext` (base + specialist) строится ОДИН раз,
>   перемешивается только колонка фичи → ускорение ~14× (полный прогон
>   19 экспертов ≈ 1-3 мин на CPU вместо ~50 мин).
> - Стабильность: Spearman ρ=0.97 между прогонами (seed-параметр).
> - Результат: у каждого эксперта 14-44 «мёртвых» фич (<1% от max важности)
>   из 82 base-фич. Пример trend: топ-5 = bb_position, above_vwap,
>   momentum_signal, w1_h1_alignment, mtf_h1_d1_divergence_flag.

#### B2. Per-expert feature pruning

> **✅ РЕАЛИЗОВАНО (2026-08-14) + A/B-валидация.**
>
> Механика (v13.1):
> - `ExpertEnsemble._expert_base_masks: dict[str, np.ndarray(bool)]` —
>   per-expert маска base-фич; применяется в `_build_expert_features`.
> - `prune_expert_features()` — два режима:
>   - **`importance_thr` (РЕКОМЕНДОВАН, по умолчанию 0.05)** — фича остаётся,
>     если `imp >= 0.05 × max(imp)`. Отбрасывает только мёртвые фичи.
>   - `keep_ratio` (legacy) — топ-K фич по рангу. **Опасен**: отбрасывает
>     mid-tier фичи, несущие коллективный сигнал при коллинеарности.
> - Двухфазное обучение: `train_all(prune_ratio=1.0)` → Фаза 1 (все фичи) →
>   permutation importance → маски → пересоздание экспертов с меньшим входом →
>   Фаза 3 (переобучение только на значимых). `MoE.train(prune_ratio=...)`.
> - Маски сохраняются в `state_dict` (`expert_base_masks`), восстанавливаются
>   при `load_state_dict` (эксперты пересоздаются с меньшим числом входов).
> - При `retrain_on_full=True` маски из train-фазы переиспользуются
>   (без повторного дорогого прохода permutation).
>
> **A/B результаты (X5, limit=3000, epochs=15):**
>
> | Режим | X5 val_acc | SBER val_acc | Фич (X5) |
> |-------|-----------|--------------|----------|
> | baseline | 35.3-43.7%* | 73.0-73.3% | 1558 |
> | keep_ratio=0.90 | 36.7% (−7pp) | 72.8% (−0.3pp) | 1387 |
> | keep_ratio=0.70 | 53.5% (limit=1500) | — | 1083 |
> | **importance_thr=0.05** | **38.2-43.0% (+2.9…+3.8pp)** | **73.3% (нейтр.)** | **994** |
>
> \* разброс baseline между запусками — X5 val_acc нестабилен (малая выборка).
>
> **Вывод:** threshold-режим безопасен и полезен (X5 +2.9-3.8pp, SBER
> нейтрально при сокращении ~37% фич → меньше переобучения и шума).
> keep_ratio-режим отклонён как вредный при коллинеарности фич
> (161 пар |ρ|>0.7 из 3321 в X5).
>
> **TODO (после валидации):** полноценный retrain всех 18 тикеров с
> `prune_importance_thr=0.05` + walk-forward CV.

#### B3. XGBoost-уровневое отбрасывание незначимых base-фич

> **✅ РЕАЛИЗОВАНО (2026-08-14).** В дополнение к отбору экспертов
> (`selected_experts`) финальный слой XGBoost может отбрасывать и
> незначимые base-фичи (LSTM эксперты продолжают получать полный набор):
> - `MultiTimeframeMoE.base_importance_thr` → `prune_features()` → маска
>   `self.base_feature_mask` (np.ndarray(bool) над base-фичами);
> - `_build_dataset(keep_base_features=...)` применяет маску при retrain
>   и при predict (`predict()`, `predict_proba_aligned()`, `quick_update()`);
> - маска сохраняется в joblib и восстанавливается при load;
> - monotone constraints и feature names строятся по маскированному набору
>   (иначе колонки не совпадут с XGBoost);
> - страховка: минимум 30% base-фич всегда сохраняется.
>
> **A/B результаты (limit=3000, epochs=15, retrain_on_full=True):**
>
> | Тикер | baseline val_acc | base_prune 0.05 | Base-фичи |
> |-------|-----------------|-----------------|-----------|
> | X5    | 36.4%           | **50.2% (+13.8pp)** | 82→73 |
> | SBER  | 73.3%           | 73.3% (нейтр.)      | 82→68 |
>
> **Вывод:** XGB-уровневый прунинг base-фич дал существенный прирост на X5
> (+13.8pp) и нейтрален на SBER → безопасно включать в производственный цикл.
>
> **Сопутствующий фикс:** найден и исправлен предсуществовавший баг —
> `prune_features()` вызывался ДО установки `self.n_base_features` (0 вместо 82),
> из-за чего expert-signal pruning некорректно считал срезы важностей.
> Исправлено: `n_base_features` устанавливается перед вызовом prune.
>
> **TODO (после валидации):** полный retrain 18 тикеров с
> `base_importance_thr=0.05` + walk-forward CV.

### Фаза C: Улучшение финального слоя (XGBoost)

#### C1. Включить monotone constraints (сейчас ОТКЛЮЧЕНЫ!)

В коде v12 определён `MONOTONE_FEATURES` (rsi, atr_pct, choppiness, hurst и т.д.),
но **комментарий в строке 470-472: monotone_constraints временно отключены** из-за
XGBoost 3.x API limitation. Это ключевая неиспользованная возможность!

**Решение:** использовать `xgb.train()` с `DMatrix(feature_names=...)` вместо
sklearn API — это позволит передать monotone constraints как dict {name: ±1}.

Ожидаемый эффект: запрет контринтуитивных зависимостей (высокий RSI → BUY),
которые в v12 приводили к 12/12 SHORT SL на SBER в бычьем тренде.

> **✅ РЕАЛИЗОВАНО (2026-08-14).** Добавлен класс `MonotoneXGB` в `models/moe.py`
> (обёртка над `xgb.train()` + `DMatrix(feature_names=...)` с sklearn-совместимым
> интерфейсом: `fit/predict_proba/score/feature_importances_`, поддержка
> `sample_weight`, `n_jobs→nthread`, явный `objective='binary:logistic'`).
> Функция `_fit_xgb()` применяет его во всех точках обучения финального слоя:
> - `models/moe.py`: `train()` (первичный Long/Short + retrain с отобранными
>   экспертами + retrain на 100% + `prune_features()` + `quick_update()`);
> - `train_all_rr1x2.py`: Long/Short на 100% + walk-forward CV folds.
> 12 монотонных фич из `MONOTONE_FEATURES` активны (лог: `Monotone constraints:
> ACTIVE (10+ фич...)`). Тест обучения X5 end-to-end: OOS val_acc 75.4%, модель
> сохранена и загружается как `MonotoneXGB`, вероятности в [0,1].

#### C2. Мульти-горизонтная агрегация с learned attention

Сейчас XGBoost получает 4 горизонта × 2 фичи (signal+confidence) для каждого
эксперта = 8 фич. Горизонты не взвешены явно — XGBoost сам разбирается.

**Улучшение:** добавить attention-слой (как ExpertFusion, уже есть в коде ~line 4120),
который учится взвешивать горизонты per-expert:
```
внимание(эксперт, горизонт) → взвешенный сигнал → 1 фича на эксперта вместо 8
```

#### C3. Проблемные тикеры (MTSS, GAZP, BITCOIN, BITCOINC) — отдельная стратегия

Для тикеров с val_acc < 0.60:
- MTSS 0.435: расследовать инверсию — возможно нужен `scale_pos_weight` коррекция
  или изменение порога; проверить распределение предсказаний vs outcomes
- GAZP 0.578: только 5 экспертов полезны — добавить экспертов с другой специализацией
  или увеличить вес base-фич (важность 65% на базе!)
- BITCOIN/BITCOINC: увеличить вес breakout/pullback/volume экспертов, 
  сократить горизонты (для крипты [2,4,8,16] уже предложено в comments)

#### C4. Per-ticker подбор параметров XGBoost

Сейчас все тикеры используют одинаковые параметры:
```
max_depth=4, lr=0.03, n_estimators=300, subsample=0.7, colsample=0.7
```
**Улучшение:** для плохих тикеров (MTSS, BITCOIN) — Optuna-подбор:
- max_depth 3-8, lr 0.01-0.1, n_estimators 100-600
- min_child_weight 1-15, reg_alpha 0-2, reg_lambda 0.5-5

#### C5. Мета-признаки в финальном слое

Добавить в XGBoost:
- rolling_winrate_long (уже есть в `_compute_rolling_winrate`)
- ATR-процентиль текущего режима
- Свежесть сигнала (bars since last signal)
- Корреляция экспертов (сколько экспертов согласны — уже есть в confidence, но можно отдельной фичей)

#### C6. Улучшение threshold search

Сейчас: `find_best_thr` sweep [0.50, 0.85], min 15 trades, WR > breakeven+2%.
**Улучшение:**
- Добавить риск-адjusted метрику (Sharpe-like): net_pnl / sqrt(trades)
- Учитывать волатильность сделки (награждать сделки с низким ATR%)
- Для тикеров с WR < breakeven при всех порогах — явный no-trade (уже есть)

---

## 4. Приоритеты реализации

| Приоритет | Задача | Ожидаемый эффект | Сложность |
|:---------:|--------|------------------|:---------:|
| **P0** | C1: Включить monotone constraints | Устранить контринтуитивные BUY/SELL | Низкая |
| **P0** | A2: Outcome-aligned эксперты | Прямая связь экспертов с TP/SL исходом | Средняя |
| **P1** | A1: Horizon-aware все эксперты | Лучшие таргеты для дескриптивных экспертов | Средняя |
| **P1** | C3: MTSS расследование | Спасти худший тикер (0.435) | Средняя |
| **P2** | B1: SHAP-анализ фич экспертов | Сокращение входов, меньше шума | Средняя |
| **P2** | C4: Optuna для плохих тикеров | +3-5% на MTSS/BITCOIN | Средняя |
| **P3** | C2: Attention агрегация горизонтов | Замена 8 фич на 1 осмысленную | Высокая |
| **P3** | C5: Мета-признаки | Контекст для XGBoost | Низкая |
| **P4** | A3: Focal Loss для binary | Ребалансировка классов | Низкая |

---

## 5. Метрики успеха

- Средний val_acc по 18 тикерам: **0.694 → ≥ 0.72**
- Количество тикеров с val_acc < 0.60: **5 → ≤ 2**
- MTSS: 0.435 → ≥ 0.60
- BITCOIN/BITCOINC: 0.504/0.479 → ≥ 0.55
- Не ухудшить хорошие тикеры (LKOH 0.873, X5 0.855)

---

## 6. Экспериментальный протокол

1. Каждое изменение внедрять **по одному** и переобучать 2-3 репрезентативных тикера
   (SBER — хороший, GAZP — проблемный, BITCOIN — crypto)
2. Сравнивать val_acc + long_sr + пороги до/после
3. Только после подтверждения на 3 тикерах — полное обучение 18 тикеров
4. Walk-forward CV для финальной валидации (walkforward_cv.py --all)

---

**Статус:** P0-C1 (monotone constraints) — ✅ реализован и протестирован на X5
(15.08.2026). Следующая задача: P0-A2 (outcome-aligned эксперты).
