14. SABR校准实战:使用市场数据校准SABR参数

好了,终于到了动手环节。

前面几章我们把SABR模型的理论、偏微分方程、近似公式都过了一遍。说实话,理论再漂亮,落不了地也是白搭。今天我们就来点实在的——拿真实的市场数据,把SABR的四个参数(α, β, ρ, ν)给校准出来。

14.1 校准的本质:我们在干什么?

校准,说白了就是找一组参数,让模型算出来的期权价格,跟市场上实际交易的价格尽可能接近。

我刚开始做这个的时候,总觉得校准是个黑盒子。后来发现,它本质上就是一个优化问题:

目标函数:最小化模型价格与市场价格的误差平方和

决策变量:α, β, ρ, ν

约束条件:α > 0, 0 ≤ β ≤ 1, -1 < ρ < 1, ν > 0

嗯,这里要注意:β 参数比较特殊。它通常不参与校准,而是根据市场经验固定下来。我个人习惯把 β 设为 0.5,这是外汇期权市场的常见做法。如果你做的是利率衍生品,β=0.5 或者 β=1 都有人用。

14.2 数据准备:从市场拿到原始报价

先看看我们要处理的数据长什么样。假设我们从彭博或者路透拿到了欧元/美元期权的波动率曲面:

期限 10D Put 25D Put ATM 25D Call 10D Call
1W 12.5 11.2 10.0 10.8 12.0
1M 13.0 11.5 10.2 11.0 12.3
3M 13.8 12.0 10.5 11.5 13.0
6M 14.5 12.5 10.8 12.0 13.5
1Y 15.2 13.0 11.0 12.5 14.0

这些是隐含波动率,单位是百分比。10D Put 表示 Delta 为 10% 的看跌期权,以此类推。ATM 就是平值期权。

我在项目中遇到过一个问题:不同数据源给的报价格式不一样。有的给 Delta,有的给行权价。所以第一步,先把所有报价统一转换成行权价和波动率的对应关系。

14.3 核心逻辑:校准流程

校准的流程其实不复杂,我画了张图帮你理清思路:

SABR参数校准流程图 步骤1:输入市场数据 期限、行权价、市场隐含波动率 步骤2:初始化参数 设定α₀, β(固定), ρ₀, ν₀的初始值 步骤3:计算模型波动率 使用Hagan近似公式计算σ_model(K, T) 步骤4:计算误差 MSE = Σ(σ_model - σ_market)² / N 收敛? 更新参数 输出α, ρ, ν

你想想看,这个流程其实跟机器学习里的梯度下降很像。我们不断调整参数,让模型输出跟真实数据之间的差距越来越小。

14.4 代码实现:从零开始写校准器

下面是我写的一个校准函数。代码不长,但该有的都有了:

import numpy as np
from scipy.optimize import minimize
from scipy.stats import norm

def sabr_vol(alpha, beta, rho, nu, F, K, T):
    """
    Hagan近似公式计算SABR隐含波动率
    """
    if abs(F - K) < 1e-6:
        # ATM情况
        z = nu / alpha * (F * K) ** ((1 - beta) / 2) * np.log(F / K)
        x_z = np.log((np.sqrt(1 - 2 * rho * z + z ** 2) + z - rho) / (1 - rho))
        term1 = alpha / (F ** (1 - beta))
        term2 = 1 + ((1 - beta) ** 2 / 24 * (np.log(F / K)) ** 2
                     + (1 - beta) ** 4 / 1920 * (np.log(F / K)) ** 4)
        term3 = 1 + ((1 - beta) ** 2 / 24 * alpha ** 2 / (F * K) ** (1 - beta)
                     + rho * beta * nu * alpha / (4 * (F * K) ** ((1 - beta) / 2))
                     + (2 - 3 * rho ** 2) / 24 * nu ** 2) * T
        return term1 * term2 * term3
    else:
        z = nu / alpha * (F * K) ** ((1 - beta) / 2) * np.log(F / K)
        x_z = np.log((np.sqrt(1 - 2 * rho * z + z ** 2) + z - rho) / (1 - rho))
        term1 = alpha / ((F * K) ** ((1 - beta) / 2))
        term2 = 1 + ((1 - beta) ** 2 / 24 * (np.log(F / K)) ** 2
                     + (1 - beta) ** 4 / 1920 * (np.log(F / K)) ** 4)
        term3 = z / x_z
        term4 = 1 + ((1 - beta) ** 2 / 24 * alpha ** 2 / (F * K) ** (1 - beta)
                     + rho * beta * nu * alpha / (4 * (F * K) ** ((1 - beta) / 2))
                     + (2 - 3 * rho ** 2) / 24 * nu ** 2) * T
        return term1 * term2 * term3 * term4


