跳到正文

Avellaneda-Stoikov 做市:保留价和库存风险

做市看上去简单:双边报价、赚点差。难在库存会积累、价格会动、报价太紧会被打穿。Avellaneda-Stoikov (2008) 把这套权衡解成两条公式:保留价和最优价差。

第 8 篇 · 共 12 篇 约 9 分钟

做市是金融里理论上最干净、实际上最难的策略。逻辑简单:挂买卖报价,赚点差,管理库存。现实是:你的成交是随机的,价格在动,库存会在你最不想要的方向上越积越多。做市商每一秒都在回答两个问题——报价中心放哪、点差开多宽——而这两个问题的答案互相纠缠。

数学上的现代处理是 Avellaneda-Stoikov (2008)。它的漂亮之处在于把这两个问题干净地解耦了:报价中心由一条保留价公式决定,点差由另一条公式决定,各管各的经济学。这是学术做市的标准参考,也是很多量化做市策略的骨架。Harris 在第 13 章专门讲了做市,对 dealer 行为有大量讨论。下面把模型和两条公式推出来,再讲怎么把它落到实盘。

做市商的问题

你在资产 SS 上做市。你挂买价 SbS^b 和卖价 SaS^a,中间价 Sm=(Sa+Sb)/2S^m = (S^a + S^b) / 2,半点差 δ=(Sa−Sb)/2\delta = (S^a - S^b) / 2。

流量是价格敏感的:买方以速率 λb(Sb)\lambda^b(S^b) 到达(你的买价越不激进,等得越久),卖方以速率 λa(Sa)\lambda^a(S^a) 到达(同理)。有人打你的买价,你买入,库存增加;有人抬你的卖价,你卖出,库存减少。你持有库存 qq(正 = 多,负 = 空)。中间价在最简版本里是无漂移布朗运动:

dS=σdWdS = \sigma dW

目标是最大化终点财富的期望效用。Avellaneda-Stoikov 用指数效用:

max⁡  E[−e−γ(XT+qTST)]\max \; E\left[ -e^{-\gamma (X_T + q_T S_T)} \right]

其中 XTX_T 是终点现金、qTSTq_T S_T 是终点库存按中间价的市值,γ\gamma 是风险厌恶。指数效用不是随手选的:它让最优策略不依赖财富水平,问题才解得动。变体里也有人加库存的运行惩罚项,结构不变。

注意这个设定里没有知情交易者——价格是外生的,流量只对你的报价距离敏感。这一点等下对比 Glosten-Milgrom 时会变得重要。

Avellaneda-Stoikov 解

用值函数 v(q,S,t)v(q, S, t)——库存 qq、中间价 SS、时刻 tt 下的最优继续价值——写 HJB 方程求解,最优报价的结构是:

Sa=Sr+δa,Sb=Sr−δbS^a = S^r + \delta_a, \quad S^b = S^r - \delta_b

报价不再围着中间价摆,而是围着一个新的中心 SrS^r——保留价——摆。这就是前面说的解耦:先定中心,再定宽度。

保留价是市商在买和卖之间无差异的价格:

Sr=Sm−q⋅γσ2(T−t)S^r = S^m - q \cdot \gamma \sigma^2 (T - t)

其中 qq 是当前库存,γ\gamma 是风险厌恶,σ\sigma 是中间价波动率,T−tT - t 是剩余时间。读一下它的方向:你多仓(q>0q > 0)时,保留价压到中间价之下——你愿意便宜点卖(想降库存),买入则要求更低的价。空仓反过来。偏移量随波动率和剩余时间放大:仓位可能伤你越狠、你还要扛它越久,你就把整个报价结构挪得越狠。

宽度那半的答案是最优半点差:

δa=δb=δ=γσ2(T−t)2+1γlog⁡(1+γκ)\delta_a = \delta_b = \delta = \frac{\gamma \sigma^2 (T - t)}{2} + \frac{1}{\gamma} \log\left(1 + \frac{\gamma}{\kappa}\right)

