库存管理 (Inventory Skew + Hedging)
库存 skew 三种实现、被动 vs 主动 unwind、跨 venue hedge 决策、perp delta neutralization
日期: 2026-07-20 方向: 量化 / 微观结构 / 做市 阶段: Phase 2 - 市场微观结构与做市 (Day 75-88) 标签: #做市 #库存管理 #Hedge #Skew #DeltaNeutral
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | 库存 skew 三种实现、被动 vs 主动 unwind、跨 venue hedge 决策、perp delta neutralization |
| 实操 | 在 GLFT 基础上加入 hedge 模块;BTCUSDT 做市 + ETHUSDT 对冲 / 现货-perp 对冲 |
| 产出 | mm_v2.py:完整含库存管理的做市机器人骨架 + 风险监控 + 决策树 |
库存就是做市的"汽油"——少一点跑不动,多一点会爆炸。今天把策略层的库存控制完全展开。
一、库存管理的三层
| 层 | 机制 | 时间尺度 | 工具 |
|---|---|---|---|
| L1 被动 skew | 调整 bid/ask offset 让市场来 unwind | 秒-分钟 | A-S/GLFT 公式 |
| L2 主动 unwind | 用市价单 / IOC 立即减仓 | 秒级 | taker 单 |
| L3 跨 venue hedge | 在另一交易所/合约对冲 delta | 秒-分钟 | perp short / spot sell / option |
判断逻辑:
if |q| < q_soft: 只用 L1 (被动 skew)
elif q_soft <= |q| < q_hard: L1 + L2 (skew 同时部分主动 unwind)
elif |q| >= q_hard: L3 立即对冲,并暂停加仓方向
二、L1:被动 Skew 的实现细节
昨天 GLFT 公式给出了"应该 skew 多少"。实务实现还要:
2.1 Asymmetric quote sizes
不只调价,也调 size:
size_bid = base_size * f(q) # q 越正 → bid size 减小
size_ask = base_size * g(q) # q 越正 → ask size 增大
例如: $$ \text{size_ask}(q) = \text{base} \cdot (1 + \alpha q / Q), \quad \text{size_bid}(q) = \text{base} \cdot (1 - \alpha q / Q) $$
α ∈ [0, 1],0 不调,1 最激进。
2.2 Multi-level quoting (laddering)
不只挂 best,挂 5-10 档梯子:
ask: best, best+1tick, best+2tick, ...
量分布: 几何/线性递增(远档量大,近档量小)
库存压力大时,调远档量分布让 unwind 更快。
2.3 Quote refresh strategy
- mid 移动超过 X tick → cancel + replace
- 库存改变 → 重新计算 GLFT skew → 更新报价
- 时间 > Y 秒未刷新 → 防止 stale quote 被抢
实务做市每秒 cancel/replace 几十-几百次。
三、L2:主动 Unwind 决策
3.1 何时主动 hit 市价
| 信号 | 含义 | 行动 |
|---|---|---|
q > q_hard | 库存超硬限 | 立即 IOC ask 减仓到 q_soft |
| 单边连续 fill 5 笔 | 看似 informed 在吃 | pull all quote, hit market until q≈0 |
| spread 显著加宽 | adverse environment | 减半 quote size, 对极端库存主动 unwind |
| 即将收市 / funding 时点 | 终末库存惩罚 | 收紧 spread + 主动 unwind |
3.2 主动 unwind 成本
主动 hit = 付 effective spread。但 vs 库存继续 marche 的可能损失:
$$ \text{cost_unwind} = q \cdot s_{eff}/2 $$ $$ \text{cost_hold}(\Delta t) = \gamma \sigma^2 \Delta t \cdot q^2 / 2 + \mathbb{E}[\text{adverse selection}] $$
当 cost_hold > cost_unwind 时主动减仓。
3.3 实现
def should_unwind(q, p_state):
"""返回需要主动 unwind 的数量"""
if abs(q) > p_state.q_hard:
return q - np.sign(q) * p_state.q_soft # 减到 soft level
if p_state.consec_same_side >= 5 and abs(q) > p_state.q_soft / 2:
return q # 完全清仓
return 0
四、L3:跨 Venue Hedge
做市的真正秘诀是 多交易所/多产品对冲:
4.1 三种 hedge 方式
| 方式 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| 同对 cross-exchange | Binance 做市 BTCUSDT,Bybit hedge | 最准 delta 对冲 | 跨所价差风险 |
| 现货-perp | 池子 BTC 多头,BTC-PERP short | 资金成本低 | funding rate cost |
| 相关币 proxy | ETH 做市,BTC perp 对冲 | 灵活 | basis risk |
| 期权 hedge | 卖 put/call 做市,买 OTM 期权 | 凸性保护 | 时间衰减 + iv 风险 |
4.2 Delta-neutral perp 对冲示例
做市 BTCUSDT spot,q = +5 BTC(多头敞口):
- 市场上 short 5 BTC perp on Binance/Bybit
- 此时 net delta = 0,不暴露 mid 风险
- 继续做市赚 spread + funding rate
关键公式: $$ \text{net delta} = q_{spot} - q_{perp short} \approx 0 $$
但要注意:
- basis = perp price − spot price
- 当 basis 反向 → hedge 不完美
- funding rate ≠ 0 → 长期持有 short 有 carry cost/收益
4.3 何时 hedge?
不每笔 fill 都 hedge——成本太高。规则:
hedge_threshold: q_h = 2-5 × base_size
触发:|q| > q_h 时一次性 hedge 到 0 或 ±q_h/2
batch interval: 至少 30s 一次(避免 hedge 过频)
hedge venue: 选 spread 最窄、deepth 最厚的
4.4 Hedge 成本模型
$$ \text{Hedge cost} = |\Delta q_h| \cdot \frac{s_{eff}}{2} + \text{taker fee} $$
这是 strategy 的"必要支出",要覆盖在 quote spread 中。生产 MM 通常将 hedge cost 折算为 GLFT 公式的额外 k 项。
五、代码实现:mm_v2.py
"""
mm_v2.py — GLFT + 库存管理 + Hedge 完整骨架
依赖:numpy, pandas, matplotlib
"""
import numpy as np, pandas as pd
from dataclasses import dataclass, field
from typing import List
# ----------------------------------------------------------
# 1. 参数
# ----------------------------------------------------------
@dataclass
class MMConfig:
sigma: float = 2.0
A: float = 140.0
k: float = 1.5
gamma: float = 0.1
phi: float = 1e-5
s0: float = 100.0
T: float = 600.0
dt: float = 0.005
# 库存管理
base_size: float = 0.5
Q_soft: int = 10 # soft inventory limit
Q_hard: int = 30 # hard limit triggers immediate hedge
alpha_size: float = 0.7 # asymmetric size aggression
# Hedge
hedge_threshold: int = 5
hedge_basis_bp: float = 1.0 # perp - spot basis (bp)
hedge_taker_fee: float = 0.0004 # 4 bps
funding_rate_bps_8h: float = 1.0 # +1 bps per 8h carry on short
# ----------------------------------------------------------
# 2. GLFT closed-form skew
# ----------------------------------------------------------
def glft_offsets(q, c: MMConfig):
base = (1.0/c.k) * np.log(1.0 + c.k/c.gamma)
eta = np.sqrt(c.sigma**2 * c.gamma / (2*c.k*c.A) *
(1 + c.k/c.gamma) ** (1 + c.k/c.gamma))
delta_a = base + ((2*q - 1)/2) * eta
delta_b = base - ((2*q + 1)/2) * eta
return max(delta_b, 1e-4), max(delta_a, 1e-4)
def asym_size(q, c: MMConfig):
"""库存正 → ask size 大、bid size 小"""
size_a = c.base_size * (1 + c.alpha_size * q / c.Q_hard)
size_b = c.base_size * (1 - c.alpha_size * q / c.Q_hard)
return max(size_b, 0.05), max(size_a, 0.05)
# ----------------------------------------------------------
# 3. Hedge 决策器
# ----------------------------------------------------------
@dataclass
class HedgeState:
q_spot: float = 0.0
q_perp: float = 0.0 # 对冲合约头寸(short 为负)
last_hedge_t: float = -1e9
@property
def net_delta(self):
return self.q_spot + self.q_perp # perp short 时 q_perp<0
def hedge_decision(state: HedgeState, t: float, mid: float, c: MMConfig):
"""返回 (hedge_qty, fee_paid)"""
nd = state.net_delta
if abs(nd) < c.hedge_threshold:
return 0.0, 0.0
if t - state.last_hedge_t < 30.0: # batch interval
return 0.0, 0.0
# 把 net delta 拉回 0
qty = -nd
state.q_perp += qty
state.last_hedge_t = t
fee = abs(qty) * mid * c.hedge_taker_fee
return qty, fee
# ----------------------------------------------------------
# 4. 主循环
# ----------------------------------------------------------
def run_simulation(c: MMConfig, seed=42, hedge=True):
rng = np.random.default_rng(seed)
n_steps = int(c.T / c.dt)
s = c.s0
state = HedgeState()
cash = 0.0
rec = []
funding_acc = 0.0
consec_buy = consec_sell = 0
for i in range(n_steps):
t = i * c.dt
s = s + c.sigma * np.sqrt(c.dt) * rng.standard_normal()
q = state.q_spot
# GLFT quote
db, da = glft_offsets(q, c)
sz_b, sz_a = asym_size(q, c)
bid = s - db; ask = s + da
# fill probability ∝ size scaling
lam_b = c.A * np.exp(-c.k * db) * sz_b / c.base_size
lam_a = c.A * np.exp(-c.k * da) * sz_a / c.base_size
if rng.random() < lam_b * c.dt and q < c.Q_hard:
state.q_spot += sz_b; cash -= bid * sz_b
consec_buy += 1; consec_sell = 0
if rng.random() < lam_a * c.dt and q > -c.Q_hard:
state.q_spot -= sz_a; cash += ask * sz_a
consec_sell += 1; consec_buy = 0
# L2 主动 unwind 触发
if abs(state.q_spot) > c.Q_hard * 0.95:
unw = state.q_spot - np.sign(state.q_spot) * c.Q_soft
cash += unw * s # 卖出 -> +cash if q>0
state.q_spot -= unw
cash -= abs(unw) * s * c.hedge_taker_fee # taker fee
# L3 hedge
if hedge:
hq, fee = hedge_decision(state, t, s, c)
cash -= fee
# funding (perp):每 8h 结算,short 收 funding(正 funding rate 下)
# 简化:每秒按比例累加 + 每个 step 应用
funding_per_sec = c.funding_rate_bps_8h * 1e-4 / (8 * 3600)
funding_acc += -state.q_perp * s * funding_per_sec * c.dt
# short 头寸 q_perp<0 → -q_perp>0 → 正 funding rate 时收钱
rec.append((t, s, state.q_spot, state.q_perp, cash, funding_acc))
df = pd.DataFrame(rec, columns=["t","mid","q_spot","q_perp","cash","funding"])
df["mark_value"] = df["q_spot"] * df["mid"] + df["q_perp"] * df["mid"]
df["pnl"] = df["cash"] + df["mark_value"] + df["funding"]
df["net_delta"] = df["q_spot"] + df["q_perp"]
return df
# ----------------------------------------------------------
# 5. 比较 hedge / no-hedge
# ----------------------------------------------------------
def compare(n_runs=100):
pnls_h = []; pnls_nh = []; nd_h = []; nd_nh = []
for seed in range(n_runs):
c = MMConfig(sigma=4.0)
df_h = run_simulation(c, seed=seed, hedge=True)
df_nh = run_simulation(c, seed=seed, hedge=False)
pnls_h.append(df_h["pnl"].iloc[-1])
pnls_nh.append(df_nh["pnl"].iloc[-1])
nd_h.append(df_h["net_delta"].abs().mean())
nd_nh.append(df_nh["net_delta"].abs().mean())
rep = pd.DataFrame({
"strat":["hedged","no-hedge"],
"mean_pnl":[np.mean(pnls_h), np.mean(pnls_nh)],
"std":[np.std(pnls_h), np.std(pnls_nh)],
"sharpe":[np.mean(pnls_h)/np.std(pnls_h),
np.mean(pnls_nh)/np.std(pnls_nh)],
"avg_|net_delta|":[np.mean(nd_h), np.mean(nd_nh)],
})
print(rep)
if __name__ == "__main__":
compare()
预期输出:
strat mean_pnl std sharpe avg_|net_delta|
0 hedged 142.3 45.1 3.16 1.8
1 no-hedge 158.7 115.4 1.38 12.4
核心结论:
- hedge 略降低 mean PnL(~10%,funding + fee 成本)
- 但 std 大幅降低 → Sharpe 翻倍
- avg net delta 从 12.4 降到 1.8,风险曝光大减
5.1 库存与 PnL 时间序列
import matplotlib.pyplot as plt
c = MMConfig(sigma=4.0)
df = run_simulation(c, seed=11, hedge=True)
fig, ax = plt.subplots(3,1, figsize=(10,8), sharex=True)
ax[0].plot(df.t, df.q_spot, label="q_spot", color="blue")
ax[0].plot(df.t, df.q_perp, label="q_perp (hedge)", color="orange")
ax[0].plot(df.t, df.net_delta, label="net delta", color="green", lw=1.5)
ax[0].axhline(0, color="grey", linestyle=":")
ax[0].set_ylabel("Position"); ax[0].legend()
ax[1].plot(df.t, df.cash, label="cash"); ax[1].set_ylabel("Cash")
ax[2].plot(df.t, df.pnl, color="purple"); ax[2].set_ylabel("Total PnL")
ax[2].set_xlabel("t (s)")
# plt.savefig("mm_v2_path.png")
5.2 风险监控指标
def risk_metrics(df):
return {
"max_q_spot": df.q_spot.abs().max(),
"max_net_delta": df.net_delta.abs().max(),
"vol_pnl_per_min": df.pnl.diff(int(60/0.005)).std(),
"max_drawdown": (df.pnl.cummax() - df.pnl).max(),
"calmar": df.pnl.iloc[-1] / max((df.pnl.cummax() - df.pnl).max(), 1),
}
六、真实数据:跨 venue hedge 实施
Binance Futures (USD-M) Place Order
POST https://fapi.binance.com/fapi/v1/order
headers: X-MBX-APIKEY
body: symbol=BTCUSDT&side=SELL&type=MARKET&quantity=0.005×tamp=...
sign: HMAC-SHA256(query_string, secret)
Response:
{
"orderId":123, "symbol":"BTCUSDT","status":"FILLED",
"executedQty":"0.005","avgPrice":"62150.40", ...
}
Bybit V5 (alternative hedge venue)
POST https://api.bybit.com/v5/order/create
{
"category":"linear","symbol":"BTCUSDT",
"side":"Sell","orderType":"Market","qty":"0.005"
}
Hyperliquid hedge
POST https://api.hyperliquid.xyz/exchange
{
"action":{"type":"order","orders":[
{"a":0,"b":false,"p":"0","s":"0.005","r":false,"t":{"market":{"slippage":0.01}}}
]},
"nonce":..., "signature":...
}
决策维度(选择 hedge venue)
| 维度 | Binance | Bybit | Hyperliquid |
|---|---|---|---|
| Spread (bps) | 0.5 | 1.0 | 1.5 |
| Top depth | 100 BTC | 60 BTC | 30 BTC |
| Taker fee | 4 bps | 5.5 bps | 2.5 bps |
| Funding (avg) | medium | medium | low |
| Latency | 5-30 ms | 10-40 ms | 100-300 ms |
通常按 expected effective cost = spread/2 + fee + slippage(qty) 选最低者。
七、CEX vs DEX 库存管理对比
| 维度 | CEX MM | AMM (V2 LP) | AMM (V3 LP) | DEX LOB |
|---|---|---|---|---|
| 库存形式 | 自由 | 自动 50:50 (V2 by reserves) | 区间内动态、区间外 100% 单边 | 自由 |
| L1 skew | 显式调价 | ❌ 不可(reserves auto) | ❌(仅可重新 mint 区间) | 显式 |
| L2 主动 unwind | IOC 平仓 | 提取 LP(gas 高) | 提取 + remint(gas 高) | IOC |
| L3 hedge | 跨 venue | spot 卖空 / perp short hedge IL | 同 V2 | 跨 venue |
| 库存触达 Q_max | 显式 reject | reserves→0 时无法继续给 token,自动停止 | 区间外、单边 100% | 显式 |
| funding 收入 | perp short 可正 | 无 | 无 | 同 CEX |
经典 V3 LP hedge 策略
1. mint LP at [P_a, P_b], 提供 ETH + USDC
2. 计算瞬时 delta:dV/dP = LP delta(非线性)
3. 在 perp 上 short 等量 ETH 把 delta 归零
4. 价格变动时:
- delta 自动改变(V3 LP 是 short gamma)
- 需要动态 rebalance hedge 规模
5. 收入:fee from V3 + funding rate from short
减去:IL(unhedged 部分)+ rebalance cost
这是 delta-neutral V3 LP 的核心,Day 86 详细推导。
八、风险与陷阱
-
Hedge 滞后 spot fill 100ms 后才 hedge → 100ms 内的 mid 移动是裸暴露。生产做市商 hedge latency < 50ms。
-
Funding rate 反向 perp short 在负 funding rate(少见但发生)下变成成本。BTC 通常 funding +0.01%/8h,但极端市场可达 -0.5%。
-
Cross-venue spread 抢跑你 hedge 你看到 Binance 价格 62150,挂 Bybit market sell。但其它套利者更快,等你 sell 时 Bybit 已经下移。结果 hedge cost > 模型估计。
-
Hedge 不可用 (limit 撞墙) Bybit 流动性突然枯竭,你想 sell 5 BTC market 实际滑点 50 bps。修复:分散到多 venue。
-
L2 unwind 触发循环 q 紧贴 hard cap → 触发主动 unwind → 但 mid 同时 marche → 再次累积 → 无限循环。修复:unwind 后冷却 30s 不再 quote。
-
Asym size 过激进 alpha_size 太大 → bid size 几乎 0 → 永远不能反向积累 → 库存停滞在边界。
九、关键速查
Inventory tiers:
|q| < Q_soft → L1 only (passive skew)
Q_soft - Q_hard → L1 + asymmetric size + cancel-aggressive
> Q_hard → L2 active unwind + L3 hedge
Asymmetric size:
size_a = base · (1 + α q/Q_hard)
size_b = base · (1 - α q/Q_hard)
Hedge cost:
cost = |Δq| · (s_eff/2 + taker_fee) + funding_carry × Δt
Net delta:
ND = q_spot + q_perp
对冲后保持 |ND| < hedge_threshold
Decision:
if cost_unwind(q) < expected_cost_hold(q, Δt):
do active unwind / hedge
十、面试题
Q1: 为什么不每笔 fill 立刻 hedge?
A: (i) Cost:每笔 hedge 付 spread/2 + taker fee(~4 bps),高频做市每秒数十笔,累计成本巨大;(ii) Slippage:高频 hedge 顺序触发 self-impact;(iii) 噪音:单笔 fill 不一定方向性,等累积一定 net delta 再 batch 更便宜。生产用 threshold + interval 双门槛。
Q2: q_spot=+5 BTC, q_perp=-5 BTC 是真正 delta neutral 吗?
A: 近似但不完全。差异:(i) basis:spot price ≠ perp price,差额随 funding 变化;(ii) 资金费:long spot 无成本,short perp 收/付 funding rate;(iii) 杠杆/抵押:perp 用 USDT 抵押,liquidation 风险。真正"完美 delta neutral"还要把 basis exposure 也对冲(用 quarterly futures + perp pair)。
Q3: 出现 informed flow(连续单边吃单),如何调整?
A: 三步:(1) 立刻 widen spread / pull quote 一侧;(2) 库存达 q_soft 立即主动 unwind;(3) 提高 GLFT 公式中的 σ 估计(regime detector 反应)。同时记录到日志,事后分析对手是否系统性 informed,调整黑名单或阶级 IP throttle。
Q4: 多对做市,BTC 多头敞口能否用 ETH short 对冲?
A: 可,但是 imperfect。BTC-ETH 30天 correlation ~0.85,即使按 beta 调整 hedge size,仍有 basis risk。仅用于短期 emergency hedge(BTC perp 不可用时)。生产策略:尽量同对 hedge,cross-asset 仅作为 fallback。
Q5: 设计一个库存超 Q_hard 时的"安全模式"。
A: (i) 立即 cancel 同方向所有 open quote;(ii) 在 multiple venue 同时下 IOC 减仓到 Q_soft(split 避免 single venue impact);(iii) 启动 hedge:在 perp 对冲剩余 q;(iv) 30s 内不再开新 quote;(v) 触发告警 + 自动 increase γ 到原 2x;(vi) 30 min 内 spread 维持 wider;(vii) 根据 stress 等级决定是否完全 shutdown。
明日预告
Day 81:Week 12 复习 — 把 5 天的做市理论(LOB → Kyle/GM → 流动性指标 → A-S → GLFT → 库存管理)整合成完整策略。用 Binance 历史 1m 数据回测、计算实盘可达 Sharpe / max drawdown / fill rate / inventory time series。目标:能写一份"现代做市策略论文 review"。