Опционные стратегии всегда связаны с множеством рисков и переменных, и именно поэтому трейдеры используют так называемые греки — показатели чувствительности опционной позиции к изменениям рынка и времени. Эти метрики позволяют глубже понять поведение опциона при колебаниях цены базового актива, изменении волатильности или приближении даты экспирации. Без понимания греков невозможно выстроить эффективное управление рисками и корректно оценить потенциальную прибыль или убытки.1
В этой статье я поделюсь практическим опытом работы с греками опционов, покажу, как их рассчитывать программно, и расскажу о нюансах, которые редко освещаются в стандартной литературе. Мы рассмотрим не только математические основы, но и реальные торговые применения этих метрик.
Математические основы ценообразования опционов
Прежде чем погружаться в детали греков, важно понимать фундаментальную модель, на которой они основаны. Формула Блэка-Шоулса остается краеугольным камнем опционного ценообразования, несмотря на свои ограничения.
Модель предполагает постоянную волатильность, нормальное распределение доходностей и отсутствие дивидендов. В реальности ни одно из этих предположений не выполняется идеально, но модель дает нам рабочий фреймворк для анализа. Особенно важно понимать, что волатильность в формуле — это не историческая волатильность актива, а подразумеваемая волатильность, заложенная в цену опциона рынком.
Для работы с опционами я использую собственную реализацию модели Блэка-Шоулса, которая позволяет легко рассчитывать все греки одновременно:
import numpy as np
import pandas as pd
from scipy.stats import norm
import yfinance as yf
import matplotlib.pyplot as plt
from datetime import datetime, timedelta
class OptionsGreeks:
def __init__(self, S, K, T, r, sigma, option_type='call'):
self.S = S # Цена базового актива
self.K = K # Цена исполнения
self.T = T # Время до экспирации в годах
self.r = r # Безрисковая ставка
self.sigma = sigma # Волатильность
self.option_type = option_type
# Предварительные расчеты
self.d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
self.d2 = self.d1 - sigma*np.sqrt(T)
def price(self):
"""Расчет справедливой цены опциона"""
if self.option_type == 'call':
return self.S * norm.cdf(self.d1) - self.K * np.exp(-self.r*self.T) * norm.cdf(self.d2)
else:
return self.K * np.exp(-self.r*self.T) * norm.cdf(-self.d2) - self.S * norm.cdf(-self.d1)
def delta(self):
"""Чувствительность к изменению цены базового актива"""
if self.option_type == 'call':
return norm.cdf(self.d1)
else:
return norm.cdf(self.d1) - 1
def gamma(self):
"""Вторая производная цены по цене базового актива"""
return norm.pdf(self.d1) / (self.S * self.sigma * np.sqrt(self.T))
def theta(self):
"""Чувствительность к изменению времени"""
theta_common = -(self.S * norm.pdf(self.d1) * self.sigma) / (2 * np.sqrt(self.T))
if self.option_type == 'call':
return (theta_common - self.r * self.K * np.exp(-self.r*self.T) * norm.cdf(self.d2)) / 365
else:
return (theta_common + self.r * self.K * np.exp(-self.r*self.T) * norm.cdf(-self.d2)) / 365
def vega(self):
"""Чувствительность к изменению волатильности"""
return self.S * norm.pdf(self.d1) * np.sqrt(self.T) / 100
def rho(self):
"""Чувствительность к изменению процентной ставки"""
if self.option_type == 'call':
return self.K * self.T * np.exp(-self.r*self.T) * norm.cdf(self.d2) / 100
else:
return -self.K * self.T * np.exp(-self.r*self.T) * norm.cdf(-self.d2) / 100
# Пример использования с реальными данными
ticker = "BABA" # Alibaba как пример волатильного актива
stock_data = yf.download(ticker, period="1y")
if isinstance(stock_data.columns, pd.MultiIndex):
current_price = stock_data['Close'][ticker].iloc[-1]
else:
current_price = stock_data['Close'].iloc[-1]
# Создаем опцион at-the-money с экспирацией через 30 дней
option = OptionsGreeks(
S=current_price,
K=current_price, # At-the-money
T=30/365, # 30 дней до экспирации
r=0.05, # 5% безрисковая ставка
sigma=0.3, # 30% волатильность
option_type='call'
)
print(f"Анализ опциона {ticker}:")
print(f"Цена опциона: ${option.price():.2f}")
print(f"Delta: {option.delta():.4f}")
print(f"Gamma: {option.gamma():.6f}")
print(f"Theta: ${option.theta():.4f}")
print(f"Vega: ${option.vega():.4f}")
print(f"Rho: ${option.rho():.4f}")
Анализ опциона BABA:
Цена опциона: $4.34
Delta: 0.5362
Gamma: 0.038633
Theta: $-0.0763
Vega: $0.1362
Rho: $0.0491
Этот код демонстрирует практический подход к расчету греков, который я использую в своих торговых системах. Обратите внимание на несколько важных деталей: я нормализую theta к дневным изменениям, делю vega и rho на 100 для получения более интуитивных значений, и использую реальные рыночные данные вместо абстрактных примеров.
Особенность данной реализации в том, что она позволяет легко адаптировать параметры под конкретные рыночные условия. В отличие от стандартных учебных примеров, здесь учитывается практическая необходимость работы с реальными данными, включая проверку на MultiIndex в pandas DataFrame, что часто возникает при загрузке данных через yfinance.
Delta: первая линия защиты портфеля
Delta — это, пожалуй, самый интуитивно понятный из греков, но его глубокое понимание требует выхода за рамки простого определения «чувствительность к изменению цены базового актива». В моей практике delta служит основой для хеджирования и построения market-neutral стратегий.
Значение delta для колл-опционов варьируется от 0 до 1, для пут-опционов — от -1 до 0. Опционы глубоко в деньгах имеют delta близкую к ±1, что означает движение цены опциона практически один к одному с базовым активом. Опционы глубоко вне денег имеют delta близкую к 0, реагируя слабо на небольшие изменения цены актива.
Однако настоящая сила delta раскрывается в контексте портфельного управления. Суммарная delta позиции показывает ее направленную экспозицию. Портфель с нулевой delta теоретически нечувствителен к небольшим движениям рынка, что делает возможным получение прибыли от других факторов — времени, волатильности или более сложных рыночных неэффективностей.
def delta_hedging_simulation(initial_price, volatility, days=30, rebalance_freq=1):
"""
Симуляция динамического дельта-хеджирования
"""
dt = 1/365 # Дневной шаг
times = np.arange(0, days*dt, dt)
# Генерируем путь цены актива
np.random.seed(42)
price_path = [initial_price]
for _ in range(len(times)-1):
price_change = price_path[-1] * (0.05*dt + volatility*np.sqrt(dt)*np.random.normal())
price_path.append(price_path[-1] + price_change)
price_path = np.array(price_path)
# Параметры опциона
strike = initial_price
r = 0.05
# Массивы для хранения результатов
option_prices = []
deltas = []
hedge_ratios = []
pnl_unhedged = []
pnl_hedged = []
initial_option_price = 0
initial_hedge_position = 0
for i, (t, S) in enumerate(zip(times, price_path)):
time_to_exp = (days*dt - t)
if time_to_exp > 0:
option = OptionsGreeks(S, strike, time_to_exp, r, volatility)
opt_price = option.price()
delta = option.delta()
if i == 0:
initial_option_price = opt_price
initial_hedge_position = -delta
option_prices.append(opt_price)
deltas.append(delta)
# Расчет P&L
option_pnl = opt_price - initial_option_price
stock_pnl = (S - initial_price) * initial_hedge_position
pnl_unhedged.append(option_pnl)
pnl_hedged.append(option_pnl + stock_pnl)
# Динамическое хеджирование каждый день
if i % rebalance_freq == 0:
hedge_ratios.append(-delta)
else:
hedge_ratios.append(hedge_ratios[-1] if hedge_ratios else initial_hedge_position)
return {
'times': times,
'prices': price_path,
'option_prices': option_prices,
'deltas': deltas,
'pnl_unhedged': pnl_unhedged,
'pnl_hedged': pnl_hedged
}
# Запуск симуляции
sim_results = delta_hedging_simulation(100, 0.25, days=30)
# Визуализация результатов
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 10))
fig.suptitle('Анализ дельта-хеджирования', fontsize=16, fontweight='bold')
# График цены актива
ax1.plot(sim_results['times'], sim_results['prices'], 'k-', linewidth=2)
ax1.set_title('Цена базового актива')
ax1.set_xlabel('Время (годы)')
ax1.set_ylabel('Цена')
ax1.grid(True, alpha=0.3)
# График цены опциона
ax2.plot(sim_results['times'], sim_results['option_prices'], 'darkgray', linewidth=2)
ax2.set_title('Цена опциона')
ax2.set_xlabel('Время (годы)')
ax2.set_ylabel('Цена опциона')
ax2.grid(True, alpha=0.3)
# График delta
ax3.plot(sim_results['times'], sim_results['deltas'], 'k-', linewidth=2)
ax3.set_title('Delta опциона')
ax3.set_xlabel('Время (годы)')
ax3.set_ylabel('Delta')
ax3.grid(True, alpha=0.3)
ax3.axhline(y=0.5, color='gray', linestyle='--', alpha=0.5)
# Сравнение P&L
ax4.plot(sim_results['times'], sim_results['pnl_unhedged'], 'r-',
label='Без хеджирования', linewidth=2)
ax4.plot(sim_results['times'], sim_results['pnl_hedged'], 'k-',
label='С дельта-хеджированием', linewidth=2)
ax4.set_title('P&L сравнение')
ax4.set_xlabel('Время (годы)')
ax4.set_ylabel('P&L')
ax4.legend()
ax4.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"Стандартное отклонение P&L без хеджирования: {np.std(sim_results['pnl_unhedged']):.3f}")
print(f"Стандартное отклонение P&L с хеджированием: {np.std(sim_results['pnl_hedged']):.3f}")
print(f"Снижение риска: {(1 - np.std(sim_results['pnl_hedged'])/np.std(sim_results['pnl_unhedged']))*100:.1f}%")

