隐含波动率如何计算
隐含波动率没有闭式解。了解求解器如何从期权价格迭代反推出波动率,查看 Python 实现,并认识不同数据供应商给出不同数值的原因。
隐含波动率通过迭代计算,而不是直接套用公式。没有任何闭式表达式可以根据期权的市场价格反推出波动率。因此,求解器会先猜测一个波动率,用 Black-Scholes 模型为期权定价,再将模型价格与市场报价比较,并不断重复,直到两者精确到一美分以内。下面将完整介绍这一方法:定价函数、使用标准库 Python 编写的搜索循环,以及两家数据供应商为何会为同一份合约发布不同的数值。
为什么隐含波动率没有封闭式公式
Black-Scholes模型只能单向计算。输入现货价格、行权价、到期时间、利率和波动率,模型就会返回理论价格。这六个变量中,有五个可以观察到。波动率无法直接观察。它代表对股票从现在到到期日将波动多远的假设。
交易者会反向求解。市场价格已经显示在屏幕上,未知变量变成波动率。隐含波动率是使Black-Scholes价格等于期权市场价格的波动率输入值。波动率项sigma在正态分布函数中出现两次,分别位于d1和d2中,无法通过代数变形将其单独解出。代数推导到此为止,接下来需要数值搜索。隐含波动率衡量什么介绍其含义;本页介绍计算方法。
模型的两个特性让搜索过程较为简单。看涨期权的模型价格会随着波动率上升而上升,没有例外,而且变化平滑。对于一个只会单调上升的量,只需不断缩小上下界,就能锁定其对应值。
求解器按合约运行一次
输出结果是每个合约一个数值,而不是每只股票一个数值。同一股票、同一到期窗口内的合约,结果也可能彼此不同。以下是2026年6月30日成交的所有苹果公司(AAPL)期权,剩余期限为20至45天,并按行权价占股价的比例分组:
每个数字背后的完整 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的百分比:
每个数字背后的完整 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为平值Vega的33.1%;在1.10-1.20区域,则为31.6%。用这么小的数去除定价误差,会产生极大的步长,可能使猜测值跌破零,而模型在此时已无法提供有效结果。生产环境中的求解器通常将两种方法结合起来:先用区间夹逼,再用牛顿法精修。Vega介绍了这一Greek本身。
为何两个来源报告的隐含波动率不同
模型是公开的,计算方法也已确定,因此分歧来自输入数据。
- 中间价与最新成交价。 求解器需要一个价格。某合约的买价为 $2.00、卖价为 $2.20,其中间价为 $2.10;但最新成交价可能是九十分钟前的 $2.02。对这类合约而言,十美分的价格差异,其影响可能超过一个波动率点。
- 股息与持有成本。 上述函数计算的是一只不派息股票的欧式看涨期权价格。到期前派发股息会压低远期价格,而各交易台采用的调整方法和利率也可能不同。
- 美式期权的提前行权。 美国个股期权可以在任何交易日行权,这项权利具有价值,但欧式公式无法计入。忽略该权利的求解器会将差异反映到波动率中。二叉树模型则会直接为行权权利定价。
- 过时的报价。 某个行权价的期权最后一次成交可能在周二,但市场仍显示报价。求解器会将收到的报价视为真实数据。
收敛情况可以显示出后两个问题。下表选取八只家喻户晓的股票,统计截至 2026 年 6 月 30 日、剩余 20 至 45 天到期的成交合约,并同时报告平值波动率和求解收敛合约的占比:
每个数字背后的完整 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 DESCTSLA在47.7%的平值波动率在这8个名称中最高,而排名最后的SPY为14.4%。在TSLA期权链中,97.8%的成交合约得出了收敛结果。其余合约的行权价对应的价格超出了任何波动率都能重现的范围,可能是报价低于内在价值,也可能是买卖价倒挂。这样的比例是否算高,是另一个问题:30%的隐含波动率高吗对此进行了讨论,而预期波动幅度则将其换算成美元区间。
同一份合约,每个交易日重新求解
隐含波动率不会被存储。系统会根据屏幕上显示的价格重新计算。下面跟踪的是最活跃的 AAPL 合约。该合约将于 2026 年 7 月 17 日到期,跟踪区间从 6 月 1 日开始,直至到期。行权价和到期日都没有变化:
每个数字背后的完整 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 天。图线上每一个点,都是根据该交易日的报价运行上述循环后得到的一次结果。我们的股票代码页面发布相同的求解结果:AAPL 隐含波动率每天对整个期权链运行计算,因此读者可以使用本页面中的代码复现其中的任何数值。
隐含波动率计算常见问题
隐含波动率有公式吗?
没有。Black-Scholes模型将波动率映射为期权价格,但这种映射不存在初等反函数,因为sigma在正态分布函数中出现了两次。您在这里或其他地方看到的每一个隐含波动率数值,都是通过迭代求解器计算得出的。
计算需要迭代多少次?
每次二分迭代都会将区间缩小一半。因此,0.01至5.0的区间经过二十次迭代后可缩小到0.00001以下;若要将结果与报价匹配到最接近的美分,通常需要约十几次迭代。接近平值期权时,Newton-Raphson方法三至四次即可收敛,但当vega较小时,迭代步长可能表现不稳定。
为什么我的经纪商和数据供应商显示的隐含波动率不同?
因为它们向求解器输入了不同的数据。常见差异包括中间价与最新成交价的选择、利率和股息假设,以及模型是否处理美式期权的提前行权。在交易清淡的执行价上,仅报价滞后就可能使结果相差几个百分点。
相同行权价的看涨期权和看跌期权,隐含波动率相同吗?
理论上相同。看跌看涨平价将相同行权价和到期日的两者联系起来。实际中,两种期权的报价分别形成,因此求解出的数值会有小幅差异。这也是不同供应商的期权链平均值存在差异的另一个原因。
没有市场数据时,我可以计算隐含波动率吗?
可以,但只能针对假设报价。上面的代码需要手动输入价格、行权价、标的现价、到期时间和利率。要匹配数据供应商的实时数值则更难,因为您必须使用供应商采用的同一组输入。
这里的每个面板都保存了背后的SQL。打开其中一个面板,然后在Strasmore终端上对实时期权链执行相同的求解。