第22章:订单流与做市策略
做市商的订单流管理
做市商这个角色,说白了就是市场的「流动性供应商」。你想想看,如果没有做市商,你想买100手BTC,可能得等半天才有人卖给你。做市商就是那个随时准备跟你成交的人。
我个人习惯把做市商的订单流管理分成三个层面:
- 报价层:在买一和卖一价挂单,赚取价差
- 库存层:管理手中持有的资产数量
- 风险层:控制单边持仓暴露的风险
我在项目中遇到过最典型的场景:一个做市商在ETH上挂了500张买单和500张卖单,结果突然来了个大单把卖单全吃了。这时候他的库存从0变成了-500(做空),如果价格继续涨,他就亏大了。
核心观点:做市商的本质不是赌方向,而是赚取买卖价差。库存风险是最大的敌人。
库存风险与订单流平衡
库存风险怎么理解?就是你手里拿着货,价格跌了你亏钱。做市商最怕的就是库存方向和市场走势相反。
我记得有一次做模拟回测,一个简单的对称做市策略,在震荡市里赚得盆满钵满。结果一遇到单边行情,库存直接爆仓。为什么会这样?因为对称做市策略没有考虑订单流的不平衡。
订单流平衡的核心指标:
| 指标 | 含义 | 做市商应对 |
|---|---|---|
| 净订单流 | 买单量 - 卖单量 | 净买入多时,提高卖价,降低买价 |
| 订单流失衡 | 主动买卖单的比例 | 失衡严重时,暂停报价或调整价差 |
| 库存偏离度 | 当前库存 / 目标库存 | 偏离过大时,主动平仓 |
实战技巧:我曾经在回测中发现,当订单流失衡超过2个标准差时,做市策略的亏损概率会飙升到70%以上。这时候最好的策略不是继续做市,而是暂停报价,等市场冷静下来。
Python实现简单做市策略
下面我写一个最简单的做市策略实现。这个策略的逻辑是:在买一价和卖一价各挂一个单,当成交后调整报价,控制库存风险。
import pandas as pd
import numpy as np
from typing import Tuple
class SimpleMarketMaker:
"""简单做市策略"""
def __init__(self, spread: float = 0.001,
max_inventory: int = 100,
target_inventory: int = 0):
"""
spread: 价差比例,默认0.1%
max_inventory: 最大库存限制
target_inventory: 目标库存
"""
self.spread = spread
self.max_inventory = max_inventory
self.target_inventory = target_inventory
self.inventory = 0 # 当前库存
self.pnl = 0 # 盈亏
def calculate_quotes(self, mid_price: float) -> Tuple[float, float]:
"""计算买卖报价"""
# 根据库存偏离调整报价
inventory_skew = (self.inventory - self.target_inventory) / self.max_inventory
# 库存为正(做多),降低买价,提高卖价
# 库存为负(做空),提高买价,降低卖价
bid_price = mid_price * (1 - self.spread/2) - inventory_skew * mid_price * 0.001
ask_price = mid_price * (1 + self.spread/2) - inventory_skew * mid_price * 0.001
return bid_price, ask_price
def on_trade(self, price: float, side: str, quantity: int):
"""处理成交事件"""
if side == 'buy': # 我们卖出了(库存减少)
self.inventory -= quantity
self.pnl += price * quantity
elif side == 'sell': # 我们买入了(库存增加)
self.inventory += quantity
self.pnl -= price * quantity
def check_risk(self) -> bool:
"""检查是否触发风险限制"""
if abs(self.inventory) > self.max_inventory:
print(f"⚠️ 库存超限!当前库存: {self.inventory}")
return True
return False
# 模拟回测
def backtest_market_maker(data: pd.DataFrame, mm: SimpleMarketMaker):
"""回测做市策略"""
results = []
for i in range(len(data)):
mid_price = data['close'].iloc[i]
# 计算报价
bid, ask = mm.calculate_quotes(mid_price)
# 模拟成交(简化版)
if data['volume'].iloc[i] > 1000: # 大成交量触发
# 假设50%概率成交在买价,50%在卖价
if np.random.random() < 0.5:
mm.on_trade(bid, 'sell', 10) # 我们买入
else:
mm.on_trade(ask, 'buy', 10) # 我们卖出
# 检查风险
if mm.check_risk():
# 触发风险控制,强制平仓
print(f"触发风控,当前盈亏: {mm.pnl:.2f}")
break
results.append({
'mid_price': mid_price,
'bid': bid,
'ask': ask,
'inventory': mm.inventory,
'pnl': mm.pnl
})
return pd.DataFrame(results)
# 使用示例
if __name__ == "__main__":
# 生成模拟数据
np.random.seed(42)
dates = pd.date_range('2024-01-01', periods=1000, freq='1min')
data = pd.DataFrame({
'close': 50000 + np.cumsum(np.random.randn(1000) * 10),
'volume': np.random.randint(100, 5000, 1000)
}, index=dates)
# 初始化做市商
mm = SimpleMarketMaker(spread=0.001, max_inventory=50)
# 回测
results = backtest_market_maker(data, mm)
print(f"最终盈亏: {mm.pnl:.2f}")
print(f"最终库存: {mm.inventory}")
⚠️ 避坑指南:我曾经在实盘测试这个策略时,发现一个严重问题——滑点。回测里假设成交价就是报价价,但实际交易中,你的买单可能被吃穿,成交价远高于报价。所以做市策略一定要考虑滑点模型。
做市策略的核心逻辑图
下面这张图展示了做市策略的核心流程,我把它画成了SVG,方便你理解整个决策链条:
做市策略的进阶思考
上面这个策略虽然简单,但已经包含了做市的核心要素。实际生产中,你还需要考虑:
- 订单簿深度:不能只看最优价,要看整个订单簿的厚度
- 成交概率模型:不同价位的挂单成交概率不同,需要动态调整
- 多品种对冲:比如做市BTC的同时,用ETH期货对冲风险
个人经验:我建议刚开始做做市策略时,先用模拟盘跑一个月。别急着上实盘。做市策略看起来简单,但坑特别多。我曾经因为一个库存计算的小bug,在回测里亏了30%——还好是模拟盘。
做市策略的核心,说白了就是一句话:用订单流管理库存风险,用价差覆盖交易成本。你只要把这两点想明白了,做市策略的基本框架就有了。