15. 曲面质量评估:拟合误差分析、交叉验证、经济合理性检验

曲面建好了,参数也调了,然后呢?

说实话,我见过太多人把曲面一画出来就觉得完事了。其实真正的活儿才刚刚开始。你想想看,一个波动率曲面,如果只是数学上拟合得好,但经济意义上说不通,那它就是一张废纸。

这一章,我们就聊聊怎么给曲面「体检」。我把它分成三个维度:拟合误差、交叉验证、经济合理性。缺一不可。

15.1 拟合误差分析:别被R²骗了

很多人喜欢盯着R²看,觉得0.99就是好模型。嗯,这里要注意——R²高不代表曲面好用。

我在项目中遇到过这样的情况:一个SVI模型在ATM附近拟合得完美,但两端的尾部完全跑偏。R²是0.98,可实际交易中,深度虚值期权的定价偏差能到5%以上。

⚠️ 避坑指南: 我曾经只关注整体R²,结果被深度虚值期权坑了一把。后来我养成了一个习惯:按行权价区间分别计算误差

我常用的误差指标有这几个:

指标 公式 说明
MAE mean(|σ_model - σ_market|) 平均绝对误差,直观
RMSE sqrt(mean((σ_model - σ_market)²)) 对大偏差更敏感
MAPE mean(|σ_model - σ_market| / σ_market) 百分比误差,适合比较
Bias mean(σ_model - σ_market) 系统性偏差,正负号重要

我个人习惯把误差按期限和行权价画成热力图。这样一眼就能看出哪里出了问题。

import numpy as np
import matplotlib.pyplot as plt

def plot_error_heatmap(strikes, maturities, errors):
    """
    绘制拟合误差热力图
    strikes: 行权价数组
    maturities: 期限数组
    errors: 二维误差矩阵
    """
    fig, ax = plt.subplots(figsize=(10, 6))
    im = ax.imshow(errors, cmap='RdYlBu_r', aspect='auto')
    
    ax.set_xticks(range(len(strikes)))
    ax.set_yticks(range(len(maturities)))
    ax.set_xticklabels([f'{s:.0f}' for s in strikes])
    ax.set_yticklabels([f'{m:.2f}' for m in maturities])
    
    plt.colorbar(im, label='拟合误差 (bps)')
    plt.title('波动率曲面拟合误差热力图')
    plt.xlabel('行权价')
    plt.ylabel('期限 (年)')
    plt.show()

# 示例:假设我们有5个行权价和4个期限
strikes = np.array([3800, 4000, 4200, 4400, 4600])
maturities = np.array([0.1, 0.3, 0.6, 1.0])
# 模拟误差数据
errors = np.random.randn(4, 5) * 10  # 单位:bps
plot_error_heatmap(strikes, maturities, errors)
💡 我的经验: 如果热力图上某个区域颜色特别深(误差大),先别急着调参数。先看看那个区域的流动性怎么样。有时候不是模型的问题,是市场数据本身就有噪音。

15.2 交叉验证:别让模型「死记硬背」

说白了,交叉验证就是看看你的模型有没有「死记硬背」市场数据。

我见过一个团队,他们的曲面在样本内误差只有2个bps,但一到样本外就崩到50个bps。这就是典型的过拟合。

我常用的交叉验证方法有三种:

  1. 时间序列交叉验证:用前N天的数据拟合,预测第N+1天的曲面
  2. 行权价留一法:每次去掉一个行权价,用剩下的数据拟合,预测被去掉的那个
  3. 随机子集验证:随机抽取80%的数据拟合,在剩下的20%上测试

我个人最推荐时间序列交叉验证。为什么?因为交易中我们永远是在用历史数据预测未来。这个场景最贴近实战。

