做市商报价模型:Avellaneda-Stoikov 模型原理

做市商的核心工作,说白了就是「低买高卖」赚差价。但怎么定这个「低」和「高」?拍脑袋肯定不行。今天我要讲的 Avellaneda-Stoikov 模型,就是一套数学框架,帮你算出最优的报价宽度。

我个人习惯把 AS 模型看作「库存管理的艺术」。你想想看,做市商手里有货,既怕砸手里,又怕卖飞了。这个模型就是帮你平衡这两件事。

模型的核心思想

AS 模型假设市场是连续交易的,做市商的目标是最大化预期效用。它把做市问题拆成两部分:

  • 库存风险:你手里持有的资产越多,价格波动的风险就越大
  • 报价策略:通过调整买卖报价,控制库存水平

我在项目中遇到过这样的情况:刚开始做市时,我总想把买卖价差压得很窄,觉得这样能多成交。结果市场一波动,库存瞬间爆仓,亏得我头皮发麻。后来用了 AS 模型,才明白报价宽度不是越窄越好。

库存风险厌恶系数 γ

这个 γ 是模型里最关键的参数。它代表你对风险的厌恶程度。

  • γ 越大:你越怕风险,报价会越宽,库存控制得更紧
  • γ 越小:你更激进,报价窄,追求更多成交

重要:γ 的取值没有标准答案。我见过有人用 0.01,也有人用 0.1。关键看你的资金规模和风险承受能力。

我记得有一次帮客户调参数,他死活不肯用 γ=0.05,觉得太保守。结果回测下来,γ=0.02 的时候爆仓了三次。嗯,从那以后他再也不敢乱调了。

最优报价宽度计算

AS 模型给出的最优报价宽度公式是这样的:

最优买价 = 中间价 - (γ * σ² * T * q) - (s/2)
最优卖价 = 中间价 + (γ * σ² * T * q) + (s/2)

其中:

符号 含义 说明
γ 库存风险厌恶系数 你有多怕风险
σ 波动率 资产价格的波动程度
T 时间窗口 你计划持仓多久
q 当前库存 你手里有多少货
s 基础价差 市场的最小价差单位

实战技巧:我建议把 σ 用滚动窗口计算,比如过去 1 小时的波动率。别用全量数据,否则反应太慢。

Python 实现报价更新逻辑

下面是我在实际项目中用过的代码。它实现了 AS 模型的报价更新逻辑:

import numpy as np

class AvellanedaStoikov:
    def __init__(self, gamma=0.05, sigma=0.02, T=1.0, spread=0.01):
        self.gamma = gamma      # 风险厌恶系数
        self.sigma = sigma      # 波动率
        self.T = T              # 时间窗口
        self.spread = spread    # 基础价差
        
    def calculate_quotes(self, mid_price, inventory):
        """
        计算最优买卖报价
        :param mid_price: 当前中间价
        :param inventory: 当前库存(正数表示多头,负数表示空头)
        :return: (bid_price, ask_price)
        """
        # 计算库存调整项
        inventory_adjust = self.gamma * (self.sigma ** 2) * self.T * inventory
        
        # 计算报价
        bid_price = mid_price - inventory_adjust - (self.spread / 2)
        ask_price = mid_price - inventory_adjust + (self.spread / 2)
        
        return bid_price, ask_price
    
    def update_quotes(self, mid_price, inventory, market_data):
        """
        实时更新报价
        :param market_data: 包含最新波动率等市场数据
        """
        # 更新波动率
        self.sigma = market_data.get('volatility', self.sigma)
        
        # 重新计算报价
        return self.calculate_quotes(mid_price, inventory)

# 使用示例
mm = AvellanedaStoikov(gamma=0.05, sigma=0.02, T=1.0, spread=0.01)
bid, ask = mm.calculate_quotes(mid_price=100.0, inventory=10)
print(f"最优买价: {bid:.2f}, 最优卖价: {ask:.2f}")

避坑指南:我曾经把 inventory 参数搞反了,多头持仓时报价反而更低,结果亏了不少。记住:多头库存多时,买价要压低,卖价也要压低,这样才能减少库存。

模型的实际应用

AS 模型不是万能的。我在实战中发现几个坑:

  • 波动率突变:模型假设波动率稳定,但市场经常不按套路出牌
  • 流动性不足:在深度很浅的市场,模型算出的报价可能根本成交不了
  • 参数过拟合:别把 γ 调得太精细,回测漂亮不代表实盘好用

你想想看,如果市场突然暴跌,波动率瞬间飙升,模型会让你把报价拉得很宽。这时候你反而可能错失抄底机会。所以,我一般会在模型外面加一层风控逻辑,比如最大库存限制、最大亏损限制等。

小结

Avellaneda-Stoikov 模型是做市商报价的经典框架。它把库存风险和报价策略结合起来,给出了数学上的最优解。但记住,模型只是工具,真正的艺术在于参数调优和风控设计。

我个人习惯先用历史数据回测,找到合适的 γ 范围,再上实盘小量测试。别一上来就全仓干,那是在赌命。

核心要点

  • γ 控制风险偏好,越大越保守
  • 报价宽度 = 基础价差 + 库存调整项
  • 库存调整项 = γ * σ² * T * q
  • 实盘必须加风控,别裸奔
Avellaneda-Stoikov 模型核心逻辑 市场数据 中间价、波动率 库存状态 当前持仓量 q 参数配置 γ, σ, T, s 核心计算 库存调整项 γ * σ² * T * q 最优买价 mid - adj - s/2 最优卖价 mid - adj + s/2 报价输出 买一价 卖一价 报价宽度 反馈循环:成交后更新库存

专注资料整理