隐含波动率如何计算和反推
了解隐含波动率为何没有闭式解,以及求解器如何通过迭代、Black-Scholes定价和Python搜索循环,从期权市场价格反推出波动率。
隐含波动率通过迭代计算,而不是直接套用公式。不存在能够将期权市场价格反推出波动率数值的闭式表达式。因此,求解器会先猜测一个波动率,再用 Black-Scholes 模型为期权定价,将模型价格与市场报价比较,并不断重复,直到两者精确到美分。下文将完整介绍这一方法:定价函数、使用标准库 Python 编写的搜索循环,以及不同数据供应商为何会为同一份合约发布不同数值。
为什么隐含波动率没有封闭形式公式
Black-Scholes模型只能单向计算。输入现货价格、行权价、到期时间、利率和波动率后,模型会返回理论价格。这六个量中有五个可以观察到。波动率无法直接观察。它表示从现在到到期日,股票价格可能变动多远。
交易者会反过来求解。市场价格已经显示在屏幕上,未知量变成了波动率。隐含波动率是使Black-Scholes价格等于期权市场价格的波动率输入值。波动率项Sigma在正态分布函数中出现两次,分别位于d1和d2中,无法通过代数变形将其单独移出。代数推导到此为止。接下来需要进行数值搜索。隐含波动率衡量什么介绍其含义;本页介绍具体计算机制。
模型的两个特性使搜索过程较为简单。看涨期权的模型价格会随着波动率上升而上升,没有例外,而且变化是平滑的。一个只会上升的量,可以通过不断收窄区间来锁定。
求解器按每份合约运行一次
输出结果是每份合约对应一个数值,而不是每只股票对应一个数值。同一股票、同一到期区间内的合约,求解结果也可能彼此不同。这些按合约得出的结果会以经纪商交易界面上的一列数据呈现,每个行权价对应一个数值:逐列读取期权链将介绍其相邻列显示的内容。2026年6月30日成交、剩余20至45天到期的所有苹果公司(AAPL)期权,按行权价占股价的比例分组:
| 行权价与现价 | 隐含波动率(%) | 合约数 |
|---|---|---|
| 0.80-0.90 | 35.5 | 24 |
| 0.90-0.95 | 29 | 10 |
| 0.95-1.00 | 28.1 | 18 |
| 1.00-1.05 | 27.4 | 18 |
| 1.05-1.10 | 27.4 | 16 |
| 1.10-1.20 | 27.3 | 20 |
每个数字背后的完整 SQL
WITH toFloat64(strike_price) / toFloat64(underlying_close) AS moneyness
SELECT multiIf(moneyness < 0.90, '0.80-0.90',
moneyness < 0.95, '0.90-0.95',
moneyness < 1.00, '0.95-1.00',
moneyness < 1.05, '1.00-1.05',
moneyness < 1.10, '1.05-1.10',
'1.10-1.20') AS strike_vs_spot,
round(100 * avg(toFloat64(implied_volatility)), 1) AS implied_vol_pct,
count() AS contract_count
FROM global_markets.options_greeks
WHERE underlying_symbol = 'AAPL'
AND date = toDate('2026-06-30')
AND days_to_expiry BETWEEN 20 AND 45
AND iv_converged = 1
AND volume > 0
AND moneyness BETWEEN 0.80 AND 1.20
GROUP BY strike_vs_spot
ORDER BY min(moneyness)沿着行权价阶梯向上看,0.80-0.90区域的求解结果为35.5%,0.95-1.00区域的求解结果为28.1%,1.10-1.20区域的求解结果为27.3%。同一家公司、同一交易日,得出6个答案。这条曲线的弯曲部分有一个名称,即波动率偏斜;对每只股票设定一个单一波动率,无法描绘出这条曲线。
Black-Scholes定价:求解器需要匹配的目标价格
S表示股价,K表示行权价,T表示距离到期的年数,r表示无风险利率,N()表示标准正态累积分布函数,即标准正态随机变量低于给定值的概率。Python通过math.erf提供这一功能,因此仅使用解释器即可完成计算。
apt-get update && apt-get install -y python3
如果当前机器上的用户不是root,请在两条命令前加上sudo。然后将以下内容保存为iv.py:
import math
def norm_cdf(x):
return 0.5 * (1.0 + math.erf(x / math.sqrt(2.0)))
def bs_call(S, K, T, r, sigma):
if T <= 0.0 or sigma <= 0.0:
return max(S - K, 0.0)
d1 = (math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
d2 = d1 - sigma * math.sqrt(T)
return S * norm_cdf(d1) - K * math.exp(-r * T) * norm_cdf(d2)
这就是完整的远期模型。输入波动率,即可得到价格。
隐含波动率的计算方法:分步说明
二分法是最适合先学习的方法。它不会发散,也不需要微积分。
- 将答案限定在0.01(年化1%)至5.0(年化500%)之间。所有有交易的期权都在这一范围内。
- 使用区间中点为期权定价。
- 如果模型价格高于市场报价,说明估计值过高:将区间上限下调至中点。如果模型价格低于市场报价,则将区间下限上调至中点。
- 当模型价格与市场报价的差距小于一美分时停止。
def implied_vol(price, S, K, T, r, lo=0.01, hi=5.0, tol=0.01):
for _ in range(100):
mid = 0.5 * (lo + hi)
diff = bs_call(S, K, T, r, mid) - price
if abs(diff) < tol:
return mid
if diff > 0.0:
hi = mid
else:
lo = mid
return 0.5 * (lo + hi)
# $100 stock, $100 strike, three months, 4% rates, $5.00 on the screen
print(round(implied_vol(5.00, 100.0, 100.0, 0.25, 0.04), 4))
运行python3 iv.py。结果约为0.226,意味着对于这一假设报价,年化隐含波动率接近22.6%。每次迭代都会将区间缩小一半。宽度为4.99的区间连续缩小二十次后,宽度将小于0.00001,因此100次迭代的上限不会成为限制。
为什么牛顿-拉夫逊法收敛更快,以及它在哪些情况下失效
二分法丢弃了模型已经掌握的信息。Vega表示波动率每变动一个单位,期权价格的变化幅度,Black-Scholes模型可以直接给出其封闭解。牛顿-拉夫逊法将其视为斜率:先衡量定价误差,再除以Vega,并据此调整估计值。
def bs_vega(S, K, T, r, sigma):
d1 = (math.log(S / K) + (r + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
return S * math.sqrt(T) * math.exp(-0.5 * d1 * d1) / math.sqrt(2.0 * math.pi)
def implied_vol_newton(price, S, K, T, r, sigma=0.5):
for _ in range(20):
v = bs_vega(S, K, T, r, sigma)
if v < 1e-8:
return None # no slope left, hand the job back to bisection
step = (bs_call(S, K, T, r, sigma) - price) / v
sigma -= step
if sigma <= 0.0:
return None # the step overshot into nonsense
if abs(step) < 1e-6:
return sigma
return None
对于平值附近的期权,牛顿-拉夫逊法通常三到四次迭代即可收敛,而二分法需要十几次。其失效点在于分母:当行权价偏离股价时,Vega会缩小。以下为同一组AAPL合约,各区域的平均Vega占平值期权Vega的比例:
| 行权价与现价 | Vega占平值(%) | 合约数 |
|---|---|---|
| 0.80-0.90 | 33.1 | 24 |
| 0.90-0.95 | 62.4 | 10 |
| 0.95-1.00 | 89.8 | 18 |
| 1.00-1.05 | 98.5 | 18 |
| 1.05-1.10 | 73.6 | 16 |
| 1.10-1.20 | 31.6 | 20 |
每个数字背后的完整 SQL
WITH toFloat64(strike_price) / toFloat64(underlying_close) AS moneyness,
(
SELECT avg(toFloat64(vega))
FROM global_markets.options_greeks
WHERE underlying_symbol = 'AAPL'
AND date = toDate('2026-06-30')
AND days_to_expiry BETWEEN 20 AND 45
AND iv_converged = 1
AND volume > 0
AND abs(toFloat64(strike_price) / toFloat64(underlying_close) - 1) < 0.025
) AS atm_vega
SELECT multiIf(moneyness < 0.90, '0.80-0.90',
moneyness < 0.95, '0.90-0.95',
moneyness < 1.00, '0.95-1.00',
moneyness < 1.05, '1.00-1.05',
moneyness < 1.10, '1.05-1.10',
'1.10-1.20') AS strike_vs_spot,
round(100 * avg(toFloat64(vega)) / atm_vega, 1) AS vega_pct_of_atm,
count() AS contract_count
FROM global_markets.options_greeks
WHERE underlying_symbol = 'AAPL'
AND date = toDate('2026-06-30')
AND days_to_expiry BETWEEN 20 AND 45
AND iv_converged = 1
AND volume > 0
AND moneyness BETWEEN 0.80 AND 1.20
GROUP BY strike_vs_spot
ORDER BY min(moneyness)两翼区域的敏感度只剩一小部分:在0.80-0.90区域为平值Vega的33.1%,在1.10-1.20区域为31.6%。用这么小的数去除定价误差,会产生极大的步长,导致估计值跌至零以下,而模型无法对该区域给出有效结果。生产环境中的求解器通常结合这两种方法:先用区间夹逼,再用牛顿法精调。Vega介绍了这一希腊字母指标本身。
为什么两个来源报告的隐含波动率不同
模型是公开的,计算方法也没有争议,因此差异来自输入数据。
- 中间价与最新成交价。 求解器需要一个价格。某合约的买价为 2.00 美元、卖价为 2.20 美元时,中间价为 2.10 美元;但最新成交价可能是九十分钟前的 2.02 美元。对于这类合约,十美分的价格差异带来的影响可能超过一个波动率点。
- 股息与持有成本。 上述函数计算的是不派息股票的欧式看涨期权价格。到期前派发股息会压低远期价格,而各交易台采用的调整方法和利率也可能不同。
- 美式提前行权。 美国个股期权可以在任何交易日行权,这项权利具有价值,但欧式公式无法纳入。忽略提前行权权利的求解器会将这部分差异计入波动率。二叉树模型则直接为行权权利定价。
- 过时报价。 某个行权价的期权即使上次成交是在周二,仍可能显示报价。求解器会将收到的报价视为真实数据。
收敛情况会让后两个问题更加明显。下表选取 2026 年 6 月 30 日的八只知名股票,统计剩余 20 至 45 天的已成交合约,并同时报告平值波动率以及求解收敛的合约占比:
| 代码 | 平值隐含波动率(%) | 已解算(%) | 合约数 |
|---|---|---|---|
| TSLA | 47.7 | 97.8 | 364 |
| MSFT | 39.6 | 95.5 | 221 |
| AMZN | 38.7 | 96.3 | 163 |
| NVDA | 38.2 | 93.8 | 209 |
| JNJ | 28.3 | 100 | 53 |
| AAPL | 27.8 | 89.6 | 154 |
| KO | 20.9 | 100 | 79 |
| SPY | 14.4 | 98.5 | 739 |
每个数字背后的完整 SQL
WITH abs(toFloat64(strike_price) / toFloat64(underlying_close) - 1) AS distance_from_spot
SELECT underlying_symbol AS symbol,
round(100 * avgIf(toFloat64(implied_volatility), iv_converged = 1 AND distance_from_spot < 0.05), 1) AS atm_iv_pct,
round(100 * countIf(iv_converged = 1) / count(), 1) AS solved_pct,
count() AS contract_count
FROM global_markets.options_greeks
WHERE date = toDate('2026-06-30')
AND underlying_symbol IN ('AAPL', 'MSFT', 'NVDA', 'AMZN', 'TSLA', 'SPY', 'KO', 'JNJ')
AND days_to_expiry BETWEEN 20 AND 45
AND volume > 0
GROUP BY symbol
HAVING countIf(iv_converged = 1 AND distance_from_spot < 0.05) > 0
ORDER BY atm_iv_pct DESC在 47.7% 的 8 个名称中,TSLA 的平值波动率最高,为 14.4%;排名最低的 SPY 为 TSLA。在 TSLA 的期权链中,97.8% 的已成交合约得出了收敛结果。其余合约的行权价对应的价格超出了任何波动率都能还原的范围,可能是低于内在价值的报价,也可能是交叉市场。这样的数值是否算高,是另一个问题:30%的隐含波动率算高吗对此进行了讨论,而预期波动幅度则将其换算成美元区间。
同一合约,每个交易日重新求解
隐含波动率不会被保存。系统会根据屏幕上显示的价格重新计算。下面列出的是到期日为2026年7月17日、交易最活跃的 AAPL 合约,并跟踪其从6月1日到到期日的变化。行权价和到期日均未改变:
| date | 隐含波动率(%) | 到期天数 |
|---|---|---|
| 2026-06-01 | 25.1 | 46 |
| 2026-06-02 | 24.9 | 45 |
| 2026-06-03 | 15.5 | 44 |
| 2026-06-04 | 26.1 | 43 |
| 2026-06-05 | 25.7 | 42 |
| 2026-06-08 | 25.6 | 39 |
| 2026-06-09 | 23.4 | 38 |
| 2026-06-10 | 25.9 | 37 |
| 2026-06-11 | 22.5 | 36 |
| 2026-06-12 | 21.5 | 35 |
| 2026-06-15 | 22.1 | 32 |
| 2026-06-16 | 21.9 | 31 |
| 2026-06-17 | 20.9 | 30 |
| 2026-06-18 | 23 | 29 |
| 2026-06-22 | 25.1 | 25 |
| 2026-06-23 | 22.8 | 24 |
| 2026-06-24 | 26.6 | 23 |
| 2026-06-25 | 26.3 | 22 |
| 2026-06-26 | 25.3 | 21 |
| 2026-06-29 | 25.8 | 18 |
每个数字背后的完整 SQL
WITH (
SELECT ticker
FROM global_markets.options_greeks
WHERE underlying_symbol = 'AAPL'
AND date = toDate('2026-06-30')
AND expiration_date = toDate('2026-07-17')
AND iv_converged = 1
AND volume > 0
ORDER BY volume DESC
LIMIT 1
) AS pinned_contract
SELECT date,
round(100 * avg(toFloat64(implied_volatility)), 1) AS implied_vol_pct,
round(avg(days_to_expiry)) AS days_to_expiry
FROM global_markets.options_greeks
WHERE ticker = pinned_contract
AND date BETWEEN toDate('2026-06-01') AND toDate('2026-07-17')
AND iv_converged = 1
AND volume > 0
GROUP BY date
ORDER BY date在 29 个交易日中,求解结果开盘为 25.1%,当时距离到期还有 46 天;收盘为 26.6%,剩余 4 天。该曲线上的每一个点,都是根据当日行情运行上述循环后得到的一次结果。我们在每个 ticker 的页面上发布相同的计算结果:AAPL隐含波动率会对整个期权链每日运行该计算,因此读者可以使用本页代码复现其中的任何数值。
隐含波动率计算常见问题
隐含波动率有计算公式吗?
没有。Black-Scholes模型可以根据波动率计算期权价格,但这个映射没有初等反函数,因为sigma在正态分布函数中出现了两次。您看到的每一个隐含波动率数值,无论来自本文还是其他来源,都是通过迭代求解器计算得出的。
计算需要进行多少次迭代?
每次二分迭代都会将区间缩小一半。因此,0.01至5.0的区间经过二十次迭代后,会缩小到0.00001以下。要将结果匹配到报价的最接近分位,通常需要约十二次迭代。牛顿-拉夫森法在平值附近通常三至四次即可收敛,但当vega较小时,迭代步长可能失控。
为什么我的经纪商和数据供应商显示的隐含波动率不同?
因为它们向求解器输入了不同的数据。常见差异包括:中间价还是最近成交价、利率和股息假设,以及模型是否处理美式期权的提前行权。在成交清淡的执行价上,仅一个过时的报价就可能使结果相差几个百分点。
同一执行价的看涨期权和看跌期权会得出相同的隐含波动率吗?
理论上会。看跌-看涨平价关系将同一执行价和到期日的两类期权联系起来。实际中,两者的报价分别形成,因此计算出的数值会略有不同。这也是不同供应商计算出的整条期权链平均值存在差异的原因之一。
没有市场数据时,我可以计算隐含波动率吗?
可以,但只能针对假设报价。上面的代码允许您手动输入期权价格、执行价、现货价格、到期时间和利率。要匹配数据供应商的实时数值则更困难,因为必须使用供应商采用的同一组输入。
这里的每个面板都保存了对应的SQL语句。打开其中一个面板,然后在Strasmore终端上,对实时期权链运行相同的求解过程。