其中 κ\kappa 是到达速率对报价距离的弹性(流量模型里 λ(δ)=Ae−κδ\lambda(\delta) = A e^{-\kappa \delta} 的那个 κ\kappa)。在 κ→∞\kappa \to \infty 的极限(很流动的市场,流量对价格极敏感)下:

δ≈γσ2(T−t)2+1κ\delta \approx \frac{\gamma \sigma^2 (T-t)}{2} + \frac{1}{\kappa}

两项各有各的经济学,值得分开看。

第一项 γσ2(T−t)/2\gamma \sigma^2 (T - t)/2 是库存风险的价码。风险厌恶越高、波动越大、剩余时间越长,它越宽。逻辑是:在波动的市场里持有库存有风险,点差是你收的风险补偿。点差开宽了,成交概率下降、流量变少,但每笔的利润变厚——这条公式就是在这个权衡上取的最优。

第二项 (1/γ)log⁡(1+γ/κ)≈1/κ(1/\gamma) \log(1 + \gamma/\kappa) \approx 1/\kappa 是基础点差:即使零库存你也要报的宽度。它由流量弹性定价——κ\kappa 大(流量对价格敏感,流动市场),你收不了多少租,点差紧;κ\kappa 小(流量无弹性),你可以收宽点差。经验上流动市场的 κ\kappa 更大,加密主流品种的 κ\kappa 比小盘股高。要说清楚的是:在 AS 模型内部,这一项来自流量弹性,不是逆向选择——模型里根本没有知情交易者。现实中逆向选择当然存在,它是你在模型外要另外补的账,下面会回到这一点。

合起来

把保留价和半点差拼起来,最优报价是:

Sa=Sm−qγσ2(T−t)+γσ2(T−t)2+1γlog⁡(1+γ/κ)S^a = S^m - q \gamma \sigma^2 (T-t) + \frac{\gamma \sigma^2 (T-t)}{2} + \frac{1}{\gamma} \log(1 + \gamma/\kappa) Sb=Sm−qγσ2(T−t)−γσ2(T−t)2−1γlog⁡(1+γ/κ)S^b = S^m - q \gamma \sigma^2 (T-t) - \frac{\gamma \sigma^2 (T-t)}{2} - \frac{1}{\gamma} \log(1 + \gamma/\kappa)

q=0q = 0 时报价关于中间价对称。q>0q > 0(多仓)时,整个结构下移:买卖价都低,你在劝市场买你的货、别再卖给你。q<0q < 0 时上移,反之。库存重到一定程度,你的卖价甚至会压到中间价以下——不是 bug,是模型在逼你降库存。

一个数值算例(含一个单位陷阱)

先踩坑。有人拿”看着顺眼”的参数直接算:γ=0.1\gamma = 0.1,κ=1.5\kappa = 1.5。基础点差 = (1/γ)log⁡(1+γ/κ)=10⋅log⁡(1+0.0667)=10⋅0.0646=0.646(1/\gamma)\log(1 + \gamma/\kappa) = 10 \cdot \log(1 + 0.0667) = 10 \cdot 0.0646 = 0.646——每边 0.65 美元?对一只 $100 的股票这是 130 bp 的全点差,荒谬地宽。数字没算错,错在把 $\gamma、、\kappa$当成了无量纲数。它们都带单位(1/价格),跟$\sigma^2$(价格²/时间)、$T-t$(时间)必须配套。参数不配单位,这个模型给出的报价可以差出几个数量级——这是用 AS 时的第一检查项。

配好单位重来。股价 Sm=100.00S^m = 100.00,日波动 2%(即 σ=2\sigma = 2 美元/√天,σ2=4\sigma^2 = 4),剩余水平期 T−t=0.1T - t = 0.1 天(约 40 分钟),γ=0.001\gamma = 0.001(1/美元),κ=10\kappa = 10(1/美元),库存 q=+500q = +500 股多仓。

