第二十八讲:可视化实战——用Plotly/Matplotlib绘制3D波动率曲面

波动率曲面这东西,说白了就是期权交易员的“作战地图”。

我刚开始做期权量化那会儿,每天盯着密密麻麻的数字表格,眼睛都快看瞎了。后来才明白——数据是死的,曲面是活的。一张好的3D图,能让你一眼看出市场情绪在哪、套利机会藏在哪。

今天咱们就动手,把波动率曲面从表格里“拎”出来,画成真正的3D图。

为什么非得画成3D?

你想想看,波动率曲面有三个维度:执行价K、剩余期限T、隐含波动率σ。二维表格只能看切片,三维图才能看到全貌。

我记得有一次做跨式套利策略,二维数据怎么看都觉得没机会。结果画成3D曲面,一眼就发现远端虚值期权那块有个“鼓包”——那是市场在定价尾部风险。后来靠这个信号,吃了一口大肉。

准备工作:数据从哪来?

咱们不搞虚的,直接生成一组模拟数据。真实场景中,这些数据来自期权市场收盘后的报价清洗。

import numpy as np
import pandas as pd

# 模拟参数
strikes = np.linspace(0.8, 1.2, 15)  # 执行价比例
ttm = np.linspace(0.1, 2.0, 12)      # 剩余期限(年)

# 生成曲面数据(带微笑效应和期限结构)
K_grid, T_grid = np.meshgrid(strikes, ttm)
sigma_surface = 0.2 + 0.1 * (K_grid - 1.0)**2 + 0.05 * np.sqrt(T_grid)

# 转成DataFrame方便处理
data = pd.DataFrame({
    'strike': K_grid.ravel(),
    'ttm': T_grid.ravel(),
    'iv': sigma_surface.ravel()
})

这里我故意加了两个特征:执行价越偏离平值,波动率越高(微笑效应);期限越长,波动率略升(期限结构)。真实市场比这复杂得多,但原理一样。

方案一:Matplotlib——稳扎稳打的老将

Matplotlib画3D图,我个人的习惯是用mplot3d工具包。它不像Plotly那么炫酷,但胜在可控性强,适合做学术论文或内部报告。

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(12, 8))
ax = fig.add_subplot(111, projection='3d')

# 绘制曲面
surf = ax.plot_surface(K_grid, T_grid, sigma_surface, 
                       cmap='viridis', alpha=0.9,
                       linewidth=0, antialiased=True)

# 设置标签
ax.set_xlabel('执行价 (K)', fontsize=12)
ax.set_ylabel('剩余期限 (T)', fontsize=12)
ax.set_zlabel('隐含波动率', fontsize=12)
ax.set_title('波动率曲面 (Matplotlib)', fontsize=14)

# 添加颜色条
fig.colorbar(surf, ax=ax, shrink=0.5, aspect=10)

plt.tight_layout()
plt.show()
我的小技巧: 画3D图时,alpha参数别设成1.0。稍微透明一点(0.8-0.9),能看到曲面背后的网格线,立体感更强。

嗯,这里要注意一点:Matplotlib的3D渲染其实有点“假”,它是把3D投影到2D上画的。如果你旋转视角,可能会发现某些线条穿透了曲面。这是老毛病了,无伤大雅。

方案二:Plotly——交互式利器

如果你要做展示、或者给交易员看,我强烈推荐Plotly。它能旋转、缩放、悬停看数值,交互感拉满。

import plotly.graph_objects as go

fig = go.Figure(data=[
    go.Surface(
        z=sigma_surface,
        x=strikes,
        y=ttm,
        colorscale='Viridis',
        opacity=0.9,
        contours={
            'z': {'show': True, 'usecolormap': True, 'highlightcolor': 'limegreen'}
        }
    )
])

fig.update_layout(
    title='波动率曲面 (Plotly)',
    scene=dict(
        xaxis_title='执行价 (K)',
        yaxis_title='剩余期限 (T)',
        zaxis_title='隐含波动率',
        camera=dict(eye=dict(x=1.5, y=1.5, z=0.8))
    ),
    width=900,
    height=700
)

fig.show()
关键参数解读:
contours.z.show=True:在曲面上画等高线,一眼看出波动率“等高”的区域。
camera.eye:设置初始视角。我习惯把眼睛放在(x=1.5, y=1.5, z=0.8),这个角度能看到曲面全貌又不失真。

Plotly还有一个杀手锏——悬停显示数值。交易员鼠标移上去,就能看到某个执行价、某个期限对应的精确波动率。这在做市商报价时特别有用。

避坑指南:我曾经踩过的雷

  • 数据顺序搞反: 有一次我画出来的曲面像被揉过的纸团,查了半天才发现是meshgrid的维度顺序和plot_surface要求的顺序不一致。记住:X是行方向,Y是列方向。
  • 缺失值处理: 真实数据中,深度虚值期权经常没有报价。如果不做插值直接画,曲面会破洞。我一般先用上一讲的方法(样条插值)补全,再可视化。
  • 颜色映射选择: 别用彩虹色!虽然好看,但色盲同事看不出来。我习惯用viridisplasma,对色盲友好,而且颜色深浅和数值高低对应直观。

进阶:叠加真实市场数据

光画平滑曲面不够,咱们把真实的期权报价也标上去,看看插值效果好不好。

# 模拟一些“真实”报价(带随机噪声)
np.random.seed(42)
real_iv = sigma_surface + np.random.normal(0, 0.02, sigma_surface.shape)

# 在Plotly上叠加散点
fig.add_trace(go.Scatter3d(
    x=K_grid.ravel(),
    y=T_grid.ravel(),
    z=real_iv.ravel(),
    mode='markers',
    marker=dict(size=3, color='red', opacity=0.6),
    name='市场报价'
))

红色散点就是市场实际报价,绿色曲面是插值结果。如果散点都贴在曲面上,说明插值做得好;如果飘得厉害,嗯...你的模型该调了。

本章核心逻辑图

下面这张图,把整个可视化流程串起来了:

波动率曲面可视化流程 数据准备 期权报价 → 清洗 → 插值 选择可视化工具 Matplotlib / Plotly Matplotlib 静态图 · 论文/报告 Plotly 交互图 · 展示/交易 绘制与优化 视角调整 · 颜色映射 · 等高线 叠加验证 散点 vs 曲面 → 评估插值质量 反馈优化

总结一下

可视化不是花架子,它是你理解波动率曲面的“第三只眼”。

  • Matplotlib:适合静态报告,可控性强,但交互性差。
  • Plotly:适合动态展示,交互感好,但文件体积大。
  • 核心原则:先插值、后绘图;先平滑、后叠加散点验证。

我个人现在的工作流是:用Plotly做日常分析,用Matplotlib出最终报告。两个工具互补,缺一不可。

最后提醒一句: 别为了好看过度平滑曲面。我曾经见过有人用高阶样条把曲面画得跟丝绸一样顺滑,结果完全抹平了市场真实的波动率微笑——那图再好看也是废的。可视化是为了洞察,不是为了艺术。

好了,代码都在上面了。打开你的Python环境,跑一遍试试。看到那个3D曲面在你眼前旋转的时候,你会觉得——嗯,这活儿值了。