第二十六章:冲击成本前沿:机器学习模型、深度学习模型、图神经网络
各位,欢迎来到冲击成本建模的「前沿阵地」。
前面我们聊了那么多传统模型,从线性到非线性,从AC到Almgren-Chriss。说实话,这些模型在大多数场景下够用了。但如果你做高频交易,或者处理的是流动性极差的资产,你会发现传统模型开始「力不从心」。为什么?因为市场结构太复杂了,订单流之间的相互作用,远不是几条曲线能拟合的。
今天,我们就来聊聊怎么用机器学习、深度学习,甚至图神经网络来干这件事。我个人觉得,这是冲击成本建模未来三到五年的主要演进方向。
1. 为什么传统模型不够用了?
先说说痛点。传统模型假设市场是「均匀」的——订单流随机到达,价格变化与成交量成某种函数关系。但真实市场呢?
- 订单流是「自相关」的:一个大单进来,后面往往跟着更多单子。
- 微观结构噪声很大:买卖价差、订单簿厚度,这些细节传统模型很难捕捉。
- 市场状态会切换:平静期和恐慌期,冲击成本完全不是一个量级。
说白了,传统模型是在「平均意义」上做预测。而机器学习模型,可以学到更精细的模式。
核心观点:机器学习不是要替代传统模型,而是在传统模型的基础上,加入更多特征,提升预测精度。我习惯把传统模型的结果作为「基准特征」,再让模型去学习残差。
2. 机器学习模型:从特征工程开始
我们先从最经典的机器学习模型说起。我个人在项目中用得最多的是梯度提升树(XGBoost、LightGBM)。为什么?因为它对特征工程的要求相对低,而且能处理非线性关系。
2.1 特征工程怎么做?
冲击成本预测的特征,我一般分成三类:
- 订单特征:订单大小(相对于平均成交量)、订单方向、订单类型(市价单/限价单)。
- 市场状态特征:当前买卖价差、订单簿深度(前5档的挂单量)、最近1分钟的成交量、波动率。
- 时序特征:过去N笔交易的成交价格序列、过去N笔订单的到达间隔。
嗯,这里要注意:特征不是越多越好。我曾经犯过一个错误,把过去100笔交易的价格都作为特征,结果模型过拟合得一塌糊涂。后来我改用了一些统计量,比如均值、标准差、偏度,效果反而更好。
2.2 模型训练与评估
训练时,我建议用滚动时间窗口来划分训练集和测试集。千万别随机打乱数据,时间序列数据一旦打乱,就相当于「偷看未来」了。
# 伪代码示例:滚动时间窗口划分
train_start = '2024-01-01'
train_end = '2024-06-30'
test_start = '2024-07-01'
test_end = '2024-07-31'
# 训练模型
model = xgb.XGBRegressor()
model.fit(X_train, y_train)
# 预测
y_pred = model.predict(X_test)
# 评估指标:除了MSE,我还会看方向准确率
# 即预测的冲击成本方向(正/负)与实际是否一致
小技巧:评估时,别只看R²。对于冲击成本,我更关注「极端值预测」的准确性。你可以把测试集按实际冲击成本大小分成10组,看每组预测误差的均值。如果模型在最大冲击成本那组误差很大,说明它没学到尾部风险。
3. 深度学习模型:捕捉时序依赖
机器学习模型有个缺点:它把每个样本当作独立的。但冲击成本有明显的时序依赖——前一秒的订单流会影响下一秒的冲击成本。这时候,深度学习就派上用场了。
3.1 LSTM与GRU
我最常用的是LSTM。为什么?因为它能记住长期依赖。比如,一个大单进来后,市场可能需要几分钟才能恢复流动性。LSTM可以捕捉这种「记忆效应」。
# 简单的LSTM模型结构
model = Sequential([
LSTM(64, return_sequences=True, input_shape=(seq_len, n_features)),
Dropout(0.2),
LSTM(32),
Dense(16, activation='relu'),
Dense(1) # 输出预测的冲击成本
])
这里有个坑:序列长度怎么选? 我建议用自相关函数来辅助判断。计算冲击成本序列的自相关,看它衰减到0需要多少步,那就是你的序列长度。我做过一个实验,对于A股市场,序列长度在20-50步(对应1-2.5秒)效果最好。
3.2 注意力机制
LSTM虽然好,但它有个问题:它对序列中所有位置「一视同仁」。但实际中,有些时刻更重要。比如,一个大单成交的瞬间,对后续冲击成本的影响远大于普通时刻。
这时候,注意力机制就很有用了。它让模型学会「关注」重要的时间步。
我的经验:在LSTM后面加一个简单的自注意力层,通常能提升2-5%的预测精度。而且,注意力权重本身可以可视化,帮你理解模型到底在关注哪些时刻——这对交易员来说很有说服力。
4. 图神经网络:建模订单流网络
好了,接下来是真正前沿的东西——图神经网络(GNN)。
你想想看,市场中的订单不是孤立的。一个订单的成交,会影响其他订单的成交概率。这本质上是一个图结构:
- 节点:每个订单或每个交易者
- 边:订单之间的时序关系或因果关系
4.1 为什么用图?
传统模型把订单流看作一个序列。但序列只能捕捉「前后」关系,捕捉不到「谁影响了谁」。比如,两个大单同时在不同交易所出现,它们之间没有时序关系,但会相互影响价格。图神经网络可以建模这种「非时序的相互作用」。
4.2 一个简单的图模型框架
我设计过一个实验,把过去N笔订单作为节点,用它们的时间间隔和价格差异作为边的权重。然后让GNN去预测下一笔订单的冲击成本。
# 伪代码:图神经网络建模冲击成本
# 1. 构建图
nodes = [order_1, order_2, ..., order_N]
edges = []
for i in range(N):
for j in range(i+1, N):
weight = 1 / (time_diff(i,j) + epsilon) # 时间越近,权重越大
edges.append((i, j, weight))
# 2. 定义GNN模型
class ImpactGNN(nn.Module):
def __init__(self):
self.conv1 = GCNConv(in_channels, 64)
self.conv2 = GCNConv(64, 32)
self.fc = nn.Linear(32, 1)
def forward(self, x, edge_index):
x = self.conv1(x, edge_index)
x = F.relu(x)
x = self.conv2(x, edge_index)
x = global_mean_pool(x, batch) # 聚合所有节点信息
return self.fc(x)
注意:图神经网络的计算量很大。对于高频数据,每秒可能有上千笔订单,构建全连接图是不现实的。我建议只连接「时间窗口内」的订单,或者用K近邻算法只连接最相关的订单。
5. 三种模型的对比与选择
说了这么多,到底该用哪个?我整理了一个表格,方便你决策:
| 模型类型 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|
| 机器学习(XGBoost等) | 训练快,可解释性强,特征工程灵活 | 无法捕捉时序依赖 | 低频交易、日频调仓、特征维度高时 |
| 深度学习(LSTM等) | 能捕捉长期时序依赖,自动特征提取 | 训练慢,需要大量数据,可解释性差 | 高频交易、订单流预测、需要记忆效应时 |
| 图神经网络(GNN) | 能建模订单间相互作用,捕捉网络效应 | 计算量大,图构建复杂,落地难度高 | 多交易所联动、大单拆单策略、流动性危机预测 |
6. 实战中的避坑指南
最后,分享几个我在项目中踩过的坑:
- 数据泄露:用未来数据预测当前,这是最常见的错误。比如,用「未来5分钟的成交量」作为特征。记住,所有特征在预测时刻必须是已知的。
- 过拟合到市场微观结构:深度学习模型很容易学到订单簿的「噪声模式」。我建议在损失函数中加入正则项,或者用早停法。
- 忽略交易成本:模型预测的冲击成本再准,如果交易成本(佣金、滑点)比它还高,那就没意义了。我习惯把交易成本也作为一个特征输入模型。
我的建议:不要一上来就上GNN。先从简单的机器学习模型开始,建立基线。然后逐步增加复杂度。如果XGBoost已经能解释80%的方差,那LSTM和GNN带来的提升可能只有5-10%。这5-10%值不值得你花一个月去调参?你自己权衡。
好了,关于前沿模型的内容就到这里。冲击成本建模这条路,越走越深,但也越走越有意思。希望今天的分享能给你一些启发。