Рис. 1: Визуальный анализ Delta опциона и дельта-хеджирования
Стандартное отклонение P&L без хеджирования: 2.419
Стандартное отклонение P&L с хеджированием: 0.543
Снижение риска: 77.6%
Эта симуляция демонстрирует ключевую концепцию дельта-хеджирования в действии. Результаты показывают, что правильно хеджированная позиция существенно снижает волатильность P&L (profit & loss), хотя и не устраняет риск полностью. Остаточный риск связан с gamma (изменением самой delta) и другими греками высших порядков.
Важная деталь, которую я заметил на практике: эффективность дельта-хеджирования сильно зависит от частоты ребалансировки и транзакционных издержек. В реальной торговле ежедневная ребалансировка может оказаться слишком дорогой, особенно для портфелей с большим количеством позиций. Оптимальная частота — это компромисс между точностью хеджирования и издержками.
Gamma: эффект ускорения опционов
Gamma представляет скорость изменения delta и является мерой нелинейности опциона. Если delta показывает, как изменится цена опциона при движении базового актива на $1, то gamma показывает, как изменится сама delta при этом движении. Эта метрика особенно важна для понимания поведения опционов в условиях больших рыночных движений.
Максимальные значения gamma достигаются у опционов at-the-money с коротким временем до экспирации. Именно поэтому такие опционы демонстрируют наиболее драматичные изменения цены при движении базового актива. Положительная gamma (характерная для длинных позиций в опционах) означает, что опцион будет набирать ценность все быстрее при движении в благоприятном направлении.
В контексте управления рисками gamma создает как возможности, так и угрозы. Positive gamma exposure может принести существенную прибыль при больших движениях рынка, но требует тщательного мониторинга delta-нейтральных позиций. Negative gamma exposure (характерная для продавцов опционов) создает ускоряющиеся убытки при неблагоприятных движениях.
def gamma_analysis(spot_range, strike, time_to_exp, volatility, risk_free_rate=0.05):
"""
Детальный анализ gamma по диапазону цен базового актива
"""
spot_prices = np.linspace(spot_range[0], spot_range[1], 100)
call_prices = []
put_prices = []
call_deltas = []
put_deltas = []
gammas = []
for S in spot_prices:
call_option = OptionsGreeks(S, strike, time_to_exp, risk_free_rate, volatility, 'call')
put_option = OptionsGreeks(S, strike, time_to_exp, risk_free_rate, volatility, 'put')
call_prices.append(call_option.price())
put_prices.append(put_option.price())
call_deltas.append(call_option.delta())
put_deltas.append(put_option.delta())
gammas.append(call_option.gamma()) # Gamma одинакова для колл и пут
return {
'spot_prices': spot_prices,
'call_prices': call_prices,
'put_prices': put_prices,
'call_deltas': call_deltas,
'put_deltas': put_deltas,
'gammas': gammas,
'strike': strike
}
# Анализ для опционов с разным временем до экспирации
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('Анализ Gamma: влияние времени и цены', fontsize=16, fontweight='bold')
strike = 100
spot_range = [80, 120]
volatility = 0.25
# Анализ для разных временных периодов
time_periods = [7/365, 30/365, 90/365] # 1 неделя, 1 месяц, 3 месяца
colors = ['black', 'darkgray', 'lightgray']
labels = ['7 дней', '30 дней', '90 дней']
for i, (tte, color, label) in enumerate(zip(time_periods, colors, labels)):
analysis = gamma_analysis(spot_range, strike, tte, volatility)
# График gamma
ax1.plot(analysis['spot_prices'], analysis['gammas'],
color=color, linewidth=2, label=label)
# График delta для call опционов
ax2.plot(analysis['spot_prices'], analysis['call_deltas'],
color=color, linewidth=2, label=label)
ax1.set_title('Gamma по цене базового актива')
ax1.set_xlabel('Цена базового актива')
ax1.set_ylabel('Gamma')
ax1.axvline(x=strike, color='red', linestyle='--', alpha=0.5, label='Strike')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax2.set_title('Delta колл-опционов')
ax2.set_xlabel('Цена базового актива')
ax2.set_ylabel('Delta')
ax2.axvline(x=strike, color='red', linestyle='--', alpha=0.5, label='Strike')
ax2.axhline(y=0.5, color='gray', linestyle=':', alpha=0.5)
ax2.legend()
ax2.grid(True, alpha=0.3)
# Анализ gamma P&L профиля
base_analysis = gamma_analysis(spot_range, strike, 30/365, volatility)
spot_changes = np.array(base_analysis['spot_prices']) - strike
# Линейное приближение (только delta)
linear_pnl = np.array(base_analysis['call_deltas']) * spot_changes
# P&L с учетом gamma
actual_pnl = np.array(base_analysis['call_prices']) - base_analysis['call_prices'][50] # 50 - ATM точка
# Gamma contribution
gamma_contribution = 0.5 * np.array(base_analysis['gammas']) * spot_changes**2
ax3.plot(base_analysis['spot_prices'], actual_pnl, 'k-', linewidth=2, label='Фактический P&L')
ax3.plot(base_analysis['spot_prices'], linear_pnl, 'gray', linewidth=2,
linestyle='--', label='Линейное приближение (Delta)')
ax3.plot(base_analysis['spot_prices'], gamma_contribution, 'darkgray',
linewidth=2, linestyle=':', label='Вклад Gamma')
ax3.set_title('Декомпозиция P&L опциона')
ax3.set_xlabel('Цена базового актива')
ax3.set_ylabel('P&L')
ax3.axvline(x=strike, color='red', linestyle='--', alpha=0.5)
ax3.legend()
ax3.grid(True, alpha=0.3)
# Gamma vs Время до экспирации для ATM опциона
time_range = np.linspace(1/365, 120/365, 50) # От 1 дня до 4 месяцев
atm_gammas = []
for t in time_range:
option = OptionsGreeks(strike, strike, t, 0.05, volatility)
atm_gammas.append(option.gamma())
ax4.plot(time_range * 365, atm_gammas, 'k-', linewidth=2)
ax4.set_title('Gamma ATM опциона vs Время до экспирации')
ax4.set_xlabel('Дни до экспирации')
ax4.set_ylabel('Gamma')
ax4.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"Максимальная gamma: {max(base_analysis['gammas']):.6f}")
print(f"Gamma на ATM: {base_analysis['gammas'][50]:.6f}")
print(f"Соотношение max/ATM gamma: {max(base_analysis['gammas'])/base_analysis['gammas'][50]:.2f}")

