Бэктестинг стратегии на скрытых марковских моделях (HMM) в Python

Бэктестинг стратегии на скрытых марковских моделях (HMM) в Python Анализ данных и Бэктесты
Пошаговое руководство по созданию скрипта для бэктестинга торговой стратегии на основе скрытых марковских моделей (HMM) с использованием библиотеки hmmlearn в Python.
Суть: Готовый Python-скрипт для бэктестинга торговой стратегии на основе скрытых марковских моделей (HMM). Код загружает исторические данные, обучает модель GaussianHMM из библиотеки hmmlearn для определения рыночных режимов и симулирует торговлю с учетом задержки на исполнение ордеров.

Исходный код

Скрытые марковские модели (Hidden Markov Models, HMM) отлично подходят для поиска скрытых состояний рынка (например: бычий тренд с низкой волатильностью, медвежий тренд с высокой волатильностью или флэт). В отличие от статической кластеризации, подробнее о которой можно узнать в статье Кластеризация криптовалют по волатильности на Python с K-Means, HMM учитывает временную последовательность и вероятности переходов между состояниями.

Ниже представлен профессиональный скрипт для бэктестинга HMM-стратегии на исторических данных индекса S&P 500 (SPY):

import numpy as np
import pandas as pd
import yfinance as yf
from hmmlearn.hmm import GaussianHMM
import matplotlib.pyplot as plt

def run_hmm_backtest():
    # 1. Загрузка исторических данных
    ticker = "SPY"
    data = yf.download(ticker, start="2015-01-01", end="2023-12-31")
    
    # Вычисляем признаки (features) для HMM
    # Логарифмическая доходность
    data['Returns'] = np.log(data['Close'] / data['Close'].shift(1))
    # Логарифмический размах (прокси для волатильности)
    data['Range'] = np.log(data['High'] / data['Low'])
    
    data.dropna(inplace=True)
    
    # Формируем матрицу признаков
    X = data[['Returns', 'Range']].values
    
    # 2. Инициализация и обучение Gaussian HMM
    # Выбираем 3 режима: например, рост/низкая вол., флэт, падение/высокая вол.
    model = GaussianHMM(n_components=3, covariance_type="full", n_iter=100, random_state=42)
    model.fit(X)
    
    # Предсказываем скрытые состояния
    data['State'] = model.predict(X)
    
    # 3. Анализ полученных состояний для построения сигналов
    # Определяем среднюю доходность для каждого состояния
    state_means = data.groupby('State')['Returns'].mean()
    print("Средняя доходность по состояниям:\n", state_means)
    
    # Находим индекс "лучшего" (бычьего) и "худшего" (медвежьего) состояний
    bull_state = state_means.idxmax()
    bear_state = state_means.idxmin()
    
    # Формируем сигналы: 
    # +1 (long) в бычьем состоянии, -1 (short) в медвежьем, 0 (flat) в нейтральном
    data['Signal'] = 0
    data.loc[data['State'] == bull_state, 'Signal'] = 1
    data.loc[data['State'] == bear_state, 'Signal'] = -1
    
    # Важно: сдвигаем сигнал на 1 день вперед, чтобы избежать look-ahead bias (заглядывания в будущее)
    data['Signal'] = data['Signal'].shift(1)
    data.dropna(inplace=True)
    
    # 4. Расчет доходности стратегии
    data['Strategy_Returns'] = data['Signal'] * data['Returns']
    
    # Кумулятивная доходность
    data['Cum_Market'] = data['Returns'].cumsum().apply(np.exp) - 1
    data['Cum_Strategy'] = data['Strategy_Returns'].cumsum().apply(np.exp) - 1
    
    # 5. Визуализация результатов
    plt.figure(figsize=(12, 6))
    plt.plot(data.index, data['Cum_Market'] * 100, label='Market (Buy & Hold)', color='gray', alpha=0.7)
    plt.plot(data.index, data['Cum_Strategy'] * 100, label='HMM Regime Strategy', color='blue')
    plt.title(f"HMM Regime Switching Strategy Backtest on {ticker}")
    plt.xlabel("Date")
    plt.ylabel("Cumulative Return (%)")
    plt.legend()
    plt.grid(True)
    plt.show()
    
    # Вывод метрик
    sharpe_market = (data['Returns'].mean() / data['Returns'].std()) * np.sqrt(252)
    sharpe_strategy = (data['Strategy_Returns'].mean() / data['Strategy_Returns'].std()) * np.sqrt(252)
    print(f"Sharpe Ratio (Market): {sharpe_market:.2f}")
    print(f"Sharpe Ratio (Strategy): {sharpe_strategy:.2f}")

if __name__ == "__main__":
    run_hmm_backtest()

Разбор параметров

  • n_components: Количество скрытых состояний модели. В данном примере установлено значение 3 (например: бычий тренд, медвежий тренд, боковик). Увеличение числа компонентов может привести к переобучению (overfitting).
  • covariance_type: Тип ковариационной матрицы, определяющий связь между признаками. Значение 'full' позволяет моделировать сложные нелинейные взаимосвязи между доходностью и волатильностью для каждого состояния.
  • n_iter: Максимальное количество итераций алгоритма Expectation-Maximization (EM) для сходимости параметров модели.
  • random_state: Фиксированный сид генератора случайных чисел для обеспечения воспроизводимости результатов кластеризации при каждом запуске.
  • shift(1): Критически важный шаг в бэктестинге. Сдвигает торговый сигнал на один шаг вперед, гарантируя, что позиция открывается по цене открытия следующего дня после фиксации состояния рынка, исключая заглядывание в будущее.

Как запустить

Для запуска скрипта вам понадобится Python версии 3.8 или выше. Установите необходимые библиотеки с помощью пакетного менеджера pip:

pip install numpy pandas yfinance hmmlearn matplotlib

После установки зависимостей сохраните приведенный выше код в файл с именем hmm_backtest.py и запустите его из терминала:

python hmm_backtest.py

Скрипт автоматически загрузит котировки ETF SPY, обучит марковскую модель, рассчитает метрики эффективности (включая коэффициент Шарпа) и построит интерактивный график сравнения доходности стратегии с пассивным удержанием актива.

Оцените статью
FinFluct