第 5 章 · 02 VIX Calculator:Riemann+Taylor 方差互换离散化 本节摘要:本节啃全仓库数学密度最高的一块骨头——VIX Calculator。VIX 是 S&P 500 的"恐慌 gauge(恐慌仪表)",但作者的目标更野心勃勃:把 CBOE 白皮书里那套数学严格实现成一个可对任意商品期权、任意预测时长的通用 VIX 计算器(本节用 Henry Hub 天然气期权演示 3 个月预期)。VIX 的数学骨架是把连续的"公平价格方差互换(variance swap)"离散化——核心工具就是黎曼和(Riemann sum)+ 泰勒展开(Taylor series)。
本节摘要:本节啃全仓库数学密度最高的一块骨头——VIX Calculator。VIX 是 S&P 500 的"恐慌 gauge(恐慌仪表)",但作者的目标更野心勃勃:把 CBOE 白皮书里那套数学严格实现成一个可对任意商品期权、任意预测时长的通用 VIX 计算器(本节用 Henry Hub 天然气期权演示 3 个月预期)。VIX 的数学骨架是把连续的"公平价格方差互换(variance swap)"离散化——核心工具就是黎曼和(Riemann sum)+ 泰勒展开(Taylor series)。我们重点讲清楚五个函数:
get_settlement_day跳过周末与联邦假日算到期时间、get_forward_strike用 call-put 价差最小处算 forward level F=K+e^{rT}*(C-P)、get_options_call/put_inclusion选 OTM 期权(更流动性)剔 priorSettle=0、compute_sigma用 ΣQ(K)*e^{rT}*ΔK/K² 再 *2/T-(F/K-1)²/T 算方差互换核心、compute_vix前月后月时间加权开方 ×100。严格遵循 CBOE 白皮书。
涉及源码:原项目
VIX Calculator.py(约 416 行),数据henry hub european options.csv+cme holidays.csv+treasury yield curve rates.csv。
⚠️ 注意:本节是全书数学最硬的一节。看代码前,强烈建议先扫一眼 CBOE 白皮书(vixwhite.pdf)对方差互换与 VIX 公式的定义,否则会把
compute_sigma里那一串 Σ、ΔK、e^{rT} 看成天书。本节的目标不是发明新公式,而是忠实复刻白皮书。
阅读完本节,你应当能够:
get_settlement_day 如何跳过周末与联邦假日、用分钟数算 time_to_expiration。F = K + e^{rT}*(C-P),并解释为何取 call-put 价差最小的行权价。compute_sigma 里 ΣQ(K)*e^{rT}*ΔK/K² 与 *2/T-(F/K-1)²/T 各项的含义。compute_vix 前月后月时间加权开方 ×100 的逻辑,以及为何前月后月分别代表 1 个月与 3 个月。VIX 是 CBOE 在 1993 年推出的"波动率指数",全称 Volatility Index,被业界称作 "fear gauge(恐慌仪表)"——它反映市场对 S&P 500 未来 30 天波动率的预期。VIX 高,说明市场恐慌、预期大波动;VIX 低,说明市场平静。
官方 VIX 的构造是:3 周到期的"近月(front month)"期权 + 1 个月到期的"远月(rear month)"期权,时间加权后代表"未来 30 天"的预期波动。但作者的目标不止于此——他把 VIX 公式做成一个通用计算器,可对任意商品期权、任意预测时长计算 VIX 风格的预期波动率。本节用 Henry Hub 天然气欧式期权演示,预测时长设为 3 个月。
VIX 的理论根基是方差互换(variance swap)——一种场外衍生品,其公平价格的方差 swap rate 等于标的资产未来方差期望。方差互换的公平价格有一个漂亮的对数展开式:
σ² = (2/T) * E[-ln(S_T/S_0)]
其中 T 是到期时间,S 是标的。用泰勒展开(Taylor series)把对数函数 ln 展开:
ln(S/K) ≈ (S-K)/K - (S-K)²/(2K²) + ...
取二阶项,再把连续的期望 E[...] 用期权价格的积分近似——这个积分就用到**黎曼和(Riemann sum)**离散化:把行权价 K 的连续区间切成离散的期权报价点,每个点贡献一个"面积"Q(K)*ΔK/K²。最终得到 VIX 公式:
σ² = (2/T) * Σ [Q(K) * e^{rT} * ΔK / K²] - (F/K₀ - 1)² / T
最后 VIX = sqrt(σ²) × 100。这就是 CBOE 白皮书的核心公式——Riemann 离散化连续积分 + Taylor 展开对数项,本节代码就是它的忠实实现。
L57-90 的 get_settlement_day 算结算日:从当前日加 time_horizon 个月,定位到月末,再往前倒数跳过周末(周六日)与联邦假日,直到遇到第 expiration_day 个交易日。expiration_hour 是到期小时(天然气期权是 16:00)。
get_time_to_expiration(L100-111)直接用 timedelta 算分钟数:
return (settlement_day - current_day).total_seconds() / 60 / 525600
把结算日与当前日的秒数差转成分钟,再除以"一年的分钟数"525600(365×24×60),得到年化的到期时间 T。注意:这里算到期时间不用跳假日(那是结算日的事),直接用自然时间差即可。
L118-136 算 forward level F 与 K₀。先 pivot 出每个行权价的 call/put 报价,找 call-put 价差绝对值最小的行权价——这就是 forward 所在:
min_diff_ind = (find_forward['call'] - find_forward['put']).apply(abs).idxmin() min_diff = float(decimal.Decimal(find_forward['call'][min_diff_ind].astype(str)) - decimal.Decimal(find_forward['put'][min_diff_ind].astype(str))) forward = min_diff_ind + np.e**(interest_rate*time_to_expiration) * (min_diff)
F = K + e^{rT}*(C - P) ——这是 call-put 平价(forward parity)的直接推论。选价差最小的 K 是为了让 e^{rT}*(C-P) 这一项的误差最小(在那个 K 上 call/put 流动性最好、报价最准)。
然后 K₀ 取不超过 F 的最大行权价:
strike = find_forward.index[find_forward.index <= forward][-1]
注意 decimal 模块:decimal.Decimal(...astype(str)) 把 float 转成字符串再转 Decimal,避开浮点误差——这是金融代码里算 forward 的标准手法,float64 在小数位上会抖动,Decimal 保证精确。
L144-175(call)与 L183-215(put)选 OTM(out-of-the-money)期权——OTM 期权流动性更好,报价更可信。call 选行权价 > K₀ 的(看涨 OTM),put 选行权价 < K₀ 的(看跌 OTM)。
关键细节是剔除 priorSettle=0 的期权:
options_call_otm_zeros = options_call_otm[options_call_otm['options-priorSettle']==0] if options_call_otm_zeros[options_call_otm_zeros['index'].diff()==1].empty: ind = len(options_call_otm) else: ind = options_call_otm_zeros['index'][options_call_otm_zeros['index'].diff()==1].iloc[0] options_call_inclusion = options_call_otm[options_call_otm['options-priorSettle']!=0][:ind]
逻辑:我们没有 bid/ask,用 priorSettle(前结算价)代替。一旦出现连续两个行权价的 priorSettle 都为 0,说明这一档已经没有交易,更远的 OTM 期权更不会有报价,全部排除。index.diff()==1 就是找"连续两个 0"的起点,从那里截断。
L222-245 是全节的数学高潮。对 call 与 put 两个 inclusion 集合,累加每个行权价的贡献:
contributions = 0.0 for i in [options_call_inclusion, options_put_inclusion]: for j in i.index: # ΔK:行权价间距 if j-1 < 0: delta = abs(i['options-strikePrice'][j] - i['options-strikePrice'][j+1]) elif j+1 == len(i): delta = abs(i['options-strikePrice'][j] - i['options-strikePrice'][j-1]) else: delta = abs(i['options-strikePrice'][j-1] - i['options-strikePrice'][j+1]) / 2 contributions += i['options-priorSettle'][j] * np.exp(interest_rate*time_to_expiration) * delta / (i['options-strikePrice'][j])**2 sigma = contributions * 2 / time_to_expiration - ((forward/strike - 1)**2) / time_to_expiration
逐项解读:
* 2 / T:乘 2 除 T——对应公式里的 2/T 因子。- (F/K₀ - 1)² / T:减去一个修正项,这是 Taylor 展开对数项的二阶修正——保证 σ² 是真正的方差而非近似偏差。合起来就是白皮书里的 σ² 公式。这一行 sigma = contributions*2/T - ((forward/strike-1)**2)/T 是全节最该背下来的一行。
L252-262 把前月(front)与后月(rear)的 σ² 时间加权,再开方 ×100:
sum1 = time_to_expiration_front * sigma_front * \ (time_to_expiration_rear*num_of_mins_year - num_of_mins_timeframe) / \ (time_to_expiration_rear*num_of_mins_year - time_to_expiration_front*num_of_mins_year) sum2 = time_to_expiration_rear * sigma_rear * \ (num_of_mins_timeframe - time_to_expiration_front*num_of_mins_year) / \ (time_to_expiration_rear*num_of_mins_year - time_to_expiration_front*num_of_mins_year) vix = ((sum1 + sum2) * num_of_mins_year / num_of_mins_timeframe)**0.5 * 100
num_of_mins_timeframe 是预测时长的总分钟数(3 个月 = 3×30×24×60),num_of_mins_year 是一年的分钟数(365×24×60)。前月权重与后月权重之和为 1,分别在"目标时长"两侧插值——前月离目标更近权重大,后月更远权重小。最后开方(σ² → σ)再 ×100,得到 VIX。