Рис. 2: Визуальный анализ Gamma: влияние времени и цены опциона
Максимальная gamma: 0.056164
Gamma на ATM: 0.055142
Соотношение max/ATM gamma: 1.02
Результаты этого анализа демонстрируют несколько ключевых закономерностей, которые я регулярно использую в практической работе:
- Во-первых, gamma достигает максимума у ATM опционов и быстро убывает по мере удаления от страйка;
- Во-вторых, gamma обратно пропорциональна времени до экспирации — чем ближе экспирация, тем выше gamma у ATM опционов.
Особенно важно понимать нелинейную природу gamma P&L. При малых движениях базового актива вклад gamma минимален, но при больших движениях он становится доминирующим. Это объясняет, почему продавцы опционов испытывают ускоряющиеся убытки при больших рыночных движениях, а покупатели получают ускоряющуюся прибыль.
Theta: цена времени и стратегии временного распада
Theta представляет скорость временного распада стоимости опциона и является единственным греком, который работает в пользу продавца опционов. В отличие от других греков, theta предсказуема в своем направлении — она всегда отрицательна для длинных позиций в опционах (за исключением глубоко в деньгах пут-опционов при низких процентных ставках).
Динамика theta не линейна по времени. Наиболее интенсивный распад происходит в последние недели перед экспирацией, особенно для ATM опционов. Эта закономерность создает основу для многих профессиональных стратегий, направленных на получение прибыли от временного распада.
Важно понимать, что theta не работает в вакууме — ее влияние переплетается с изменениями волатильности и движениями базового актива. В практической торговле я часто наблюдаю ситуации, когда положительная theta позиции полностью нивелируется ростом подразумеваемой волатильности или неблагоприятными движениями цены.
def theta_decay_analysis(strike, initial_vol, days_to_analyze=60):
"""
Анализ временного распада для различных сценариев
"""
# Параметры базового сценария
spot = strike # ATM опцион
r = 0.05
# Временная сетка
days = np.arange(days_to_analyze, 0, -1) # Обратный отсчет до экспирации
time_to_exp = days / 365
# Сценарии волатильности
vol_scenarios = {
'Низкая волатильность': 0.15,
'Средняя волатильность': 0.25,
'Высокая волатильность': 0.35
}
# Сценарии движения цены
price_scenarios = {
'Цена без изменений': 1.0,
'Рост на 5%': 1.05,
'Падение на 5%': 0.95
}
results = {}
for vol_name, vol in vol_scenarios.items():
for price_name, price_mult in price_scenarios.items():
scenario_key = f"{vol_name} + {price_name}"
option_prices = []
theta_values = []
daily_theta_pnl = []
for i, t in enumerate(time_to_exp):
current_spot = spot * price_mult
if t > 0:
option = OptionsGreeks(current_spot, strike, t, r, vol)
price = option.price()
theta = option.theta()
option_prices.append(price)
theta_values.append(theta)
if i > 0:
# Расчет фактического изменения цены за день
actual_change = price - option_prices[i-1]
# Предсказанное theta изменение
predicted_theta_change = theta_values[i-1]
daily_theta_pnl.append({
'actual_change': actual_change,
'predicted_theta': predicted_theta_change,
'theta_accuracy': abs(actual_change - predicted_theta_change)
})
results[scenario_key] = {
'days': days[:-1] if len(option_prices) < len(days) else days, 'option_prices': option_prices, 'theta_values': theta_values, 'daily_pnl': daily_theta_pnl } return results # Визуализация theta анализа theta_results = theta_decay_analysis(100, 0.25, 60) fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12)) fig.suptitle('Анализ Theta: временной распад в различных сценариях', fontsize=16, fontweight='bold') # График цены опциона vs время colors = ['black', 'darkgray', 'lightgray'] scenarios_to_plot = [ 'Средняя волатильность + Цена без изменений', 'Низкая волатильность + Цена без изменений', 'Высокая волатильность + Цена без изменений' ] for i, scenario in enumerate(scenarios_to_plot): data = theta_results[scenario] ax1.plot(data['days'][:len(data['option_prices'])], data['option_prices'], color=colors[i], linewidth=2, label=scenario.split(' + ')[0]) ax1.set_title('Временной распад ATM колл-опциона') ax1.set_xlabel('Дни до экспирации') ax1.set_ylabel('Цена опциона') ax1.legend() ax1.grid(True, alpha=0.3) # График theta values for i, scenario in enumerate(scenarios_to_plot): data = theta_results[scenario] ax2.plot(data['days'][:len(data['theta_values'])], data['theta_values'], color=colors[i], linewidth=2, label=scenario.split(' + ')[0]) ax2.set_title('Theta по времени до экспирации') ax2.set_xlabel('Дни до экспирации') ax2.set_ylabel('Theta ($ за день)') ax2.legend() ax2.grid(True, alpha=0.3) # Анализ эффективности theta стратегий strategy_scenarios = [ 'Средняя волатильность + Цена без изменений', 'Средняя волатильность + Рост на 5%', 'Средняя волатильность + Падение на 5%' ] cumulative_pnl = {} for scenario in strategy_scenarios: data = theta_results[scenario] if len(data['option_prices']) > 1:
# Расчет кумулятивного P&L для продавца опциона
initial_price = data['option_prices'][0]
daily_pnl = [(initial_price - price) for price in data['option_prices']]
cumulative_pnl[scenario] = daily_pnl
for i, (scenario, pnl_data) in enumerate(cumulative_pnl.items()):
days_range = theta_results[scenario]['days'][:len(pnl_data)]
ax3.plot(days_range, pnl_data, color=colors[i], linewidth=2,
label=scenario.split(' + ')[1])
ax3.set_title('P&L продажи ATM колл-опциона')
ax3.set_xlabel('Дни до экспирации')
ax3.set_ylabel('Кумулятивный P&L')
ax3.legend()
ax3.grid(True, alpha=0.3)
ax3.axhline(y=0, color='red', linestyle='--', alpha=0.5)
# Анализ точности theta предсказаний
base_scenario = 'Средняя волатильность + Цена без изменений'
if theta_results[base_scenario]['daily_pnl']:
theta_accuracy = [day['theta_accuracy'] for day in theta_results[base_scenario]['daily_pnl']]
days_for_accuracy = theta_results[base_scenario]['days'][1:len(theta_accuracy)+1]
ax4.plot(days_for_accuracy, theta_accuracy, 'k-', linewidth=2)
ax4.set_title('Точность прогноза Theta')
ax4.set_xlabel('Дни до экспирации')
ax4.set_ylabel('Отклонение от прогноза ($)')
ax4.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Расчет статистики theta эффективности
base_data = theta_results['Средняя волатильность + Цена без изменений']
if base_data['daily_pnl']:
avg_theta = np.mean([abs(day['predicted_theta']) for day in base_data['daily_pnl']])
avg_accuracy = np.mean([day['theta_accuracy'] for day in base_data['daily_pnl']])
print(f"Средняя дневная theta: ${avg_theta:.4f}")
print(f"Средняя точность прогноза: ${avg_accuracy:.4f}")
print(f"Относительная точность: {(1 - avg_accuracy/avg_theta)*100:.1f}%")

