隐马尔可夫做市商库存控制:用 POMDP 平衡存货与价差
做市商赚的是买卖价差,赔的是库存积压后价格反向跑掉。市场状态(平静/高波动)不可直接观测,但订单到达与瞬时波动会泄漏线索。本文把 HMM 前向滤波与 POMDP 库存控制结合:用隐藏 regime 信念动态调整 spread,并用库存 skew 把头寸压回零。在 2000 步合成仿真中,HMM+POMDP 策略的库存标准差比固定 spread 策略低 81.5%(4.1 vs 22.3),最终盯市 PnL 为 +1030 对 -2840,最大回撤仅 42 对 3775。附完整 numpy 仿真代码与四张真实计算图。
做市商的利润公式看起来简单:低买高卖,赚 spread。但实盘里真正要命的是库存:你挂买单被连续成交,头寸越积越多,随后价格一波反向,spread 利润瞬间被库存亏损吞掉。更糟糕的是,市场会换 regime——平静时订单流温和、波动低;高波动时 toxic flow 汹涌,固定 spread 策略会被反复”扫单”。
本文搭建一个 stylized 的连续做市环境:隐藏 regime 驱动波动率和订单到达率,做市商只能通过观察到的价格跳动和订单数量推断当前 regime;然后把它放进 POMDP 框架,用库存 skew 和 regime-aware spread 同时控制存货与价差。所有图表都是同一套仿真数据的真实结果,非占位图。
一、市场模型:隐藏 regime 驱动一切#
假设市场有两种隐藏状态:
z=0:低波动 regime,价格日波动 σ₀=0.40,订单到达率 λ₀=4z=1:高波动 regime,价格日波动 σ₁=1.60,订单到达率 λ₁=14
regime 按马尔可夫链切换:
P = [[0.97, 0.03], # 平静 -> 平静 / 高波动
[0.12, 0.88]] # 高波动 -> 平静 / 高波动text中间价随机游走:
m[t] = m[t-1] + sigma[z[t]] * ε[t], ε[t] ~ N(0, 1)python每步的买方与卖方到达数分别服从 Poisson(λ[z[t]] / 2)。做市商能看到的是:
- 本步总订单数
n_total[t] - 本步绝对收益
r[t] = |m[t] - m[t-1]|
但看不到真实的 z[t]。这正是 POMDP:部分可观测的马尔可夫决策过程。