保留价偏移:qγσ2(T−t)=500⋅0.001⋅4⋅0.1=0.20q \gamma \sigma^2 (T-t) = 500 \cdot 0.001 \cdot 4 \cdot 0.1 = 0.20 美元,所以 Sr=99.80S^r = 99.80。

半点差:库存分量 γσ2(T−t)/2=0.0002\gamma \sigma^2 (T-t)/2 = 0.0002,可以忽略;基础分量 (1/γ)log⁡(1+γ/κ)=1000⋅log⁡(1.0001)≈0.10(1/\gamma)\log(1 + \gamma/\kappa) = 1000 \cdot \log(1.0001) \approx 0.10 美元。半点差 ≈0.10\approx 0.10。

报价:买 99.80−0.10=99.7099.80 - 0.10 = 99.70,卖 99.80+0.10=99.9099.80 + 0.10 = 99.90。

看清楚发生了什么:你的卖价 $99.90 低于中间价 $100.00。500 股的多仓让模型宁可”亏”10 美分相对中间价出货,也不愿继续抱着库存赌方向。这就是保留价机制的全部意义——报价中心跟着库存走,点差只管宽度。实操的量级感:γσ2(T−t)\gamma \sigma^2 (T-t)(库存每股的报价偏移斜率)配出来后,主动做市商的总点差通常落在 1 bp 到 100 bp 之间,具体取决于资产波动率、风险容忍度和水平期。

跟 Glosten-Milgrom 和 Ho-Stoll 的关系

现代视角把做市拆成两个正交的问题:逆向选择(我的对手方是不是比我知道得多?)和库存管理(我的仓位风险怎么控?)。Glosten-Milgrom(第 4 篇)是前者的标准模型:dealer 报静态买卖价,点差纯粹补偿被知情者挑着打的损失,没有库存概念。Ho-Stoll (1981) 和 Avellaneda-Stoikov 是后者的模型:点差和报价倾斜管理库存风险,模型里没有知情者。

在 AS 里令 γ→0\gamma \to 0,库存项全部消失,只剩 1/κ1/\kappa 的基础点差——结构上呼应了 Glosten-Milgrom 的核心洞见”零库存也要有点差”,但经济学不同:GM 的点差是信息租,AS 的基础点差是流量弹性租。真实做市商两种租都要收:AS 给你库存那半的公式,逆向选择那半要靠 GM 的思路在模型外补。两个模型不是竞争关系,是同一张账单的两行。

再加上 Almgren-Chriss(第 9 篇)——最优执行之于单边大单,正如 Avellaneda-Stoikov 之于双边报价,连风险厌恶参数的角色都平行。这三个模型都吃透,现代微观结构的骨架就齐了。

从模型到实盘:缺的那几块

AS 的报价是起点,不是终点。真实高频做市商在它上面至少要补这些账。

排队位置。模型假设你挂了就能成交;现实是你排在队尾,好成交轮不到你,坏成交(价格要穿过你了)你跑不掉。Harris 第 13 章和第 1 篇讲过这个。报价被狙击。HFT 检测到你的陈旧报价后反向成交——他们不是基本面意义上的知情者,这是报价变动上的统计套利,但对你的账本来说效果跟逆向选择一模一样。延迟。模型是连续时间的;你有毫秒级延迟,撤单永远慢半拍。Tick 网格。模型价格连续;现实报价必须落在离散 tick 上(美股 1 美分,加密随交易所和交易对而异),取整本身就会改变你的排队命运。借券成本。空头库存要付借券费,负库存一侧的报价要把它算进去。费用与返佣。maker-taker 收费结构直接进出每笔成交的损益。

一个生产做市系统在 AS 骨架上叠几百条这类调整。核心公式给结构,剩下的是工程。

标定与实现

