5D Library › 信贷周期、自组织临界性与市场相变的统计物理 · English
第五章 隐含波动率、偏度与恐慌指数
布莱克–斯科尔斯公式与隐含波动率
布莱克与斯科尔斯在 1973 年给出了欧式期权的定价公式 (Black and Scholes 1973)。它假设标的资产价格服从几何布朗运动,波动率 不变,可以无摩擦地连续交易。看涨期权的价格为
其中 是标的价格, 是行权价, 是到期时间, 是无风险利率, 是标准正态分布函数。公式中只有 不能直接观察。反过来,给定市场上的期权价格,求出使公式价格等于市场价格的 ,这个数就叫隐含波动率。它没有解析解,但可以用牛顿法迭代:每一步用期权价格对 的导数(称为 vega)修正一次。
如果布莱克–斯科尔斯的假设成立,同一到期日、不同行权价的期权应该给出同一个隐含波动率。实际上并非如此。
证据
1987 年之后的偏度。 鲁宾斯坦报告,标准普尔 500 指数期权的隐含波动率在 1987 年 10 月股灾之后呈现出明显而持久的形态:行权价越低(越是虚值的看跌期权),隐含波动率越高 (Rubinstein 1994)。这种形态通常称为波动率「偏度」或「微笑」。
跳跃怎样产生偏度
一种产生偏度的方式,是让价格过程除了连续的波动之外,还会偶尔发生向下的跳跃。默顿在 1976 年给出了这种「跳跃–扩散」过程下的期权价格:它等于一系列布莱克–斯科尔斯价格按跳跃次数的泊松概率加权求和 (Merton 1976)。
推导
标的价格 100,三个月到期,连续部分的波动率为 15%;此外平均每两年发生一次跳跃,跳跃的平均幅度约为 −10%,标准差 10%。先用一个已知的价格检验牛顿法,再对每个行权价用虚值期权(行权价低于 100 用看跌期权,高于 100 用看涨期权)的跳跃–扩散价格反推布莱克–斯科尔斯隐含波动率。
# Black-Scholes (1973) prices, implied volatility by Newton's method, and the skew that appears when
# the true process has downward jumps (Merton 1976 jump-diffusion, priced exactly as a Poisson-
# weighted sum of Black-Scholes prices). Parameters are illustrative, not fitted to any market.
import math
Ncdf = lambda x: 0.5 * math.erfc(-x / math.sqrt(2))
npdf = lambda x: math.exp(-x * x / 2) / math.sqrt(2 * math.pi)
def bs(S, K, T, r, s, call=True):
d1 = (math.log(S / K) + (r + s * s / 2) * T) / (s * math.sqrt(T)); d2 = d1 - s * math.sqrt(T)
if call: return S * Ncdf(d1) - K * math.exp(-r * T) * Ncdf(d2)
return K * math.exp(-r * T) * Ncdf(-d2) - S * Ncdf(-d1)
def vega(S, K, T, r, s):
d1 = (math.log(S / K) + (r + s * s / 2) * T) / (s * math.sqrt(T))
return S * npdf(d1) * math.sqrt(T)
def implied_vol(price, S, K, T, r, call=True, s=0.3):
for it in range(50):
diff = bs(S, K, T, r, s, call) - price
if abs(diff) < 1e-10: return s, it
s = max(1e-4, s - diff / vega(S, K, T, r, s)) # Newton step
raise ValueError("no convergence")
def merton(S, K, T, r, s, lam, muj, dj, call=True, nmax=40):
k = math.exp(muj + dj * dj / 2) - 1 # mean relative jump size
lp = lam * (1 + k)
tot = 0.0
for n in range(nmax):
sn = math.sqrt(s * s + n * dj * dj / T)
rn = r - lam * k + n * math.log(1 + k) / T
tot += math.exp(-lp * T) * (lp * T) ** n / math.factorial(n) * bs(S, K, T, rn, sn, call)
return tot
S, T, r = 100.0, 0.25, 0.03
p = bs(S, 95, T, r, 0.22, call=False)
iv, it = implied_vol(p, S, 95, T, r, call=False)
print(f"round trip: put K = 95 priced at sigma 22 % -> {p:.4f}; Newton recovers {iv:.6f} in {it} steps")
print("strike option jump-diffusion price Black-Scholes implied vol")
ivs = {}
for K in (80, 85, 90, 95, 100, 105, 110, 115, 120):
call = K >= S # out-of-the-money option at each strike
pr = merton(S, K, T, r, 0.15, lam=0.5, muj=-0.10, dj=0.10, call=call)
ivs[K], _ = implied_vol(pr, S, K, T, r, call)
print(f" {K:4d} {'call' if call else 'put ':4s} {pr:8.4f} {ivs[K]:6.2%}")
print(f"skew: implied vol at 90 minus at 110 = {ivs[90] - ivs[110]:+.2%}"
f" (diffusion sd alone is 15.00 %; with no jumps every strike would show 15.00 %)")
# round trip: put K = 95 priced at sigma 22 % -> 1.9997; Newton recovers 0.220000 in 3 steps
# strike option jump-diffusion price Black-Scholes implied vol
# 80 put 0.0958 23.41%
# 85 put 0.2347 21.41%
# 90 put 0.5592 19.46%
# 95 put 1.3503 18.00%
# 100 call 3.7864 17.11%
# 105 call 1.6830 16.61%
# 110 call 0.6137 16.33%
# 115 call 0.1856 16.19%
# 120 call 0.0483 16.19%
# skew: implied vol at 90 minus at 110 = +3.13% (diffusion sd alone is 15.00 %; with no jumps every strike would show 15.00 %)牛顿法三步就恢复了 22% 的隐含波动率。在跳跃–扩散模型中,行权价 80 的看跌期权隐含波动率约为 23.4%,行权价 110 以上的看涨期权约为 16.2–16.3%;90 与 110 之间相差约 3.1 个百分点。所有行权价的隐含波动率都高于 15%,因为跳跃本身增加了总波动;而左边高得多,因为跳跃主要是向下的,使深度虚值的看跌期权比在纯连续模型中更有价值。
注
偏度不是「崩盘的概率」。 期权价格是风险中性的价格:它们既反映市场参与者对下跌可能性的判断,也反映他们为避免下跌而愿意支付的保险费,也就是风险溢价。虚值看跌期权的隐含波动率高,可能是因为下跌的可能性被认为较大,也可能是因为对保护的需求大、而愿意卖出这种保险的人要求高额补偿,通常两者兼而有之。所以不能把偏度直接换算成「市场认为下个月崩盘的概率」。另外,产生偏度的机制也不只跳跃一种,随机波动率模型(波动率与价格负相关)也能产生类似的形态。
恐慌指数与均值回归
芝加哥期权交易所的 VIX 指数常被称为「恐慌指数」。惠利说明了它的来历:1993 年推出时用的是标准普尔 100 指数平值期权的隐含波动率;2003 年改为用标准普尔 500 指数一系列行权价的期权价格计算未来 30 天的预期波动率,不再依赖布莱克–斯科尔斯模型 (Whaley 2009)。它衡量的同样是风险中性的预期,并且在股市下跌时往往急升。
波动率指数的一个显著特征是均值回归:急升之后往往回落,长期低迷之后往往回升。一个常用的简化描述是让它的对数服从奥恩斯坦–乌伦贝克过程 (Uhlenbeck and Ornstein 1930):
越大,回归越快;偏离的「半衰期」为 。
推导
用一个半衰期为 30 个交易日、长期水平为 18 的模拟序列(不是真实的 VIX),先计算一次急升到 40 之后、没有新冲击时的回落路径(也是这一模型下的中位数路径);再看用 1 年和 10 年的日数据估计半衰期有多准;最后计算「隐含波动率排名」(IV Rank)与百分位这两个常用指标。
# Mean reversion of a volatility index, modelled as an Ornstein-Uhlenbeck process for its logarithm:
# x = ln(V), dx = theta (mu - x) dt + eta dW (daily steps of 1/252 year)
# mu = ln 18, half-life 30 trading days, eta = 1.1 per sqrt(year). A simulated series, not the VIX.
import math, random, statistics as st
MU, HALF, ETA, DT = math.log(18), 30, 1.1, 1 / 252
TH = math.log(2) / (HALF * DT)
def path(n, rnd, x0=MU):
phi, sd = math.exp(-TH * DT), ETA * math.sqrt((1 - math.exp(-2 * TH * DT)) / (2 * TH))
x, out = x0, []
for _ in range(n):
x = MU + phi * (x - MU) + sd * rnd.gauss(0, 1); out.append(x)
return out
def half_life(x):
"""Regress x[t+1] on x[t]; half-life = -ln 2 / ln(slope), in days."""
a, b = x[:-1], x[1:]
ma, mb = st.fmean(a), st.fmean(b)
s = sum((u - ma) * (v - mb) for u, v in zip(a, b)) / sum((u - ma) ** 2 for u in a)
return -math.log(2) / math.log(s) if 0 < s < 1 else float("inf")
rnd = random.Random(4)
print("level after a spike to 40 with no new shocks (the median path), days:", ", ".join(
f"{h}: {math.exp(MU + (math.log(40) - MU) * math.exp(-TH * DT * h)):.1f}" for h in (0, 10, 30, 60, 120)))
for years in (1, 10):
est = sorted(half_life(path(252 * years, rnd)) for _ in range(200))
print(f"half-life estimated from {years:2d} year(s) of daily data, 200 samples: median {est[100]:.0f} days,"
f" middle 80 % {est[20]:.0f}-{est[180]:.0f} (true {HALF})")
v = [math.exp(u) for u in path(252, rnd)]
cur, lo, hi = v[-1], min(v), max(v)
print(f"one simulated year: last {cur:.1f}, range {lo:.1f}-{hi:.1f};"
f" IV-rank-style position {(cur - lo) / (hi - lo):.0%}, percentile {sum(u < cur for u in v) / len(v):.0%}")
# level after a spike to 40 with no new shocks (the median path), days: 0: 40.0, 10: 33.9, 30: 26.8, 60: 22.0, 120: 18.9
# half-life estimated from 1 year(s) of daily data, 200 samples: median 18 days, middle 80 % 9-37 (true 30)
# half-life estimated from 10 year(s) of daily data, 200 samples: median 29 days, middle 80 % 23-36 (true 30)
# one simulated year: last 26.8, range 13.4-34.1; IV-rank-style position 65%, percentile 85%在这个模型中,急升到 40 之后,没有新冲击时 30 个交易日回到约 27,120 天回到约 19;这也是分布的中位数,由于对数正态分布右偏,平均值略高。但这条路径不是保证:均值回归描述的是平均的倾向,一次急升之后完全可能出现更高的急升。
估计结果更值得注意:用 1 年的数据估计,半衰期的中位数只有约 18 天(真值 30),而且散布在 9 到 37 天之间;用 10 年的数据才接近真值。短样本系统地高估了均值回归的速度,这是自回归系数在小样本中向下偏的结果。
最后一行显示两种常用指标可以相差很大:同一个读数,在过去一年的区间中位于 65% 的位置(IV Rank 的算法:减去最低值、除以区间),却高于过去一年 85% 的日子(百分位)。IV Rank 只由最高值和最低值两天决定,一次极端读数就会改变它;百分位用到了全部数据。
本章小结
隐含波动率是使布莱克–斯科尔斯公式与市场价格一致的波动率,可以用牛顿法反推。1987 年之后,指数期权出现了持久的偏度;含向下跳跃的价格过程可以产生这种形态。偏度是风险中性的价格,包含风险溢价,不能直接读作崩盘概率。波动率指数有均值回归的倾向,但短样本会高估回归速度,常用的 IV Rank 与百分位可以给出很不相同的读数。下一章讨论两种更有野心的方法:在危机来临之前识别它的信号。
参考文献
Fan, Y. (2026). Credit Cycles, Self-Organised Criticality and the Statistical Physics of Market Phase Transitions: From Minsky's Financial Instability Hypothesis and Sandpile Power-Law Avalanches to Implied-Volatility Skew, Read Beside Classical Chinese Thought on Rise and Decline [信贷周期、自组织临界性与市场相变的统计物理] (Version 1.0.0). 5D Library. https://doi.org/10.5281/zenodo.23754711 · Licensed under Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0).