Волатильность — это статистический показатель, характеризующий изменчивость цены актива за определенный промежуток времени. По сути, это мера риска, связанного с ценовыми колебаниями финансового инструмента. Высокая волатильность означает значительные и частые изменения цены, в то время как низкая волатильность указывает на более стабильное движение цены с меньшими отклонениями.
На практике волатильность влияет на множество аспектов финансовых рынков:
- Управление рисками — волатильность напрямую связана с определением потенциальных убытков в инвестиционном портфеле.
- Ценообразование деривативов — модели ценообразования опционов, такие как Black-Scholes, используют волатильность как один из ключевых параметров.
- Построение торговых стратегий — многие алгоритмические стратегии основаны на прогнозировании и использовании волатильности.
- Оценка рыночных настроений — изменения в волатильности часто сигнализируют о смене настроений участников рынка
- Выбор активов для инвестирования — разные классы активов имеют различные характеристики волатильности
Интересно, что восприятие волатильности часто бывает асимметричным: инвесторы воспринимают восходящую волатильность (рост цен) более благосклонно, чем нисходящую (падение цен), хотя с математической точки зрения обе имеют одинаковый вес при расчетах. Эта особенность человеческой психологии создает дополнительные сложности при моделировании рыночного поведения.
Различные типы волатильности
В финансовом анализе выделяют несколько типов волатильности, каждый из которых имеет свое применение:
Историческая волатильность (Historical Volatility, HV)
Это показатель, рассчитываемый на основе прошлых ценовых данных. Данный тип волатильности отражает фактические колебания цены в прошлом и широко используется для базового анализа рисков. Однако важно понимать, что историческая волатильность — это взгляд в прошлое, который не всегда может адекватно предсказать будущие ценовые движения.
Подразумеваемая волатильность (Implied Volatility, IV)
Это оценка будущей волатильности, извлекаемая из рыночных цен опционов. Она показывает ожидания рынка относительно будущих колебаний цены базового актива. IV часто используется как индикатор рыночного настроения и «страха» инвесторов. Индекс VIX, например, является мерой подразумеваемой волатильности опционов на индекс S&P 500.
Реализованная волатильность (Realized Volatility, RV)
Это фактическая волатильность, наблюдаемая на определенном временном интервале. То есть, по сути, апостериорная мера, которая может использоваться для сравнения с прогнозируемой волатильностью.
Локальная волатильность (Local Volatility)
Локальная волатильность — концепция, введенная Бруно Дюпиром, представляет волатильность как функцию времени и цены базового актива. Модели локальной волатильности пытаются объяснить улыбку волатильности (volatility smile) — явление, при котором опционы с разными страйками имеют разную подразумеваемую волатильность.
Стохастическая волатильность (Stochastic Volatility)
Это подход, который моделирует волатильность как случайный процесс. Модели стохастической волатильности, такие как Heston model или SABR model, позволяют волатильности изменяться со временем согласно определенному стохастическому процессу.
Понимание этих различных типов волатильности критично для построения эффективных торговых стратегий и моделей управления рисками. В следующих разделах мы рассмотрим методы расчета и прогнозирования разных типов.
Классические методы расчета волатильности
Начнем с рассмотрения классических методов расчета волатильности, которые служат фундаментом для более сложных моделей. Несмотря на ограниченность этих методов, понимание их принципов необходимо для освоения более продвинутых подходов.
Историческая волатильность и ее расчет
Историческая волатильность обычно рассчитывается как стандартное отклонение логарифмических доходностей за определенный период. Этот метод широко применяется благодаря своей простоте и доступности исторических данных.
Шаги расчета исторической волатильности:
- Сбор исторических данных о ценах актива за выбранный период (например, дневные цены закрытия за последний год)
- Расчет логарифмических доходностей: r_t = ln(P_t / P_{t-1}), где P_t — цена в момент времени t
- Вычисление среднего значения логарифмических доходностей: μ = (1/n) * Σ r_t
- Расчет дисперсии доходностей: σ² = (1/n) * Σ (r_t — μ)²
- Вычисление стандартного отклонения: σ = √σ²
- Аннуализация волатильности (если необходимо): σ_annual = σ * √T, где T — количество периодов в году.
Реализация этого метода на Python выглядит следующим образом:
import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt
from scipy import stats
def calculate_historical_volatility(ticker, period='1y', window=21, trading_days=252):
"""
Рассчитывает историческую волатильность для заданного тикера
Parameters:
ticker (str): Символ акции или ETF
period (str): Период для загрузки данных ('1d', '5d', '1mo', '3mo', '6mo', '1y', '2y', '5y', '10y', 'ytd', 'max')
window (int): Окно для расчета волатильности (в днях)
trading_days (int): Количество торговых дней в году для аннуализации
Returns:
DataFrame: Датафрейм с ценами, доходностями и волатильностью
"""
# Загрузка данных с auto_adjust=False
data = yf.download(ticker, period=period, auto_adjust=False)
# Расчет логарифмических доходностей
data['log_return'] = np.log(data['Close'] / data['Close'].shift(1))
# Расчет скользящей волатильности
data['volatility'] = data['log_return'].rolling(window=window).std() * np.sqrt(trading_days)
# Расчет полной исторической волатильности за весь период
full_period_vol = data['log_return'].std() * np.sqrt(trading_days)
print(f"\n Историческая волатильность {ticker} за весь период: {full_period_vol:.4f} ({full_period_vol*100:.2f}%)")
return data
calculate_historical_volatility('AAPL', period='1y', window=21, trading_days=252)
1 of 1 completed
Историческая волатильность AAPL за весь период: 0.3254 (32.54%)

Рис. 1: Таблица цен акций Apple (AAPL) с расчетом логарифмических доходностей и скользящей волатильности
Если построить график волатильности за 2 года, то он будет выглядеть так:
stock_data = calculate_historical_volatility('AAPL', period='2y')
plt.figure(figsize=(12, 6))
plt.plot(stock_data['volatility'], color='darkred')
plt.title('Историческая 21-дневная волатильность Apple')
plt.xlabel('Дата')
plt.ylabel('Волатильность')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
1 of 1 completed
Историческая волатильность AAPL за весь период: 0.2683 (26.83%)