def calibrate_sabr(market_strikes, market_vols, F, T, beta=0.5):
    """
    校准SABR参数
    market_strikes: 行权价数组
    market_vols: 市场隐含波动率数组
    F: 远期价格
    T: 期限(年)
    beta: 固定beta参数
    """
    def objective(params):
        alpha, rho, nu = params
        # 约束检查
        if alpha <= 0 or abs(rho) >= 1 or nu <= 0:
            return 1e10
        
        model_vols = np.array([
            sabr_vol(alpha, beta, rho, nu, F, K, T)
            for K in market_strikes
        ])
        
        # 均方误差
        mse = np.mean((model_vols - market_vols) ** 2)
        return mse
    
    # 初始值设定
    # alpha初始值:用ATM波动率估算
    atm_idx = np.argmin(np.abs(market_strikes - F))
    alpha_init = market_vols[atm_idx] * (F ** (1 - beta))
    
    # 初始猜测
    x0 = [alpha_init, 0.0, 0.3]
    
    # 参数边界
    bounds = [(1e-6, None), (-0.999, 0.999), (1e-6, None)]
    
    result = minimize(objective, x0, method='L-BFGS-B',
                      bounds=bounds, options={'maxiter': 1000})
    
    return result.x  # [alpha, rho, nu]


# 使用示例
if __name__ == "__main__":
    # 假设市场数据
    strikes = np.array([1.05, 1.08, 1.10, 1.12, 1.15])
    vols = np.array([0.125, 0.112, 0.100, 0.108, 0.120])
    F = 1.10  # 远期汇率
    T = 0.25  # 3个月
    
    alpha, rho, nu = calibrate_sabr(strikes, vols, F, T)
    print(f"校准结果: alpha={alpha:.4f}, rho={rho:.4f}, nu={nu:.4f}")

💡 个人经验:初始值的选择很关键。我踩过坑,初始值设得不好,优化器直接跑到局部最优去了。建议先用ATM波动率反推alpha的初始值,这样收敛会快很多。

14.5 避坑指南:校准中的常见问题

我曾经在一个项目里连续跑了三天校准,结果出来的参数怎么看怎么不对劲。后来发现是数据出了问题。这里总结几个常见坑:

  • 数据质量问题:市场报价里偶尔会有异常值。比如某个深度虚值期权的波动率突然跳得很高。我建议先做一次数据清洗,把明显偏离的点剔除掉。
  • 参数可识别性问题:有时候ρ和ν会互相"打架"。你想想看,这两个参数都影响波动率曲线的斜率和曲率。如果数据不够丰富,它们可能无法被唯一确定。解决办法是固定其中一个,或者加正则化项。
  • 期限结构问题:不同期限的SABR参数应该分开校准。不要试图用一个参数集拟合整个曲面。我一般每个期限单独校准,然后看参数随期限的变化是否平滑。

⚠️ 重要提醒:校准完成后,一定要做回测检验。把校准得到的参数代回模型,重新计算波动率,跟市场数据对比。如果误差超过0.5个波动率点,说明校准可能有问题。

14.6 结果分析:校准得好不好?

校准完了,怎么判断结果好坏?我一般看三个指标:

  1. 均方根误差(RMSE):一般应该小于0.2个波动率点。如果大于0.5,说明模型可能不适合这个市场。
  2. 参数合理性:α应该在0.01-0.5之间,ρ通常在-0.5到0.5之间,ν在0.1-1.0之间。如果参数跑出这个范围,要警惕。
  3. 残差分布:把模型波动率减去市场波动率,画个散点图。残差应该随机分布在零轴附近,不能有明显的系统性偏差。

说实话,校准这件事,七分技术三分经验。代码谁都能写,但参数调得好不好,就看你对市场的理解深不深了。


公众号:蓝海资料掘金营,微信deep3321