第二十八讲:可视化实战——用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()
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是列方向。 - 缺失值处理: 真实数据中,深度虚值期权经常没有报价。如果不做插值直接画,曲面会破洞。我一般先用上一讲的方法(样条插值)补全,再可视化。
- 颜色映射选择: 别用彩虹色!虽然好看,但色盲同事看不出来。我习惯用
viridis或plasma,对色盲友好,而且颜色深浅和数值高低对应直观。
进阶:叠加真实市场数据
光画平滑曲面不够,咱们把真实的期权报价也标上去,看看插值效果好不好。
# 模拟一些“真实”报价(带随机噪声)
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:适合动态展示,交互感好,但文件体积大。
- 核心原则:先插值、后绘图;先平滑、后叠加散点验证。
我个人现在的工作流是:用Plotly做日常分析,用Matplotlib出最终报告。两个工具互补,缺一不可。
好了,代码都在上面了。打开你的Python环境,跑一遍试试。看到那个3D曲面在你眼前旋转的时候,你会觉得——嗯,这活儿值了。