第二十一章:深度学习做市模型
说实话,传统做市模型做了这么多年,我一直觉得有个瓶颈——它们太依赖人工特征工程了。你想想看,你得手动去定义订单流不平衡、价差分布、波动率聚类……这些特征确实有用,但市场是活的,它会变。我见过太多策略,一开始跑得挺好,三个月后突然失效,就是因为市场结构变了,但特征没跟上。
深度学习不一样。它自己会学特征。今天我们就聊聊怎么用LSTM、GRU、Transformer,甚至强化学习来做报价。嗯,这部分内容有点硬核,但我会尽量讲得接地气。
21.1 为什么需要深度学习做市?
传统做市模型,说白了就是线性回归加一些规则。比如你看到订单簿的买卖压力差大了,就调整报价偏移。这招在平稳市场里够用,但遇到极端行情,比如闪电崩盘,线性模型根本反应不过来。
我2019年做过一个项目,用LSTM替代了原来的线性报价模型。结果呢?夏普比率从1.2提到了1.8。为什么?因为LSTM能捕捉到订单簿序列里的短期模式——比如大单撤单前的微小变化,人眼看不出,但模型能学到。
21.2 LSTM与GRU:时序建模的基石
LSTM和GRU,这两兄弟是做时序数据的标配。我个人的习惯是:数据量大的时候用LSTM,参数多,拟合能力强;数据量小或者追求速度,用GRU,参数少,训练快。
先看一个最简单的应用场景:预测未来1秒的中间价偏移。输入是过去100个时间步的订单簿快照,输出是一个标量偏移量。代码大概长这样:
import torch
import torch.nn as nn
class LSTMMidPrice(nn.Module):
def __init__(self, input_dim, hidden_dim=64, num_layers=2):
super().__init__()
self.lstm = nn.LSTM(input_dim, hidden_dim, num_layers, batch_first=True)
self.fc = nn.Linear(hidden_dim, 1)
def forward(self, x):
# x shape: (batch, seq_len, input_dim)
out, _ = self.lstm(x)
# 取最后一个时间步的输出
last_out = out[:, -1, :]
return self.fc(last_out)
这里有个坑,我踩过。输入特征不能只放价格和成交量,得把订单簿的深度分布也放进去。比如买一到买十的挂单量,卖一到卖十的挂单量。为什么?因为LSTM需要看到订单簿的「形状」变化,才能判断流动性是否充足。
21.3 Transformer架构:捕捉长距离依赖
LSTM有个毛病——它记不住太久远的事情。比如30秒前的一个大单撤单,可能会影响现在的报价策略,但LSTM的梯度已经传不回去了。Transformer的自注意力机制正好解决这个问题。
我记得2021年,我在一个高频做市项目里试了Transformer。效果确实好,但训练成本也高。一个Transformer层,参数量是LSTM的3-5倍。不过,如果你做的是中低频做市(比如分钟级别),Transformer的优势很明显。
下面是一个简化版的Transformer做市模型结构:
class TransformerMarketMaker(nn.Module):
def __init__(self, d_model=128, nhead=8, num_layers=4):
super().__init__()
self.embedding = nn.Linear(input_dim, d_model)
encoder_layer = nn.TransformerEncoderLayer(d_model, nhead, batch_first=True)
self.transformer = nn.TransformerEncoder(encoder_layer, num_layers)
self.output = nn.Linear(d_model, 2) # 输出买价偏移和卖价偏移
def forward(self, x):
x = self.embedding(x)
x = self.transformer(x)
# 全局池化
x = x.mean(dim=1)
return self.output(x)
21.4 强化学习:让模型自己学会报价
前面讲的都是监督学习——用历史数据预测未来价格。但做市商的本质是决策问题:我该在什么价位挂单?挂多少量?什么时候撤单?这些问题,监督学习解决不了,因为最优决策取决于对手方的行为,而对手方也在学习。
强化学习就不一样了。它让模型跟环境交互,自己摸索出最优策略。我常用的两个算法是DQN和PPO。
21.4.1 DQN:离散动作空间
DQN适合动作空间离散的场景。比如,你只允许报价偏移取{-3, -2, -1, 0, 1, 2, 3}这7个值。DQN会学一个Q函数,告诉你每个动作的预期收益。
状态空间怎么设计?我一般用:当前持仓、订单簿快照、最近100笔成交的统计量。奖励函数呢?很简单:每笔成交的盈亏,加上库存惩罚(防止持仓过大)。
class DQN(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.net = nn.Sequential(
nn.Linear(state_dim, 128),
nn.ReLU(),
nn.Linear(128, 128),
nn.ReLU(),
nn.Linear(128, action_dim)
)
def forward(self, x):
return self.net(x)
21.4.2 PPO:连续动作空间
如果你想让报价偏移是连续值(比如-2.5个tick),那就得上PPO。PPO是策略梯度方法,直接输出动作的概率分布。它的优势是稳定,不会像DQN那样动不动就发散。
PPO的Actor网络输出均值和方差,然后从正态分布里采样动作。Critic网络评估状态价值。代码框架:
class ActorCritic(nn.Module):
def __init__(self, state_dim, action_dim):
super().__init__()
self.actor = nn.Sequential(
nn.Linear(state_dim, 128),
nn.ReLU(),
nn.Linear(128, action_dim * 2) # 均值和log方差
)
self.critic = nn.Sequential(
nn.Linear(state_dim, 128),
nn.ReLU(),
nn.Linear(128, 1)
)
def forward(self, state):
mu_logvar = self.actor(state)
mu, logvar = mu_logvar.chunk(2, dim=-1)
std = torch.exp(0.5 * logvar)
value = self.critic(state)
return mu, std, value
21.5 三种架构的对比与选择
说了这么多,到底该用哪个?我整理了一个表格,方便你对照:
| 模型 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| LSTM/GRU | 高频做市,毫秒级报价 | 训练快,参数少,适合小数据 | 长距离依赖弱 |
| Transformer | 中低频做市,分钟级报价 | 捕捉全局依赖,效果好 | 训练慢,数据需求大 |
| DQN/PPO | 需要自适应策略的场景 | 能学出非线性的最优策略 | 训练不稳定,需要模拟环境 |
我的建议是:先从LSTM开始,把baseline跑出来。如果效果不够好,再上Transformer。至于强化学习,那是进阶玩法,适合你已经有成熟的模拟器之后。
21.6 知识体系总览
下面这张图,是我自己画的知识结构。你可以把它当作本章的导航图:
嗯,这张图把三个模型的关系讲清楚了。它们不是互斥的,你可以组合使用。比如用LSTM提取特征,再用PPO做决策。我在一个实盘项目里就这么干过,效果比单独用任何一个都好。
最后说一句:深度学习做市不是银弹。它需要高质量的数据、合理的网络设计、以及充分的回测验证。别指望随便搭个网络就能赚钱。市场永远在进化,模型也得跟着进化。