Рис. 3: Визуальный анализа Theta: временной распад опциона в различных сценариях
Средняя дневная theta: $0.0647
Средняя точность прогноза: $0.0018
Относительная точность: 97.3%
Этот расширенный анализ theta демонстрирует несколько практически важных закономерностей:
- Во-первых, theta наиболее предсказуема в стабильных рыночных условиях без больших движений цены и волатильности;
- Во-вторых, эффективность theta-стратегий существенно зависит от выбора времени входа и управления позицией в последние недели до экспирации.
Обратите внимание на нелинейный характер ускорения временного распада. В последние 30 дней до экспирации theta может увеличиваться в несколько раз по сравнению с начальными значениями. Это создает как возможности для агрессивных стратегий сбора премии, так и повышенные риски для покупателей опционов.
Vega: волатильность как ключевой драйвер прибыли
Vega измеряет чувствительность цены опциона к изменениям подразумеваемой волатильности и является одним из наиболее сложных для понимания греков. В отличие от цены базового актива или времени, волатильность не наблюдается напрямую — она извлекается из рыночных цен опционов через обратную задачу к модели Блэка-Шоулса.
В моей практике vega оказывается часто более важным фактором P&L, чем движения самого базового актива. Рыночные кризисы сопровождаются резкими скачками подразумеваемой волатильности, которые могут увеличить стоимость опционов в разы, даже если базовый актив движется против позиции. Этот эффект особенно заметен в периоды резких падений и кризисов, когда корреляция между активами возрастает, а инвесторы готовы переплачивать за страхование.
Понимание природы и механики поверхности волатильности (volsurface — зависимости подразумеваемой волатильности от страйка и времени до экспирации) является важным для профессиональной работы с опционами. Показатели smile и skew в волатильности создают арбитражные возможности и помогают выявлять неэффективно оцененные опционы.
class VolatilitySurfaceAnalyzer:
"""Анализатор поверхности волатильности"""
def __init__(self, spot_price, risk_free_rate=0.05):
self.spot = spot_price
self.r = risk_free_rate
def calculate_implied_vol_surface(self, strikes, time_horizons, market_prices, option_types):
"""
Расчет поверхности подразумеваемой волатильности
В реальности здесь был бы численный solver для IV
Для демонстрации используем синтетические данные
"""
vol_surface = {}
for t in time_horizons:
vol_surface[t] = {}
for strike in strikes:
# Синтетическая волатильность с smile эффектом
moneyness = strike / self.spot
base_vol = 0.25
# Создаем волатильность smile
if moneyness < 0.95: # ITM puts, OTM calls vol_adjustment = 0.05 * (0.95 - moneyness) ** 0.5 elif moneyness > 1.05: # OTM puts, ITM calls
vol_adjustment = 0.03 * (moneyness - 1.05) ** 0.5
else:
vol_adjustment = 0
# Добавляем эффект времени
time_adjustment = 0.02 * np.exp(-t * 4) # Короткие опционы более волатильны
vol_surface[t][strike] = base_vol + vol_adjustment + time_adjustment
return vol_surface
def vega_exposure_analysis(self, portfolio_positions):
"""
Анализ vega exposure портфеля позиций
portfolio_positions: список словарей с параметрами позиций
"""
total_vega = 0
position_vegas = []
for pos in portfolio_positions:
option = OptionsGreeks(
S=pos['spot'], K=pos['strike'], T=pos['time_to_exp'],
r=self.r, sigma=pos['vol'], option_type=pos['type']
)
position_vega = option.vega() * pos['quantity']
total_vega += position_vega
position_vegas.append({
'strike': pos['strike'],
'type': pos['type'],
'quantity': pos['quantity'],
'individual_vega': option.vega(),
'position_vega': position_vega,
'time_to_exp': pos['time_to_exp']
})
return {
'total_portfolio_vega': total_vega,
'individual_positions': position_vegas
}
# Практический пример: построение vega-neutral spread
def create_vega_neutral_spread():
"""
Создание вега-нейтрального спреда
"""
spot = 100
analyzer = VolatilitySurfaceAnalyzer(spot)
# Определяем позицию: длинный ATM straddle
base_vol = 0.25
time_to_exp = 30/365
long_straddle = [
{'spot': spot, 'strike': 100, 'time_to_exp': time_to_exp,
'vol': base_vol, 'type': 'call', 'quantity': 1},
{'spot': spot, 'strike': 100, 'time_to_exp': time_to_exp,
'vol': base_vol, 'type': 'put', 'quantity': 1}
]
# Анализ vega exposure исходной позиции
straddle_analysis = analyzer.vega_exposure_analysis(long_straddle)
total_vega = straddle_analysis['total_portfolio_vega']
print(f"Vega длинного ATM стрэддла: {total_vega:.4f}")
# Добавляем короткие опционы для нейтрализации vega
# Используем более далекие страйки с тем же временем до экспирации
hedge_strikes = [90, 110] # OTM опционы
hedge_positions = []
for strike in hedge_strikes:
# Расчет количества для хеджа (упрощенная версия)
call_option = OptionsGreeks(spot, strike, time_to_exp, 0.05, base_vol, 'call')
put_option = OptionsGreeks(spot, strike, time_to_exp, 0.05, base_vol, 'put')
# Продаем OTM опционы для снижения vega
hedge_qty = -total_vega / (4 * call_option.vega()) # Примерное количество
hedge_positions.extend([
{'spot': spot, 'strike': strike, 'time_to_exp': time_to_exp,
'vol': base_vol, 'type': 'call', 'quantity': hedge_qty},
{'spot': spot, 'strike': strike, 'time_to_exp': time_to_exp,
'vol': base_vol, 'type': 'put', 'quantity': hedge_qty}
])
# Полная позиция
full_position = long_straddle + hedge_positions
final_analysis = analyzer.vega_exposure_analysis(full_position)
return {
'original_vega': total_vega,
'final_vega': final_analysis['total_portfolio_vega'],
'hedge_effectiveness': abs(final_analysis['total_portfolio_vega']) / abs(total_vega),
'position_details': final_analysis['individual_positions']
}
# Симуляция влияния изменения волатильности
def volatility_shock_simulation():
"""
Симуляция влияния шока волатильности на различные стратегии
"""
spot = 100
strike = 100
time_to_exp = 30/365
base_vol = 0.25
# Диапазон изменения волатильности
vol_range = np.linspace(0.15, 0.45, 31) # От 15% до 45%
strategies = {
'Длинный ATM Call': {'type': 'call', 'quantity': 1},
'Длинный ATM Straddle': [
{'type': 'call', 'quantity': 1},
{'type': 'put', 'quantity': 1}
],
'Iron Condor': [
{'strike': 95, 'type': 'put', 'quantity': 1}, # Long put
{'strike': 100, 'type': 'put', 'quantity': -1}, # Short put
{'strike': 100, 'type': 'call', 'quantity': -1}, # Short call
{'strike': 105, 'type': 'call', 'quantity': 1} # Long call
]
}
results = {}
for strategy_name, positions in strategies.items():
if not isinstance(positions, list):
positions = [positions]
strategy_pnl = []
strategy_vega = []
for vol in vol_range:
total_value = 0
total_vega = 0
for pos in positions:
pos_strike = pos.get('strike', strike)
option = OptionsGreeks(spot, pos_strike, time_to_exp, 0.05, vol, pos['type'])
position_value = option.price() * pos['quantity']
position_vega = option.vega() * pos['quantity']
total_value += position_value
total_vega += position_vega
strategy_pnl.append(total_value)
strategy_vega.append(total_vega)
# Нормализуем P&L относительно базовой волатильности
base_idx = 10 # Индекс для 25% волатильности
normalized_pnl = [pnl - strategy_pnl[base_idx] for pnl in strategy_pnl]
results[strategy_name] = {
'pnl': normalized_pnl,
'vega': strategy_vega,
'vol_range': vol_range
}
return results
# Визуализация результатов
vega_results = volatility_shock_simulation()
vega_neutral_example = create_vega_neutral_spread()
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('Анализ Vega: влияние волатильности на опционные стратегии', fontsize=16, fontweight='bold')
# График P&L vs волатильность
colors = ['black', 'darkgray', 'lightgray']
for i, (strategy, data) in enumerate(vega_results.items()):
ax1.plot(data['vol_range'] * 100, data['pnl'],
color=colors[i], linewidth=2, label=strategy)
ax1.set_title('P&L опционных стратегий vs волатильность')
ax1.set_xlabel('Подразумеваемая волатильность (%)')
ax1.set_ylabel('P&L изменение ($)')
ax1.axvline(x=25, color='red', linestyle='--', alpha=0.5, label='Базовая IV')
ax1.axhline(y=0, color='gray', linestyle=':', alpha=0.5)
ax1.legend()
ax1.grid(True, alpha=0.3)
# График Vega по стратегиям
for i, (strategy, data) in enumerate(vega_results.items()):
ax2.plot(data['vol_range'] * 100, data['vega'],
color=colors[i], linewidth=2, label=strategy)
ax2.set_title('Vega опционных стратегий')
ax2.set_xlabel('Подразумеваемая волатильность (%)')
ax2.set_ylabel('Vega')
ax2.axvline(x=25, color='red', linestyle='--', alpha=0.5)
ax2.axhline(y=0, color='gray', linestyle=':', alpha=0.5)
ax2.legend()
ax2.grid(True, alpha=0.3)
# Анализ эффективности vega хеджирования
hedge_data = vega_neutral_example
ax3.bar(['Исходная Vega', 'После хеджа'],
[hedge_data['original_vega'], hedge_data['final_vega']],
color=['darkgray', 'black'], alpha=0.7)
ax3.set_title('Эффективность Vega хеджирования')
ax3.set_ylabel('Portfolio Vega')
ax3.axhline(y=0, color='red', linestyle='--', alpha=0.5)
for i, v in enumerate([hedge_data['original_vega'], hedge_data['final_vega']]):
ax3.text(i, v + 0.001, f'{v:.4f}', ha='center', va='bottom', fontweight='bold')
# Временная структура волатильности (синтетическая)
time_horizons = np.array([7, 14, 30, 60, 90, 180]) / 365
vol_term_structure = [0.28, 0.26, 0.25, 0.24, 0.23, 0.22] # Обычная структура
ax4.plot(time_horizons * 365, vol_term_structure, 'ko-', linewidth=2, markersize=6)
ax4.set_title('Временная структура волатильности')
ax4.set_xlabel('Дни до экспирации')
ax4.set_ylabel('Подразумеваемая волатильность')
ax4.grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
print(f"\nРезультаты vega-нейтрального хеджирования:")
print(f"Исходная vega: {vega_neutral_example['original_vega']:.4f}")
print(f"Vega после хеджа: {vega_neutral_example['final_vega']:.4f}")
print(f"Эффективность хеджа: {(1-vega_neutral_example['hedge_effectiveness'])*100:.1f}%")

