Разработка системы VAR (Value at Risk) для криптопортфеля
Value at Risk (VaR) — статистическая оценка максимального убытка портфеля за определённый временной горизонт с заданной вероятностью. «VaR 95% на 1 день = $5,000» означает: с вероятностью 95% однодневный убыток не превысит $5,000 (и с вероятностью 5% — превысит).
Методы расчёта VaR
Historical Simulation VaR — наиболее распространённый и интуитивный:
import numpy as np
import pandas as pd
from scipy import stats
class HistoricalVaR:
def __init__(self, confidence_level=0.95, lookback_days=252):
self.confidence = confidence_level
self.lookback = lookback_days
def calculate(self, portfolio_value, positions, price_history):
"""
portfolio_value: текущая стоимость портфеля
positions: {symbol: qty}
price_history: DataFrame с ценами за lookback_days
"""
# Рассчитываем исторические returns портфеля
portfolio_returns = []
for i in range(1, len(price_history)):
daily_pnl = 0
for symbol, qty in positions.items():
if symbol in price_history.columns:
prev_price = price_history[symbol].iloc[i-1]
curr_price = price_history[symbol].iloc[i]
daily_pnl += qty * (curr_price - prev_price)
portfolio_returns.append(daily_pnl / portfolio_value)
portfolio_returns = np.array(portfolio_returns)
# VaR = percentile убытков
var_pct = np.percentile(portfolio_returns, (1 - self.confidence) * 100)
var_usd = abs(var_pct) * portfolio_value
return {
'var_pct': var_pct,
'var_usd': var_usd,
'confidence': self.confidence,
'horizon_days': 1
}
Parametric (Variance-Covariance) VaR:
def parametric_var(positions, prices, cov_matrix, confidence=0.95, horizon=1):
weights = np.array([positions[s] * prices[s] for s in positions.keys()])
portfolio_value = weights.sum()
weights_pct = weights / portfolio_value
# Portfolio variance
portfolio_variance = weights_pct @ cov_matrix @ weights_pct
portfolio_std = np.sqrt(portfolio_variance * horizon)
# Нормальное распределение: z-score для confidence level
z_score = stats.norm.ppf(1 - confidence)
var_pct = z_score * portfolio_std
var_usd = abs(var_pct) * portfolio_value
return var_usd
Monte Carlo VaR — наиболее точный для криптопортфелей (учитывает fat tails):
def monte_carlo_var(portfolio_value, returns_history, n_simulations=10000,
confidence=0.95, horizon=1):
mean = returns_history.mean()
std = returns_history.std()
# Симулируем N сценариев
simulated_returns = np.random.normal(mean, std, (n_simulations, horizon))
simulated_pnl = portfolio_value * simulated_returns.sum(axis=1)
var = np.percentile(simulated_pnl, (1 - confidence) * 100)
return abs(var)
CVaR (Conditional Value at Risk / Expected Shortfall)
CVaR — средний убыток в худших (1 - confidence)% сценариев. Лучше чем VaR для описания хвостового риска:
def calculate_cvar(returns, portfolio_value, confidence=0.95):
var_threshold = np.percentile(returns, (1 - confidence) * 100)
tail_returns = returns[returns <= var_threshold]
cvar_pct = tail_returns.mean()
return abs(cvar_pct) * portfolio_value
Проблема fat tails в крипте
Крипторынок имеет значительно более тяжёлые «хвосты» распределения returns по сравнению с нормальным распределением. Параметрический VaR на нормальном распределении существенно недооценивает риск.
Решения:
Student's t-distribution: лучше описывает fat tails. Добавляем параметр degrees of freedom (df):
from scipy.stats import t
def t_distribution_var(mean, std, df, confidence=0.95):
t_score = t.ppf(1 - confidence, df)
return abs(mean + std * t_score)
Историческое распределение + EVT (Extreme Value Theory): для хвостов используем обобщённое распределение Парето (GPD) для описания экстремальных событий.
GARCH-VaR: учитывает кластеризацию волатильности — в крипте периоды высокой волатильности следуют за периодами высокой волатильности.
Backtesting VaR (Kupiec Test)
Проверяем: насколько часто реальные убытки превышали предсказанный VaR?
def kupiec_test(var_predictions, actual_returns, confidence=0.95):
"""
Если VaR 95% — нарушения должны происходить в 5% случаев
"""
violations = actual_returns < -var_predictions
n_violations = violations.sum()
n_total = len(actual_returns)
expected_violations = n_total * (1 - confidence)
# Биномиальный тест
p_value = stats.binom_test(n_violations, n_total, 1 - confidence)
return {
'n_violations': n_violations,
'expected_violations': expected_violations,
'violation_rate': n_violations / n_total,
'p_value': p_value,
'model_valid': p_value > 0.05
}
Multi-horizon VaR
VaR масштабируется с горизонтом по квадратному корню времени (для нормального распределения):
VaR(n days) = VaR(1 day) × √n
Для криптопортфеля рассчитываем VaR для нескольких горизонтов:
| Горизонт | Применение |
|---|---|
| 1 день | Ежедневный risk monitoring |
| 7 дней | Недельный отчёт |
| 30 дней | Стресс-тестирование |
Компонентный VaR
Маржинальный VaR: вклад каждой позиции в общий VaR портфеля. Позволяет определить, какие позиции добавляют больше всего риска.
def component_var(positions, cov_matrix, portfolio_var):
"""Вклад каждой позиции в общий VaR"""
weights = np.array(list(positions.values()))
weights_pct = weights / weights.sum()
# Marginal contribution
marginal = cov_matrix @ weights_pct / portfolio_var
component = weights_pct * marginal
return dict(zip(positions.keys(), component))
Dashboard и отчётность
Realtime VaR monitor: пересчёт при каждом изменении позиций или еженедельно при использовании cov_matrix с rolling window.
VaR Report (ежедневный):
- Current 1-day VaR (95% и 99%)
- CVaR (Expected Shortfall)
- VaR breakdown по активам
- Backtesting: нарушения за последние 30 дней
Alerts: если текущий VaR превышает лимит (например, VaR 99% > 5% капитала) → Telegram alert.
Разрабатываем систему VaR с поддержкой исторического, параметрического и Monte Carlo методов, fat-tail корректировкой, backtesting модулем (Kupiec test) и ежедневным reporting.