Рис. 2: Историческая 21-дневная волатильность Apple
Этот код не только рассчитывает полную историческую волатильность за весь период, но и показывает, как волатильность меняется со временем, используя скользящее окно. Визуализация этих изменений может помочь идентифицировать периоды повышенной рыночной турбулентности.
Метод прост и легко интерпретируем. Однако у исторической волатильности есть существенные ограничения:
- Она предполагает, что будущие колебания будут похожи на прошлые, что не всегда верно;
- Чувствительна к выбору временного окна и периода расчета;
- Не учитывает неравномерное распределение волатильности во времени (кластеризацию);
- Не отражает рыночные ожидания будущей волатильности.
Поэтому в профессиональном анализе редко ограничиваются только исторической волатильностью и дополняют ее другими метриками и методами.
Взвешенные методы оценки волатильности
Для устранения некоторых недостатков простой исторической волатильности были разработаны взвешенные методы, которые придают большее значение недавним наблюдениям. Эти методы основаны на предположении, что недавние ценовые движения имеют большее влияние на будущую волатильность, чем отдаленные события.
Экспоненциально взвешенная скользящая средняя (EWMA)
Метод EWMA, популяризированный RiskMetrics (J.P. Morgan), использует экспоненциальное сглаживание для придания большего веса недавним наблюдениям:
σ²_t = λ * σ²_{t-1} + (1 — λ) * r²_{t-1}
где:
- σ²_t — дисперсия в момент времени t;
- λ — параметр сглаживания (обычно 0.94 для дневных данных);
- r²_{t-1} — квадрат доходности за предыдущий период.
Реализация на Python:
def calculate_ewma_volatility(returns, lambda_param=0.94, trading_days=252):
"""
Рассчитывает волатильность методом EWMA
Parameters:
returns (Series): Логарифмические доходности актива
lambda_param (float): Параметр сглаживания (обычно 0.94 для дневных данных)
trading_days (int): Количество торговых дней в году для аннуализации
Returns:
Series: Временной ряд волатильности EWMA
"""
# Инициализация
variance = returns.var()
variances = [variance]
# Расчет EWMA
for t in range(1, len(returns)):
if np.isnan(returns[t-1]):
variances.append(variances[-1])
else:
variance = lambda_param * variances[-1] + (1 - lambda_param) * returns[t-1]**2
variances.append(variance)
# Преобразование в Series и аннуализация
ewma_volatility = np.sqrt(variances) * np.sqrt(trading_days)
ewma_vol_series = pd.Series(ewma_volatility, index=returns.index)
return ewma_vol_series
# Пример использования
returns = stock_data['log_return'].dropna()
ewma_vol = calculate_ewma_volatility(returns)
# Сравнение разных методов
plt.figure(figsize=(14, 7))
plt.plot(stock_data['volatility'], label='Классическая историческая (21 день)', alpha=0.7)
plt.plot(ewma_vol, label='EWMA (λ=0.94)', color='orange', alpha=0.8)
plt.title('Сравнение методов расчета волатильности')
plt.xlabel('Дата')
plt.ylabel('Аннуализированная волатильность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Рис. 3: Сравнение методов расчета волатильности: Классическая историческая и EWMA
Метод Парцена (Parzen window)
Еще один подход к взвешиванию — использование ядерных функций (kernel functions), таких как окно Парцена. Этот метод позволяет более гибко задавать веса для наблюдений на разных временных расстояниях.
def parzen_volatility(returns, window_size=30, trading_days=252):
"""
Рассчитывает волатильность с использованием окна Парцена
Parameters:
returns (Series): Логарифмические доходности актива
window_size (int): Размер окна
trading_days (int): Количество торговых дней в году для аннуализации
Returns:
Series: Временной ряд волатильности с взвешиванием по Парцену
"""
vol_series = pd.Series(index=returns.index)
for i in range(window_size, len(returns)):
window = returns.iloc[i-window_size:i]
# Создание весов с использованием функции Парцена
weights = np.arange(1, window_size + 1) / window_size
weights = weights**2 * (1 - weights)**2 * 15/8 # Парцен с параметром q=2
weights = weights / weights.sum() # Нормализация весов
# Взвешенная оценка дисперсии
weighted_variance = np.sum(weights * (window - window.mean())**2)
vol_series.iloc[i] = np.sqrt(weighted_variance * trading_days)
return vol_series
parzen_vol = parzen_volatility(returns)
# Сравнение разных методов
plt.figure(figsize=(14, 7))
plt.plot(stock_data['volatility'], label='Классическая историческая (21 день)', alpha=0.7)
plt.plot(parzen_vol, label='Волатильность с использованием окна Парцена (30 дней)', color='crimson', alpha=0.8)
plt.title('Сравнение методов расчета волатильности')
plt.xlabel('Дата')
plt.ylabel('Аннуализированная волатильность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Рис. 4: Сравнение методов расчета волатильности: Классическая историческая и Метод Парцена
Преимущество взвешенных методов заключается в том, что они лучше улавливают изменения в характере волатильности и быстрее реагируют на новые рыночные условия. Однако все эти методы по-прежнему основаны исключительно на исторических данных и не учитывают ожидания рынка.
GARCH модели и их модификации
Модели авторегрессионной условной гетероскедастичности (GARCH) представляют собой значительный шаг вперед по сравнению с классическими методами расчета волатильности. Эти модели способны учитывать такое важное свойство финансовых временных рядов, как кластеризация волатильности — тенденцию периодов высокой волатильности сменяться периодами низкой волатильности.
Основы GARCH моделей
Стандартная модель GARCH(p,q) описывается следующим образом:
σ²_t = ω + Σ(i=1 до p) α_i * r²_{t-i} + Σ(j=1 до q) β_j * σ²_{t-j}
где:
- σ²_t — условная дисперсия в момент времени t
- ω — константа (долгосрочная дисперсия)
- α_i — параметры ARCH-компоненты (влияние прошлых шоков)
- r²_{t-i} — квадраты прошлых доходностей
- β_j — параметры GARCH-компоненты (влияние прошлой волатильности)
- σ²_{t-j} — прошлые значения условной дисперсии
Наиболее распространенной модификацией является GARCH(1,1), которая имеет вид:
σ²_t = ω + α * r²_{t-1} + β * σ²_{t-1}
Реализация GARCH(1,1) с использованием библиотеки arch в Python:
from arch import arch_model
def fit_garch_model(returns, p=1, q=1, mean='Zero', vol='GARCH', dist='Normal'):
"""
Подгоняет GARCH модель к временному ряду доходностей
Parameters:
returns (Series): Логарифмические доходности актива
p (int): Порядок ARCH компоненты
q (int): Порядок GARCH компоненты
mean (str): Спецификация условного среднего ('Zero', 'Constant', 'AR', 'ARX')
vol (str): Тип модели волатильности ('GARCH', 'EGARCH', 'TARCH', 'GJR', 'FIGARCH')
dist (str): Распределение ошибок ('Normal', 'StudentsT', 'SkewStudentsT')
Returns:
model_fit: Подогнанная модель GARCH
forecast: Прогноз волатильности
"""
# Создание модели
model = arch_model(returns.dropna(), p=p, q=q, mean=mean, vol=vol, dist=dist)
# Подгонка модели
model_fit = model.fit(disp='off')
# Прогноз волатильности
forecast = model_fit.forecast(horizon=1)
return model_fit, forecast
# Пример использования
returns = stock_data['log_return'].dropna() * 100 # Масштабируем для лучшей сходимости
garch_model, garch_forecast = fit_garch_model(returns)
# Вывод параметров модели
print(garch_model.summary())
# Извлечение условной волатильности
garch_vol = pd.Series(
np.sqrt(garch_model.conditional_volatility**2 * 252) / 100, # Аннуализация и масштабирование обратно
index=returns.index
)
# Визуализация
plt.figure(figsize=(14, 7))
plt.plot(stock_data['volatility'], label='Историческая (21 день)', alpha=0.6)
plt.plot(ewma_vol, label='EWMA (λ=0.94)', alpha=0.6)
plt.plot(garch_vol, label='GARCH(1,1)', color='red', linewidth=1.5)
plt.title('Сравнение методов расчета волатильности')
plt.xlabel('Дата')
plt.ylabel('Аннуализированная волатильность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Zero Mean — GARCH Model Results
==============================================================================
Dep. Variable: log_return R-squared: 0.000
Mean Model: Zero Mean Adj. R-squared: 0.002
Vol Model: GARCH Log-Likelihood: -920.839
Distribution: Normal AIC: 1847.68
Method: Maximum Likelihood BIC: 1860.32
No. Observations: 500
Date: Mon, Apr 28 2025 Df Residuals: 500
Time: 15:09:12 Df Model: 0
Volatility Model
============================================================================
coef std err t P>|t| 95.0% Conf. Int.
—————————————————————————-
omega 0.0303 5.379e-02 0.563 0.574 [-7.516e-02, 0.136]
alpha[1] 0.0489 2.162e-02 2.260 2.384e-02 [6.481e-03,9.125e-02]
beta[1] 0.9463 3.653e-02 25.907 5.517e-148 [ 0.875, 1.018]
============================================================================
Covariance estimator: robust

Рис. 5: График волатильности акций Apple 3 методами: историческая волатильность, EWMA, GARCH
Продвинутые модификации GARCH
В реальном мире финансовые временные ряды обладают рядом свойств, которые не полностью учитываются в стандартной модели GARCH. Для решения этих проблем были разработаны различные модификации:
EGARCH (Exponential GARCH)
Модель экспоненциального GARCH учитывает асимметричную реакцию волатильности на положительные и отрицательные шоки (так называемый «эффект рычага»). В финансах часто наблюдается, что отрицательные новости вызывают более сильный рост волатильности, чем позитивные.
Основное уравнение EGARCH(1,1):
ln(σ²_t) = ω + α * (|z_{t-1}| — E[|z|]) + γ * z_{t-1} + β * ln(σ²_{t-1})
где z_t = r_t / σ_t — стандартизированные инновации.
GJR-GARCH
Другой популярный подход к моделированию асимметрии — GJR-GARCH, названный по инициалам его создателей (Glosten, Jagannathan и Runkle):
σ²_t = ω + (α + γ * I_{t-1}) * r²_{t-1} + β * σ²_{t-1}
где I_{t-1} — индикаторная функция, равная 1, если r_{t-1} < 0, и 0 в противном случае.
# Сравнение различных спецификаций GARCH
models = {
'GARCH(1,1)': {'p': 1, 'q': 1, 'vol': 'GARCH'},
'EGARCH(1,1)': {'p': 1, 'q': 1, 'vol': 'EGARCH'},
'GJR-GARCH(1,1)': {'p': 1, 'q': 1, 'vol': 'GJR'}
}
results = {}
for name, specs in models.items():
model = arch_model(returns.dropna(),
p=specs['p'],
q=specs['q'],
mean='Zero',
vol=specs['vol'],
dist='StudentsT')
model_fit = model.fit(disp='off')
results[name] = {
'AIC': model_fit.aic,
'BIC': model_fit.bic,
'Log-Likelihood': model_fit.loglikelihood,
'Volatility': pd.Series(
np.sqrt(model_fit.conditional_volatility**2 * 252) / 100,
index=returns.index
)
}
# Сравнение моделей
comparison = pd.DataFrame({name: {'AIC': res['AIC'], 'BIC': res['BIC'], 'Log-Likelihood': res['Log-Likelihood']}
for name, res in results.items()}).T
print(comparison)
# Визуализация волатильности из разных моделей
plt.figure(figsize=(14, 7))
for name, res in results.items():
plt.plot(res['Volatility'], label=name, alpha=0.7)
plt.title('Сравнение различных спецификаций GARCH')
plt.xlabel('Дата')
plt.ylabel('Аннуализированная волатильность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
TGARCH и другие модели с порогами
TGARCH (Threshold GARCH) и другие пороговые модели позволяют параметрам модели меняться в зависимости от состояния рынка. Например, они могут использовать разные коэффициенты для режимов высокой и низкой волатильности.
FIGARCH (Fractionally Integrated GARCH)
FIGARCH моделирует долговременную память в процессе волатильности, что приводит к гиперболическому затуханию автокорреляционной функции волатильности. Это свойство часто наблюдается в финансовых временных рядах.
Прогнозирование волатильности с помощью GARCH
Одно из ключевых преимуществ моделей GARCH — возможность прогнозирования будущей волатильности. Для GARCH(1,1) формула прогноза на k шагов вперед имеет вид:
σ²_{t+k|t} = ω + (α + β) * σ²_{t+k-1|t}
По мере увеличения горизонта прогноза, прогнозируемая волатильность стремится к долгосрочному среднему значению:
σ²_{∞} = ω / (1 — α — β)
from arch import arch_model
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# Step 1: Fit a GARCH(1,1) model
garch_model = arch_model(
returns.dropna(),
p=1,
q=1,
mean='Zero',
vol='GARCH',
dist='StudentsT'
)
garch_model_fit = garch_model.fit(disp='off')
# Print summary of the fitted model
print(garch_model_fit.summary())
# Step 2: Calculate historical GARCH volatility
garch_vol = pd.Series(
np.sqrt(garch_model_fit.conditional_volatility**2 * 252) / 100,
index=returns.index
)
# Step 3: Forecast volatility
def forecast_garch_volatility(model_fit, horizon=21, method='analytic', simulations=1000):
"""
Прогнозирует волатильность с помощью подогнанной GARCH модели
Parameters:
model_fit: Подогнанная модель GARCH
horizon (int): Горизонт прогнозирования (в днях)
method (str): Метод прогнозирования ('analytic', 'simulation', 'bootstrap')
simulations (int): Количество симуляций для методов 'simulation' и 'bootstrap'
Returns:
DataFrame: Прогноз волатильности
"""
# Прогнозирование
forecast = model_fit.forecast(horizon=horizon, method=method, simulations=simulations)
# Извлечение прогноза дисперсии
variance_forecast = forecast.variance.iloc[-1]
# Создание датафрейма с прогнозом
last_date = model_fit.model.y.index[-1] # Access data through model_fit.model.y
future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=horizon, freq='B')
# Аннуализация волатильности
vol_forecast = pd.Series(
np.sqrt(variance_forecast * 252) / 100, # Предполагается, что исходные данные были масштабированы *100
index=future_dates
)
return vol_forecast
# Прогнозирование волатильности
forecast_horizon = 21 # Прогноз на месяц вперед
vol_forecast = forecast_garch_volatility(garch_model_fit, horizon=forecast_horizon)
Zero Mean — GARCH Model Results
====================================================================================
Dep. Variable: log_return R-squared: 0.000
Mean Model: Zero Mean Adj. R-squared: 0.002
Vol Model: GARCH Log-Likelihood: -881.197
Distribution: Standardized Student’s t AIC: 1770.39
Method: Maximum Likelihood BIC: 1787.25
No. Observations: 500
Date: Mon, Apr 28 2025 Df Residuals: 500
Time: 15:24:36 Df Model: 0
Volatility Model
===========================================================================
coef std err t P>|t| 95.0% Conf. Int.
—————————————————————————
omega 0.4657 0.383 1.216 0.224 [ -0.285, 1.217]
alpha[1] 0.1219 7.683e-02 1.587 0.113 [-2.865e-02, 0.273]
beta[1] 0.6997 0.210 3.332 8.625e-04 [ 0.288, 1.111]
Distribution
========================================================================
coef std err t P>|t| 95.0% Conf. Int.
————————————————————————
nu 3.9315 0.706 5.572 2.515e-08 [ 2.549, 5.314]
========================================================================
Covariance estimator: robust
Модели GARCH значительно улучшают качество моделирования волатильности по сравнению с классическими методами, но и они имеют ограничения:
- Предполагают определенную форму условного распределения доходностей;
- Могут иметь проблемы со сходимостью при оценке параметров;
- Чувствительны к выбросам в данных;
- Не учитывают структурные изменения на рынке.
В следующих разделах мы рассмотрим еще более продвинутые методы, которые преодолевают эти ограничения.
Расчет волатильности для высокочастотных данных
С развитием технологий и доступностью высокочастотных данных (тиковых данных, данных с минутными интервалами) появились новые подходы к оценке волатильности, которые позволяют получить более точные результаты. Эти методы особенно важны для алгоритмической торговли и управления рисками в реальном времени.
Реализованная волатильность (Realized Volatility)
Реализованная волатильность (RV) — это оценка волатильности, основанная на внутридневных доходностях. Преимущество этого подхода в том, что он использует больше информации по сравнению с классическими методами, основанными только на дневных ценах закрытия.
Базовая формула для расчета реализованной волатильности:
RV_t = √(Σ(i=1 до N) r²_{t,i})
где r_{t,i} — внутридневные доходности за день t.
Вот как можно посчитать ее на Python:
import pandas as pd
import numpy as np
import yfinance as yf
def download_intraday_data(ticker, start_date, end_date, interval='5m'):
data = yf.download(ticker, start=start_date, end=end_date, interval=interval, progress=False)
return data
def calculate_realized_volatility(price_data, freq='5min', annualization_factor=252*78):
"""
Рассчитывает реализованную волатильность на основе внутридневных данных
"""
# Ресемплирование данных до указанной частоты
resampled = price_data['Close'].resample(freq).last().dropna()
# Расчет логарифмических доходностей
returns = np.log(resampled / resampled.shift(1)).dropna()
# Группировка по дням и расчет реализованной волатильности
squared_returns = returns**2
daily_rv = squared_returns.groupby(pd.Grouper(freq='D')).sum().dropna()
realized_vol = np.sqrt(daily_rv * annualization_factor)
return realized_vol
# Скачивание реальных внутридневных данных
intraday_data = download_intraday_data('AAPL', start_date='2025-04-01', end_date='2025-04-28', interval='5m')
# Пересчет реализованной волатильности
realized_vol = calculate_realized_volatility(intraday_data)
print(realized_vol)
Ticker AAPL
Datetime
2025-04-01 00:00:00+00:00 1.965615
2025-04-02 00:00:00+00:00 1.667150
2025-04-03 00:00:00+00:00 11.448111
2025-04-04 00:00:00+00:00 7.039537
2025-04-05 00:00:00+00:00 0.000000
2025-04-06 00:00:00+00:00 0.000000
2025-04-07 00:00:00+00:00 14.094030
2025-04-08 00:00:00+00:00 7.045986
2025-04-09 00:00:00+00:00 10.552356
2025-04-10 00:00:00+00:00 7.971537
2025-04-11 00:00:00+00:00 4.702488
2025-04-12 00:00:00+00:00 0.000000
2025-04-13 00:00:00+00:00 0.000000
2025-04-14 00:00:00+00:00 8.068386
2025-04-15 00:00:00+00:00 2.590927
2025-04-16 00:00:00+00:00 3.488328
2025-04-17 00:00:00+00:00 2.343996
2025-04-18 00:00:00+00:00 0.000000
2025-04-19 00:00:00+00:00 0.000000
2025-04-20 00:00:00+00:00 0.000000
2025-04-21 00:00:00+00:00 3.099348
2025-04-22 00:00:00+00:00 3.589552
2025-04-23 00:00:00+00:00 4.947933
2025-04-24 00:00:00+00:00 1.872168
2025-04-25 00:00:00+00:00 1.774270
# Визуализация реализованной волатильности
plt.figure(figsize=(14, 7))
plt.plot(realized_vol, label='Реализованная волатильность', color='darkgreen')
plt.title('Реализованная волатильность Apple (на основе 5-минутных данных)')
plt.xlabel('Дата')
plt.ylabel('Аннуализированная волатильность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Рис. 6: Реализованная волатильность акций Apple на основе 5-минутных данных
Биполярная реализованная волатильность (Bipower Variation)
Биполярная вариация — это метод, устойчивый к выбросам и джампам (резким скачкам цен). Он оценивает непрерывную компоненту волатильности, игнорируя случайные выбросы:
def calculate_bipower_variation(price_data, freq='5min', annualization_factor=252*78):
"""
Рассчитывает биполярную вариацию для оценки непрерывной компоненты волатильности
"""
# Ресемплирование данных
resampled = price_data['Close'].resample(freq).last().dropna()
# Расчет логарифмических доходностей
returns = np.log(resampled / resampled.shift(1)).dropna()
# Группировка по дням
daily_returns = returns.groupby(pd.Grouper(freq='D'))
# Расчет биполярной вариации для каждого дня
daily_bv = daily_returns.apply(lambda x: np.sum(np.abs(x[:-1].values) * np.abs(x[1:].values)) * np.pi/2)
# Аннуализация
bipower_vol = np.sqrt(daily_bv * annualization_factor)
return bipower_vol
bipolar_vol = calculate_bipower_variation(intraday_data, freq='5min', annualization_factor=252*78)
plt.figure(figsize=(14, 7))
plt.plot(bipolar_vol, label='Биполярная реализованная волатильность', color='darkblue')
plt.title('Биполярная реализованная волатильность Apple (на основе 5-минутных данных)')
plt.xlabel('Дата')
plt.ylabel('Аннуализированная волатильность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Рис. 7: Биполярная реализованная волатильность акций Apple на основе 5-минутных данных
Джамп-компонента волатильности (Jump Component)
Можно также выделить джамп-компоненту волатильности, которая показывает вклад резких движений цены:
Jump_t = max(RV_t — BV_t, 0)
Эта метрика особенно полезна для анализа рисков, связанных с резкими рыночными событиями.
def calculate_jump_component(realized_vol, bipolar_vol):
if isinstance(realized_vol, pd.DataFrame):
realized_vol = realized_vol['AAPL']
if isinstance(bipolar_vol, pd.DataFrame):
bipolar_vol = bipolar_vol['AAPL']
rv_variance = realized_vol**2
bv_variance = bipolar_vol**2
jump_variance = np.maximum(rv_variance - bv_variance, 0)
jump_component = np.sqrt(jump_variance)
# Защита от деления на ноль
jump_contribution = np.where(rv_variance > 0, jump_variance / rv_variance * 100, 0)
return jump_component, jump_contribution
calculate_jump_component(realized_vol, bipolar_vol)
(Datetime
2025-04-01 00:00:00+00:00 0.983253
2025-04-02 00:00:00+00:00 0.000000
2025-04-03 00:00:00+00:00 9.652204
2025-04-04 00:00:00+00:00 3.688950
2025-04-05 00:00:00+00:00 0.000000
2025-04-06 00:00:00+00:00 0.000000
2025-04-07 00:00:00+00:00 10.065162
2025-04-08 00:00:00+00:00 3.797893
2025-04-09 00:00:00+00:00 0.000000
2025-04-10 00:00:00+00:00 3.750661
2025-04-11 00:00:00+00:00 0.589774
2025-04-12 00:00:00+00:00 0.000000
2025-04-13 00:00:00+00:00 0.000000
2025-04-14 00:00:00+00:00 7.215884
2025-04-15 00:00:00+00:00 0.000000
2025-04-16 00:00:00+00:00 2.133624
2025-04-17 00:00:00+00:00 0.875778
2025-04-18 00:00:00+00:00 0.000000
2025-04-19 00:00:00+00:00 0.000000
2025-04-20 00:00:00+00:00 0.000000
2025-04-21 00:00:00+00:00 2.116673
2025-04-22 00:00:00+00:00 2.703209
2025-04-23 00:00:00+00:00 3.979249
2025-04-24 00:00:00+00:00 0.475482
2025-04-25 00:00:00+00:00 0.000000
Freq: D, dtype: float64,
array([25.02268801, 0. , 71.08620035, 27.46105469, 0. ,
0. , 51.00011666, 29.05372431, 0. , 22.13764889,
1.57295439, 0. , 0. , 79.98447944, 0. ,
37.41116213, 13.95962823, 0. , 0. , 0. ,
46.64089248, 56.71248966, 64.67769715, 6.45026823, 0. ]))
Реализованная квартальная волатильность (Realized Quarticity)
Реализованная квартальность используется для оценки точности и вариабельности реализованной волатильности:
RQ_t = N * Σ(i=1 до N) r⁴_{t,i}
import yfinance as yf
import pandas as pd
import numpy as np
# Загрузка внутридневных данных AAPL с 5-минутным интервалом
def download_intraday_data(ticker, start_date, end_date, interval='5m'):
data = yf.download(ticker, start=start_date, end=end_date, interval=interval, progress=False)
return data
# Параметры загрузки
ticker = 'AAPL'
start_date = '2025-04-01' # Начальная дата
end_date = '2025-04-28' # Конечная дата
# Загрузка данных
price_data = download_intraday_data(ticker, start_date, end_date, interval='5m')
def calculate_realized_quarticity(price_data, freq='5m', annualization_factor=252*78*78):
# Ресемплирование данных
resampled = price_data['Close'].resample(freq).last().dropna()
# Расчет логарифмических доходностей
returns = np.log(resampled / resampled.shift(1)).dropna()
# Группировка по дням
daily_returns = returns.groupby(pd.Grouper(freq='D'))
# Количество наблюдений в дне
n = 78 # для 5-минутных данных, 78 наблюдений в день
# Расчет реализованной квартальности для каждого дня
daily_rq = daily_returns.apply(lambda x: n * np.sum(x**4))
# Квадратный корень для преобразования в шкалу волатильности
realized_quarticity = np.sqrt(np.sqrt(daily_rq * annualization_factor))
return realized_quarticity
# Вызов функции для расчета реализованной квартальности
realized_quarticity = calculate_realized_quarticity(price_data, freq='5min')
print(realized_quarticity)
Ticker AAPL
Datetime
2025-04-01 00:00:00+00:00 0.786649
2025-04-02 00:00:00+00:00 0.666551
2025-04-03 00:00:00+00:00 8.197528
2025-04-04 00:00:00+00:00 3.335661
2025-04-05 00:00:00+00:00 0.000000
2025-04-06 00:00:00+00:00 0.000000
2025-04-07 00:00:00+00:00 7.138228
2025-04-08 00:00:00+00:00 2.988992
2025-04-09 00:00:00+00:00 3.885689
2025-04-10 00:00:00+00:00 3.620322
2025-04-11 00:00:00+00:00 1.636625
2025-04-12 00:00:00+00:00 0.000000
2025-04-13 00:00:00+00:00 0.000000
2025-04-14 00:00:00+00:00 5.460762
2025-04-15 00:00:00+00:00 0.901881
2025-04-16 00:00:00+00:00 1.596083
2025-04-17 00:00:00+00:00 0.805649
2025-04-18 00:00:00+00:00 0.000000
2025-04-19 00:00:00+00:00 0.000000
2025-04-20 00:00:00+00:00 0.000000
2025-04-21 00:00:00+00:00 1.816832
2025-04-22 00:00:00+00:00 2.031250
2025-04-23 00:00:00+00:00 3.090295
2025-04-24 00:00:00+00:00 0.669338
2025-04-25 00:00:00+00:00 0.622908
Использование высокочастотных данных позволяет получить более точные оценки волатильности, но требует дополнительной обработки для учета микроструктурного шума (microstructure noise) — искажений, вызванных особенностями рыночной микроструктуры, такими как эффект отскока цен (bid-ask bounce).
Волатильность облигаций и особенности ее расчета
Волатильность облигаций имеет свои особенности по сравнению с акциями и требует специфических подходов к расчету. Основным фактором, влияющим на цену облигации, является изменение процентных ставок, поэтому волатильность облигаций часто рассматривается через призму процентного риска.
Дюрация и выпуклость как меры риска облигаций
Дюрация Маколея (Macaulay Duration) — средневзвешенный срок до погашения денежных потоков облигации, где весами выступают приведенные стоимости этих потоков. Этот показатель измеряется в годах и рассчитывается по формуле:
D = Σ(t * PV(CF_t)) / P
где:
- t — время до каждого денежного потока;
- PV(CF_t) — приведенная стоимость денежного потока в момент t;
- P — цена облигации.
Модифицированная дюрация (Modified Duration) — показатель, который напрямую связан с чувствительностью цены облигации к изменению доходности:
MD = D / (1 + y/m)
где:
- D — дюрация Маколея;
- y — доходность к погашению;
- m — количество купонных выплат в году.
Процентное изменение цены облигации при изменении доходности можно приблизительно оценить как:
ΔP/P ≈ -MD * Δy
Выпуклость (Convexity) — мера кривизны зависимости цены облигации от доходности, которая учитывает нелинейный характер этой зависимости:
C = Σ(t * (t + 1) * PV(CF_t)) / (P * (1 + y/m)²)
Включение выпуклости позволяет получить более точную оценку изменения цены:
ΔP/P ≈ -MD * Δy + 0.5 * C * (Δy)²
Код для расчета этих показателей:
def calculate_bond_duration_convexity(face_value, coupon_rate, years_to_maturity, market_rate, frequency=2):
"""
Рассчитывает дюрацию и выпуклость облигации
Parameters:
face_value (float): Номинальная стоимость облигации
coupon_rate (float): Годовая купонная ставка (в десятичном формате)
years_to_maturity (float): Количество лет до погашения
market_rate (float): Рыночная процентная ставка (в десятичном формате)
frequency (int): Частота купонных выплат в году
Returns:
tuple: (цена, дюрация Маколея, модифицированная дюрация, выпуклость)
"""
periods = int(years_to_maturity * frequency)
coupon_payment = face_value * coupon_rate / frequency
discount_rate = market_rate / frequency
# Расчет цены облигации
cash_flows = np.array([coupon_payment] * periods)
cash_flows[-1] += face_value
time_points = np.arange(1, periods + 1) / frequency
discount_factors = 1 / (1 + discount_rate) ** (time_points * frequency)
present_values = cash_flows * discount_factors
price = np.sum(present_values)
# Расчет дюрации Маколея
macaulay_duration = np.sum(present_values * time_points) / price
# Расчет модифицированной дюрации
modified_duration = macaulay_duration / (1 + discount_rate)
# Расчет выпуклости
convexity = np.sum(present_values * time_points * (time_points + 1/frequency)) / (price * (1 + discount_rate)**2)
return price, macaulay_duration, modified_duration, convexity
# Пример использования
bond_params = {
'face_value': 1000, # Номинал $1000
'coupon_rate': 0.05, # Купон 5%
'years_to_maturity': 10, # 10 лет до погашения
'market_rate': 0.055, # Рыночная ставка 5.5%
'frequency': 2 # Полугодовые выплаты
}
price, mac_duration, mod_duration, convexity = calculate_bond_duration_convexity(**bond_params)
print(f"Цена облигации: ${price:.2f}")
print(f"Дюрация Маколея: {mac_duration:.4f} лет")
print(f"Модифицированная дюрация: {mod_duration:.4f} лет")
print(f"Выпуклость: {convexity:.6f}")
# Анализ чувствительности цены к изменению доходности
delta_yields = np.linspace(-0.02, 0.02, 41) # Изменения доходности от -2% до +2%
prices = []
approx_prices_duration = []
approx_prices_duration_convexity = []
for dy in delta_yields:
# Точная цена при новой доходности
new_price, _, _, _ = calculate_bond_duration_convexity(
bond_params['face_value'],
bond_params['coupon_rate'],
bond_params['years_to_maturity'],
bond_params['market_rate'] + dy,
bond_params['frequency']
)
prices.append(new_price)
# Приближение с использованием только дюрации
delta_p_duration = -mod_duration * dy * price
approx_prices_duration.append(price + delta_p_duration)
# Приближение с использованием дюрации и выпуклости
delta_p_convexity = 0.5 * convexity * (dy**2) * price
approx_prices_duration_convexity.append(price + delta_p_duration + delta_p_convexity)
# Визуализация
plt.figure(figsize=(14, 7))
plt.plot(delta_yields*100, prices, label='Точная цена', linewidth=2)
plt.plot(delta_yields*100, approx_prices_duration, label='Приближение через дюрацию', linestyle='--')
plt.plot(delta_yields*100, approx_prices_duration_convexity, label='Приближение через дюрацию и выпуклость', linestyle='-.')
plt.axvline(x=0, color='black', linestyle='-', alpha=0.3)
plt.axhline(y=price, color='black', linestyle='-', alpha=0.3)
plt.title('Чувствительность цены облигации к изменению доходности')
plt.xlabel('Изменение доходности (базисные пункты)')
plt.ylabel('Цена облигации ($)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
Цена облигации: $961.93
Дюрация Маколея: 7.9426 лет
Модифицированная дюрация: 7.7300 лет
Выпуклость: 72.706946

Рис. 8: Чувствительность цены облигации к изменению доходности
Волатильность процентных ставок и ее влияние на облигации
Волатильность процентных ставок является ключевым фактором риска для облигаций. Существует несколько подходов к ее оценке:
Историческая волатильность доходностей облигаций
Аналогично расчету волатильности для акций, можно вычислить стандартное отклонение изменений доходности:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
def calculate_yield_volatility(yield_data, window=21, annualization_factor=252):
"""
Рассчитывает волатильность доходностей облигаций
Parameters:
yield_data (Series): Временной ряд доходностей облигаций
window (int): Окно для расчета скользящей волатильности
annualization_factor (int): Фактор для аннуализации
Returns:
Series: Волатильность доходностей облигаций
"""
# Расчет абсолютных изменений доходности (в базисных пунктах)
yield_changes = yield_data.diff() * 100 # умножаем на 100 для перевода в б.п.
# Расчет скользящей волатильности
yield_volatility = yield_changes.rolling(window=window).std() * np.sqrt(annualization_factor)
return yield_volatility
# Симуляция данных по доходностям облигаций
np.random.seed(42) # Для воспроизводимости результатов
dates = pd.date_range(start='2023-01-01', periods=252, freq='B') # 252 торговых дня
yield_data = pd.Series(
np.linspace(2.5, 3.0, len(dates)) + np.random.normal(0, 0.02, len(dates)), # Тренд + шум
index=dates
)
# Вычисление волатильности доходностей
yield_volatility = calculate_yield_volatility(yield_data, window=21, annualization_factor=252)
# Визуализация результатов
plt.figure(figsize=(14, 7))
# График доходностей
plt.subplot(2, 1, 1)
plt.plot(yield_data, label='Доходность облигаций', color='blue')
plt.title('Доходность облигаций')
plt.xlabel('Дата')
plt.ylabel('Доходность (%)')
plt.legend()
plt.grid(True, alpha=0.3)
# График волатильности
plt.subplot(2, 1, 2)
plt.plot(yield_volatility, label='Волатильность доходностей', color='green')
plt.title('Волатильность доходностей облигаций (скользящее окно 21 день)')
plt.xlabel('Дата')
plt.ylabel('Аннуализированная волатильность (б.п.)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Рис. 9: Историческая волатильность доходностей облигаций (с окном 21 день)
Подразумеваемая волатильность процентных ставок
Для оценки ожиданий рынка относительно будущей волатильности процентных ставок можно использовать цены опционов на облигации или процентные фьючерсы:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy import stats
def estimate_implied_rate_volatility(option_price, strike, underlying_price, time_to_expiry, risk_free_rate):
"""
Оценивает подразумеваемую волатильность процентных ставок из цен опционов
использует метод Ньютона для решения уравнения
Parameters:
option_price (float): Цена опциона
strike (float): Цена исполнения
underlying_price (float): Цена базового актива (облигации или фьючерса)
time_to_expiry (float): Время до истечения срока опциона (в годах)
risk_free_rate (float): Безрисковая процентная ставка
Returns:
float: Подразумеваемая волатильность
"""
def black_scholes(sigma):
d1 = (np.log(underlying_price / strike) + (risk_free_rate + 0.5 * sigma**2) * time_to_expiry) / (sigma * np.sqrt(time_to_expiry))
d2 = d1 - sigma * np.sqrt(time_to_expiry)
option_value = underlying_price * stats.norm.cdf(d1) - strike * np.exp(-risk_free_rate * time_to_expiry) * stats.norm.cdf(d2)
return option_value - option_price
# Начальное предположение о волатильности
sigma = 0.2
# Метод Ньютона для нахождения корня уравнения
for _ in range(100):
diff = black_scholes(sigma)
if abs(diff) < 1e-6:
break
# Численное вычисление производной
epsilon = 1e-6
derivative = (black_scholes(sigma + epsilon) - diff) / epsilon
# Обновление значения волатильности
sigma = sigma - diff / derivative
# Проверка на валидность значения
if sigma <= 0:
sigma = 0.001
return sigma
# Симуляция данных
np.random.seed(42) # Для воспроизводимости результатов
n_samples = 100 # Количество симулированных точек
# Генерация параметров
option_prices = np.random.uniform(5, 20, n_samples) # Цены опционов
strikes = np.random.uniform(90, 110, n_samples) # Цены исполнения
underlying_prices = np.random.uniform(95, 105, n_samples) # Цены базовых активов
time_to_expiry = np.random.uniform(0.1, 1, n_samples) # Время до истечения (в годах)
risk_free_rates = np.random.uniform(0.01, 0.05, n_samples) # Безрисковые ставки
# Вычисление подразумеваемой волатильности
implied_vols = []
for i in range(n_samples):
implied_vol = estimate_implied_rate_volatility(
option_price=option_prices[i],
strike=strikes[i],
underlying_price=underlying_prices[i],
time_to_expiry=time_to_expiry[i],
risk_free_rate=risk_free_rates[i]
)
implied_vols.append(implied_vol)
# Создание DataFrame для анализа
data = pd.DataFrame({
'Option Price': option_prices,
'Strike': strikes,
'Underlying Price': underlying_prices,
'Time to Expiry': time_to_expiry,
'Risk-Free Rate': risk_free_rates,
'Implied Volatility': implied_vols
})
# Визуализация
plt.figure(figsize=(14, 7))
# Scatter plot зависимости подразумеваемой волатильности от цены опциона
plt.subplot(1, 2, 1)
plt.scatter(data['Option Price'], data['Implied Volatility'], alpha=0.7, color='blue')
plt.title('Подразумеваемая волатильность vs Цена опциона')
plt.xlabel('Цена опциона')
plt.ylabel('Подразумеваемая волатильность')
plt.grid(True, alpha=0.3)
# Scatter plot зависимости подразумеваемой волатильности от времени до истечения
plt.subplot(1, 2, 2)
plt.scatter(data['Time to Expiry'], data['Implied Volatility'], alpha=0.7, color='green')
plt.title('Подразумеваемая волатильность vs Время до истечения')
plt.xlabel('Время до истечения (годы)')
plt.ylabel('Подразумеваемая волатильность')
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Рис. 10: Подразумеваемая волатильность vs Цена опциона / Цена достижения
Волатильность спредов между облигациями
Спреды между облигациями (например, между корпоративными облигациями и государственными облигациями аналогичной дюрации) также являются важным показателем риска. Волатильность спредов может быть рассчитана аналогично волатильности самих доходностей.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
def calculate_spread_volatility(corp_yields, gov_yields, window=21, annualization_factor=252):
"""
Рассчитывает волатильность спредов между корпоративными и государственными облигациями
Parameters:
corp_yields (Series): Временной ряд доходностей корпоративных облигаций
gov_yields (Series): Временной ряд доходностей государственных облигаций
window (int): Окно для расчета скользящей волатильности
annualization_factor (int): Фактор для аннуализации
Returns:
tuple: (Спреды, Волатильность спредов)
"""
# Расчет спредов (в базисных пунктах)
spreads = (corp_yields - gov_yields) * 100
# Расчет изменений спредов
spread_changes = spreads.diff()
# Расчет скользящей волатильности
spread_volatility = spread_changes.rolling(window=window).std() * np.sqrt(annualization_factor)
return spreads, spread_volatility
# Симуляция данных
np.random.seed(42) # Для воспроизводимости результатов
dates = pd.date_range(start='2023-01-01', periods=252, freq='B') # 252 торговых дня
# Генерация данных для корпоративных и государственных облигаций
corp_yields = pd.Series(
np.linspace(4.0, 4.5, len(dates)) + np.random.normal(0, 0.05, len(dates)), # Тренд + шум
index=dates
)
gov_yields = pd.Series(
np.linspace(3.0, 3.2, len(dates)) + np.random.normal(0, 0.03, len(dates)), # Тренд + шум
index=dates
)
# Вычисление спредов и волатильности спредов
spreads, spread_volatility = calculate_spread_volatility(corp_yields, gov_yields, window=21, annualization_factor=252)
# Визуализация результатов
plt.figure(figsize=(14, 7))
# График спредов
plt.subplot(2, 1, 1)
plt.plot(spreads, label='Спреды (б.п.)', color='blue')
plt.title('Спреды между корпоративными и государственными облигациями')
plt.xlabel('Дата')
plt.ylabel('Спред (б.п.)')
plt.legend()
plt.grid(True, alpha=0.3)
# График волатильности спредов
plt.subplot(2, 1, 2)
plt.plot(spread_volatility, label='Волатильность спредов', color='green')
plt.title('Волатильность спредов (скользящее окно 21 день)')
plt.xlabel('Дата')
plt.ylabel('Аннуализированная волатильность (б.п.)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()

Рис. 11: Волатильность спредов между корпоративными и государственными облигациями
Волатильность деривативов
Деривативы, как производные инструменты, имеют особую связь с волатильностью. Для опционов волатильность является одним из ключевых факторов ценообразования, в то время как для фьючерсов волатильность влияет на рыночный риск и маржинальные требования.
Подразумеваемая волатильность опционов
Подразумеваемая волатильность (IV) — это оценка будущей волатильности базового актива, полученная из рыночных цен опционов путем обращения формулы ценообразования опционов, обычно модели Блэка-Шоулза.
def calculate_implied_volatility(option_price, underlying_price, strike, time_to_expiry,
risk_free_rate, option_type='call'):
"""
Рассчитывает подразумеваемую волатильность из цены опциона
Parameters:
option_price (float): Рыночная цена опциона
underlying_price (float): Цена базового актива
strike (float): Цена исполнения опциона
time_to_expiry (float): Время до истечения срока опциона (в годах)
risk_free_rate (float): Безрисковая процентная ставка
option_type (str): Тип опциона ('call' или 'put')
Returns:
float: Подразумеваемая волатильность
"""
from scipy import stats
from scipy.optimize import brentq
def black_scholes_price(sigma):
d1 = (np.log(underlying_price / strike) + (risk_free_rate + 0.5 * sigma**2) * time_to_expiry) / (sigma * np.sqrt(time_to_expiry))
d2 = d1 - sigma * np.sqrt(time_to_expiry)
if option_type.lower() == 'call':
price = underlying_price * stats.norm.cdf(d1) - strike * np.exp(-risk_free_rate * time_to_expiry) * stats.norm.cdf(d2)
else: # put
price = strike * np.exp(-risk_free_rate * time_to_expiry) * stats.norm.cdf(-d2) - underlying_price * stats.norm.cdf(-d1)
return price - option_price
try:
# Используем метод Брента для нахождения корня уравнения
implied_vol = brentq(black_scholes_price, 0.001, 5.0)
return implied_vol
except ValueError:
# Если решение не найдено, возвращаем NaN
return np.nan
Улыбка волатильности и поверхность волатильности
Улыбка волатильности (Volatility Smile) — это график зависимости подразумеваемой волатильности от цены исполнения (страйка) опциона при фиксированном сроке до истечения. Название происходит от характерной U-образной формы графика, напоминающей улыбку.
Поверхность волатильности (Volatility Surface) — это трехмерная поверхность, которая показывает зависимость подразумеваемой волатильности от цены исполнения и времени до истечения срока опциона.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.optimize import brentq
from scipy import stats
def calculate_implied_volatility(option_price, underlying_price, strike, time_to_expiry,
risk_free_rate, option_type='call'):
"""
Рассчитывает подразумеваемую волатильность из цены опциона
"""
def black_scholes_price(sigma):
d1 = (np.log(underlying_price / strike) + (risk_free_rate + 0.5 * sigma**2) * time_to_expiry) / (sigma * np.sqrt(time_to_expiry))
d2 = d1 - sigma * np.sqrt(time_to_expiry)
if option_type.lower() == 'call':
price = underlying_price * stats.norm.cdf(d1) - strike * np.exp(-risk_free_rate * time_to_expiry) * stats.norm.cdf(d2)
else: # put
price = strike * np.exp(-risk_free_rate * time_to_expiry) * stats.norm.cdf(-d2) - underlying_price * stats.norm.cdf(-d1)
return price - option_price
try:
# Используем метод Брента для нахождения корня уравнения
implied_vol = brentq(black_scholes_price, 0.001, 5.0)
return implied_vol
except ValueError:
# Если решение не найдено, возвращаем NaN
return np.nan
def plot_volatility_smile(option_chain, underlying_price, risk_free_rate, time_to_expiry):
"""
Строит улыбку волатильности для заданного срока до истечения
"""
# Расчет подразумеваемой волатильности для каждого опциона
implied_vols_call = []
implied_vols_put = []
moneyness = []
for _, row in option_chain.iterrows():
strike = row['Strike']
call_price = row['Call_Price']
put_price = row['Put_Price']
# Монейность опциона (отношение страйка к цене базового актива)
m = strike / underlying_price
moneyness.append(m)
# Расчет подразумеваемой волатильности
iv_call = calculate_implied_volatility(call_price, underlying_price, strike,
time_to_expiry, risk_free_rate, 'call')
iv_put = calculate_implied_volatility(put_price, underlying_price, strike,
time_to_expiry, risk_free_rate, 'put')
implied_vols_call.append(iv_call)
implied_vols_put.append(iv_put)
# Удаляем NaN значения
moneyness = np.array(moneyness)
implied_vols_call = np.array(implied_vols_call)
implied_vols_put = np.array(implied_vols_put)
valid_indices = ~np.isnan(implied_vols_call) & ~np.isnan(implied_vols_put)
moneyness = moneyness[valid_indices]
implied_vols_call = implied_vols_call[valid_indices]
implied_vols_put = implied_vols_put[valid_indices]
# Визуализация улыбки волатильности
plt.figure(figsize=(12, 6))
plt.scatter(moneyness, implied_vols_call, label='Calls', color='blue', alpha=0.7)
plt.scatter(moneyness, implied_vols_put, label='Puts', color='red', alpha=0.7)
# Сглаживание кривых
x_smooth = np.linspace(min(moneyness), max(moneyness), 100)
# Полиномиальная аппроксимация (добавляем проверку на минимальное количество точек)
if len(moneyness) > 3:
call_poly = np.poly1d(np.polyfit(moneyness, implied_vols_call, 2))
put_poly = np.poly1d(np.polyfit(moneyness, implied_vols_put, 2))
plt.plot(x_smooth, call_poly(x_smooth), color='blue', linestyle='--', label='Сглаженная кривая Calls')
plt.plot(x_smooth, put_poly(x_smooth), color='red', linestyle='--', label='Сглаженная кривая Puts')
# Вертикальная линия для ат-де-моней
plt.axvline(x=1, color='black', linestyle='-', alpha=0.5, label='Ат-де-моней (K/S = 1)')
plt.title(f'Улыбка волатильности (срок до истечения: {time_to_expiry*365:.0f} дней)')
plt.xlabel('Монейность (K/S)')
plt.ylabel('Подразумеваемая волатильность')
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Генерация случайных данных для опционов
np.random.seed(42) # Для воспроизводимости результатов
n_strikes = 20 # Количество страйков
# Параметры для симуляции
underlying_price = 100 # Текущая цена базового актива
risk_free_rate = 0.05 # Безрисковая процентная ставка
time_to_expiry = 0.25 # Время до истечения (в годах, пример: 3 месяца)
# Генерация значений страйков
strikes = np.linspace(80, 120, n_strikes)
# Генерация цен опционов (call и put)
call_prices = []
put_prices = []
for strike in strikes:
# Моделируем цены опционов как случайные значения вокруг теоретической цены Блэка-Шоулза
sigma = 0.2 + 0.1 * np.abs(strike - underlying_price) / underlying_price # Волатильность зависит от монейности
d1 = (np.log(underlying_price / strike) + (risk_free_rate + 0.5 * sigma**2) * time_to_expiry) / (sigma * np.sqrt(time_to_expiry))
d2 = d1 - sigma * np.sqrt(time_to_expiry)
call_price = underlying_price * stats.norm.cdf(d1) - strike * np.exp(-risk_free_rate * time_to_expiry) * stats.norm.cdf(d2)
put_price = strike * np.exp(-risk_free_rate * time_to_expiry) * stats.norm.cdf(-d2) - underlying_price * stats.norm.cdf(-d1)
# Добавляем случайный шум к ценам опционов
call_prices.append(call_price + np.random.normal(0, 0.5))
put_prices.append(put_price + np.random.normal(0, 0.5))
# Создание DataFrame для цепочки опционов
option_chain = pd.DataFrame({
'Strike': strikes,
'Call_Price': call_prices,
'Put_Price': put_prices
})
# Вызов функции для построения улыбки волатильности
plot_volatility_smile(option_chain, underlying_price, risk_free_rate, time_to_expiry)

Рис. 12: График улыбки волатильности опционов
Что показывает этот график:
- Показывает точки подразумеваемой волатильности для колл-опционов (синие точки) и пут-опционов (красные точки).
- Содержит сглаженные кривые для колл- и пут-опционов (пунктирные линии).
- Вертикальную черную линию, обозначающую ат-де-моней (монейность = 1).
Как устроен код:
- Мы генерируем случайные данные — создаем серию страйков (strikes) в диапазоне от 80 до 120.
- Для каждого страйка вычисляем теоретические цены опционов (call_price и put_price) с использованием формулы Блэка-Шоулза.
- Добавляем случайный шум к ценам опционов, чтобы имитировать рыночные колебания.
- Формируем DataFrame, содержащий страйки, цены колл-опционов и цены пут-опционов.
- Для каждого опциона рассчитываем подразумеваемую волатильность с помощью функции calculate_implied_volatility.
- Строим график улыбки волатильности, показывая зависимость подразумеваемой волатильности от монейности (отношения страйка к цене базового актива).
Критерии выбора метода расчета волатильности
Выше мы рассмотрели много методов расчета волатильности. Однако на самом деле это лишь вершина айсберга методов — их существуют десятки, если не сотни. Так какой же лучше выбрать?
Эксперты советуют выбирать метод исходя из горизонта инвестирования и класса активов.
| Тип инвестирования | Рекомендуемые методы расчета волатильности |
| Долгосрочные инвестиции (5+ лет) | 1. Годовая историческая волатильность |
| 2. GARCH(1,1) с годовым окном | |
| 3. Реализованная волатильность (дневные данные) | |
| 4. Многомерные модели (DCC-GARCH) | |
| 5. Фрактальный анализ Hurst Exponent | |
| Среднесрочные стратегии (6 месяцев-5 лет) | 1. EWMA (λ=0.94-0.97) |
| 2. EGARCH с асимметричным эффектом | |
| 3. Реализованная волатильность (часовые данные) | |
| 4. Скользящее окно 63-126 дней | |
| 5. Модель Heston (стохастическая волатильность) | |
| Краткосрочный трейдинг | 1. Реализованная волатильность (5-15 мин) |
| 2. Микроструктурные модели (RV + биполярная вариация) | |
| 3. Ультра-EWMA (λ=0.99) | |
| 4. Модель SABR для опционов | |
| 5. Machine Learning (LSTM на order book data) | |
| Опционные стратегии | 1. Подразумеваемая волатильность (IV) |
| 2. Локальная волатильность (Dupire) | |
| 3. Поверхность волатильности 3D | |
| 4. Модель Heston-Nandi | |
| 5. Анализ «улыбки волатильности» | |
| Портфельный менеджмент | 1. Полная ковариационная матрица |
| 2. PCA-анализ главных компонент | |
| 3. Иерархические модели (HDDC) | |
| 4. Реализованная корреляция | |
| 5. Stress-testing на исторических кризисах |
Общие рекомендации:
- Для акций используйте комбинацию исторической и подразумеваемой волатильности. Для портфельного анализа учитывайте корреляции между активами.
- Если вы инвестируете в облигации, то фокусируйтесь на волатильности доходностей, а не цен. Используйте модифицированную дюрацию и выпуклость для оценки риска.
- Если вы инвестируете в деривативы, стройте поверхность волатильности для опционов, мониторьте изменения веги для опционных позиций, учитывайте динамику соотношения HV и IV.
- Всегда тестируйте несколько методов расчета, визуализируйте динамику волатильности на графиках и учитывайте транзакционные издержки и ликвидность при торговле на основе волатильности.
Типичные ошибки при расчете волатильности
Игнорирование кластеризации волатильности
Финансовые временные ряды имеют свойство образовывать кластеры высокой и низкой волатильности. Использование простого стандартного отклонения без учета этого фактора может привести к недооценке рисков.
Некорректная аннуализация
При переводе дневной, недельной или внутридневной волатильности в годовую часто допускаются ошибки в выборе множителя (√252 для дневных данных, √52 для недельных и т.д.).
Игнорирование асимметрии распределения
Многие модели предполагают нормальное распределение доходностей, тогда как реальные распределения часто имеют «тяжелые хвосты».
Использование слишком коротких временных окон
Это приводит к «шумным» оценкам волатильности, особенно для активов с низкой ликвидностью.
Пренебрежение структурными изменениями
Не относитесь к построенной модели расчета волатильности, как к чему-то фундаментальному. Рынки имеют природу меняться со временем, и модели, обученные на исторических данных, не всегда адекватно отражают текущую ситуацию. Поэтому рекомендуется обновлять параметры моделей по мере поступления новых данных.
Выводы
Волатильность остается одним из наиболее важных и многогранных понятий в современной финансовой аналитике. Как мы убедились, существует множество методов ее расчета — от простых исторических оценок до сложных стохастических моделей. Однако не существует «идеального» метода, подходящего для всех ситуаций.
Ключ к успешному использованию волатильности в инвестировании и трейдинге лежит в понимании природы каждого метода, его ограничений и области применения. Комбинация разных подходов, постоянное тестирование и адаптация к меняющимся рыночным условиям — вот что отличает профессиональных участников рынка.