要把模型跑起来,三个参数要估。σ\sigma 用中间价的已实现波动率,1–5 分钟滚动窗口。κ\kappa 从报价距离与成交强度的关系里估(报价更新数据回归)。γ\gamma 没法从数据里估——它是你的风险偏好,由交易授权设定,模型单位下常见量程 1 到 10410^4;设太小,报价紧到被人打穿,设太大,你宽到没有流量。波动率飙升的市况下要么让 σ\sigma 的估计快速跟上、要么直接调高 γ\gamma——总之别拿平静市况的参数硬扛崩盘,这个模型的所有输入都是市况的函数。

核心逻辑几行就写完了:

import numpy as np

def avellaneda_stoikov_quotes(mid, inventory, sigma, T_remaining,
                              gamma=1.0, kappa=1.0):
    """
    按 Avellaneda-Stoikov 计算最优买卖报价。
    mid: 当前中间价
    inventory: 当前库存(正 = 多)
    sigma: 中间价每期波动率
    T_remaining: 剩余时间水平期(跟 sigma 同单位)
    gamma: 风险厌恶
    kappa: 到达速率弹性
    """
    reservation = mid - inventory * gamma * sigma**2 * T_remaining
    half_spread = (gamma * sigma**2 * T_remaining) / 2 + \
                  (1 / gamma) * np.log(1 + gamma / kappa)
    return reservation - half_spread, reservation + half_spread

真实实现在这上面加 tick 取整、排队位置调整和库存硬限制——最后这条特别要强调:模型本身不约束库存,它只是温和地把你推回 q=0q = 0;真实 desk 的硬限制必须在模型外面加,别指望倾斜项替你挡住极端行情里的单边积累。

Arithmion 把这套流程打包了。arithmion.marketmaking.AvellanedaStoikov 提供带标定参数的实时报价计算、库存跟踪与倾斜、每笔成交的 P&L 归因、用真实 L2 数据的回测,以及多资产、跳跃扩散中间价、借券成本这些扩展;MarketMaker 模块负责自动标定——你给历史 L2 数据,它估 σ\sigma、κ\kappa,帮你选 γ\gamma。整条链接到 arithmion.execution 和 arithmion.tca 做完整绩效分析。

用它交易时真正要盯的东西

把模型当日常工具用的时候,几条经验值得写在屏幕边上。保留价是你的锚:库存一重,报价中心就该动,抗拒这个倾斜等于跟模型对赌方向。半点差是你的风险预算:紧一分,成交多一分,风险也多一分。如果你老老实实跟着模型走,库存会均值回复到零附近——反过来说,库存长期不回零,说明有什么东西(延迟、毒性流量、参数漂移)在模型外面漏水。最要紧的一条:AS 不检测逆向选择。就算点差挂对了,你还是会输给知情者;在多个水平期上监控你的已实现价差,如果它是负的,你有毒性问题,需要模型之外的一个独立信号(流量毒时干脆不报价)。

Harris 第 13 章有实战派的完整视角,Avellaneda-Stoikov 原文是正式参考,Cartea-Jaimungal-Penalva (2015) 把这个模型和它的各路扩展整理成了教科书。

参考文献

  • Harris, L. Trading and Exchanges: Market Microstructure for Practitioners. Oxford University Press, 2003. 第 13 章(dealers 和做市商)。ISBN: 978-0-19-514470-3.
  • Avellaneda, M., Stoikov, S. “High-Frequency Trading in a Limit Order Book.” Quantitative Finance, 2008.
  • Glosten, L., Milgrom, P. “Bid, Ask and Transaction Prices in a Specialist Market with Heterogeneously Informed Traders.” Journal of Financial Economics, 1985.
  • Ho, T., Stoll, H. “Optimal Dealer Pricing Under Transactions and Return Uncertainty.” Journal of Financial Economics, 1981.
  • Cartea, A., Jaimungal, S., Penalva, J. Algorithmic and High-Frequency Trading. Cambridge University Press, 2015.