Рис. 4: Анализ Vega: влияние волатильности на опционные стратегии
Vega длинного ATM стрэддла: 0.2278
Результаты vega-нейтрального хеджирования:
Исходная vega: 0.2278
Vega после хеджа: 0.0000
Эффективность хеджа: 100.0%
Данный анализ показывает фундаментальную роль волатильности в опционном ценообразовании. Результаты демонстрируют, что стратегии с положительной vega (как длинный straddle) получают значительную прибыль при росте волатильности, в то время как стратегии с отрицательной vega (как iron condor) наоборот ведут себя хуже в таких сценариях.
Отмечу, что vega не является константой — она изменяется в зависимости от монейности опциона и времени до экспирации. ATM опционы имеют максимальную vega, которая снижается по мере приближения к экспирации или удаления от ATM уровня. Это создает дополнительные сложности в управлении vega-экспозиции портфеля.
Rho: влияние процентных ставок на опционное ценообразование
Rho измеряет чувствительность цены опциона к изменениям безрисковой процентной ставки и часто недооценивается трейдерами, особенно в периоды низких ставок. Однако в текущей макроэкономической среде с изменчивой монетарной политикой центральных банков понимание rho становится все более актуальным.
Механизм влияния процентных ставок на опционы работает через дисконтирование будущих денежных потоков. Рост ставок увеличивает стоимость колл-опционов и снижает стоимость пут-опционов, поскольку в модели Блэка-Шоулса страйк дисконтируется по безрисковой ставке. Этот эффект наиболее выражен у опционов с длительным временем до экспирации и страйками, далекими от текущей цены актива.
В практической торговле я наблюдал значительное влияние rho во время периодов неопределенности процентной политики ФРС. Длинные call опционы на индексы показывали дополнительную прибыльность не только от роста базового актива, но и от повышения ставок, что создавало дополнительный хвост распределений для позиций.
class InterestRateAnalyzer:
"""Анализатор влияния процентных ставок на опционы"""
def __init__(self):
self.base_scenarios = {
'Низкие ставки': 0.01,
'Умеренные ставки': 0.05,
'Высокие ставки': 0.08
}
def rho_sensitivity_analysis(self, spot, strike, time_to_exp, volatility):
"""
Анализ чувствительности к процентным ставкам
"""
rate_range = np.linspace(0.001, 0.12, 50) # От 0.1% до 12%
call_prices = []
put_prices = []
call_rhos = []
put_rhos = []
for rate in rate_range:
call_option = OptionsGreeks(spot, strike, time_to_exp, rate, volatility, 'call')
put_option = OptionsGreeks(spot, strike, time_to_exp, rate, volatility, 'put')
call_prices.append(call_option.price())
put_prices.append(put_option.price())
call_rhos.append(call_option.rho())
put_rhos.append(put_option.rho())
return {
'rates': rate_range,
'call_prices': call_prices,
'put_prices': put_prices,
'call_rhos': call_rhos,
'put_rhos': put_rhos
}
def macro_strategy_analysis(self, rate_expectations):
"""
Анализ макроэкономических стратегий с учетом rho
"""
spot = 4200 # SPY приблизительно
strikes = [4000, 4200, 4400] # ITM, ATM, OTM
time_horizons = [30/365, 90/365, 365/365] # 1 месяц, 3 месяца, 1 год
volatility = 0.20
strategies = {}
for horizon in time_horizons:
for strike in strikes:
strategy_key = f"Call_{strike}_{int(horizon*365)}d"
rho_impacts = {}
for scenario, rate in rate_expectations.items():
option = OptionsGreeks(spot, strike, horizon, rate, volatility, 'call')
rho_impacts[scenario] = {
'price': option.price(),
'rho': option.rho(),
'rate': rate
}
strategies[strategy_key] = rho_impacts
return strategies
# Симуляция макроэкономических сценариев
def fed_policy_simulation():
"""
Симуляция влияния политики ФРС на опционные позиции
"""
analyzer = InterestRateAnalyzer()
# Сценарии политики ФРС
fed_scenarios = {
'Голубиная политика': 0.02, # Низкие ставки
'Нейтральная политика': 0.05, # Текущий уровень
'Ястребиная политика': 0.08 # Агрессивное повышение
}
# Анализ влияния на различные стратегии
macro_analysis = analyzer.macro_strategy_analysis(fed_scenarios)
# Построение heat map влияния rho
strategy_names = []
scenario_names = list(fed_scenarios.keys())
price_impacts = []
for strategy, scenarios in macro_analysis.items():
strategy_names.append(strategy)
strategy_impacts = []
base_price = scenarios['Нейтральная политика']['price']
for scenario in scenario_names:
price_change = scenarios[scenario]['price'] - base_price
strategy_impacts.append(price_change)
price_impacts.append(strategy_impacts)
return {
'strategy_names': strategy_names,
'scenario_names': scenario_names,
'price_impacts': np.array(price_impacts),
'detailed_analysis': macro_analysis
}
# Анализ rho для различных типов позиций
def portfolio_rho_analysis():
"""
Анализ rho exposure сложного портфеля
"""
spot = 100
current_rate = 0.05
volatility = 0.25
# Сложный портфель с различными экспирациями
portfolio = [
# Короткие позиции (продажа премии)
{'strike': 95, 'time_to_exp': 30/365, 'type': 'put', 'quantity': -10},
{'strike': 105, 'time_to_exp': 30/365, 'type': 'call', 'quantity': -10},
# Длинные позиции для защиты
{'strike': 90, 'time_to_exp': 30/365, 'type': 'put', 'quantity': 10},
{'strike': 110, 'time_to_exp': 30/365, 'type': 'call', 'quantity': 10},
# Длинная LEAPS позиция
{'strike': 100, 'time_to_exp': 365/365, 'type': 'call', 'quantity': 5}
]
rate_scenarios = [0.01, 0.03, 0.05, 0.07, 0.09]
portfolio_analysis = []
for rate in rate_scenarios:
total_value = 0
total_rho = 0
position_details = []
for pos in portfolio:
option = OptionsGreeks(
spot, pos['strike'], pos['time_to_exp'],
rate, volatility, pos['type']
)
position_value = option.price() * pos['quantity']
position_rho = option.rho() * pos['quantity']
total_value += position_value
total_rho += position_rho
position_details.append({
'strike': pos['strike'],
'type': pos['type'],
'quantity': pos['quantity'],
'expiration_days': pos['time_to_exp'] * 365,
'individual_rho': option.rho(),
'position_rho': position_rho,
'position_value': position_value
})
portfolio_analysis.append({
'rate': rate,
'total_portfolio_value': total_value,
'total_portfolio_rho': total_rho,
'positions': position_details
})
return portfolio_analysis
# Визуализация результатов rho анализа
fed_simulation = fed_policy_simulation()
portfolio_rho_data = portfolio_rho_analysis()
# Создание базового анализа rho
spot = 100
rho_analyzer = InterestRateAnalyzer()
short_term_analysis = rho_analyzer.rho_sensitivity_analysis(spot, 100, 30/365, 0.25)
long_term_analysis = rho_analyzer.rho_sensitivity_analysis(spot, 100, 365/365, 0.25)
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
fig.suptitle('Анализ Rho: влияние процентных ставок', fontsize=16, fontweight='bold')
# График цен опционов vs процентные ставки
ax1.plot(short_term_analysis['rates'] * 100, short_term_analysis['call_prices'],
'k-', linewidth=2, label='Call 30d')
ax1.plot(short_term_analysis['rates'] * 100, short_term_analysis['put_prices'],
'k--', linewidth=2, label='Put 30d')
ax1.plot(long_term_analysis['rates'] * 100, long_term_analysis['call_prices'],
'darkgray', linewidth=2, label='Call 1y')
ax1.plot(long_term_analysis['rates'] * 100, long_term_analysis['put_prices'],
'darkgray', linestyle='--', linewidth=2, label='Put 1y')
ax1.set_title('Цены опционов vs процентные ставки')
ax1.set_xlabel('Процентная ставка (%)')
ax1.set_ylabel('Цена опциона ($)')
ax1.legend()
ax1.grid(True, alpha=0.3)
ax1.axvline(x=5, color='red', linestyle=':', alpha=0.5, label='Текущая ставка')
# График rho values
ax2.plot(short_term_analysis['rates'] * 100, short_term_analysis['call_rhos'],
'k-', linewidth=2, label='Call Rho 30d')
ax2.plot(short_term_analysis['rates'] * 100, short_term_analysis['put_rhos'],
'k--', linewidth=2, label='Put Rho 30d')
ax2.plot(long_term_analysis['rates'] * 100, long_term_analysis['call_rhos'],
'darkgray', linewidth=2, label='Call Rho 1y')
ax2.plot(long_term_analysis['rates'] * 100, long_term_analysis['put_rhos'],
'darkgray', linestyle='--', linewidth=2, label='Put Rho 1y')
ax2.set_title('Rho опционов по процентным ставкам')
ax2.set_xlabel('Процентная ставка (%)')
ax2.set_ylabel('Rho')
ax2.legend()
ax2.grid(True, alpha=0.3)
ax2.axvline(x=5, color='red', linestyle=':', alpha=0.5)
ax2.axhline(y=0, color='gray', linestyle=':', alpha=0.5)
# Heat map влияния ставок на стратегии
strategy_labels = [name.replace('Call_', '').replace('_', ' ') for name in fed_simulation['strategy_names']]
im = ax3.imshow(fed_simulation['price_impacts'], cmap='RdBu_r', aspect='auto')
ax3.set_xticks(range(len(fed_simulation['scenario_names'])))
ax3.set_xticklabels(fed_simulation['scenario_names'], rotation=45)
ax3.set_yticks(range(len(strategy_labels)))
ax3.set_yticklabels(strategy_labels, fontsize=8)
ax3.set_title('Влияние политики ФРС на опционы')
# Добавляем значения в ячейки
for i in range(len(strategy_labels)):
for j in range(len(fed_simulation['scenario_names'])):
text = ax3.text(j, i, f'{fed_simulation["price_impacts"][i, j]:.1f}',
ha="center", va="center", color="black", fontsize=8)
plt.colorbar(im, ax=ax3, label='Изменение цены ($)')
# График portfolio rho по ставкам
rates = [data['rate'] for data in portfolio_rho_data]
portfolio_values = [data['total_portfolio_value'] for data in portfolio_rho_data]
portfolio_rhos = [data['total_portfolio_rho'] for data in portfolio_rho_data]
ax4_twin = ax4.twinx()
line1 = ax4.plot(np.array(rates) * 100, portfolio_values, 'k-', linewidth=2, label='Стоимость портфеля')
line2 = ax4_twin.plot(np.array(rates) * 100, portfolio_rhos, 'darkgray', linewidth=2, label='Portfolio Rho')
ax4.set_title('Портфельный анализ Rho')
ax4.set_xlabel('Процентная ставка (%)')
ax4.set_ylabel('Стоимость портфеля ($)', color='black')
ax4_twin.set_ylabel('Portfolio Rho', color='darkgray')
ax4.grid(True, alpha=0.3)
# Объединяем легенды
lines1, labels1 = ax4.get_legend_handles_labels()
lines2, labels2 = ax4_twin.get_legend_handles_labels()
ax4.legend(lines1 + lines2, labels1 + labels2, loc='upper left')
plt.tight_layout()
plt.show()
# Анализ результатов
base_rate_idx = 2 # Индекс для 5% ставки
base_portfolio_value = portfolio_rho_data[base_rate_idx]['total_portfolio_value']
base_portfolio_rho = portfolio_rho_data[base_rate_idx]['total_portfolio_rho']
print(f"\nАнализ портфельного rho exposure:")
print(f"Базовая стоимость портфеля (5% ставка): ${base_portfolio_value:.2f}")
print(f"Portfolio Rho: {base_portfolio_rho:.2f}")
rate_shock_scenarios = [0.01, 0.09] # Шоки в -4% и +4%
for shock_rate in rate_shock_scenarios:
shock_idx = next(i for i, data in enumerate(portfolio_rho_data) if abs(data['rate'] - shock_rate) < 0.001)
shock_value = portfolio_rho_data[shock_idx]['total_portfolio_value']
actual_change = shock_value - base_portfolio_value
predicted_change = base_portfolio_rho * (shock_rate - 0.05) * 100
print(f"\nСценарий {shock_rate*100:.0f}% ставки:")
print(f" Фактическое изменение: ${actual_change:.2f}")
print(f" Прогноз по Rho: ${predicted_change:.2f}")
print(f" Точность прогноза: {(1 - abs(actual_change - predicted_change)/abs(actual_change))*100:.1f}%")

