4、经典做市模型:Avellaneda-Stoikov模型、基于库存的做市模型、基于信号的做市模型、模型参数校准
做市模型这东西,说白了就是回答三个问题:报什么价?报多深?什么时候调? 我入行那会儿,团队还在用Excel手动算报价,后来发现根本跑不过机器。今天聊的这几个模型,算是做市领域的「基本功」,你想想看,理解了它们,你就能看懂市面上80%的做市策略。
4.1 Avellaneda-Stoikov模型:做市界的「Hello World」
这个模型是2008年提出的,虽然年代久远,但至今仍是很多量化团队的起点。我个人习惯把它叫做「库存厌恶模型」——核心思想很简单:库存越多,报价越激进,目的是把货甩出去。
模型假设做市商是风险中性的,但有一个风险厌恶系数γ。它推导出的最优报价公式长这样:
# 伪代码示意:Avellaneda-Stoikov 最优报价
def compute_quotes(s, q, gamma, sigma, T, k):
"""
s: 当前中间价
q: 当前库存(正数表示多头)
gamma: 风险厌恶系数
sigma: 波动率
T: 剩余时间
k: 订单簿深度参数
"""
# 库存调整项
inventory_skew = -gamma * sigma**2 * q * T
# 最优买价和卖价
bid = s + inventory_skew - (1/gamma) * (1 + gamma * sigma**2 * T / (2*k))
ask = s + inventory_skew + (1/gamma) * (1 + gamma * sigma**2 * T / (2*k))
return bid, ask
嗯,这里要注意:公式里的k是订单簿的流动性参数,我见过有人直接取0.1,结果报价飘得离谱。实际项目中,k需要根据历史数据做最大似然估计。
核心洞察: Avellaneda-Stoikov 模型告诉我们,做市不是「对称报价」。库存为正时,买价要压得更低,卖价也要降低——说白了就是「贱卖贵买」,加速库存回归中性。
我在项目中遇到过一个问题:这个模型假设波动率σ是常数,但实盘中波动率会突变。有一次比特币闪崩,模型还在按低波动率报价,结果库存瞬间爆仓。后来我加了一个波动率实时更新模块,才稳住。
4.2 基于库存的做市模型:更直接的玩法
Avellaneda-Stoikov 其实也属于库存模型,但这里说的「基于库存的做市模型」更直接——它不依赖复杂的随机控制理论,而是用一些启发式规则。
常见的做法是:
- 库存阈值触发:当库存超过某个阈值(比如总资金的5%),直接调整报价偏移量
- 线性调整:报价偏移 = -α × 库存量,α是调整系数
- 非线性调整:用sigmoid函数做平滑,避免报价突变
举个例子,我曾在某商品期货上用过这个策略:
# 基于库存的报价调整
def inventory_adjusted_price(mid_price, inventory, max_inventory=100):
# 库存比率
ratio = inventory / max_inventory
# 非线性调整:用tanh做平滑
skew = 0.5 * np.tanh(ratio * 3) # 3是陡峭度参数
bid = mid_price - spread/2 - skew * spread
ask = mid_price + spread/2 - skew * spread
return bid, ask
避坑指南: 我曾经把α设得太大,结果库存稍微波动一下,报价就来回跳,产生了大量「虚假成交」。后来我加了低通滤波,让报价变化更平滑。
这种模型的优点是简单、计算快,适合高频场景。缺点是缺乏理论支撑,参数调起来全凭经验。我个人习惯先用Avellaneda-Stoikov跑仿真,再用库存模型做线上微调。
4.3 基于信号的做市模型:让数据说话
如果说库存模型是「被动防守」,那信号模型就是「主动进攻」。它的思路是:利用市场微观结构信号,预测短期价格走势,然后调整报价。
常见的信号包括:
- 订单簿不平衡:买一量和卖一量的比值
- 成交单流:主动买和主动卖的成交量差
- 价差变化:买卖价差的短期趋势
- 波动率聚集:用GARCH模型预测未来波动
我做过一个实盘策略,用LSTM预测未来1秒的价格方向,然后动态调整报价偏移:
# 基于信号模型的报价调整(简化版)
def signal_based_quote(mid_price, signal_value, base_spread):
# signal_value 范围 [-1, 1],正数表示看涨
# 看涨时,提高卖价,降低买价
bid_offset = -0.3 * signal_value * base_spread
ask_offset = 0.3 * signal_value * base_spread
bid = mid_price - base_spread/2 + bid_offset
ask = mid_price + base_spread/2 + ask_offset
return bid, ask
注意: 信号模型容易过拟合。我见过有人用20个特征做XGBoost,回测曲线漂亮得不行,一上实盘就亏成狗。信号越简单越好,我个人建议不超过3个特征。
你想想看,信号模型和库存模型其实可以结合。比如:库存高的时候,信号权重降低;库存低的时候,信号权重提高。这种混合策略在实盘中表现更稳健。
4.4 模型参数校准:别让参数毁了你的策略
模型再好,参数不对也是白搭。参数校准这块,我踩过的坑比走过的路还多。
常见的校准方法有:
| 参数 | 校准方法 | 我的经验 |
|---|---|---|
| 风险厌恶系数 γ | 最大似然估计(MLE) | 用过去30天的成交数据,滚动估计 |
| 波动率 σ | EWMA或GARCH | EWMA的衰减因子取0.94比较稳 |
| 流动性参数 k | 订单簿斜率拟合 | 用限价单深度数据做线性回归 |
| 库存调整系数 α | 网格搜索+回测 | 先粗搜再细搜,别一上来就全量 |
举个例子,校准γ的时候,我习惯这么做:
# 用历史数据校准风险厌恶系数
def calibrate_gamma(price_series, inventory_series, realized_pnl):
# 假设做市商的目标是最大化期望效用
# 用MLE估计gamma
def neg_log_likelihood(gamma):
# 计算每个时间点的效用
utility = -np.exp(-gamma * realized_pnl)
return -np.sum(np.log(utility))
result = minimize(neg_log_likelihood, x0=0.1, bounds=[(0.01, 10)])
return result.x[0]
重要提醒: 参数校准不是「一次搞定,终身使用」。市场环境变了,参数也得跟着变。我建议每天收盘后重新校准一次,遇到重大行情变化(比如加息、财报),盘中也要重新校准。
最后说一句,模型参数校准这件事,回测里表现最好的参数,实盘往往不是最优的。为什么?因为回测用的是历史数据,而市场是动态演化的。我个人的做法是:回测选3-5组参数,实盘用小资金并行跑,让市场来「投票」。
好了,经典做市模型就聊到这儿。下一章我们会讲做市策略的回测框架,到时候我会分享一个我自己写的回测引擎——嗯,那个引擎帮我抓过不少bug。