论文详细解析:复现所需数据与步骤流程
我已经完整阅读了这篇论文 "An Impulse Control Approach to Market Making in a Hawkes LOB Market"。以下是对复现所需的详细解析:
📋 论文核心信息
标题: An Impulse Control Approach to Market Making in a Hawkes LOB Market
作者: Konark Jain, Nick Firoozye, Jonathan Kochems, Philip Treleaven
机构: UCL计算机科学系, JP Morgan量化研究部门
arXiv: 2510.26438v2 (2025年10月)
🎯 核心研究内容
问题定义: 在由互激Hawkes过程驱动的高保真限价订单簿(LOB)环境中,研究最优做市(Market Making)问题。
创新点:
摒弃传统布朗运动驱动的中间价模型,显式捕捉订单簿动态
引入脉冲控制框架,考虑做市商无法在每个LOB事件都更新策略的现实约束
提出两种求解方法:Deep Galerkin Method (DGM) 和 基于PPO的强化学习
RL方法在仅60个训练周期内达到年化Sharpe比率31.54
📊 复现所需数据
- LOB模拟器核心参数
论文使用高保真Hawkes驱动LOB模拟器(基于[27]的工作),需要以下参数:
事件类型 (12维Hawkes过程):
E = {LOaskD, LOaskT, COaskT, MOask, LOaskIS,
LObidIS, LObidT, CObidT, MObid, LObidD}
状态空间 (23维):
状态变量 维度 描述
Xt 1 做市商现金头寸
Yt 1 库存(整数)
st = p(a)-p(b) 1 买卖价差
n(ζ)t / q(ζ)t 2×2 相对队列位置和大小
λ(i)t d Hawkes过程强度
Ht-τH:t 变长 近期事件历史窗口
Hawkes强度方程 (指数核):
dλ(i)(t) = γi(µi - λ(i)(t))dt + αi dN(i)t
λ(i)(t) = µi + αi ∫₀ᵗ e^(-γi(t-s)) dN(i)s
- 模拟环境超参数
基础设置
T = 300 # 交易周期(秒)
Δτ = 0.1 # 决策间隔(秒)
initial_cash = $2000
库存惩罚参数
η = 10 # 运行库存惩罚
κ = ? # 终端清算惩罚
交易成本
transaction_cost = 1bps (0.01%)
采样分布
Y ~ N(0, 4) # 库存分布
Pmid ~ N(200, 100) # 中间价分布
Spreads ~ Geometric(0.8) × 0.01
- 训练数据需求
训练集: 60个episode的模拟轨迹
测试集: 5分钟OOS测试,多个episode
数据量: 约数百万个时间步的交互数据
复现步骤流程
阶段1: LOB模拟器搭建
1.1 实现Hawkes过程生成器
关键组件
class HawkesProcess:
def init (self, mu, alpha, gamma):
self.mu = mu # 基强度
self.alpha = alpha # 激励参数
self.gamma = gamma # 衰减参数
def simulate_intensity(self, events):
# 实现dλ(i)(t)方程
pass
def generate_arrivals(self, T):
# 使用Thinning算法生成点过程
pass
1.2 实现订单簿动态
附录A中的状态变量动态
- dXt: 现金变化
- dYt: 库存变化
- dp(ζ)t: 最佳价格变化
- dq(ζ)t: 最佳报价量变化
- dq(ζ,D)t: 第二档报价量变化
- dn(ζ)t: 队列优先级变化
- dP(mid)t: 中间价变化
阶段2: 强化学习框架
2.1 状态空间构建
state = {
'cash': Xt,
'inventory': Yt,
'spread': p(a)t - p(b)t,
'queue_position': n(ζ)t / q(ζ)t,
'hawkes_intensities': λ(i)t,
'event_history': Ht-τH:t
}'
2.2 动作空间 (限制版)
Arestricted = {
LO(a)T: 在卖方最优价下单,
LO(b)T: 在买方最优价下单,
CO(a)T: 取消卖方订单,
CO(b)T: 取消买方订单
}
2.3 奖励结构
reward = -η * Y² * Δt # 库存惩罚
-
ΔXti # 现金变化
-
Δ(Yti × Pmid) # 库存盯市
-
K(S(τj), ψj) # 干预成本
-
0.0001 × |YT| × Pmid # 交易费用(1bps)
阶段3: PPO + Self-Imitation Learning训练
3.1 双网络架构
决策网络 dχ (何时干预)
class DecisionNetwork(nn.Module):
输入: 状态St
输出: dt ∈ {0, 1}
3.2 PPO目标函数
L_PPO(χ, ξ) = E[min(rt × Ât, clip(rt, 1-ε, 1+ε) × Ât)]
3.3 自模仿学习(SIL)增强
L_SIL = -E_{(st,at,Rt)∼Bgood} [1{Rt > Vθ(st)} × log π(at|st)]
L_total = L_PPO + β_SIL × L_SIL - β_entropy × H(π)
阶段4: Deep Galerkin Method (对比方法)
4.1 三个神经网络
ϕ(t, St) = ϕθ(t, St) # 值函数
d(t, St) = dχ(t, St) # 二元控制
u(t, St) = uξ(t, St) # 干预策略
4.2 损失函数
L_DGM = L_interior + L_boundary
L_interior = E[(1-dχ) × Lϕ + dχ × M_uξϕ]²
L_boundary = E[|ϕ(T, ST) - (XT + YT×Pmid - κY²)|]²
预期性能指标
方法 设置 Sharpe比率 平均绝对库存
DGM Poisson LOB 4.54 0.891
DGM Hawkes(MO only) 0.78 21.56
DGM Full 12D Hawkes 不收敛 不收敛
PPO+SIL Exponential Kernel 31.54 低
PPO+SIL Power-Law Kernel 28.81 低
Probabilistic Agent Exponential 7.73 中
关键注意事项
脉冲控制约束: 做市商无法在每个事件都响应,论文使用0.1秒决策间隔
库存惩罚敏感性:
η=0.1/1.0 → "Pump & Dump"策略
η=10 → 正常做市策略
η=100 → 过度保守
交易成本阈值: 策略在2bps以上成本时失效
核函数选择: 指数核可能导致非经济策略,幂律核更稳定
状态消融: 强度λ和相对位置n/q特征至关重要,移除后策略失效
论文 vs 代码实现对比分析
一、核心差异总结
● 1. 核函数实现严重偏离论文 ⚠️
| 组件 | 论文设计 | 代码实现 | 偏离程度 |
|---|---|---|---|
| ExponentialKernel | 在Hawkes过程中建模事件间时间衰减K(Δt) = α × exp(-β × Δt) | 只是在MLP输出上乘以exp(-β)与时间无关 | 🔴 严重偏离 |
| PowerLawKernel | 长程依赖建模K(Δt) = α × (Δt+c)^(-β) | 对特征本身做幂律变换不是时间衰减函数 | 🔴 严重偏离 |
| 事件历史编码 | 自核函数计算Σ αᵢⱼ × exp(-βⱼ × Δtₖ) | 完全缺失 | 🔴 核心缺失 |
代码问题示例:
当前实现 - 错误
class ExponentialKernel(nn.Module):
def forward(self, x):
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
这只是在特征上乘以一个常数!与时间无关
x = x * torch.exp(-self.beta * torch.ones_like(x)) # ❌ 错误
return self.fc_out(x)
- 状态表示不完整 🟡
| 状态组件 | 论文要求 | 代码实现 |
|---|---|---|
| 事件历史 | 计算所有历史事件的核函数加权和 | ❌ 缺失 |
| 最近事件时间 | Δt₁,...,Δtₖ (距离上次事件的时间) | ❌ 缺失 |
| 现金/库存 | ✓ | ✓ |
| 订单簿状态 | ✓ | ✓ |
| Hawkes强度 | ✓ | ✓ |
- 强化学习算法简化 🟡
| 算法组件 | 论文 | 代码 | 影响 |
|---|---|---|---|
| PPO clip | ✓ | ✓ | 正确 |
| GAE | ✓ | ✓ | 正确 |
| SIL | ✓ | ✓ | ⚠️ 简化版 |
| Actor-Critic | 分离决策/动作网络 | ✓ | 正确 |
二、代码质量问题
● 1. 成交概率固定 (line 177-184)
if np.random.random() < 0.8: # 硬编码80%成交率
- 应该根据订单簿深度、价差动态计算
- 归一化方式简陋 (line 156-169)
obs = [
self.cash / 1000000, # 硬编码除数
self.inventory / 100,
] - 应该使用running statistics
- 奖励计算未按论文规范
- 缺少中间价格变化的proper计算
- 库存惩罚系数固定,未按论文公式
三、关键性能问题
line 196 - 效率低下
n_steps = int(self.market_cfg.decision_interval / self.hawkes_cfg.dt)
for _ in range(n_steps): # 0.1/0.001 = 100次循环
event = self.hawkes.simulate_step(self.hawkes_cfg.dt)
每次step调用100次Hawkes模拟,严重影响训练速度
🚀 提升空间和改进方案
优先级1:修复核函数实现 🔴
问题:当前核函数与时间无关,完全偏离论文
改进方案:
class TrueExponentialKernel(nn.Module):
"""
论文正确的实现
"""
def init (self, n_events: int = 6):
super().init ()
每个事件对有自己的可学习参数
self.alpha = nn.Parameter(torch.randn(n_events, n_events) * 0.1)
self.beta = nn.Parameter(torch.ones(n_events) * 1.0)
def forward(self, event_history: torch.Tensor,
current_time: float) -> torch.Tensor:
"""
计算事件历史对当前强度的影响
Args:
event_history: [(event_type, time), ...]
current_time: 当前时间
Returns:
intensity_contribution: 每个事件类型的强度贡献
"""
contribution = torch.zeros(6)
for event_type, event_time in event_history:
dt = current_time - event_time
for j in range(6):
# λ_j += α_{ij} × exp(-β_j × Δt)
contribution[j] += self.alpha[event_type, j] * \
torch.exp(-self.beta[j] * dt)
return contribution
在环境中使用:
class MarketMakingEnv:
def init (self, ...):
self.event_history = [] # [(event_type, time), ...]
self.kernel = TrueExponentialKernel()
def _get_obs(self) -> np.ndarray:
# 计算事件历史特征
kernel_features = self.kernel(self.event_history, self.time)
obs = [
self.cash / self.running_cash_std,
self.inventory / 100,
self.lob.spread / 0.1,
self.lob.mid_price / 200,
# ... 其他基础特征
]
# 添加核函数特征
obs.extend(kernel_features.cpu().numpy())
return np.array(obs, dtype=np.float32)
优先级2:添加事件历史编码 🔴
论文要求维护所有历史事件的时间戳:
class MarketMakingEnv:
def init (self, ...):
self.event_history = deque(maxlen=1000) # 保存最近1000个事件
self.last_event_times = [0.0] * 6 # 每种类型的最后事件时间
def step(self, action: int):
old_mid = self.lob.mid
# Hawkes事件模拟
for _ in range(n_steps):
event = self.hawkes.simulate_step(dt)
if event:
self.lob.apply_event(event)
# 记录事件
self.event_history.append((event, self.time))
self.last_event_times[event.value] = self.time
# 计算时间差特征
time_deltas = [self.time - t for t in self.last_event_times]
# 新观察包含时间差
obs = self._get_obs()
obs = np.concatenate([obs, time_deltas])
优先级3:动态成交概率 🟡
def _check_fills(self) -> Tuple[int, float]:
"""根据订单簿状态动态计算成交概率"""
filled = 0
pnl = 0.0
# 计算我们的订单优先级
if self.our_bid_active:
# 成交概率 = 我们的量 / 总买量
priority = 1.0 / self.lob.bid_vol
fill_prob = min(0.95, priority * 2.0) # 动态概率
if np.random.random() < fill_prob:
filled = 1
pnl = -self.lob.bid_price * self.market_cfg.contract_size
self.our_bid_active = False
self.inventory += 1
# 类似处理ask...
优先级4:Running Normalization 🟢
class RunningNormalizer:
"""运行时归一化"""
def init (self, shape: Tuple[int], clip: float = 5.0):
self.shape = shape
self.count = 0
self.mean = np.zeros(shape)
self.var = np.ones(shape)
self.clip = clip
def update(self, x: np.ndarray):
self.count += 1
delta = x - self.mean
self.mean += delta / self.count
self.var += delta * (x - self.mean)
def normalize(self, x: np.ndarray) -> np.ndarray:
std = np.sqrt(self.var / (self.count + 1e-8))
return np.clip((x - self.mean) / (std + 1e-8),
-self.clip, self.clip)
在环境中使用
class MarketMakingEnv:
def init (self, ...):
self.normalizer = RunningNormalizer((self.state_dim,))
def _get_obs(self):
obs = self._compute_raw_obs()
self.normalizer.update(obs)
return self.normalizer.normalize(obs)
优先级5:优化Hawkes模拟 🟢
class VectorizedHawkesProcess:
"""向量化加速的Hawkes过程"""
def init (self, config: HawkesConfig):
self.config = config
预计算衰减矩阵
self.decay_matrix = np.diag(config.gamma)
self.excitation_matrix = config.alpha
def simulate_vectorized(self, dt: float, n_steps: int) -> List[EventType]:
"""一次模拟多步,减少循环开销"""
events = []
# 批量更新强度
intensities = np.tile(self.config.mu, (n_steps, 1))
for t in range(n_steps):
# 检查是否发生事件
total_intensity = intensities[t].sum()
if self.rng.random() < total_intensity * dt:
# 选择事件类型
probs = intensities[t] / total_intensity
event_type = self.rng.choice(6, p=probs)
events.append(EventType(event_type))
# 更新后续时间步的强度
if t < n_steps - 1:
decay = np.exp(-self.config.beta * dt)
intensities[t+1:] += self.excitation[event_type] * decay
return events
优先级6:完整的奖励函数 🟢
def step(self, action: int):
... 执行动作 ...
# 正确的奖励计算 (按照论文)
new_mid = self.lob.mid
dt = self.market_cfg.decision_interval
# 1. 现金变化 (交易盈亏)
trade_pnl = cash_flow
# 2. 盯市损益 (未实现盈亏)
mtm_pnl = self.inventory * (new_mid - old_mid) * contract_size
# 3. 库存风险惩罚 (按论文公式3.8)
# r_inv = -η × q² × Δt
inventory_penalty = -self.market_cfg.eta * (self.inventory ** 2) * dt
# 4. 交易成本
transaction_cost = -fills * self.market_cfg.transaction_cost
# 5. 价格变化惩罚 (防止方向性赌博)
# 如果持有正库存且价格下跌,给予额外惩罚
price_change_penalty = 0
if self.inventory != 0:
price_change = (new_mid - old_mid) / old_mid
price_change_penalty = -0.5 * abs(price_change) * abs(self.inventory)
# 总奖励
reward = trade_pnl + mtm_pnl + inventory_penalty + \
transaction_cost + price_change_penalty
# 归一化
reward = reward / 100.0
return next_state, reward, done, info
📋 实施路线图
● 阶段1:修复核心实现(1-2周)
- ✅ 重写核函数 - 实现真正的Exponential和PowerLaw核
- ✅ 添加事件历史 - 维护历史事件队列和时间戳
- ✅ 修复状态表示 - 添加核函数特征和时间差特征
阶段2:优化训练流程(1周)
- ✅ 动态成交概率 - 根据订单簿状态计算
- ✅ Running Normalization - 运行时统计归一化
- ✅ 向量化Hawkes - 加速模拟
阶段3:改进奖励和评估(1周)
- ✅ 完整奖励函数 - 按论文公式3.8实现
- ✅ 添加更多指标 - 最大回撤、胜率、盈亏比
- ✅ 可视化工具 - 绘制学习曲线、持仓变化
阶段4:高级优化(可选)
- 🔄 多智能体训练 - 训练多个agent相互竞争
- 🔄 Curriculum Learning - 从简单市场逐渐增加复杂度
- 🔄 Meta-Learning - 快速适应不同市场条件
🎯 预期改进效果
| 改进项 | 预期提升 | 难度 |
|---|---|---|
| 修复核函数 | 显著 (50-100%) | 高 |
| 事件历史编码 | 显著 (30-50%) | 中 |
| 动态成交概率 | 中等 (15-25%) | 低 |
| Running归一化 | 中等 (10-20%) | 低 |
| 优化Hawkes | 速度提升3-5x | 中 |
| 完整奖励函数 | 中等 (20-30%) | 低 |
📖 具体代码示例
完整的改进环境类
class ImprovedMarketMakingEnv:
"""
改进版做市商环境 - 符合论文规范
"""
def init (self, hawkes_cfg: HawkesConfig,
market_cfg: MarketConfig,
kernel_type: str = 'exponential'):
self.hawkes_cfg = hawkes_cfg
self.market_cfg = market_cfg
# 核函数
if kernel_type == 'exponential':
self.kernel = ExponentialKernel(n_events=6)
elif kernel_type == 'powerlaw':
self.kernel = PowerLawKernel(n_events=6)
# 事件历史 (论文第3.2节)
self.event_history = deque(maxlen=1000)
self.last_event_times = np.zeros(6)
# 归一化器
self.obs_normalizer = RunningNormalizer((32,)) # 更大的状态维度
self.reward_normalizer = RunningNormalizer(())
self.reset()
def reset(self) -> np.ndarray:
"""重置环境"""
np.random.seed(self.seed)
self.cash = self.market_cfg.initial_cash
self.inventory = 0
self.hawkes = HawkesProcess(self.hawkes_cfg)
self.lob = LimitOrderBook()
self.time = 0.0
# 重置事件历史
self.event_history.clear()
self.last_event_times = np.zeros(6)
return self._get_obs()
def _get_obs(self) -> np.ndarray:
"""
构建完整的状态表示
包含:
1. 基础特征 (cash, inventory, spread, price, volume)
2. Hawkes强度 (6维)
3. 核函数特征 (6维) - 事件历史的影响
4. 时间差特征 (6维) - 距离每种事件最后发生的时间
"""
# 1. 基础特征 (8维)
base_features = np.array([
self.cash,
self.inventory,
self.lob.ask_price - self.lob.bid_price, # spread
self.lob.mid,
self.lob.bid_price,
self.lob.ask_price,
self.lob.bid_vol,
self.lob.ask_vol,
], dtype=np.float32)
# 2. Hawkes强度 (6维)
hawkes_intensities = self.hawkes.get_intensities()
# 3. 核函数特征 (6维)
kernel_features = self.kernel.compute(
self.event_history,
self.time
).numpy()
# 4. 时间差特征 (6维)
time_deltas = self.time - self.last_event_times
# 拼接所有特征
obs = np.concatenate([
base_features, # 8
hawkes_intensities, # 6
kernel_features, # 6
time_deltas, # 6
]) # 总共26维
# 运行时归一化
self.obs_normalizer.update(obs)
return self.obs_normalizer.normalize(obs)
def step(self, action: int) -> Tuple[np.ndarray, float, bool, dict]:
"""执行一步"""
old_mid = self.lob.mid
old_inventory = self.inventory
# 模拟Hawkes过程
n_steps = int(self.market_cfg.decision_interval / self.hawkes_cfg.dt)
for _ in range(n_steps):
event = self.hawkes.simulate_step(self.hawkes_cfg.dt)
if event:
self.lob.apply_event(event)
# 记录事件
self.event_history.append((event.value, self.time))
self.last_event_times[event.value] = self.time
# 执行动作
self._execute_action(action)
# 检查成交
fills, cash_flow = self._check_fills()
self.cash += cash_flow
# 计算奖励 (按照论文公式3.8)
reward = self._compute_reward(fills, cash_flow, old_mid)
# 归一化奖励
self.reward_normalizer.update(np.array([reward]))
normalized_reward = self.reward_normalizer.normalize(np.array([reward]))[0]
self.time += self.market_cfg.decision_interval
done = self.time >= self.hawkes_cfg.T
info = {
'time': self.time,
'cash': self.cash,
'inventory': self.inventory,
'fills': fills,
'mid_price': self.lob.mid,
'raw_reward': reward, # 原始奖励
'normalized_reward': normalized_reward, # 归一化奖励
}
return self._get_obs(), normalized_reward, done, info
def _compute_reward(self, fills: int, cash_flow: float,
old_mid: float) -> float:
"""
按照论文计算奖励
R = r_trade + r_mtm + r_inv + r_tc
"""
new_mid = self.lob.mid
dt = self.market_cfg.decision_interval
contract_size = self.market_cfg.contract_size
# 1. 交易利润
r_trade = cash_flow
# 2. 盯市损益
r_mtm = self.inventory * (new_mid - old_mid) * contract_size
# 3. 库存风险惩罚 (论文公式3.8)
r_inv = -self.market_cfg.eta * (self.inventory ** 2) * dt
# 4. 交易成本
r_tc = -fills * self.market_cfg.transaction_cost
# 总奖励
reward = r_trade + r_mtm + r_inv + r_tc
return reward
def _check_fills(self) -> Tuple[int, float]:
"""
动态成交概率
根据订单簿深度和位置计算
"""
filled = 0
pnl = 0.0
if self.our_bid_active:
# 成交概率与订单优先级相关
# 如果我们的订单在队列前端,成交概率更高
priority = 1.0 / max(1, self.lob.bid_vol)
base_prob = 0.3 # 基础概率
# 动态概率 (可以根据价差、波动率调整)
fill_prob = min(0.95, base_prob + priority * 0.5)
if np.random.random() < fill_prob:
filled = 1
pnl = -self.lob.bid_price * self.market_cfg.contract_size
self.our_bid_active = False
self.inventory += 1
if self.our_ask_active:
priority = 1.0 / max(1, self.lob.ask_vol)
base_prob = 0.3
fill_prob = min(0.95, base_prob + priority * 0.5)
if np.random.random() < fill_prob:
filled += 1
pnl += self.lob.ask_price * self.market_cfg.contract_size
self.our_ask_active = False
self.inventory -= 1
return filled, pnl
🔑 核心要点总结
● ⚠️ 最严重的问题
- 核函数实现错误 - 与时间无关,完全偏离论文
- 事件历史缺失 - 论文核心机制未实现
- 状态表示不完整 - 缺少核函数特征和时间差
✅ 实现正确的部分
- ✓ Hawkes过程模拟
- ✓ PPO+SIL算法框架
- ✓ 决策网络/动作网络分离架构
- ✓ 基础环境逻辑
🚀 快速见效的改进 (低投入高回报)
- 添加事件历史 - 预期提升30-50%
- 动态成交概率 - 预期提升15-25%
- Running归一化 - 预期提升10-20%
📊 性能对比预测
| 版本 | 预期Sharpe | 预期收益率 | 实现难度 |
|---|---|---|---|
| 当前代码 | 0.5-0.8 | 基准 | - |
| +事件历史 | 0.7-1.1 | +30-50% | 中 |
| +核函数修复 | 1.0-1.5 | +50-100% | 高 |
| +所有改进 | 1.5-2.2 | +100-150% | 高 |
需要我帮你实现某个具体的改进吗?比如:
- 实现真正的ExponentialKernel
- 添加事件历史编码
- 优化成交概率计算
- 或者其他优先级更高的改进