Рис. 5: Визуальный анализ Rho: влияние процентных ставок на опцион
Анализ портфельного rho exposure:
Базовая стоимость портфеля (5% ставка): $46.49
Portfolio Rho: 2.52
Сценарий 1% ставки:
Фактическое изменение: $-9.67
Прогноз по Rho: $-10.07
Точность прогноза: 95.9%
Сценарий 9% ставки:
Фактическое изменение: $10.43
Прогноз по Rho: $10.07
Точность прогноза: 96.5%
Результаты анализа rho демонстрируют несколько важных закономерностей, которые часто упускаются из виду:
- Во-первых, влияние процентных ставок наиболее выражено у длинных опционов и становится особенно значимым для LEAPS (Long-term Equity Anticipation Securities) с экспирацией более года;
- Во-вторых, портфели с комплексными опционными стратегиями могут иметь существенную экспозицию по rho, которая требует отдельного мониторинга.
Особенно интересно поведение rho в различных рыночных режимах. В периоды повышения ставок длинные call позиции получают дополнительную поддержку, в то время как put опционы теряют в стоимости даже при неизменной цене базового актива. Это создает дополнительные возможности для macro-directional стратегий, особенно в периоды четких трендов монетарной политики.
Корреляции и зависимости между греками: системный подход к управлению рисками
Понимание взаимодействий между греками — это то, что отличает профессиональных трейдеров от любителей. В реальной торговле греки не действуют изолированно, а создают сложную систему взаимозависимостей. Изменение одного фактора запускает каскад изменений в других метриках риска.
Наиболее очевидная связь существует между delta и gamma — gamma показывает, как быстро изменяется delta при движении базового актива. Но есть и более тонкие взаимодействия: vega и theta связаны через волатильность временного распада, а rho влияет на все остальные греки через дисконтирование в модели ценообразования.
В моей практике я наблюдал, как во время рыночных кризисов корреляции между греками усиливаются. Рост волатильности (положительный для vega) часто сопровождается ускорением временного распада (отрицательный theta эффект) и увеличением gamma (что усиливает delta hedging требования). Понимание этих взаимосвязей помогает строить более устойчивые торговые системы.
class IntegratedGreeksAnalyzer:
"""
Комплексный анализатор взаимодействий греков
"""
def __init__(self, portfolio_positions):
self.positions = portfolio_positions
self.correlation_matrix = None
def calculate_portfolio_greeks(self, market_params):
"""
Расчет всех греков для портфеля
"""
portfolio_metrics = {
'delta': 0, 'gamma': 0, 'theta': 0,
'vega': 0, 'rho': 0, 'total_value': 0
}
position_details = []
for pos in self.positions:
# Обновляем параметры из market_params
current_spot = market_params.get('spot', pos['spot'])
current_vol = market_params.get('volatility', pos['volatility'])
current_rate = market_params.get('rate', pos.get('rate', 0.05))
option = OptionsGreeks(
S=current_spot,
K=pos['strike'],
T=pos['time_to_exp'],
r=current_rate,
sigma=current_vol,
option_type=pos['type']
)
# Расчет метрик позиции
pos_metrics = {
'strike': pos['strike'],
'type': pos['type'],
'quantity': pos['quantity'],
'expiration': pos['time_to_exp'],
'price': option.price(),
'delta': option.delta() * pos['quantity'],
'gamma': option.gamma() * pos['quantity'],
'theta': option.theta() * pos['quantity'],
'vega': option.vega() * pos['quantity'],
'rho': option.rho() * pos['quantity'],
'position_value': option.price() * pos['quantity']
}
# Суммируем по портфелю
for greek in ['delta', 'gamma', 'theta', 'vega', 'rho']:
portfolio_metrics[greek] += pos_metrics[greek]
portfolio_metrics['total_value'] += pos_metrics['position_value']
position_details.append(pos_metrics)
return portfolio_metrics, position_details
def stress_test_scenario(self, base_params, stress_scenarios):
"""
Стресс-тестирование портфеля по различным сценариям
"""
results = {}
base_metrics, _ = self.calculate_portfolio_greeks(base_params)
for scenario_name, scenario_params in stress_scenarios.items():
# Комбинируем базовые параметры со сценарием
test_params = {**base_params, **scenario_params}
scenario_metrics, _ = self.calculate_portfolio_greeks(test_params)
# Расчет изменений
pnl_change = scenario_metrics['total_value'] - base_metrics['total_value']
# Декомпозиция P&L по грекам (приближенная)
spot_change = scenario_params.get('spot', base_params['spot']) - base_params['spot']
vol_change = scenario_params.get('volatility', base_params['volatility']) - base_params['volatility']
time_change = scenario_params.get('time_decay', 0) # В днях
rate_change = scenario_params.get('rate', base_params.get('rate', 0.05)) - base_params.get('rate', 0.05)
# Приближенный вклад каждого фактора
delta_pnl = base_metrics['delta'] * spot_change
gamma_pnl = 0.5 * base_metrics['gamma'] * (spot_change ** 2)
theta_pnl = base_metrics['theta'] * time_change
vega_pnl = base_metrics['vega'] * vol_change
rho_pnl = base_metrics['rho'] * rate_change
results[scenario_name] = {
'total_pnl': pnl_change,
'delta_contribution': delta_pnl,
'gamma_contribution': gamma_pnl,
'theta_contribution': theta_pnl,
'vega_contribution': vega_pnl,
'rho_contribution': rho_pnl,
'unexplained': pnl_change - (delta_pnl + gamma_pnl + theta_pnl + vega_pnl + rho_pnl),
'scenario_params': test_params,
'final_metrics': scenario_metrics
}
return results
def greeks_correlation_analysis(self, market_scenarios):
"""
Анализ корреляций между греками в различных рыночных условиях
"""
greeks_data = {greek: [] for greek in ['delta', 'gamma', 'theta', 'vega', 'rho']}
scenario_labels = []
for scenario_name, params in market_scenarios.items():
metrics, _ = self.calculate_portfolio_greeks(params)
for greek in greeks_data.keys():
greeks_data[greek].append(metrics[greek])
scenario_labels.append(scenario_name)
# Создаем DataFrame для корреляционного анализа
df = pd.DataFrame(greeks_data)
correlation_matrix = df.corr()
return correlation_matrix, df, scenario_labels
# Практический пример: сложный портфель для stress testing
def create_complex_portfolio():
"""
Создание сложного портфеля для демонстрации взаимодействий
"""
return [
# Iron Condor на индексе
{'strike': 4100, 'type': 'put', 'quantity': 5, 'time_to_exp': 45/365,
'spot': 4200, 'volatility': 0.18},
{'strike': 4150, 'type': 'put', 'quantity': -5, 'time_to_exp': 45/365,
'spot': 4200, 'volatility': 0.18},
{'strike': 4250, 'type': 'call', 'quantity': -5, 'time_to_exp': 45/365,
'spot': 4200, 'volatility': 0.18},
{'strike': 4300, 'type': 'call', 'quantity': 5, 'time_to_exp': 45/365,
'spot': 4200, 'volatility': 0.18},
# Long straddle на отдельных акциях
{'strike': 150, 'type': 'call', 'quantity': 10, 'time_to_exp': 30/365,
'spot': 150, 'volatility': 0.35},
{'strike': 150, 'type': 'put', 'quantity': 10, 'time_to_exp': 30/365,
'spot': 150, 'volatility': 0.35},
# LEAPS для длинного exposure
{'strike': 200, 'type': 'call', 'quantity': 3, 'time_to_exp': 365/365,
'spot': 200, 'volatility': 0.25}
]
# Определение стресс-сценариев
def define_stress_scenarios():
"""
Определение стресс-сценариев для тестирования
"""
return {
'Базовый сценарий': {
'spot': 4200, 'volatility': 0.20, 'rate': 0.05
},
'Рыночный краш': {
'spot': 3780, 'volatility': 0.45, 'rate': 0.03, 'time_decay': 7 # -10% + vol spike
},
'Рост + сжатие vol': {
'spot': 4620, 'volatility': 0.12, 'rate': 0.06, 'time_decay': 7 # +10% + vol crush
},
'Боковое движение': {
'spot': 4200, 'volatility': 0.15, 'rate': 0.05, 'time_decay': 14 # Время работает
},
'Повышение ставок': {
'spot': 4200, 'volatility': 0.20, 'rate': 0.08, 'time_decay': 0
},
'Экстремальная волатильность': {
'spot': 4200, 'volatility': 0.60, 'rate': 0.05, 'time_decay': 3
}
}
# Запуск комплексного анализа
portfolio = create_complex_portfolio()
stress_scenarios = define_stress_scenarios()
analyzer = IntegratedGreeksAnalyzer(portfolio)
# Базовые параметры рынка
base_market_params = {
'spot': 4200,
'volatility': 0.20,
'rate': 0.05
}
# Стресс-тестирование
stress_results = analyzer.stress_test_scenario(base_market_params, stress_scenarios)
# Анализ корреляций греков
correlation_results = analyzer.greeks_correlation_analysis(stress_scenarios)
# Визуализация результатов
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(16, 12))
fig.suptitle('Интегрированный анализ греков: взаимодействия и риски', fontsize=16, fontweight='bold')
# 1. P&L декомпозиция по сценариям
scenarios = list(stress_results.keys())
pnl_components = ['delta_contribution', 'gamma_contribution', 'theta_contribution',
'vega_contribution', 'rho_contribution']
component_labels = ['Delta', 'Gamma', 'Theta', 'Vega', 'Rho']
x = np.arange(len(scenarios))
width = 0.15
colors = ['black', 'darkgray', 'gray', 'lightgray', 'silver']
for i, (component, label, color) in enumerate(zip(pnl_components, component_labels, colors)):
values = [stress_results[scenario][component] for scenario in scenarios]
ax1.bar(x + i*width, values, width, label=label, color=color, alpha=0.8)
ax1.set_title('P&L декомпозиция по грекам')
ax1.set_xlabel('Стресс-сценарии')
ax1.set_ylabel('Вклад в P&L ($)')
ax1.set_xticks(x + width*2)
ax1.set_xticklabels([s.replace(' ', '\n') for s in scenarios], fontsize=9)
ax1.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
ax1.grid(True, alpha=0.3, axis='y')
ax1.axhline(y=0, color='red', linestyle='--', alpha=0.7)
# 2. Общий P&L по сценариям
total_pnl = [stress_results[scenario]['total_pnl'] for scenario in scenarios]
colors_pnl = ['green' if pnl > 0 else 'red' for pnl in total_pnl]
bars = ax2.bar(scenarios, total_pnl, color=colors_pnl, alpha=0.7)
ax2.set_title('Общий P&L по сценариям')
ax2.set_ylabel('P&L ($)')
ax2.set_xticklabels([s.replace(' ', '\n') for s in scenarios], fontsize=9)
ax2.grid(True, alpha=0.3, axis='y')
ax2.axhline(y=0, color='black', linestyle='-', alpha=0.3)
# Добавляем значения на столбцы
for bar, pnl in zip(bars, total_pnl):
height = bar.get_height()
ax2.text(bar.get_x() + bar.get_width()/2., height + (50 if height > 0 else -100),
f'${pnl:.0f}', ha='center', va='bottom' if height > 0 else 'top',
fontweight='bold', fontsize=10)
# 3. Корреляционная матрица греков
correlation_matrix = correlation_results[0]
im = ax3.imshow(correlation_matrix.values, cmap='RdBu_r', aspect='auto', vmin=-1, vmax=1)
ax3.set_xticks(range(len(correlation_matrix.columns)))
ax3.set_yticks(range(len(correlation_matrix.columns)))
ax3.set_xticklabels(correlation_matrix.columns)
ax3.set_yticklabels(correlation_matrix.columns)
ax3.set_title('Корреляционная матрица греков')
# Добавляем значения корреляций в ячейки
for i in range(len(correlation_matrix.columns)):
for j in range(len(correlation_matrix.columns)):
text = ax3.text(j, i, f'{correlation_matrix.iloc[i, j]:.2f}',
ha="center", va="center",
color="white" if abs(correlation_matrix.iloc[i, j]) > 0.5 else "black",
fontweight='bold')
plt.colorbar(im, ax=ax3, label='Корреляция')
# 4. Риск-профиль портфеля
base_metrics, _ = analyzer.calculate_portfolio_greeks(base_market_params)
greeks_values = [base_metrics[greek] for greek in ['delta', 'gamma', 'theta', 'vega', 'rho']]
greek_names = ['Delta', 'Gamma', 'Theta', 'Vega', 'Rho']
# Нормализуем значения для радарной диаграммы
max_abs_values = [max(abs(stress_results[scenario][f'{greek.lower()}_contribution'])
for scenario in scenarios) for greek in greek_names]
# Создаем радарную диаграмму
angles = np.linspace(0, 2*np.pi, len(greek_names), endpoint=False)
angles = np.concatenate((angles, [angles[0]])) # Замыкаем круг
current_exposure = []
max_risk = []
for i, greek in enumerate(['delta', 'gamma', 'theta', 'vega', 'rho']):
current_val = abs(base_metrics[greek])
max_val = max_abs_values[i] if max_abs_values[i] > 0 else 1
current_exposure.append(current_val / max_val)
max_risk.append(1.0)
current_exposure.append(current_exposure[0])
max_risk.append(max_risk[0])
ax4.plot(angles, max_risk, 'o-', linewidth=2, color='lightgray', alpha=0.5, label='Max Risk')
ax4.fill(angles, max_risk, alpha=0.1, color='lightgray')
ax4.plot(angles, current_exposure, 'o-', linewidth=2, color='black', label='Current Exposure')
ax4.fill(angles, current_exposure, alpha=0.3, color='black')
ax4.set_xticks(angles[:-1])
ax4.set_xticklabels(greek_names)
ax4.set_ylim(0, 1.2)
ax4.set_title('Риск-профиль портфеля')
ax4.legend(loc='upper right', bbox_to_anchor=(1.2, 1.0))
ax4.grid(True)
plt.tight_layout()
plt.show()
# Вывод ключевых метрик
print("\n=== АНАЛИЗ ПОРТФЕЛЯ ГРЕКОВ ===")
print(f"Базовые метрики портфеля:")
for greek, value in base_metrics.items():
if greek != 'total_value':
print(f" {greek.capitalize()}: {value:.2f}")
print(f" Общая стоимость: ${base_metrics['total_value']:.2f}")
print(f"\nНаиболее рискованные сценарии:")
sorted_scenarios = sorted(stress_results.items(), key=lambda x: abs(x[1]['total_pnl']), reverse=True)
for scenario, data in sorted_scenarios[:3]:
print(f" {scenario}: ${data['total_pnl']:.2f}")
main_contributor = max(pnl_components, key=lambda x: abs(data[x]))
print(f" Основной фактор: {main_contributor.replace('_contribution', '')} (${data[main_contributor]:.2f})")
print(f"\nКорреляции греков (|r| > 0.5):")
corr_matrix = correlation_results[0]
for i in range(len(corr_matrix.columns)):
for j in range(i+1, len(corr_matrix.columns)):
corr_value = corr_matrix.iloc[i, j]
if abs(corr_value) > 0.5:
print(f" {corr_matrix.columns[i]} - {corr_matrix.columns[j]}: {corr_value:.3f}")

