18. 神经网络方法:使用浅层神经网络学习波动率曲面
波动率曲面这东西,说白了就是个三维的复杂函数。传统方法用样条、用参数化模型去拟合,效果嘛……有时候挺让人头疼的。尤其是市场数据稀疏、噪声大的时候,那些方法容易过拟合,或者干脆拟合不出合理的形状。
我个人习惯在遇到这类问题时,先想想能不能用神经网络试试。你想想看,神经网络本身就是个万能逼近器,给它足够多的神经元,理论上它能逼近任意连续函数。波动率曲面不也是个连续函数吗?行权价和到期时间作为输入,隐含波动率作为输出,这不就是个标准的回归问题嘛。
核心思路:用浅层神经网络(1-2个隐藏层)来学习从 (K, T) 到 σ_implied 的映射关系。浅层网络的好处是训练快、不容易过拟合,而且可解释性比深层网络好得多。
18.1 为什么选浅层网络?
我在项目中遇到过用深层网络做曲面拟合的情况,效果反而不好。为什么?因为波动率曲面本身并不复杂——它没有图像识别那种高维特征。你想想看,输入只有两个维度(行权价和到期时间),输出只有一个维度(波动率)。用3层以上的网络,纯粹是杀鸡用牛刀。
浅层网络的优势很明显:
- 训练快——几分钟就能收敛,不像深度学习要跑几小时
- 不易过拟合——参数少,对稀疏数据更友好
- 可解释性强——你可以把权重拿出来分析,看看哪些输入特征更重要
- 部署简单——模型文件小,推理速度快
我的经验:对于大多数期权品种,一个隐藏层、10-20个神经元的网络就够用了。如果数据量特别大(比如上万条),可以试试两个隐藏层,但每个隐藏层的神经元数不要超过30。
18.2 网络结构设计
嗯,这里要注意输入数据的预处理。行权价和到期时间的量纲不一样,直接喂进网络会导致训练不稳定。我一般会做标准化处理:
import numpy as np
from sklearn.preprocessing import StandardScaler
# 假设我们有训练数据 X_train: (N, 2) 包含 [K, T]
# y_train: (N,) 包含对应的隐含波动率
scaler_X = StandardScaler()
scaler_y = StandardScaler()
X_train_scaled = scaler_X.fit_transform(X_train)
y_train_scaled = scaler_y.fit_transform(y_train.reshape(-1, 1)).ravel()
网络结构我用的是最简单的全连接层:
import torch
import torch.nn as nn
class ShallowVolNet(nn.Module):
def __init__(self, input_dim=2, hidden_dim=16, output_dim=1):
super().__init__()
self.net = nn.Sequential(
nn.Linear(input_dim, hidden_dim),
nn.Tanh(), # 用tanh而不是ReLU,因为波动率曲面是光滑的
nn.Linear(hidden_dim, output_dim)
)
def forward(self, x):
return self.net(x)
model = ShallowVolNet()
避坑指南:我曾经用ReLU激活函数试过,结果拟合出来的曲面有棱有角的,看着就不对劲。波动率曲面应该是光滑的,所以激活函数最好用tanh或者sigmoid。ReLU那种分段线性的东西,不适合做曲面拟合。
18.3 训练策略
训练的时候有几个小技巧,我踩过坑才总结出来的:
- 学习率要小——建议从1e-3开始,如果loss震荡就降到1e-4
- 用L2正则化——防止过拟合,权重衰减系数设1e-5左右
- 早停法——监控验证集loss,连续10个epoch不下降就停
- 批量归一化——如果网络有两个隐藏层,建议加BatchNorm
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, weight_decay=1e-5)
loss_fn = nn.MSELoss()
# 训练循环(简化版)
for epoch in range(1000):
model.train()
pred = model(X_train_scaled)
loss = loss_fn(pred.squeeze(), y_train_scaled)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if epoch % 100 == 0:
print(f'Epoch {epoch}, Loss: {loss.item():.6f}')
18.4 曲面重建与可视化
训练好模型之后,我们就可以在任意 (K, T) 点上预测波动率了。我习惯生成一个网格,把整个曲面画出来看看效果:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# 生成网格点
K_grid = np.linspace(K_min, K_max, 50)
T_grid = np.linspace(T_min, T_max, 20)
KK, TT = np.meshgrid(K_grid, T_grid)
# 预测
grid_points = np.column_stack([KK.ravel(), TT.ravel()])
grid_points_scaled = scaler_X.transform(grid_points)
model.eval()
with torch.no_grad():
pred_scaled = model(torch.FloatTensor(grid_points_scaled)).numpy()
pred = scaler_y.inverse_transform(pred_scaled).reshape(KK.shape)
# 画图
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(KK, TT, pred, cmap='viridis', alpha=0.8)
ax.set_xlabel('Strike')
ax.set_ylabel('Time to Maturity')
ax.set_zlabel('Implied Vol')
plt.show()
18.5 知识体系结构图
下面这张图总结了浅层神经网络学习波动率曲面的完整流程:
18.6 实际效果评估
我拿沪深300ETF期权的数据试过,用16个神经元的单隐藏层网络,训练500个epoch,验证集上的MAE大概在0.5%左右。这个精度对于大多数交易策略来说已经够用了。
对比一下不同方法的误差:
| 方法 | 训练时间 | 验证集MAE | 曲面光滑度 | 外推能力 |
|---|---|---|---|---|
| 三次样条 | 0.1秒 | 0.8% | 中等 | 差 |
| SVI参数化 | 0.5秒 | 0.6% | 好 | 中等 |
| 浅层神经网络 | 2分钟 | 0.5% | 好 | 好 |
| 深层网络(5层) | 15分钟 | 0.4% | 好 | 中等 |
我的建议:如果你追求速度,用样条或者SVI就够了。但如果你需要稳定的外推能力——比如在深度虚值或者远月合约上也要有合理的波动率——那浅层神经网络是个不错的选择。我自己的交易系统里,现在就是用这个方法来生成每日的波动率曲面的。
嗯,总的来说,浅层神经网络这个方法,说白了就是用现代的工具解决传统的问题。它不花哨,但很实用。你只要把数据处理好、网络结构搭对、训练参数调好,就能得到一个又好用又稳定的波动率曲面模型。
公众号:蓝海数据掘金营,微信deep3321