def time_series_cv(data, fit_func, window=20):
    """
    时间序列交叉验证
    data: 按时间排序的波动率数据列表
    fit_func: 拟合函数,输入训练数据,返回模型
    """
    errors = []
    for i in range(window, len(data)):
        train = data[i-window:i]
        test = data[i:i+1]
        
        model = fit_func(train)
        pred = model.predict(test)
        
        error = np.abs(pred - test['vol'])
        errors.append(error)
    
    return np.mean(errors), np.std(errors)

# 使用示例
mean_err, std_err = time_series_cv(vol_data, fit_svi_model)
print(f'平均预测误差: {mean_err:.2f} bps')
print(f'误差标准差: {std_err:.2f} bps')
⚠️ 注意: 交叉验证的窗口大小很关键。窗口太小,模型学不到足够的信息;窗口太大,又可能包含市场结构变化。我一般用20-30个交易日,差不多一个月的窗口。

15.3 经济合理性检验:曲面要「讲人话」

这是最容易被忽略的一步,但也是最重要的一步。

你想想看,一个波动率曲面,如果它的斜率是负的、曲率是正的,但市场明明在恐慌中——那这个曲面就是错的。数学上再完美也没用。

我总结了几条经济合理性的「红线」:

  • 无套利条件:曲面不能存在日历套利或蝶式套利机会
  • 期限结构单调性:短期波动率通常高于长期(除非市场极度平静)
  • 微笑对称性:正常情况下,虚值看跌和虚值看涨的波动率应该大致对称
  • 尾部行为:深度虚值期权的波动率不应该出现异常跳跃

我记得有一次,一个同事的曲面在某个行权价附近出现了「凹陷」。数学上拟合得很好,但经济上完全说不通——那个行权价附近根本没有流动性。后来我们查了一下,是那个交易员报了一个错误的价格。

🔍 经济合理性检查清单:
  1. 检查所有行权价和期限组合是否存在套利机会
  2. 对比曲面隐含的远期波动率与历史波动率
  3. 检查曲面在不同市场状态下的稳定性(涨、跌、震荡)
  4. 与同类资产或相关市场的波动率曲面做横向对比

15.4 综合评估框架

说了这么多,怎么把这些东西串起来?

我习惯用一个综合评分卡来评估曲面质量。每个维度打分,最后加权平均。

评估维度 权重 评分标准
拟合误差 (MAE) 30% <5 bps: 10分, 5-10 bps: 7分, >10 bps: 4分
交叉验证稳定性 25% 预测误差 <10 bps: 10分, 10-20 bps: 7分, >20 bps: 4分
无套利条件 25% 完全无套利: 10分, 少量可忽略: 7分, 存在明显套利: 0分
经济合理性 20% 完全合理: 10分, 基本合理: 7分, 存在疑问: 4分

总分低于7分的曲面,我建议重新拟合或者换模型。别舍不得,有时候重新来过比修修补补更快。

💡 一个小技巧: 我每次拟合完曲面,都会先跑一遍无套利检查。如果发现有套利机会,我会先怀疑数据质量,而不是模型。数据干净了,模型自然就好了。

15.5 本章小结

曲面质量评估,说白了就是三个字:准、稳、合理。

  • :拟合误差要小,但不能只看整体,要看局部
  • :交叉验证要过,模型不能过拟合
  • 合理:经济上要讲得通,不能出现无套利漏洞

这三步走下来,你的曲面才算真正「能用」。别嫌麻烦,我见过太多人因为跳过这一步,在实盘中吃了大亏。

波动率曲面质量评估框架 曲面质量评估 拟合误差分析 交叉验证 经济合理性检验 MAE / RMSE 误差热力图 区间误差分析 时间序列CV 行权价留一法 随机子集验证 无套利条件 期限结构 微笑对称性 综合评分 → 曲面可用性判定
📌 核心要点: 曲面质量评估不是一次性的工作。市场在变,数据在变,你的曲面也需要持续监控。我建议每周至少做一次全面的质量评估,尤其是在市场波动剧烈的时候。

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