主题切换
7.3 做市策略思想
概念详解
做市(Market Making)是指持续在买卖双方同时报价,通过赚取买卖价差(bid-ask spread)来盈利的交易策略。做市商是金融市场流动性的核心提供者,他们承担的职责是以自有资金买卖资产,为市场提供即时成交的便利。
做市策略的核心逻辑可以简单概括为:
- 以略低于合理价格的价位挂出买单(bid)
- 以略高于合理价格的价位挂出卖单(ask)
- 当买卖双方都成交后,赚取价差收益
- 管理持仓风险(inventory risk),避免单边持仓暴露
与趋势跟踪策略不同,做市策略追求的是"交易的频率而非方向"——每次交易的利润微薄(通常几个tick),但通过高频交易累积可观的收益。
做市策略面临的核心挑战是逆向选择(Adverse Selection):当价格发生剧烈变动时,你的报价会单向成交——价格大涨时你的卖单被大量吃掉(你空头持仓增加),价格大跌时你的买单被大量吃掉(你多头持仓增加)。这意味着做市商总是在"错误的方向"上积累仓位。
为了应对逆向选择,做市商需要:
- 合理定价:准确估计资产的公允价值(fair price)
- 动态价差:根据波动率和持仓状况调整买卖价差
- 库存管理:当持仓偏离目标时,调整报价方向以促进回补
- 对冲:利用相关资产对冲持仓风险
名词解释:做市商
持续提供双边报价(同时报出买价和卖价)以提供市场流动性,并通过买卖价差盈利。
名词解释:逆向选择
做市商面临的系统性风险:当订单成交后,价格倾向于朝不利于做市商的方向移动,意味着做市商的对手方拥有信息优势。
名词解释:库存风险(Inventory Risk)
做市商因持有净多头或净空头仓位而暴露于价格波动风险。库存管理是做市策略的核心问题。
名词解释:买卖价差(Bid-Ask Spread)
买价与卖价之间的差额,是做市商的主要利润来源。价差大小反映了资产的流动性、波动率和信息不对称程度。
数学原理
Avellaneda-Stoikov 做市模型
Avellaneda和Stoikov(2008)提出了做市策略的经典数学模型。做市商在时刻
$$W_t = X_t + q_t \cdot S_t$$
其中
$$dS_t = \sigma \, dW_t$$
做市商的控制变量是买卖报价相对于中间价的偏移:
- 买价:
- 卖价:
做市商的目标是最大化效用函数(考虑风险厌恶参数
$$\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$$
其中
Python实战
📌 案例1:简单做市报价
此处有展示代码展开 ▼
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:完整的做市策略模拟器
此处有展示代码展开 ▼
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:库存偏斜分析
此处有展示代码展开 ▼
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)
- 单边成交比例(判断逆向选择严重程度的代理指标)
延伸阅读
- Avellaneda, M., & Stoikov, S. (2008). High-frequency Trading in a Limit Order Book. Quantitative Finance, 8(3), 217-224. — 做市策略数学建模的经典论文。
- 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. — 库存风险管理的高级模型。
- Cartea, A., Jaimungal, S., & Ricci, J. (2014). Buy Low, Sell High: A High Frequency Trading Perspective. SIAM Journal on Financial Mathematics. — 高频环境下的做市策略。
- Menkveld, A. J. (2013). High Frequency Trading and the New Market Makers. Journal of Financial Markets, 16(4), 712-740. — 高频做市商对市场质量的实证研究。
- Gould, M. D., et al. (2013). Limit Order Books. Quantitative Finance, 13(11), 1709-1742. — 限价订单簿的全面综述。
本章要点
- 做市策略通过持续提供双边报价赚取买卖价差,是市场流动性的核心来源
- 逆向选择是做市商面临的主要风险——总是在错误的方向上成交
- Avellaneda-Stoikov模型给出了最优报价的解析形式:价差随持仓和风险厌恶增加而扩大
- 库存管理通过偏斜报价来实现——净多头时调低买卖价以促进卖出,净空头时反之
- 做市策略的成功关键在于:准确估计公允价值和动态管理库存风险
- 现代做市策略通常结合统计套利信号来"偏置"报价方向,提高长期收益