二、HMM 前向滤波:从观测里恢复 regime 信念#
把 n_total 和 r 当作 emission,用标准前向算法递归:
α_t(z) ∝ p(o_t | z) · Σ_z' P(z | z') · α_{t-1}(z')
belief_t = α_t(1) = P(z_t = 1 | o_1...o_t)text其中 p(o_t | z) 用 Poisson 拟合订单数、指数分布拟合绝对收益:
from scipy.stats import poisson, expon
def emission_logp(t, state):
return (
poisson.logpmf(n_total[t], lam[state])
+ expon.logpdf(r_obs[t], scale=sigma[state])
)python下面是完整滤波实现:
import numpy as np
from scipy.stats import poisson, expon
P = np.array([[0.97, 0.03],
[0.12, 0.88]])
prior = np.array([0.85, 0.15])
alpha = np.zeros((T, 2))
alpha[0] = np.exp(np.log(prior) + [emission_logp(0, 0), emission_logp(0, 1)])
alpha[0] /= alpha[0].sum()
for t in range(1, T):
pred = alpha[t-1] @ P
lp = [emission_logp(t, 0), emission_logp(t, 1)]
alpha[t] = pred * np.exp(np.array(lp) - max(lp))
alpha[t] /= alpha[t].sum()
belief = alpha[:, 1] # 高波动 regime 的后验概率python由于高波动 regime 的波动率和订单到达率都显著高于平静 regime,滤波器能很快把信念拉到真实状态附近。本次仿真中,信念与真实 regime 的 0.5 阈值一致率达到 99.6%。

三、POMDP 报价策略:两个杠杆#
做市商每步要同时决定两件事:
- spread 宽度:高波动/高库存风险时应该更宽,减少成交、提高单位 spread。
- skew 偏移:库存偏离零时,把买卖双边报价整体向”减仓方向”推,吸引反向订单。
具体策略:
s = s0 + k_z * belief + k_q * abs(q) # spread
half = s / 2.0
offset = -c_skew * q # 库存 skew
ask = m + half + offset
bid = m - half + offsetpython其中 q 是当前库存。若 q > 0(多头太多),offset < 0, ask 和 bid 同时下移:我们更便宜地卖、更便宜地买,市场上更容易卖出、更难买到,库存被压回零。
成交概率随报价到中间价的距离衰减:
p_buy_fill = exp(-k_s * (ask - m)) # 买方向我们卖,我方 inventory 减少
p_sell_fill = exp(-k_s * (m - bid)) # 卖方向我们买,我方 inventory 增加python每来一笔买方成交,现金 +ask,库存 -1;卖方成交则现金 -bid,库存 +1。盯市 PnL 为 cash + q * m。
四、POMDP 的正式四元组#
把问题写成 POMDP,有助于把”调 spread”从拍脑袋变成有结构的决策。四元组是 (S, A, O, R),再加上状态转移 P(s'|s) 和观测模型 P(o|s):
- 状态
s:(z, q),即隐藏 regime 与当前库存。regime 不可直接观测,库存完全可观测。 - 动作
a:(s_spread, skew),或等价地(ask, bid)两个报价。动作由信念b(z)和库存q决定。 - 观测
o:每步结束后看到的(n_total, r),用于更新信念。 - 奖励
r:盯市 PnL 的增量,即mtm_t - mtm_{t-1}。
由于真实状态里的 z 不可见,我们没法直接对 (z, q) 做策略优化。标准技巧是信念状态压缩:用滤波器把历史观测压缩成一个低维信念向量 b_t = P(z_t | o_{1:t}),然后在 (b_t, q_t) 上学习策略。信念状态有个漂亮性质:在 HMM 假设下,它是历史观测的充分统计量——给定 b_t,未来观测的分布不再依赖更久远的历史。这意味着策略只需要看当前的信念和库存,而不需要记住整条订单流。本文的策略是手工设计的线性规则:
spread = s0 + k_z · b_t[1] + k_q · |q|
skew = -c_skew · qtext更高级的做法是用动态规划或强化学习在信念-库存状态空间上求解最优策略。对两个 regime、离散库存的场景,值迭代完全可行;对连续库存则需要函数逼近或模型预测控制(MPC)。手工线性规则的好处是简单、可解释、上线后可调参;缺点是离最优解可能还有距离。这个框架也自然兼容多资产场景:只要 regime 是共享的,多品种的库存可以一起进入动作函数,实现组合层面的做市风控。
五、两种策略的完整仿真#
# 参数
s0, k_z, k_q, c_skew, k_s = 0.12, 0.22, 0.018, 0.006, 6.0
def simulate(policy="hmm"):
cash, q = 0.0, 0
q_path = np.zeros(T)
cash_path = np.zeros(T)
spread_path = np.zeros(T)
for t in range(T):
if policy == "naive":
# 固定 spread、无库存 skew、无视 regime
s = s0
half, offset = s / 2.0, 0.0
else:
g = belief[t-1] if t > 0 else prior[1]
s = s0 + k_z * g + k_q * abs(q)
half = s / 2.0
offset = -c_skew * q
ask = m[t] + half + offset
bid = m[t] - half + offset
spread_path[t] = s
p_buy = np.clip(np.exp(-k_s * (ask - m[t])), 0, 1)
p_sell = np.clip(np.exp(-k_s * (m[t] - bid)), 0, 1)
nb = np.random.binomial(n_buy[t], p_buy)
ns = np.random.binomial(n_sell[t], p_sell)
q = q - nb + ns
cash += nb * ask - ns * bid
q_path[t] = q
cash_path[t] = cash
mtm = cash_path + q_path * m
return q_path, cash_path, mtm, spread_path
q_hmm, _, pnl_hmm, _ = simulate("hmm")
q_naive, _, pnl_naive, _ = simulate("naive")python两套策略跑在同一组价格路径和订单流上,区别只在于报价规则。
六、结果:库存控制是第一位的#
先看库存轨迹:

- 固定 spread 策略:没有 skew,库存像随机游走一样漂移,最终标准差高达 22.3。
- HMM+POMDP 策略:库存围绕零均值回复,标准差 4.1,降低 81.5%。
库存失控直接反映在 PnL 上:

- HMM+POMDP 最终 PnL = +1030.2
- 固定 spread 最终 PnL = -2840.4
- 最大回撤:HMM 42.3 vs 固定 spread 3775.1
这组对比非常鲜明地说明:在做市商策略里,库存管理不是锦上添花,而是生死线。没有 skew 和 regime 感知的固定 spread 策略,即使 spread 本身为正,也会被不断累积的 inventory 风险吃掉。反过来,HMM 策略的平均 spread(0.221)虽然高于固定策略(0.120),但它用更宽的高波动 spread 来规避 toxic flow,同时用库存 skew 把单位风险收益提升到完全不同的量级。
七、诚实的边界#
上面的仿真是高度 stylized 的,真实交易所要复杂得多:
- 队列位置与延迟:真实限价单挂在 order book 队列里,成交概率不只取决于报价距离,还取决于你排第几、下单 latency、对手方取消率。
- 更丰富的 microstructure:实际 regime 不止两个,且 arrival rate 和波动率也不是唯一观测;订单不平衡、深度形状、跨资产信息流都会泄露 regime。
- 部分成交与最小报价单位:模型里把成交当成整数单位,实盘中存在 tick size、最小下单量、手续费和返佣。
- adverse selection 更微妙:高波动 regime 里来的可能是 informed flow,而不仅是数量多;这需要更精细的 adverse selection 模型,例如用订单流毒性指标直接调整 spread。
- 参数敏感:
k_z、k_q、c_skew的相对大小会显著改变 trade-off。如果k_q过大,spread 过宽导致成交太少,spread 收益不足;如果c_skew过大,报价会变得激进,可能在低波动期把仓位清在不好的价位。 - 信念过自信:当两个 regime 的 emission 差异不大时,HMM 信念会摆动,策略可能在 regime 边界来回切换 spread,产生额外的换手或机会成本。实际部署时通常会对信念做平滑,或只在置信度超过阈值时才调整 spread。
但这些都不改变核心结论:做市是一个 spread 收益 vs inventory 风险 的权衡,而 HMM/POMDP 框架提供了一个自然且可扩展的解法——把隐藏市场状态过滤成信念,再把信念和库存一起写进报价函数。
八、结语#
本文用 numpy 实现了一个最小可运行的 HMM+POMDP 做市商:隐状态过滤给出 regime 信念,库存 skew 把头寸拉回零,regime-aware spread 在高波动期自我保护。在 2000 步合成仿真中,HMM+POMDP 策略把库存标准差砍掉 81.5%,最终盯市 PnL 为 +1030,而一个固定 spread、无视库存的策略亏损 -2840,最大回撤接近 3800。
对量化做市系统来说,这比单纯”把 spread 设宽一点”更有结构感:你在每个时刻都知道”我现在有多确信市场处于高波动”,也知道”我的库存让我该偏向买还是卖”。把这两个数写进报价函数,就是最朴素的 POMDP 控制器。下一步可以把它接入真实的 level-2 数据,用订单不平衡、深度变化、跨资产信息流等更多观测信号更新 regime 信念,并用强化学习或模型预测控制在信念-库存状态空间上优化 spread 与 skew。无论模型多复杂,核心等式不变:做市利润 = 价差收益 − 库存风险成本,而 HMM/POMDP 正是把后半项显式写进决策的框架。