You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Python中用样条插值波动率偏斜并解决负值问题

波动率偏斜曲线插值负值问题分析与解决

我有两组数据用于描述波动率曲面偏斜的y(x)函数:

  • x_points(行权价):[22.56, 27.07, 31.58, 36.10, 40.61, 45.12, 49.63, 54.14, 58.66, 63.17, 67.68]
  • y_points(波动率相对值):[97.44, 87.32, 79.73, 75.47, 73.58, 74.53, 78.61, 83.64, 88.03, 92.26, 96.44]

尝试用三次样条插值生成0到200区间的曲线时,出现不符合预期的负值,代码如下:

def f(x):
    x = np.linspace(0, 200, 399)
    tck = interpolate.splrep(x_points, y_points)
    return interpolate.splev(x, tck)

问题原因

三次样条插值的核心是在给定数据点间生成二阶连续导数的曲线,但它没有针对外插区间的约束逻辑:

  • 原始数据的x范围仅为22.5667.68,022.56和67.68~200属于外插区域
  • 三次样条外插时会沿着边界点的导数趋势无限延伸,而原始数据两端的导数趋势未做约束时,极易出现向下震荡,导致本应非负的波动率出现负值

解决方案

1. 给三次样条添加边界约束

通过scipy.interpolate.splrep的bc_type参数设置边界条件,强制外插时曲线保持合理趋势。比如设置两端的一阶导数为0(平坦延伸),或根据原始数据趋势设置导数:

import numpy as np
from scipy import interpolate

x_points = [22.56, 27.07, 31.58, 36.10, 40.61, 45.12, 49.63, 54.14, 58.66, 63.17, 67.68]
y_points = [97.44, 87.32, 79.73, 75.47, 73.58, 74.53, 78.61, 83.64, 88.03, 92.26, 96.44]

def constrained_spline(x):
    # 设置边界一阶导数为0,让曲线在两端平坦延伸
    tck = interpolate.splrep(x_points, y_points, bc_type=((1, 0.0), (1, 0.0)))
    return interpolate.splev(x, tck)

# 生成0~200的采样点
x_new = np.linspace(0, 200, 399)
y_new = constrained_spline(x_new)

2. 使用单调插值(Pchip)

原始数据呈现先递减后递增的单调趋势(从97.44降至73.58再升至96.44),PchipInterpolator可保证插值曲线严格遵循数据的单调性,彻底避免震荡和负值:

from scipy.interpolate import PchipInterpolator

def pchip_interp(x):
    pchip = PchipInterpolator(x_points, y_points)
    return pchip(x)

y_new_pchip = pchip_interp(x_new)

3. 分段处理插值与外插

将0~200区间分成三段,针对性处理:

  • 0~22.56:按左端趋势做常数/线性延伸
  • 22.56~67.68:用三次样条或Pchip插值
  • 67.68~200:按右端趋势做常数/线性延伸
def piecewise_interp(x):
    y = np.zeros_like(x)
    # 左外插区间:常数延伸
    left_mask = x < x_points[0]
    y[left_mask] = y_points[0]
    
    # 插值区间:三次样条
    interp_mask = (x >= x_points[0]) & (x <= x_points[-1])
    tck = interpolate.splrep(x_points, y_points)
    y[interp_mask] = interpolate.splev(x[interp_mask], tck)
    
    # 右外插区间:常数延伸
    right_mask = x > x_points[-1]
    y[right_mask] = y_points[-1]
    
    return y

y_new_piecewise = piecewise_interp(x_new)

4. 带非负约束的曲线拟合

如果需要更严格的非负约束,可通过优化方法拟合带约束的曲线(以5次多项式为例):

from scipy.optimize import minimize

# 定义5次多项式拟合函数
def poly_func(params, x):
    return params[0] + params[1]*x + params[2]*x**2 + params[3]*x**3 + params[4]*x**4 + params[5]*x**5

# 定义损失函数
def loss(params, x, y):
    return np.sum((poly_func(params, x) - y)**2)

# 初始参数与非负约束
init_params = np.zeros(6)
cons = ({'type': 'ineq', 'fun': lambda params: poly_func(params, np.linspace(0,200,100))})

# 优化求解
result = minimize(loss, init_params, args=(x_points, y_points), constraints=cons)

# 生成拟合结果
def constrained_poly(x):
    return poly_func(result.x, x)

y_new_poly = constrained_poly(x_new)

内容的提问来源于stack exchange,提问作者Giovanni Venticinque

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 00:52:38