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 训练策略

训练的时候有几个小技巧,我踩过坑才总结出来的:

  1. 学习率要小——建议从1e-3开始,如果loss震荡就降到1e-4
  2. 用L2正则化——防止过拟合,权重衰减系数设1e-5左右
  3. 早停法——监控验证集loss,连续10个epoch不下降就停
  4. 批量归一化——如果网络有两个隐藏层,建议加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 知识体系结构图

下面这张图总结了浅层神经网络学习波动率曲面的完整流程:

浅层神经网络学习波动率曲面 - 知识体系 输入层 行权价 K, 到期时间 T 数据预处理 标准化 (StandardScaler) 隐藏层 (1-2层) 神经元数: 10-20 激活函数: Tanh σ 训练策略 损失函数: MSE 优化器: Adam L2正则化 + 早停法 学习率: 1e-3 ~ 1e-4 批量归一化 (可选) 输出: 任意 (K, T) 点的隐含波动率 光滑、连续、可微的波动率曲面

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