Рис. 6: Стресс-тестирование опционного портфеля с декомпозицией P&L по грекам. Первый чарт показывает вклад каждого фактора риска (Delta, Gamma, Theta, Vega, Rho) в изменение стоимости портфеля при различных рыночных сценариях. Второй чарт демонстрирует общий P&L портфеля, где наибольшие убытки наблюдаются в сценарии рыночного краха с одновременным ростом волатильности. Третий чарт представляет корреляционную матрицу между греками, выявляя значимые взаимосвязи факторов риска. Последний чарт отображает текущий риск-профиль портфеля в виде радарной диаграммы с нормализацией относительно максимально возможного exposure
=== АНАЛИЗ ПОРТФЕЛЯ ГРЕКОВ ===
Базовые метрики портфеля:
Delta: 12.99
Gamma: -0.00
Theta: 0.23
Vega: -2.41
Rho: 7.13
Общая стоимость: $52336.70
Наиболее рискованные сценарии:
Рыночный краш: $-5507.05
Основной фактор: delta ($-5456.18)
Рост + сжатие vol: $5422.67
Основной фактор: delta ($5456.18)
Экстремальная волатильность: $-33.01
Основной фактор: vega ($-0.96)
Корреляции греков (|r| > 0.5):
delta - rho: 0.921
gamma - theta: -0.963
gamma - vega: 0.992
theta - vega: -0.970
Этот комплексный анализ демонстрирует реальную сложность управления опционными портфелями. Результаты показывают, что в экстремальных рыночных условиях простое суммирование эффектов отдельных греков дает неполную картину — взаимодействия между факторами могут как усиливать, так и компенсировать друг друга.
Особенно важно отметить роль gamma в ускорении убытков при больших движениях рынка. В сценарии рыночного краша gamma-эффект может многократно превышать линейные delta-потери, создавая нелинейные риски, которые сложно хеджировать традиционными методами. Это объясняет, почему профессиональные опционные маркет-мейкеры уделяют особое внимание gamma exposure management.
Заключение
Глубокое понимание греков опционов важно для всех, кто серьезно работает с деривативами. Поскольку каждая метрика несет важную информацию о поведении позиций в различных рыночных сценариях.
Современные рынки требуют системного подхода к риск-менеджменту, где традиционные греки дополняются продвинутыми метриками и анализом перекрестных эффектов. Современные технологии и библиотеки позволяют достаточно быстро реализовать эти концепции в автоматизированных торговых системах, однако успех в трейдинге по-прежнему сильно зависит от четкого понимания что вы делаете и почему, от математических основ и практических нюансов.
Инвестиции в изучение греков опционов окупаются через более точное управление рисками, лучшее понимание динамики рынков и способности строить более сложные и прибыльные торговые стратегии.