4. 库存管理模型:基于马尔可夫链的库存预测,最优库存水平计算
做市商最怕什么?怕库存被砸穿,也怕库存被拉爆。
说白了,库存管理就是高频做市里的“命门”。你策略再牛,订单流预测再准,库存一失控,前面赚的全得吐回去。我个人习惯把库存管理比作“走钢丝”——库存高了怕方向反转,库存低了又怕错失行情。
这一章,我们来聊聊怎么用马尔可夫链预测库存变化,以及如何算出那个“刚刚好”的最优库存水平。
4.1 为什么是马尔可夫链?
先问个问题:库存的变化有没有规律?
有,但很难用简单的线性模型去拟合。因为订单流是随机的,成交也是随机的。你想想看,下一秒的库存状态,其实只取决于当前的状态——你手里有多少货,以及接下来会成交多少。
这不就是马尔可夫链的典型场景吗?
马尔可夫链的核心假设是:未来只与现在有关,与过去无关。在高频做市里,这个假设基本成立。因为订单簿的微观结构变化极快,几毫秒前的库存状态对下一秒的影响微乎其微。
核心观点: 马尔可夫链把库存变化建模成一个状态转移过程。每个库存水平是一个“状态”,成交或撤单就是“转移事件”。
4.2 库存状态的离散化
实际交易中,库存是连续的——你可以持有100.5个ETH。但马尔可夫链要求状态是离散的。所以第一步,得把库存切分成若干档位。
我在项目中遇到过一个问题:档位切得太细,状态空间爆炸,计算量扛不住;切得太粗,精度又不够。后来我总结了一个经验:按最小交易单位的整数倍来切。
举个例子,假设你交易的是BTC永续合约,最小交易单位是0.001 BTC。那你的库存状态可以设为:
- 状态0:空仓(0 BTC)
- 状态1:持有0.001 BTC
- 状态2:持有0.002 BTC
- ……
- 状态N:持有N * 0.001 BTC
上限N怎么定?看你的风险敞口限制。比如你规定最大库存不能超过10 BTC,那N就是10000。嗯,这个状态空间确实不小,但别急,后面有办法优化。
4.3 转移概率矩阵的构建
有了状态,接下来就是算“从状态i跳到状态j的概率”。这个矩阵就是马尔可夫链的核心——转移概率矩阵P。
怎么算?两种方式:
- 历史统计法: 回看过去一段时间,统计从库存i变成库存j的次数,除以总次数。
- 订单流模型法: 根据当前订单簿的买卖挂单量,估算成交概率,再推导转移概率。
我个人更推荐第二种。因为高频环境变化太快,历史统计往往滞后。用实时订单流来算,更贴近当前市场。
来看一段伪代码,展示怎么构建转移矩阵:
# 假设库存状态从0到N
N = 100 # 最大库存水平
P = [[0.0 for _ in range(N+1)] for _ in range(N+1)]
# 遍历每个状态
for i in range(N+1):
# 计算从状态i转移到其他状态的概率
# buy_prob: 下一笔成交是买入的概率(库存增加)
# sell_prob: 下一笔成交是卖出的概率(库存减少)
# idle_prob: 没有成交的概率(库存不变)
# 从订单簿数据中实时计算
buy_prob = orderbook.bid_volume / (orderbook.bid_volume + orderbook.ask_volume)
sell_prob = orderbook.ask_volume / (orderbook.bid_volume + orderbook.ask_volume)
idle_prob = 1.0 - (buy_prob + sell_prob) # 简化处理
# 填充转移概率
if i > 0:
P[i][i-1] = sell_prob # 库存减少
P[i][i] = idle_prob # 库存不变
if i < N:
P[i][i+1] = buy_prob # 库存增加
小技巧: 实际生产中,转移概率不是静态的。我习惯每100毫秒重新计算一次P矩阵,用滑动窗口的方式。这样既能跟上市场变化,又不会太耗CPU。
4.4 稳态分布与库存预测
有了转移矩阵,我们就可以做两件事:
- 短期预测: 给定当前库存状态,预测未来几步的库存分布。
- 长期预测: 计算稳态分布,看看如果一直做市,库存最终会收敛到什么水平。
短期预测很简单,就是矩阵乘法。假设当前库存状态向量是v₀(一个one-hot向量),那么t步后的状态分布就是:
vₜ = v₀ × Pᵗ
稳态分布呢?就是解方程 π = π × P,其中π是稳态概率向量。这个方程的解,告诉你长期来看,库存落在每个档位的概率是多少。
我曾经踩过一个坑:直接用线性代数库去解稳态分布,结果矩阵太大,算到超时。后来改用幂迭代法,每次只做矩阵向量乘法,收敛很快。嗯,这里要注意,幂迭代法需要保证矩阵是遍历的——也就是说,从任何状态都能到达任何其他状态。
4.5 最优库存水平的计算
预测库存只是第一步。我们真正想要的是:当前库存应该控制在什么水平,才能让预期收益最大、风险最小?
这就涉及到最优库存水平的计算了。我常用的方法是“风险调整后的期望收益最大化”。
定义几个变量:
- Q:当前库存量
- V(Q):持有库存Q的“价值函数”,包括预期收益和风险惩罚
- γ:风险厌恶系数,越大越厌恶风险
- σ²:库存价值的波动率
那么最优库存水平Q*满足:
Q* = argmax [ E[ΔP×Q] - γ × σ² × Q² ]
其中E[ΔP×Q]是预期库存收益,γ×σ²×Q²是风险惩罚项。
这个公式看着简单,但实际用起来有几个坑:
避坑指南: 我曾经直接把波动率σ设成历史波动率,结果在市场突变时,最优库存水平完全失效。后来我改成用实时波动率,结合马尔可夫链预测的库存分布来动态调整γ参数。效果好了很多。
来看一个实际的计算流程:
def optimal_inventory(current_inv, P, gamma, sigma):
"""
计算最优库存水平
current_inv: 当前库存
P: 转移概率矩阵
gamma: 风险厌恶系数
sigma: 波动率
"""
# 1. 用马尔可夫链预测未来N步的库存分布
N = 10 # 预测步数
v = [0] * len(P)
v[current_inv] = 1.0
future_dist = v
for _ in range(N):
future_dist = mat_vec_mul(future_dist, P)
# 2. 计算每个库存水平的期望收益
expected_profit = 0
for inv_level, prob in enumerate(future_dist):
# 假设每单位库存的预期收益是spread的一半
expected_profit += prob * inv_level * (spread / 2)
# 3. 计算风险惩罚
risk_penalty = gamma * (sigma ** 2) * (current_inv ** 2)
# 4. 最优库存就是让 expected_profit - risk_penalty 最大的那个
# 这里简化处理,实际需要遍历或求导
optimal = current_inv
best_value = expected_profit - risk_penalty
# 尝试微调库存水平
for delta in [-1, 0, 1]:
new_inv = current_inv + delta
if 0 <= new_inv <= N:
new_value = expected_profit - gamma * (sigma ** 2) * (new_inv ** 2)
if new_value > best_value:
best_value = new_value
optimal = new_inv
return optimal
4.6 知识体系总览
这一章的内容比较多,我画了一张图帮你理清思路:
4.7 实战中的几个要点
最后,分享几个我在实战中总结的要点:
- 状态空间压缩: 如果N太大,可以用“分桶”的方式,把相近的库存水平合并成一个状态。比如0-0.1 BTC算一个状态,0.1-0.2 BTC算另一个。精度损失不大,但计算量能降一个数量级。
- 转移矩阵的更新频率: 我建议不要每笔成交都更新,那样太频繁。每100-200毫秒更新一次就够,跟订单簿的快照频率保持一致。
- 最优库存不是固定值: 它是动态的,随市场波动率和你的风险偏好变化。我习惯在系统里设一个“目标库存区间”,只要库存落在这个区间内,就不做强制调整,避免频繁交易产生额外成本。
一句话总结: 马尔可夫链帮你“看清”库存的未来走势,最优库存水平帮你“决定”现在该怎么做。两者结合,就是一套完整的库存管理闭环。
好了,这一章就到这里。库存管理是个细活,代码写起来不难,难的是参数调优和边界情况处理。多跑回测,多观察实盘,慢慢就能找到感觉。
公众号:蓝海资料掘金营,微信deep3321