Настройка мониторинга дрифта торговой AI-модели
Дрифт торговой модели особенно опасен: в отличие от продуктового ML, где деградация проявляется в метриках пользователя через дни или недели, ухудшение торговой модели может за несколько часов привести к значительным финансовым потерям. Ранняя детекция дрифта критична.
Специфика дрифта в торговых системах
Structural break — резкое изменение рыночного режима (кризис, смена ДКП регулятора). Признаки становятся нерелевантными мгновенно.
Gradual concept drift — постепенное изменение паттернов: арбитражные возможности закрываются по мере того, как их начинают использовать другие участники рынка.
Seasonality shift — изменение сезонных паттернов (ликвидность меняется в праздники, квартальные экспирации опционов).
Alpha decay — естественное снижение предсказательной силы сигнала с течением времени.
Метрики мониторинга дрифта
import pandas as pd
import numpy as np
from scipy.stats import ks_2samp
from evidently.report import Report
from evidently.metric_preset import DataDriftPreset
class TradingModelDriftMonitor:
def __init__(self, reference_window_days=60, current_window_days=5):
self.ref_window = reference_window_days
self.cur_window = current_window_days
def compute_feature_drift(self, feature_df: pd.DataFrame) -> dict:
ref_end = feature_df.index[-1] - pd.Timedelta(days=self.cur_window)
ref_start = ref_end - pd.Timedelta(days=self.ref_window)
reference = feature_df[ref_start:ref_end]
current = feature_df.tail(self.cur_window * 390) # ~390 bars/day
drift_results = {}
for col in feature_df.columns:
ks_stat, p_value = ks_2samp(reference[col].dropna(), current[col].dropna())
psi = self._compute_psi(reference[col], current[col])
drift_results[col] = {
'ks_stat': ks_stat,
'ks_p_value': p_value,
'psi': psi,
'is_drifted': psi > 0.2 or p_value < 0.01
}
return drift_results
def monitor_prediction_quality(self, predictions_df: pd.DataFrame) -> dict:
"""Мониторинг качества предсказаний по proxy-метрикам"""
# IC (Information Coefficient) - корреляция предсказания с будущей доходностью
ic = predictions_df['predicted_return'].corr(predictions_df['actual_return'])
# Rolling IC за последние 20 дней vs исторический IC
rolling_ic = predictions_df.rolling(20)['predicted_return'].corr(
predictions_df['actual_return']
).iloc[-1]
historical_ic = predictions_df['predicted_return'].corr(
predictions_df['actual_return']
)
return {
'current_ic': ic,
'rolling_ic_20d': rolling_ic,
'historical_ic': historical_ic,
'ic_degradation': (historical_ic - rolling_ic) / abs(historical_ic),
'is_critical': rolling_ic < historical_ic * 0.5 # IC упал на 50%+
}
Мониторинг рыночных режимов
class MarketRegimeDetector:
def __init__(self, lookback=252):
self.lookback = lookback
def detect_regime(self, prices: pd.Series) -> str:
returns = prices.pct_change().dropna()
recent = returns.tail(20)
historical = returns.tail(self.lookback)
# Волатильность
recent_vol = recent.std() * np.sqrt(252)
hist_vol = historical.std() * np.sqrt(252)
# Тренд
sma_short = prices.tail(10).mean()
sma_long = prices.tail(50).mean()
if recent_vol > hist_vol * 1.5:
return "HIGH_VOLATILITY" # Требует консервативных лимитов
elif sma_short > sma_long * 1.02:
return "UPTREND"
elif sma_short < sma_long * 0.98:
return "DOWNTREND"
else:
return "SIDEWAYS"
def check_regime_change(self, current_regime: str, trained_regime: str) -> bool:
"""Нужно ли переобучение из-за смены режима?"""
incompatible_pairs = [
("HIGH_VOLATILITY", "SIDEWAYS"),
("HIGH_VOLATILITY", "UPTREND"),
("UPTREND", "DOWNTREND"),
]
return (current_regime, trained_regime) in incompatible_pairs
Алерты и эскалация
| Уровень | Условие | Действие |
|---|---|---|
| Warning | IC деградировал на 25% | Slack уведомление, начать мониторинг чаще |
| Alert | PSI > 0.2 на ключевых признаках | PagerDuty, рассмотреть pause |
| Critical | IC деградировал на 50% | Автоматическая пауза торговли, экстренное переобучение |
| Emergency | Структурный дрейф всех признаков | Остановка торговли, ручная проверка |
Мониторинг должен обновляться каждые 15-30 минут в trading hours, а не раз в день — скорость реакции критична.







