Skip to content

7.3 做市策略思想

概念详解

做市(Market Making)是指持续在买卖双方同时报价,通过赚取买卖价差(bid-ask spread)来盈利的交易策略。做市商是金融市场流动性的核心提供者,他们承担的职责是以自有资金买卖资产,为市场提供即时成交的便利。

做市策略的核心逻辑可以简单概括为:

  1. 以略低于合理价格的价位挂出买单(bid)
  2. 以略高于合理价格的价位挂出卖单(ask)
  3. 当买卖双方都成交后,赚取价差收益
  4. 管理持仓风险(inventory risk),避免单边持仓暴露

与趋势跟踪策略不同,做市策略追求的是"交易的频率而非方向"——每次交易的利润微薄(通常几个tick),但通过高频交易累积可观的收益。

做市策略面临的核心挑战是逆向选择(Adverse Selection):当价格发生剧烈变动时,你的报价会单向成交——价格大涨时你的卖单被大量吃掉(你空头持仓增加),价格大跌时你的买单被大量吃掉(你多头持仓增加)。这意味着做市商总是在"错误的方向"上积累仓位。

为了应对逆向选择,做市商需要:

  • 合理定价:准确估计资产的公允价值(fair price)
  • 动态价差:根据波动率和持仓状况调整买卖价差
  • 库存管理:当持仓偏离目标时,调整报价方向以促进回补
  • 对冲:利用相关资产对冲持仓风险
名词解释:做市商

持续提供双边报价(同时报出买价和卖价)以提供市场流动性,并通过买卖价差盈利。

名词解释:逆向选择

做市商面临的系统性风险:当订单成交后,价格倾向于朝不利于做市商的方向移动,意味着做市商的对手方拥有信息优势。

名词解释:库存风险(Inventory Risk)

做市商因持有净多头或净空头仓位而暴露于价格波动风险。库存管理是做市策略的核心问题。

名词解释:买卖价差(Bid-Ask Spread)

买价与卖价之间的差额,是做市商的主要利润来源。价差大小反映了资产的流动性、波动率和信息不对称程度。

数学原理

Avellaneda-Stoikov 做市模型

Avellaneda和Stoikov(2008)提出了做市策略的经典数学模型。做市商在时刻 t 持有现金 Xt 和资产数量 qt,其财富为:

$$W_t = X_t + q_t \cdot S_t$$

其中 St 是资产的中间价,假设服从布朗运动:

$$dS_t = \sigma \, dW_t$$

做市商的控制变量是买卖报价相对于中间价的偏移:

  • 买价:ptb=Stδtb
  • 卖价:pta=St+δta

做市商的目标是最大化效用函数(考虑风险厌恶参数 γ):

$$\max_{\delta^b, \delta^a} \mathbb{E}\left[-\exp(-\gamma W_T)\right]$$

在对称信息假设下,最优买价和卖价的偏移为:

$$\delta^b = \delta^a = \frac{\gamma \sigma^2 (T-t)}{2} q^2 + \frac{1}{\gamma} \ln(1 + \frac{\gamma}{\kappa})$$

其中 κ 是订单到达率参数。该公式揭示了一个关键规律:持仓越大,价差越宽,做市商会通过扩大价差来抑制进一步持仓。

库存偏斜(Inventory Skew)

实际应用中,做市商根据当前持仓调整报价:

$$p^b = S_t - \frac{s}{2} - \beta \cdot q_t$$
$$p^a = S_t + \frac{s}{2} - \beta \cdot q_t$$

其中 s 是基准价差,β 是库存偏斜系数。当 qt>0(净多头),买卖价都下调以主动卖出;当 qt<0(净空头),买卖价都上调以主动买入。

Python实战

📌 案例1:简单做市报价