上图展示了 VIX 计算器的输出:输入 Henry Hub 天然气期权数据后,打印出一个代表"未来 3 个月预期波动率"的数字。这个数字越大,说明天然气市场预期未来 3 个月波动越剧烈。
🎯 反直觉发现:本节的反直觉不在某一行代码,而在整个项目的定位。VIX 官方只算 S&P 500、只算 30 天,但作者把它抽象成一个通用方差互换离散化器——换上 Henry Hub 天然气期权,就能算天然气的"VIX";换上任意预测时长,就能算任意时间窗口的预期波动。这背后的洞察是:VIX 的本质不是 S&P 500 专属,而是"用期权报价反推方差互换公平价格"的一套数学,谁有 OTM 期权报价,谁就能算自己的 VIX。Riemann sum + Taylor 不是为了炫技,而是把"连续方差互换"这个理论概念,变成任何商品都能套用的工程工具。
(settlement-current).total_seconds()/60/525600,525600 = 365×24×60。F = K + e^{rT}*(C-P),选价差最小的 K 保证 e^{rT}*(C-P) 误差最小。decimal.Decimal(x.astype(str)) 算 forward,金融代码的标准手法。find_forward.index[find_forward.index<=forward][-1]。interest_rate = cmt_rate[maturity==timeframe][Date==tradedate]/100,用同期限国债收益率作无风险利率。num_of_mins_timeframe 是目标时长,前月后月在其两侧插值,开方 ×100 得 VIX。get_settlement_day 跳周末与联邦假日算结算日;get_time_to_expiration 用分钟数/年分钟数算 T。get_forward_strike:F = K + e^{rT}*(C-P),取 call-put 价差最小的 K;K₀ 是不超过 F 的最大行权价。get_options_call/put_inclusion 选 OTM 期权(更流动),剔除连续 priorSettle=0 之后的所有档。compute_sigma:σ² = Σ[Q(K)*e^{rT}*ΔK/K²]*2/T - (F/K₀-1)²/T,这是 Riemann + Taylor 的最终落地。compute_vix:前月后月 σ² 时间加权,开方 ×100 得 VIX。第 5 章到此结束。我们从线性的股票/期货世界跨入了非线性的期权世界——Straddle 教会我们"赌波动率而非方向"和"quantamental 选行权价",VIX Calculator 教会我们"用 Riemann+Taylor 把理论方差互换变成任意商品可用的工程工具"。下一章,我们将把这些思维全面展开——Oil Money 是一个 quantamental 大项目,把基本面(石油产量、GDP、利率)与量化(协整、聚类、回归)熔于一炉,正是 Straddle 节那个观点的终极实践。