Головна▸Статті▸Часові ряди та прогнозування

Фінансові часові ряди

ML для фінансових часових рядів - прогнозування акцій, моделювання волатильності, виявлення режимів та управління ризиками.

mysimulator teamОновлено 2026-01≈ 6 хв читання▶ Open the simulation

Фінансові ринки - одні з найскладніших цілей для прогнозування. Вони шумні, нестаціонарні та ефективні - тобто передбачувані патерни швидко арбітражуються. Проте ML має доведену цінність у прогнозуванні волатильності, управлінні ризиками та інтеграції альтернативних даних.

Де використовується

Фінансовий ML забезпечує критичні застосування:

Продукти: Bloomberg, Refinitiv, Kensho, Two Sigma, Citadel, хедж-фонди.

Типові дані

Тип даних Джерело Частота оновлення Виклики
Цінові дані Біржі Тіки до денних Survivorship bias, спліти
Order book Біржові фіди Мікросекунди Великий обсяг, латентність
Фундаментальні Звітність, earnings Квартально Point-in-time точність
Альтернативні Новини, соціальні, супутник Real-time до денних Шум, покриття
Макро Центробанки, уряд Місячно/квартально Ревізії, вирівнювання

Що ви робите на практиці

  1. Чітко визначте ціль - Дохідність? Волатильність? Напрямок?
  2. Встановіть правильну часову лінію - Коли б ви це знали?
  3. Обробіть look-ahead bias - Тільки point-in-time дані
  4. Побудуйте walk-forward валідацію - Без витоку майбутньої інформації
  5. Врахуйте транзакційні витрати - Slippage, ринковий вплив
  6. Тестуйте через режими - Бичий, ведмежий, висока/низька волатильність
  7. Впровадьте ризик-контролі - Ліміти позицій, контроль просадок
  8. Моніторте 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
    }

Типові помилки та чек-лист

Чек-лист для продакшену

Перед деплоєм: - [ ] 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

Пов'язані сторінки

Додаткові матеріали

Спробуйте наживо

Дослідіть пов'язану модель на практиці: відкрийте GARCH Volatility Clustering Lab і змінюйте параметри під час роботи. Усе лишається у вашому браузері.

▶ Відкрити симуляцію GARCH Volatility Clustering Lab

Що ви знайшли?

Додати кроки відтворення (опційно)