PYTHON5 行 · 180 B
📄此处有展示代码5 行 · 180 B展开 ▼
python
fair_price = (bid + ask) / 2
inventory = current_position
spread = base_spread + inventory_skew * inventory
bid_quote = fair_price - spread / 2
ask_quote = fair_price + spread / 2
点击展开可浏览运行结果
--- 结果 ---
fair_price = array([-0.45932829, -0.27945481,  0.15248701,  0.36037629, -0.19771954,
        0.08495695,  1.73269936,  0.47100627, -0.105962  ,  0.23405706,
       -1.19109445, -0.24612181,  0.15109624,  0.2749809
inventory = array([ 0.35778736,  0.56078453,  1.08305124,  1.05380205, -1.37766937,
       -0.93782504,  0.51503527,  0.51378595,  0.51504769,  3.85273149,
        0.57089051,  1.13556564,  0.95400176,  0.6513912
spread = array([-1.39946107e+00, -8.96301279e-01,  7.52972801e-01,  6.59878512e-01,
        5.99139820e-01, -4.66796879e-01,  7.27802723e-01, -6.64723968e-01,
        6.09055368e-01,  1.77980204e+00,  1.885729
bid_quote = array([ 0.24040224,  0.16869583, -0.22399939,  0.03043704, -0.49728945,
        0.31835539,  1.368798  ,  0.80336825, -0.41048968, -0.65584396,
       -1.28538094, -0.15695778,  0.47016188, -0.5479076
ask_quote = array([-1.15905883, -0.72760545,  0.52897341,  0.69031555,  0.10185037,
       -0.14844149,  2.09660072,  0.13864429,  0.19856569,  1.12395809,
       -1.09680797, -0.33528585, -0.1679694 ,  1.0978695

📌 案例2:完整的做市策略模拟器

PYTHON96 行 · 3.8 KB
📄此处有展示代码96 行 · 3.8 KB展开 ▼
python
import numpy as np
from collections import deque

class MarketMaker:
    """基于Avellaneda-Stoikov模型的做市策略模拟器"""
    
    def __init__(self, initial_cash=1000000, initial_position=0,
                 base_spread=0.02, gamma=0.01, sigma=0.30,
                 max_position=1000, order_arrival_rate=10):
        self.cash = initial_cash
        self.position = initial_position
        self.base_spread = base_spread
        self.gamma = gamma          # 风险厌恶系数
        self.sigma = sigma          # 波动率估计
        self.max_position = max_position
        self.kappa = order_arrival_rate
        self.trades = []
        self.pnl_history = []
        
    def calculate_quotes(self, fair_price, remaining_time=1.0):
        """根据Avellaneda-Stoikov模型计算最优报价"""
        # 库存风险调整
        inventory_risk = self.gamma * self.sigma**2 * remaining_time * self.position**2
        
        # 基本价格偏移(从A-S模型推导的简化形式)
        reserve_price = fair_price - self.position * self.gamma * self.sigma**2 * remaining_time
        
        # 价差:基础价差 + 库存调整 + 风险调整
        spread = self.base_spread + self.gamma * self.sigma**2 * remaining_time * abs(self.position)
        # 保证价差不为负
        spread = max(spread, 0.005)
        
        # 仓位限制
        if abs(self.position) >= self.max_position:
            spread *= 2.0  # 接近仓位上限时大幅扩大价差
        
        bid_quote = reserve_price - spread / 2
        ask_quote = reserve_price + spread / 2
        
        return bid_quote, ask_quote, reserve_price
    
    def simulate_trade(self, trade_type, quantity, price):
        """模拟成交"""
        if trade_type == 'SELL':  # 对手方卖给我们(我们买入)
            self.position += quantity
            self.cash -= quantity * price
        elif trade_type == 'BUY':   # 对手方从我们这里买(我们卖出)
            self.position -= quantity
            self.cash += quantity * price
        self.trades.append({
            'type': trade_type, 'qty': quantity, 'price': price,
            'position': self.position, 'cash': self.cash
        })
    
    def calculate_pnl(self, current_price):
        """计算实时PnL(Mark-to-Market)"""
        return self.cash + self.position * current_price

# ===== 模拟做市过程 =====
np.random.seed(42)
market_maker = MarketMaker(initial_cash=1000000, base_spread=0.05, 
                           gamma=0.005, sigma=0.25)

n_steps = 500
price = 100.0
prices = [price]

for t in range(n_steps):
    # 模拟价格随机游走
    price_change = np.random.normal(0, 0.1)
    price += price_change
    prices.append(price)
    
    fair_price = price
    remaining = 1.0 - t / n_steps
    
    bid_q, ask_q, reserve = market_maker.calculate_quotes(fair_price, remaining)
    
    # 模拟成交(简化:以一定概率在bid/ask价格成交)
    fill_prob = 0.3  # 每个时间步有30%概率在一边成交
    if np.random.random() < fill_prob and market_maker.position < market_maker.max_position:
        # 以bid价买入
        market_maker.simulate_trade('SELL', 10, bid_q)
    if np.random.random() < fill_prob and market_maker.position > -market_maker.max_position:
        # 以ask价卖出
        market_maker.simulate_trade('BUY', 10, ask_q)
    
    if t % 50 == 0:
        spread_bp = (ask_q - bid_q) * 10000 / fair_price
        pnl = market_maker.calculate_pnl(price)
        print(f"Step {t:3d}: 价格={price:.2f} 持仓={market_maker.position:4d} "
              f"价差={spread_bp:.1f}bp PnL={pnl:.0f}")

final_pnl = market_maker.calculate_pnl(price)
print(f"\n最终PnL: {final_pnl:.0f}  最终持仓: {market_maker.position}")
print(f"总成交笔数: {len(market_maker.trades)}")
点击展开可浏览运行结果
Step   0: 价格=100.05 持仓=   0 价差=5.0bp PnL=1000000
Step  50: 价格=100.68 持仓= -40 价差=6.1bp PnL=1000001
Step 100: 价格=101.44 持仓=-100 价差=7.1bp PnL=999957
Step 150: 价格=101.23 持仓= -60 价差=6.0bp PnL=999999
Step 200: 价格=100.95 持仓=  20 价差=5.1bp PnL=1000009
Step 250: 价格=100.95 持仓= 140 价差=7.0bp PnL=1000054
Step 300: 价格=100.44 持仓= 180 价差=7.1bp PnL=1000009
Step 350: 价格=101.71 持仓= 200 价差=6.8bp PnL=1000271
Step 400: 价格=102.48 持仓= 190 价差=6.1bp PnL=1000418
Step 450: 价格=103.06 持仓= 160 价差=5.3bp PnL=1000512

最终PnL: 1000707  最终持仓: 260
总成交笔数: 322

📌 案例3:库存偏斜分析

PYTHON49 行 · 1.6 KB
📄此处有展示代码49 行 · 1.6 KB展开 ▼
python
import numpy as np

def inventory_skew_analysis(fair_price, base_spread, positions, skew_coeff):
    """
    分析库存偏斜对报价的影响
    
    返回每个持仓水平下的买单偏移、卖单偏移
    """
    results = []
    for pos in positions:
        # 买价:从中间价减去半价差,再加库存偏斜
        bid_offset = -(base_spread / 2) + skew_coeff * pos
        # 卖价:从中间价加上半价差,再加库存偏斜
        ask_offset = (base_spread / 2) + skew_coeff * pos
        
        bid_price = fair_price + bid_offset
        ask_price = fair_price + ask_offset
        effective_spread = ask_price - bid_price
        
        results.append({
            'position': pos,
            'bid_price': bid_price,
            'ask_price': ask_price,
            'spread': effective_spread,
            'skewed': pos != 0
        })
    return results

# 分析不同持仓水平下的报价偏斜
fair = 100.0
base_spr = 0.10
skew_coeff = 0.005

positions = [-200, -100, -50, 0, 50, 100, 200]
results = inventory_skew_analysis(fair, base_spr, positions, skew_coeff)

print("库存偏斜分析(基准价差 = 0.10):")
print(f"{'持仓':>6} {'买价':>8} {'卖价':>8} {'价差':>8} {'报价方向':>12}")
print("-" * 50)
for r in results:
    skew_dir = ""
    if r['position'] > 0:
        skew_dir = "偏好卖出"
    elif r['position'] < 0:
        skew_dir = "偏好买入"
    else:
        skew_dir = "中性"
    print(f"{r['position']:>6} {r['bid_price']:>8.3f} {r['ask_price']:>8.3f} "
          f"{r['spread']:>8.3f} {skew_dir:>12}")
点击展开可浏览运行结果
库存偏斜分析(基准价差 = 0.10):
    持仓       买价       卖价       价差         报价方向
--------------------------------------------------
  -200   98.950   99.050    0.100         偏好买入
  -100   99.450   99.550    0.100         偏好买入
   -50   99.700   99.800    0.100         偏好买入
     0   99.950  100.050    0.100           中性
    50  100.200  100.300    0.100         偏好卖出
   100  100.450  100.550    0.100         偏好卖出
   200  100.950  101.050    0.100         偏好卖出

常见误区

误区1:做市策略是"稳赚不赔"的。 价差收入看起来很诱人,但逆向选择成本经常超过价差收入。研究估计,在部分市场中逆向选择成本占价差的比例可达50-80%。成功的做市商靠的是精准定价和严格风控,而非简单的买卖报价。

误区2:价差越大越好。 扩大价差虽然增加了每笔交易的潜在利润,但降低了成交频率和市场份额。做市商面临的是"单笔利润 vs 成交概率"的权衡。最优价差需要通过订单簿动态和市场博弈来确定。

误区3:做市只需要关注当前价格。 实际上,做市商需要预测短期价格方向以管理库存。如果一个做市商在价格即将暴跌前积累了大量多头仓位,累积的亏损可能远超全天的价差收入。

误区4:持仓回归中性是唯一正确的库存管理策略。 在某些场景下(如观察到显著的价格趋势),做市商可以有意识地"偏执"仓位以获利。这被称为"informed market making",介于纯做市和投机交易之间。

误区5:做市策略不需要考虑交易成本。 做市策略的每笔交易利润极薄,交易费用(佣金、税、清算费)会显著侵蚀收益。高频做市商通常需要与交易所协商获得做市商费率优惠(甚至返还费用)。

实战练习

练习1: 基于Avellaneda-Stoikov模型,编写一个做市策略的蒙特卡洛回测系统。回测参数:资产价格=100,波动率=25%,做市1个月(21个交易日)。分析以下要素对策略收益的影响:

  • 波动率误判(实际波动高于或低于预期波动)
  • 持仓上限设置
  • 风险厌恶参数 γ

练习2: 设计一个"带有方向判断的做市策略"(informed market making):

  • 当短期趋势信号看涨时,将买卖报价整体上调(降低买入概率、增加卖出概率)
  • 当短期趋势信号看跌时,将买卖报价整体下调
  • 分析该策略相对于纯做市策略的改进效果

练习3: 研究做市策略的风险指标体系,编写代码计算以下指标:

  • 日均PnL和夏普比率
  • 最大回撤(Drawdown)
  • 持仓过夜风险(Overnight Position Risk)
  • 单边成交比例(判断逆向选择严重程度的代理指标)

延伸阅读

  1. Avellaneda, M., & Stoikov, S. (2008). High-frequency Trading in a Limit Order Book. Quantitative Finance, 8(3), 217-224. — 做市策略数学建模的经典论文。
  2. Guéant, O., Lehalle, C. A., & Fernandez-Tapia, J. (2013). Dealing with the Inventory Risk: A Solution to the Market Making Problem. Mathematics and Financial Economics, 7(4), 477-507. — 库存风险管理的高级模型。
  3. Cartea, A., Jaimungal, S., & Ricci, J. (2014). Buy Low, Sell High: A High Frequency Trading Perspective. SIAM Journal on Financial Mathematics. — 高频环境下的做市策略。
  4. Menkveld, A. J. (2013). High Frequency Trading and the New Market Makers. Journal of Financial Markets, 16(4), 712-740. — 高频做市商对市场质量的实证研究。
  5. Gould, M. D., et al. (2013). Limit Order Books. Quantitative Finance, 13(11), 1709-1742. — 限价订单簿的全面综述。

本章要点

  • 做市策略通过持续提供双边报价赚取买卖价差,是市场流动性的核心来源
  • 逆向选择是做市商面临的主要风险——总是在错误的方向上成交
  • Avellaneda-Stoikov模型给出了最优报价的解析形式:价差随持仓和风险厌恶增加而扩大
  • 库存管理通过偏斜报价来实现——净多头时调低买卖价以促进卖出,净空头时反之
  • 做市策略的成功关键在于:准确估计公允价值动态管理库存风险
  • 现代做市策略通常结合统计套利信号来"偏置"报价方向,提高长期收益