Фінансові ринки - одні з найскладніших цілей для прогнозування. Вони шумні, нестаціонарні та ефективні - тобто передбачувані патерни швидко арбітражуються. Проте ML має доведену цінність у прогнозуванні волатильності, управлінні ризиками та інтеграції альтернативних даних.
Де використовується
Фінансовий ML забезпечує критичні застосування:
- Кількісний трейдинг: Генерація альфи, витяг сигналів
- Управління ризиками: VaR, стрес-тестування, оптимізація портфеля
- Прогнозування волатильності: Ціноутворення опціонів, хеджування
- Альтернативні дані: Сентимент, супутникові знімки, транзакції
- Фрод та комплаєнс: Виявлення аномалій, AML-скринінг
Продукти: Bloomberg, Refinitiv, Kensho, Two Sigma, Citadel, хедж-фонди.
Типові дані
| Тип даних | Джерело | Частота оновлення | Виклики |
|---|---|---|---|
| Цінові дані | Біржі | Тіки до денних | Survivorship bias, спліти |
| Order book | Біржові фіди | Мікросекунди | Великий обсяг, латентність |
| Фундаментальні | Звітність, earnings | Квартально | Point-in-time точність |
| Альтернативні | Новини, соціальні, супутник | Real-time до денних | Шум, покриття |
| Макро | Центробанки, уряд | Місячно/квартально | Ревізії, вирівнювання |
Що ви робите на практиці
- Чітко визначте ціль - Дохідність? Волатильність? Напрямок?
- Встановіть правильну часову лінію - Коли б ви це знали?
- Обробіть look-ahead bias - Тільки point-in-time дані
- Побудуйте walk-forward валідацію - Без витоку майбутньої інформації
- Врахуйте транзакційні витрати - Slippage, ринковий вплив
- Тестуйте через режими - Бичий, ведмежий, висока/низька волатильність
- Впровадьте ризик-контролі - Ліміти позицій, контроль просадок
- Моніторте decay - Альфа деградує з часом
Спочатку базова лінія
Почніть з перевірених статистичних моделей перед ML:
import pandas as pd
import numpy as np
from arch import arch_model
class FinancialBaseline:
def __init__(self, returns):
"""Ініціалізація з рядом дохідностей."""
self.returns = returns
def garch_volatility(self, p=1, q=1, horizon=1):
"""GARCH модель для прогнозування волатильності."""
model = arch_model(
self.returns * 100, # Масштабування для стабільності
vol='Garch',
p=p, q=q
)
fitted = model.fit(disp='off')
# Прогноз волатильності
forecast = fitted.forecast(horizon=horizon)
variance_forecast = forecast.variance.iloc[-1].values / 10000
return {
'volatility_forecast': np.sqrt(variance_forecast),
'model': fitted,
'aic': fitted.aic,
'bic': fitted.bic
}
def momentum_signal(self, lookback=20):
"""Простий сигнал моментуму."""
momentum = self.returns.rolling(lookback).mean()
signal = np.sign(momentum)
return signal
def mean_reversion_signal(self, lookback=20, threshold=2):
"""Сигнал mean reversion на основі z-score."""
rolling_mean = self.returns.rolling(lookback).mean()
rolling_std = self.returns.rolling(lookback).std()
zscore = (self.returns - rolling_mean) / rolling_std
signal = -np.sign(zscore) * (np.abs(zscore) > threshold)
return signal
# Walk-forward валідація
def walk_forward_backtest(data, model_fn, train_window=252, test_window=21):
"""Правильне walk-forward тестування."""
results = []
for i in range(train_window, len(data) - test_window, test_window):
train_data = data.iloc[i-train_window:i]
test_data = data.iloc[i:i+test_window]
# Навчання тільки на історичних даних
model = model_fn(train_data)
# Генерація прогнозів для тестового періоду
predictions = model.predict(test_data)
results.append({
'start_date': test_data.index[0],
'end_date': test_data.index[-1],
'predictions': predictions,
'actuals': test_data
})
return results
Чому GARCH базова лінія: - Добре зрозумілі статистичні властивості - Захоплює кластеризацію волатильності - Швидко фітується, легко інтерпретується - Важко перевершити для прогнозування волатильності
Виявлення режимів
Ринки поводяться по-різному в різних режимах:
from hmmlearn import hmm
import numpy as np
class RegimeDetector:
def __init__(self, n_regimes=2):
self.n_regimes = n_regimes
self.model = None
def fit(self, returns, volatility=None):
"""Фітування Hidden Markov Model для виявлення режимів."""
# Підготовка фіч
features = returns.values.reshape(-1, 1)
if volatility is not None:
features = np.column_stack([features, volatility.values])
# Фітування HMM
self.model = hmm.GaussianHMM(
n_components=self.n_regimes,
covariance_type='full',
n_iter=100,
random_state=42
)
self.model.fit(features)
return self
def predict_regime(self, returns, volatility=None):
"""Прогнозування поточного режиму."""
features = returns.values.reshape(-1, 1)
if volatility is not None:
features = np.column_stack([features, volatility.values])
states = self.model.predict(features)
probabilities = self.model.predict_proba(features)
return {
'current_regime': states[-1],
'regime_probabilities': probabilities[-1],
'regime_history': states
}
def get_regime_characteristics(self):
"""Отримання характеристик кожного режиму."""
means = self.model.means_
covars = self.model.covars_
regimes = {}
for i in range(self.n_regimes):
regimes[f'regime_{i}'] = {
'mean_return': means[i][0],
'volatility': np.sqrt(covars[i][0][0])
}
return regimes
Feature Engineering для фінансів
def create_financial_features(df):
"""Створення фіч для фінансових часових рядів."""
features = pd.DataFrame(index=df.index)
# Дохідності на різних горизонтах
for lag in [1, 5, 21, 63]:
features[f'return_{lag}d'] = df['close'].pct_change(lag)
# Міри волатильності
features['volatility_21d'] = df['close'].pct_change().rolling(21).std()
features['volatility_63d'] = df['close'].pct_change().rolling(63).std()
# Ціна відносно ковзних середніх
for window in [20, 50, 200]:
ma = df['close'].rolling(window).mean()
features[f'price_to_ma{window}'] = df['close'] / ma - 1
# RSI
delta = df['close'].diff()
gain = delta.where(delta > 0, 0).rolling(14).mean()
loss = (-delta.where(delta < 0, 0)).rolling(14).mean()
rs = gain / loss
features['rsi_14'] = 100 - (100 / (1 + rs))
# Фічі об'єму
if 'volume' in df.columns:
features['volume_ratio'] = df['volume'] / df['volume'].rolling(21).mean()
features['dollar_volume'] = df['close'] * df['volume']
# Волатильність волатильності
features['vol_of_vol'] = features['volatility_21d'].rolling(21).std()
return features
Метрики оцінки
| Метрика | Призначення | Інтерпретація |
|---|---|---|
| Sharpe ratio | Ризик-скоригована дохідність | > 1 добре, > 2 відмінно |
| Max drawdown | Найгірша просадка | Менше краще |
| Hit rate | Точність напрямку | > 50% з перевагою |
| Information ratio | Альфа vs бенчмарк | > 0.5 добре |
| Calmar ratio | Дохідність / max drawdown | > 1 для трейдингу |
| Turnover | Частота торгівлі | Вплив на витрати |
def calculate_trading_metrics(returns, benchmark_returns=None, rf_rate=0.02):
"""Розрахунок торгових метрик."""
# Фактор ануалізації (для денних дохідностей)
ann_factor = 252
# Базові метрики
total_return = (1 + returns).prod() - 1
ann_return = (1 + total_return) ** (ann_factor / len(returns)) - 1
ann_vol = returns.std() * np.sqrt(ann_factor)
# Sharpe ratio
sharpe = (ann_return - rf_rate) / ann_vol if ann_vol > 0 else 0
# Maximum drawdown
cumulative = (1 + returns).cumprod()
rolling_max = cumulative.expanding().max()
drawdowns = cumulative / rolling_max - 1
max_drawdown = drawdowns.min()
# Calmar ratio
calmar = ann_return / abs(max_drawdown) if max_drawdown != 0 else 0
# Hit rate
hit_rate = (returns > 0).mean()
return {
'total_return': total_return,
'annual_return': ann_return,
'annual_volatility': ann_vol,
'sharpe_ratio': sharpe,
'max_drawdown': max_drawdown,
'calmar_ratio': calmar,
'hit_rate': hit_rate
}
Типові помилки та чек-лист
- [ ] Look-ahead bias: Використання майбутньої інформації у фічах
- [ ] Survivorship bias: Тільки акції, що вижили
- [ ] Перенавчання: Стратегія працює тільки на тренувальному періоді
- [ ] Транзакційні витрати ігноруються: Нереалістична прибутковість
- [ ] Зміна режиму: Модель навчена в інших ринкових умовах
- [ ] Data snooping: Занадто багато варіацій стратегії тестовано
- [ ] Point-in-time порушення: Використання переглянутих даних
- [ ] Market impact: Не враховано slippage
Чек-лист для продакшену
Перед деплоєм: - [ ] Walk-forward валідація через різні режими - [ ] Аналіз чутливості до транзакційних витрат - [ ] Ліміти просадок та правила розміру позицій - [ ] Point-in-time дані верифіковано - [ ] Вимоги до латентності виконано
Після деплою: - [ ] P&L атрибуція та моніторинг - [ ] Виявлення режимів для перемикання стратегій - [ ] Моніторинг decay - чи зникає альфа? - [ ] Ризик-ліміти виконуються в реальному часі - [ ] Порівняння з бенчмарком щоденно
Інструменти та стек
| Категорія | Інструменти |
|---|---|
| Дані | Bloomberg, Refinitiv, Yahoo Finance, Quandl |
| Бектестинг | Backtrader, Zipline, VectorBT |
| Ризик | pyfolio, quantstats, empyrical |
| Волатильність | arch, statsmodels |
| ML | LightGBM, XGBoost, PyTorch |
| Виконання | IBKR API, Alpaca, FIX protocol |
Приклад міні-кейсу
Сценарій: Прогнозування волатильності для маркет-мейкінгу опціонів.
Підхід: 1. Ціль: 5-денна реалізована волатильність 2. Фічі: Історична волатильність, implied vol, індикатори режиму 3. Модель: Ансамбль GARCH та gradient boosting 4. Валідація: Walk-forward з 2 роками навчання, 1 місяць тест
Результати: - Перевершила GARCH на 15% за RMSE - Особливо краще під час переходів режимів - Використовується для коригування поверхні волатильності
Ключові уроки: - GARCH все ще найкращий для нормальних умов - ML додає цінності при зміні режимів та з альтернативними даними - Ансамбль робустніший за одну модель - Потрібно перенавчати щомісяця для уникнення decay
Пов'язані сторінки
- Індекс: ML Field Guide
- Виявлення аномалій у часових рядах
- Прогнозування попиту
- Виявлення аномалій
Додаткові матеріали
- Advances in Financial ML - Marcos Lopez de Prado
- Machine Learning for Asset Managers
- Quantitative Trading - Ernest Chan
Спробуйте наживо
Дослідіть пов'язану модель на практиці: відкрийте GARCH Volatility Clustering Lab і змінюйте параметри під час роботи. Усе лишається у вашому браузері.
▶ Відкрити симуляцію GARCH Volatility Clustering Lab