4、Avellaneda-Stoikov模型:经典做市模型的数学推导与参数含义
做市策略里,Avellaneda-Stoikov模型是个绕不开的经典。说实话,我最早接触这个模型是在2015年,当时团队要做一个期权做市系统,翻遍了文献发现这个模型最接地气。它不像有些模型那样纯理论,而是给出了你能直接用的报价公式。
今天我们就把它掰开揉碎,看看数学推导和每个参数到底在说什么。
4.1 模型的核心思想
Avellaneda-Stoikov模型解决什么问题?简单说:你作为做市商,手里有库存,怎么定价才能既赚到价差,又不让库存风险失控?
模型假设市场上有两类订单:
- 限价单:你挂的买一卖一,等着被吃
- 市价单:对手方直接吃掉你的挂单
你的目标是在一个时间区间 [0, T] 内,最大化期望收益,同时惩罚库存风险。嗯,这里要注意,模型假设你的效用函数是指数型的,也就是对风险极度厌恶。
核心公式:
最优买价:p_b = S - (γσ²T + 1/γ) · (1 - e^{-κ(T-t)})
最优卖价:p_a = S + (γσ²T + 1/γ) · (1 - e^{-κ(T-t)})
其中 S 是中间价,γ 是风险厌恶系数,σ 是波动率,κ 是订单到达强度。
4.2 数学推导:从目标函数到报价公式
推导过程其实不复杂,但有几个关键跳跃点。我当年第一次看论文时,卡在HJB方程那里好几天。咱们一步步来。
4.2.1 定义状态变量
设你的库存为 q(t),现金为 x(t)。中间价 S(t) 服从几何布朗运动:
dS = μ dt + σ dW
你的限价单在买价 p_b 和卖价 p_a 处挂着。对手方市价单到达是泊松过程,到达率 λ 与你的报价偏离中间价的程度有关:
λ_b = A · exp(-κ · (S - p_b))
λ_a = A · exp(-κ · (p_a - S))
这里 A 是基础到达率,κ 是敏感度系数。说白了,你报价越激进(离中间价越近),被吃的概率越大。
4.2.2 目标函数
你的目标是最大化终端时刻的期望效用:
max E[ -exp(-γ · (x_T + q_T · S_T)) ]
这个指数效用函数,γ 越大表示越怕风险。我在实盘中发现,γ 取 0.1 到 1.0 之间比较合理,具体要看你的资金规模和风险偏好。
4.2.3 HJB方程求解
通过动态规划,得到值函数 V(t, x, q, S) 满足的HJB方程。这里省略中间推导,直接给解的形式:
V(t, x, q, S) = -exp(-γ · (x + q · S + θ(t, q)))
其中 θ(t, q) 是时间调整项,形式为:
θ(t, q) = - (γσ²/2) · q² · (T - t) + (1/γ) · ln( ... )
最终得到最优报价偏移量:
δ_b = (γσ²T + 1/γ) · (1 - e^{-κ(T-t)}) // 买价偏移
δ_a = (γσ²T + 1/γ) · (1 - e^{-κ(T-t)}) // 卖价偏移
注意,买价和卖价的偏移量是对称的,但方向相反。
我的经验:实际应用中,很少有人直接用这个对称公式。因为市场买卖压力往往不平衡,我会在偏移量上加一个偏置项,比如根据订单簿 imbalance 调整。
4.3 参数含义详解
每个参数都有明确的物理意义。我列个表,方便你对照:
| 参数 | 符号 | 含义 | 典型取值范围 | 影响 |
|---|---|---|---|---|
| 风险厌恶系数 | γ | 你对库存风险的厌恶程度 | 0.01 - 1.0 | γ越大,报价越宽,库存变化越慢 |
| 波动率 | σ | 标的资产的年化波动率 | 0.1 - 0.5 | σ越大,报价越宽,防止被波动吃掉 |
| 订单到达强度 | κ | 对手方对报价的敏感度 | 0.5 - 5.0 | κ越大,报价越靠近中间价 |
| 时间区间 | T | 策略的持有期限 | 1秒 - 1天 | T越大,报价越宽,因为风险暴露时间长 |
| 当前时间 | t | 策略已运行时间 | 0 ≤ t ≤ T | 越接近T,报价越窄,因为快结束了 |
4.3.1 风险厌恶系数 γ
这是最关键的参数。γ 决定了你愿意承担多少库存风险。我见过一些新手把 γ 设得特别小,结果库存堆积如山,一波行情就亏回去了。
为什么会这样?因为 γ 小意味着你不在乎库存,报价很窄,频繁成交,库存快速积累。一旦方向判断错误,亏损远超赚到的价差。
避坑指南:我曾经在回测时把 γ 设为 0.001,结果夏普比率看起来很高。但实盘一周就亏了5%。后来发现,回测数据没有包含滑点和冲击成本。γ 太小,实盘里你的挂单会被高频交易者反复收割。
4.3.2 波动率 σ
波动率直接影响报价宽度。高波动时,你需要更宽的价差来保护自己。我习惯用5分钟级别的已实现波动率,而不是日频数据。因为做市是高频行为,日波动率太粗糙。
4.3.3 订单到达强度 κ
κ 反映市场深度。如果 κ 很大,说明对手方对价格很敏感,你稍微偏离中间价,订单就没了。这种情况下,报价要尽量贴近中间价。
我在实盘中观察到,κ 在开盘和收盘时差异很大。开盘时 κ 小,因为流动性还没起来;收盘时 κ 大,大家都在抢着平仓。
4.4 模型的实际应用
理论讲完了,咱们看看怎么用。下面是一个简化版的 Python 实现:
import numpy as np
class AvellanedaStoikov:
def __init__(self, gamma=0.1, sigma=0.2, kappa=1.5, T=3600):
self.gamma = gamma
self.sigma = sigma
self.kappa = kappa
self.T = T # 总时间,单位秒
def get_quotes(self, mid_price, inventory, t):
"""
计算最优买价和卖价
:param mid_price: 当前中间价
:param inventory: 当前库存(正数表示多头)
:param t: 当前时间(秒)
"""
# 剩余时间
tau = self.T - t
# 风险调整项
risk_adj = (self.gamma * self.sigma**2 * tau +
1.0 / self.gamma) * (1 - np.exp(-self.kappa * tau))
# 库存偏移(不对称项)
inventory_adj = -self.gamma * self.sigma**2 * tau * inventory
# 最终报价
bid = mid_price - risk_adj + inventory_adj
ask = mid_price + risk_adj + inventory_adj
return bid, ask
这段代码里,我加了一个库存偏移项 inventory_adj。这是原模型没有的,但实盘中非常必要。如果你持有多头,买价要更低,防止继续加仓;卖价也要更低,鼓励平仓。
关键点:Avellaneda-Stoikov模型给出了一个理论框架,但实盘必须根据市场微观结构做调整。我个人习惯在模型输出上再加一个「流动性过滤器」——如果买卖价差小于某个阈值,就不挂单,防止被套利者盯上。
4.5 模型的局限性
没有模型是完美的。Avellaneda-Stoikov有几个假设需要注意:
- 中间价服从几何布朗运动:实际市场有跳跃、有自相关,不是简单的随机游走
- 订单到达是泊松过程:实际有聚集效应,大单来了会引发一连串订单
- 不考虑对手方策略:假设对手方是随机到达的,但实际有HFT在盯着你的挂单
嗯,这些局限不是让你不用这个模型,而是提醒你:模型是拐杖,不是腿。用它来理解市场结构,但最终决策要结合实盘数据。
我的建议:先用这个模型做回测,观察参数敏感性。然后逐步加入自己的修正项,比如订单簿 imbalance、历史波动率曲面、甚至新闻情绪。做市策略的竞争力,往往体现在这些「非标准」的细节里。
这张图把整个模型的输入、计算、输出串起来了。你写代码时,就按这个流程来组织逻辑。先读市场数据,再算参数,最后输出报价。
好了,Avellaneda-Stoikov模型的核心内容就这些。记住,理论是基础,但实盘才是检验真理的唯一标准。多跑回测,多观察参数变化对结果的影响,慢慢你就能找到感觉。