3. 隐含波动率与历史波动率:波动率套利的双轮驱动
波动率,说白了就是市场情绪的体温计。做高频交易这几年,我越来越觉得,搞懂波动率比预测涨跌方向要靠谱得多。方向可能猜错,但波动率的规律——嗯,它是有迹可循的。
这一章我们聚焦两个核心概念:历史波动率和隐含波动率。前者是过去的价格波动记录,后者是市场对未来波动的预期。两者之间的差值,就是我们套利的空间。
3.1 历史波动率计算:给市场拍一张X光片
历史波动率(HV)反映的是过去一段时间内资产价格的波动程度。我个人习惯用对数收益率来计算,因为它更符合金融数据的统计特性。
计算步骤其实就三步:
- 取对数收益率:\( r_t = \ln(P_t / P_{t-1}) \)
- 计算标准差:\( \sigma = \sqrt{\frac{1}{N-1} \sum_{t=1}^{N} (r_t - \bar{r})^2} \)
- 年化处理:\( \sigma_{annual} = \sigma \times \sqrt{T} \),T是每年的交易天数(通常取252)
我在项目中遇到过一个问题:用收盘价算出来的波动率,跟用日内高频数据算出来的,差距能有多大?答案是——非常大。收盘价只反映一天结束时的状态,而高频数据能捕捉到盘中的剧烈波动。做高频套利,我建议至少用5分钟或15分钟K线数据。
核心要点:历史波动率是「回顾性」指标,它告诉你市场过去有多颠簸,但不能保证未来也这样。
下面是一个简单的Python实现,计算过去20个交易日的滚动历史波动率:
import numpy as np
import pandas as pd
def calc_historical_volatility(price_series, window=20, trading_days=252):
"""
计算滚动历史波动率
:param price_series: 价格序列(pandas Series)
:param window: 滚动窗口大小
:param trading_days: 年化交易天数
:return: 年化波动率序列
"""
# 计算对数收益率
log_returns = np.log(price_series / price_series.shift(1))
# 滚动标准差
rolling_std = log_returns.rolling(window=window).std()
# 年化
hv = rolling_std * np.sqrt(trading_days)
return hv
# 示例用法
prices = pd.Series([100, 102, 101, 105, 107, 106, 108, 110])
hv = calc_historical_volatility(prices, window=5)
print(hv)
避坑指南:我曾经用30分钟K线算HV,结果发现跟5分钟K线算出来的差了0.3个波动率点。后来才意识到,采样频率越低,波动率越容易被低估。做高频策略,采样频率至少要比你的交易频率高一个数量级。
3.2 隐含波动率提取:从期权价格反推市场预期
隐含波动率(IV)是从期权市场价格反推出来的。你想想看,期权价格里包含了市场对未来波动的一致预期。我们只需要把其他参数(行权价、到期时间、无风险利率、标的价格)代入BSM模型,反解出波动率就行。
BSM模型公式长这样:
\( C = S_0 N(d_1) - K e^{-rT} N(d_2) \)
其中:
- \( d_1 = \frac{\ln(S_0/K) + (r + \sigma^2/2)T}{\sigma\sqrt{T}} \)
- \( d_2 = d_1 - \sigma\sqrt{T} \)
反解IV没有解析解,只能用数值方法。我习惯用牛顿-拉夫森法,收敛速度快,一般迭代5-10次就能达到精度要求。
from scipy.stats import norm
from scipy.optimize import brentq
def bsm_price(S, K, T, r, sigma, option_type='call'):
"""BSM模型定价"""
d1 = (np.log(S/K) + (r + 0.5*sigma**2)*T) / (sigma*np.sqrt(T))
d2 = d1 - sigma*np.sqrt(T)
if option_type == 'call':
price = S * norm.cdf(d1) - K * np.exp(-r*T) * norm.cdf(d2)
else:
price = K * np.exp(-r*T) * norm.cdf(-d2) - S * norm.cdf(-d1)
return price
def implied_volatility(market_price, S, K, T, r, option_type='call'):
"""使用二分法提取隐含波动率"""
def objective(sigma):
return bsm_price(S, K, T, r, sigma, option_type) - market_price
try:
iv = brentq(objective, 0.01, 5.0) # 波动率范围1%-500%
return iv
except ValueError:
return np.nan
# 示例:假设市场价5.2,标的价格100,行权价105,剩余30天,无风险利率3%
iv = implied_volatility(5.2, 100, 105, 30/365, 0.03)
print(f"隐含波动率: {iv:.4f}")
注意:当期权深度实值或深度虚值时,BSM模型反解IV可能会失败。我遇到过极端情况——深度虚值期权价格极低,导致二分法找不到解。这时候可以改用vega加权法,或者直接剔除这些异常数据。
3.3 波动率微笑与偏斜:市场在告诉你什么?
如果BSM模型是完美的,那么同一到期日、不同行权价的期权应该算出相同的IV。但现实是——IV会随着行权价变化而变化,画出来就像一张微笑的嘴,这就是「波动率微笑」。
为什么会这样?说白了,市场认为极端行情发生的概率比BSM模型假设的要高。尤其是股票市场,大跌的概率往往大于大涨,所以虚值看跌期权的IV通常高于虚值看涨期权——这叫「波动率偏斜」。
我整理了一张表,方便你理解不同市场的特征:
| 市场类型 | 微笑/偏斜特征 | 典型原因 |
|---|---|---|
| 股票/股指 | 左偏斜(看跌IV > 看涨IV) | 投资者恐惧下跌,对冲需求大 |
| 外汇 | 对称微笑(两端高、中间低) | 双边极端事件概率相近 |
| 商品 | 右偏斜或复杂形态 | 供需冲击、地缘政治影响 |
实战经验:我在做股指期权套利时,发现一个规律——当偏斜程度(虚值看跌IV - 平值IV)超过历史均值2个标准差时,往往预示着市场即将出现剧烈波动。这个信号比单纯看IV绝对值要灵敏得多。
3.4 波动率期限结构:短期恐慌还是长期担忧?
波动率期限结构,就是把同一行权价、不同到期日的IV连成一条曲线。正常情况下,远期IV会高于近期IV,因为时间越长不确定性越大。但市场恐慌时,短期IV会飙升,甚至超过远期IV——这叫「倒挂」。
我习惯用期限结构斜率来判断市场情绪:
- 正斜率(正常):远期IV > 近期IV,市场情绪平稳
- 平坦:近期IV ≈ 远期IV,市场预期未来波动不会变化
- 倒挂(负斜率):近期IV > 远期IV,短期恐慌情绪浓厚
下面这张SVG图展示了波动率套利的核心知识体系:
这张图把整个知识体系串起来了。左边是历史波动率的计算流程,右边是隐含波动率的提取方法,底部是两者的交汇点——波动率微笑和期限结构分析。做套利策略时,我通常先看期限结构判断大方向,再看微笑/偏斜找具体机会。
个人习惯:我每天开盘前会快速扫描三个指标:1)平值IV与20日HV的差值;2)偏斜程度(25D看跌IV - 25D看涨IV);3)近月与远月IV的期限结构斜率。这三个指标基本能告诉我今天该做多波动率还是做空波动率。
嗯,波动率的世界就是这么有意思。它不像价格那样随机游走,而是有规律可循的。掌握了HV和IV的计算与解读,你就拿到了波动率套利的钥匙。下一节我们会深入具体的套利策略——但那是后话了。