Расчет Value at Risk (VaR) через Монте-Карло и GARCH на Python

Расчет Value at Risk (VaR) через Монте-Карло и GARCH на Python ЛИЧНЫЙ БЮДЖЕТ и ЭКОНОМИЯ
Пошаговое руководство по расчету метрики Value at Risk (VaR) методом Монте-Карло с динамической волатильностью GARCH(1,1) на Python для квант-разработчиков.
Суть: Расчет Value at Risk (VaR) методом Монте-Карло с GARCH-волатильностью позволяет учесть эффект кластеризации волатильности (volatility clustering) и «тяжелых хвостов» в финансовых временных рядах. Мы обучаем модель GARCH(1,1) на исторических доходностях, извлекаем текущую условную дисперсию и параметры модели, после чего генерируем тысячи будущих траекторий цен с динамически обновляемой волатильностью на каждом шаге симуляции.

Исходный код

Классический метод исторического моделирования или параметрический VaR предполагают постоянную волатильность активов во времени. Однако на реальных рынках волатильность изменчива и имеет свойство группироваться (периоды высокой волатильности сменяются периодами затишья). Для решения этой проблемы в количественных финансах (Quantitative Finance) применяется связка модели GARCH(1,1) и симуляции Монте-Карло.

Перед тем как перейти к расчету рисков, часто требуется предварительно очистить рыночные данные от шума. В этом вам поможет Фильтр Калмана на Python: Сглаживание ценовых рядов с pykalman. Если же вы работаете с терабайтами тиковых данных для построения микроструктурных признаков, ознакомьтесь с руководством Как вычислить профиль объема (VPVR) на 50 ГБ данных с Dask.

Ниже представлен оптимизированный и векторизованный код на Python для расчета GARCH-Monte Carlo VaR. Мы используем библиотеку arch для калибровки модели и numpy для проведения высокоскоростных симуляций.

import numpy as np
import pandas as pd
from arch import arch_model

def calculate_garch_mc_var(returns: pd.Series, confidence_level: float = 0.99, horizon: int = 10, n_simulations: int = 50000) -> float:
    # Очистка данных от NaN
    clean_returns = returns.dropna()
    
    # Подгонка модели GARCH(1,1)
    model = arch_model(clean_returns, vol='Garch', p=1, q=1, dist='normal', rescale=False)
    res = model.fit(disp='off')
    
    # Извлечение параметров модели
    mu = res.params['mu']
    omega = res.params['omega']
    alpha = res.params['alpha[1]']
    beta = res.params['beta[1]']
    
    # Последнее известное состояние (t = T)
    last_var = res.conditional_volatility.iloc[-1] ** 2
    last_resid = res.resid.iloc[-1]
    
    # Массивы для симуляции: строки - симуляции, столбцы - шаги по времени (horizon)
    sim_vars = np.zeros((n_simulations, horizon))
    sim_resids = np.zeros((n_simulations, horizon))
    sim_returns = np.zeros((n_simulations, horizon))
    
    # Шаг 1: Симуляция для t + 1
    z = np.random.normal(0, 1, n_simulations)
    sim_vars[:, 0] = omega + alpha * (last_resid ** 2) + beta * last_var
    sim_resids[:, 0] = np.sqrt(sim_vars[:, 0]) * z
    sim_returns[:, 0] = mu + sim_resids[:, 0]
    
    # Шаги от 2 до horizon
    for t in range(1, horizon):
        z = np.random.normal(0, 1, n_simulations)
        # Волатильность на шаге t зависит от шока и волатильности на шаге t-1
        sim_vars[:, t] = omega + alpha * (sim_resids[:, t-1] ** 2) + beta * sim_vars[:, t-1]
        sim_resids[:, t] = np.sqrt(sim_vars[:, t]) * z
        sim_returns[:, t] = mu + sim_resids[:, t]
        
    # Суммируем логарифмические доходности по строкам для получения совокупной доходности за весь горизонт
    cumulative_returns = np.sum(sim_returns, axis=1)
    
    # Находим квантиль распределения доходностей
    var_threshold = np.percentile(cumulative_returns, (1 - confidence_level) * 100)
    
    # Возвращаем VaR как положительную величину возможных потерь
    return -var_threshold

# Пример использования
if __name__ == '__main__':
    # Генерируем синтетические данные (например, 1000 дней доходностей акций)
    np.random.seed(42)
    historical_returns = pd.Series(np.random.normal(0.0005, 0.015, 1000))
    
    var_99_10d = calculate_garch_mc_var(historical_returns, confidence_level=0.99, horizon=10, n_simulations=100000)
    print(f'10-дневный 99% GARCH-MC VaR: {var_99_10d:.4%}')

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

  • returns: Временной ряд исторических логарифмических доходностей актива. Важно использовать именно логарифмические доходности, так как они обладают свойством аддитивности во времени.
  • confidence_level: Уровень доверия для расчета VaR. Значение 0.99 означает, что мы ищем максимальные потери, которые не будут превышены с вероятностью 99%.
  • horizon: Временной горизонт прогнозирования (в торговых днях). Например, 10 означает оценку риска на 10 дней вперед.
  • n_simulations: Количество генерируемых траекторий Монте-Карло. Чем больше симуляций, тем выше точность оценки «хвостов» распределения, но тем дольше выполняется расчет. Оптимально выбирать от 50 000 до 100 000.

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

Для запуска скрипта вам понадобятся библиотеки numpy, pandas и arch. Установите их через терминал:

pip install numpy pandas arch

После установки импортируйте функцию calculate_garch_mc_var в свой проект, передайте в нее реальные исторические цены акций или индексов (предварительно преобразовав их в логарифмические доходности через np.log(prices / prices.shift(1))) и получите точную оценку рыночного риска.

Для высокочастотного трейдинга (HFT) и мониторинга инфраструктурных рисков в реальном времени расчет VaR часто интегрируют с системами сбора метрик. Подробнее об этом читайте в статье Интеграция InfluxDB и Grafana для HFT: Мониторинг Latency и Slippage.

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