流动性指标 (Liquidity Metrics)
spread 类指标、深度类指标、价格冲击类(Amihud / Kyle / Hasbrouck)、Roll 隐式 spread
日期: 2026-07-17 方向: 量化 / 微观结构 / 做市 阶段: Phase 2 - 市场微观结构与做市 (Day 75-88) 标签: #做市 #微观结构 #流动性 #Amihud #Roll
今日目标
| 类型 | 内容 |
|---|---|
| 学习 | spread 类指标、深度类指标、价格冲击类(Amihud / Kyle / Hasbrouck)、Roll 隐式 spread |
| 实操 | 用 Binance 1m/1d K线计算 BTC/ETH/SOL 三对 Amihud、effective spread、Kyle 经验 λ |
| 产出 | liquidity.py:6 个指标、不同时间尺度对比、跨币流动性热图 |
流动性是看不见的,但可以从多个角度估测。今天我们把昨天 Kyle 理论和 microstructure 落到 6 个可计算的指标。
一、流动性的三维定义
Black (1971) 给的经典定义:"Liquidity is the ability to trade quickly without significant price impact"。三个维度:
| 维度 | 含义 | 代表指标 |
|---|---|---|
| Tightness | spread 多窄 | quoted/effective spread |
| Depth | 不动价格能成交多少 | 顶档量、累积深度 |
| Resiliency | 价格冲击恢复速度 | impact reversal、Hasbrouck VAR |
实务做市看四类:
- Spread 类:Quoted、Effective、Realized、Roll
- Depth 类:Top-of-book、N-tick depth、Top-N depth
- Impact 类:Kyle λ、Amihud、Hasbrouck λ、Square-root law
- Time 类:trade arrival rate、turnover
二、Spread 指标
2.1 Quoted Spread
$$ s_{quoted} = a - b, \quad s_{quoted}^{rel} = \frac{a-b}{m} $$
最直观但易被博弈:做市商挂 1-tick spread 但 size=$10,看似 tight,深度为 0。
2.2 Effective Spread
衡量 taker 实际付出的"过桥费":
$$ s_{eff} = 2 \cdot |p_{trade} - m_{trade}| $$
其中 m_trade 是成交时的 mid。如果 taker buy p > m,差额×2 就是隐含 round-trip 成本。
关键:可以用公开 trade tape 计算,无需簿。
2.3 Realized Spread
成交后等 5min/15min,看价格是否回归(noise)还是 marche(informed):
$$ s_{real} = 2 \cdot D \cdot (p_{trade} - m_{t+\Delta}) $$
其中 D = +1 buy / -1 sell。s_eff − s_real 就是 adverse selection cost——昨天 GM 模型的 AS。
2.4 Roll (1984) 隐式 Spread
只用收盘价、不需簿:
$$ s_{Roll} = 2 \sqrt{-\text{Cov}(r_t, r_{t-1})} $$
直觉:在 noise dominated 市场,价格在 bid 和 ask 间 bouncing,相邻 return 负相关。负 cov 越大,spread 越大。
Crypto 应用:当你只能拿到日 K 线(小币 / DEX 池),Roll 是唯一能估 spread 的方法。
三、Depth 指标
3.1 Top-of-Book Depth
V_b1、V_a1——best 档量。但 hidden / iceberg 让真实深度 > 显示深度。
3.2 N-tick Depth
best ± N tick 内累计量。Binance BTCUSDT 5-tick 深度通常 50-200 BTC(峰值时段更厚)。
3.3 Cost-to-Trade-Q
更实用:成交 Q 单位的平均加权价 vs mid,给出"实际滑点":
$$ \text{slippage}(Q) = \frac{\sum p_i q_i}{Q} - m, \quad \sum q_i = Q $$
Day 83 我们用这个推 Almgren-Chriss。
四、Impact 指标
4.1 Amihud (2002) Illiquidity
最经典、最简单:
$$ \boxed{\text{ILLIQ}_t = \frac{|r_t|}{V_t}} $$
r_t 是日收益率,V_t 是日成交额。直觉:每 $1 交易引起的 |return|。
与 Kyle λ 关系:Amihud ≈ Kyle λ / 价格 — 因为 Δp = λ q 取绝对值除以总流量后约等于 λ / m。
4.2 Hasbrouck (2009) VAR-based λ
更精细:用 trade-by-trade VAR 估非对称冲击。 $$ r_t = \sum \beta_i r_{t-i} + \lambda \cdot q_t^{signed} + \epsilon_t $$
MM 实务:用 Hasbrouck 估永久冲击,区分临时/永久 part。
4.3 Square-root Law
实证(Almgren-Chriss、Tóth-Bouchaud):
$$ \text{Impact}(Q) \approx \sigma_{daily} \cdot \kappa \cdot \sqrt{Q / V_{daily}} $$
κ ≈ 0.1 - 1.0。在 BTCUSDT Binance 通常 κ ≈ 0.4。
为什么 √ 而非线性?(i) 大单切片执行被吸收;(ii) 永久 part 仅由信息含量决定,size 边际信息含量递减。
五、代码实现:liquidity.py
"""
liquidity.py — 6 类流动性指标 + Binance 真实数据
依赖:numpy, pandas, requests, matplotlib
"""
import numpy as np, pandas as pd, requests
from dataclasses import dataclass
# ----------------------------------------------------------
# 1. 取 Binance K 线 + trade
# ----------------------------------------------------------
def klines(symbol, interval="1m", limit=1000):
url = "https://fapi.binance.com/fapi/v1/klines"
r = requests.get(url, params={"symbol":symbol, "interval":interval, "limit":limit}).json()
cols = ["open_time","open","high","low","close","volume",
"close_time","quote_vol","ntrades","taker_buy_base","taker_buy_quote","_"]
df = pd.DataFrame(r, columns=cols)
for c in ["open","high","low","close","volume","quote_vol","taker_buy_base","taker_buy_quote"]:
df[c] = df[c].astype(float)
df["ts"] = pd.to_datetime(df["open_time"], unit="ms")
return df
# ----------------------------------------------------------
# 2. 6 个指标
# ----------------------------------------------------------
def quoted_spread(book_df):
"""book_df 含 best_bid, best_ask 列"""
return (book_df["best_ask"] - book_df["best_bid"]).mean()
def effective_spread_from_trades(trades_df):
"""trades_df: price, side(buy/sell), mid"""
s = 2 * (trades_df["price"] - trades_df["mid"]).abs()
return s.mean()
def realized_spread(trades_df, k=300):
"""trades_df 包含 mid_future (k 秒后 mid)"""
D = trades_df["side"].map({"buy":1, "sell":-1})
return (2 * D * (trades_df["price"] - trades_df["mid_future"])).mean()
def roll_spread(returns: pd.Series):
""" Roll 1984 隐式 spread = 2 sqrt(-cov(r_t, r_t-1)) """
cov = returns.autocorr(lag=1) * returns.var()
if cov >= 0:
return 0.0
return 2 * np.sqrt(-cov)
def amihud_illiq(df):
"""日 Amihud = mean(|r| / V_quote)"""
df = df.copy()
df["ret"] = df["close"].pct_change()
illiq = (df["ret"].abs() / df["quote_vol"]).replace([np.inf, -np.inf], np.nan).dropna()
return illiq.mean()
def hasbrouck_lambda(df_1m):
"""
用 1 分钟 净成交方向 net_signed_volume regress return
Binance 给 taker_buy_base / volume,可推 net buy ratio
net_signed_q = taker_buy_base - (volume - taker_buy_base) = 2*taker_buy - volume
"""
df = df_1m.copy()
df["net_q"] = 2 * df["taker_buy_base"] - df["volume"]
df["ret"] = df["close"].pct_change()
df = df.dropna()
cov = np.cov(df["ret"], df["net_q"])[0,1]
var = np.var(df["net_q"])
return cov / var if var > 0 else None
# ----------------------------------------------------------
# 3. 跑三个交易对
# ----------------------------------------------------------
def liquidity_panel(symbols=("BTCUSDT","ETHUSDT","SOLUSDT")):
rows = []
for sym in symbols:
d_1m = klines(sym, "1m", 1000)
d_1d = klines(sym, "1d", 90)
d_1d["ret"] = d_1d["close"].pct_change()
roll = roll_spread(d_1d["ret"].dropna())
amihud = amihud_illiq(d_1d)
hasb = hasbrouck_lambda(d_1m)
# 简化 quoted spread 用 1m high-low 估算
d_1m["pseudo_spread_bps"] = (d_1m["high"] - d_1m["low"]) / d_1m["close"] * 1e4
rows.append({
"symbol": sym,
"daily_vol_quote": d_1d["quote_vol"].mean(),
"amihud_illiq": amihud,
"roll_spread": roll,
"hasbrouck_lambda": hasb,
"pseudo_spread_bps_1m": d_1m["pseudo_spread_bps"].median(),
})
return pd.DataFrame(rows)
if __name__ == "__main__":
panel = liquidity_panel()
print(panel.to_string(index=False))
预期输出(数量级示例):
symbol daily_vol_quote amihud_illiq roll_spread hasbrouck_lambda pseudo_spread_bps_1m
BTCUSDT 3.2e10 2.1e-13 0.00012 1.4e-10 4.5
ETHUSDT 1.8e10 5.6e-13 0.00018 4.1e-10 5.8
SOLUSDT 4.1e9 3.4e-12 0.00045 2.3e-9 12.3
观察:
- Amihud 排序 BTC < ETH < SOL,与"小币更不流动"直觉一致
- Hasbrouck λ 同序,证明 Kyle λ ≈ Amihud × P 的等价
- pseudo spread 用 1m H-L 是粗估,但跨 symbol 可比
5.1 跨币热图绘制
import matplotlib.pyplot as plt
panel = liquidity_panel(("BTCUSDT","ETHUSDT","SOLUSDT","DOGEUSDT","SUIUSDT"))
metrics = ["amihud_illiq","roll_spread","hasbrouck_lambda","pseudo_spread_bps_1m"]
norm = panel[metrics].apply(lambda c: (c - c.min())/(c.max()-c.min()), axis=0)
plt.imshow(norm.values, cmap="hot", aspect="auto")
plt.yticks(range(len(panel)), panel["symbol"]); plt.xticks(range(len(metrics)), metrics, rotation=30)
plt.colorbar(label="Normalized illiquidity")
plt.title("流动性热图 — 越红越不流动")
plt.tight_layout()
# plt.savefig("liq_heatmap.png")
六、真实数据接入
Binance Klines 字段
GET https://fapi.binance.com/fapi/v1/klines?symbol=BTCUSDT&interval=1m&limit=2
返回数组(每行 12 字段):
[
[ 1709337540000, "62150.10","62156.30","62149.90","62154.50",
"12.345", 1709337599999, "767001.23", 145, "6.123","380456.12","0" ],
...
]
索引含义:
0 open_time
1 open
2 high
3 low
4 close
5 volume (base)
6 close_time
7 quote_vol (USD)
8 num_trades
9 taker_buy_base_vol
10 taker_buy_quote_vol
11 ignore
计算 maker/taker imbalance
imbalance = (taker_buy_base - (volume - taker_buy_base)) / volume
∈ [-1, +1],正=主动买占优,负=主动卖
这个 imbalance 是 Hasbrouck λ 的输入,也是 OFI 的简化版(Day 82)。
七、CEX vs DEX 对比
| 指标 | CEX (Binance BTC) | DEX V2 (Uni ETH/USDC) | DEX V3 集中流动性 | DEX LOB (Hyperliquid) |
|---|---|---|---|---|
| Quoted spread | 0.5-1 bps | 30 bps (V2 0.3% fee) | 5-10 bps (active range) | 1-2 bps |
| Top depth | 20-100 BTC | $2M reserves | $200K active | 5-30 BTC |
| Amihud | 1e-13 | 1e-11 | 1e-12 | 5e-13 |
| Effective vs quoted | quoted ≈ eff | eff = quoted = pool fee | eff < quoted (bonus) | quoted ≈ eff |
| Roll spread | 极低 | 高(block 离散) | 中 | 低 |
| Resiliency | 秒级回归 | 1 block (~12s ETH) | 同上 + arb | 100ms-1s |
| Hidden liq | iceberg | 无 | 无 | 链下 hidden |
| 隐含成本来源 | spread + fee | fee + IL + slippage | 同上 + concentration risk | spread + fee + funding |
核心观察:
- AMM 的 "spread" 是协议参数(fee tier),与微观结构无关——所以 V2 永远 30 bps(或 5/30/100 bps tier),不会因为流动性变多而收窄。
- V3 通过让 LP 选择集中区间,间接产生"有效 spread"。窄区间 LP 等价于"在两个限价单间做市"。
- DEX LOB(Hyperliquid)的 spread 已经接近 CEX,但 funding rate 机制不同。
八、风险与陷阱
-
Amihud 在低 volume 日 explodes 分母 V→0 导致 spike。crypto 节假日 / 周末必须 winsorize 到 1-99 percentile。
-
Roll cov ≥ 0 → 公式失效 存在趋势市(informed dominated)时 cov 转正,Roll 给出 0 spread。说明该窗口"非 noise dominated",要切换 Hasbrouck。
-
K 线 high-low 不是真 spread 1m HL 反映窗口内最大波动而非瞬时 spread。在 1s 级别 spread 远窄于此。但跨币比较仍可用。
-
Maker rebate 让 effective spread 为负 Binance Futures 顶级做市商可能有 -1 bps maker rebate。这时挂单+成交相当于 spread 收益。
-
Latency 导致的 stale liquidity 你看到的 best ask 100 BTC 可能在 5ms 后被对手 cancel。真实可执行深度 ≪ snapshot 深度。生产估计要用历史 fill 数据反推。
-
Multi-venue fragmentation BTC 同时在 Binance / Bybit / OKX / CME。单交易所 Amihud 高估真实 illiquidity。
九、关键速查
Quoted spread s = a - b
Quoted relative s/m
Effective spread 2|p_trade - m_trade|
Realized spread 2D(p_trade - m_{t+Δ})
Adverse selection eff - real
Roll spread 2 √(−Cov(r_t, r_{t-1}))
Amihud mean(|r|/V_quote)
Hasbrouck λ OLS slope of return on signed volume
Cost-to-trade Q ∫ p(q)dq / Q − m
Square-root law impact ∝ σ √(Q/V)
经验阈值(BTC 现货 1m)
quoted spread: 1 - 2 bps (1 = tight, 4 = stress)
top depth: 1 - 5 BTC (>5 → very deep)
1m volume: $5 - 50M
Amihud (daily): 1e-13 - 5e-13
十、面试题
Q1: 为什么 Quoted spread 不能单独用于流动性评估?
A: (i) 仅看 best 1 档掩盖 hidden / iceberg;(ii) 尺寸不对称(best ask 0.1 BTC 与 best ask 50 BTC 同 spread 但流动性截然不同);(iii) 易被 quote-stuffing 压窄;(iv) 不反映 resiliency。必须配合 effective spread + depth。
Q2: 给一个只有日收盘价的小市值币,如何估 spread?
A: 用 Roll 1984:
s = 2√(-cov(r_t, r_{t-1}))。如果 cov ≥ 0 用 Corwin-Schultz high-low 估计或 Abdi-Ranaldo close-high-low。Crypto 上 Hasbrouck Bayesian 是更稳健的选择。
Q3: Amihud 与 Kyle λ 的数学关系?
A:
Amihud = E[|r|/V] = E[|λq/p|/V]。如果q ≈ V(即所有量都是 signed flow),则Amihud ≈ λ/p。所以 Amihud 是 price-normalized λ,跨资产可比。
Q4: 在 V3 LP 上下文中,"effective spread" 怎么定义?
A: V3 LP 在区间
[P_a, P_b]内提供流动性,swap 时实际过桥成本 = pool fee tier (5/30/100 bps) + slippage along curve。特殊:当区间外,LP 暴露完全转为单边代币,相当于 spread 突变到 +∞。所以 V3 effective spread 是状态依赖的。
Q5: 流动性危机时 (e.g. May 19 2021) 哪些指标最先恶化?
A: 顺序:(1) Top depth 立即蒸发(MM 撤单);(2) Quoted spread 跳 10-100x;(3) Effective > quoted(taker 必须 cross 多档);(4) Amihud 飙升;(5) Roll cov 转正(trending market);(6) 多 venue 价差扩大。做市启示:top depth + spread 是最敏感的早期信号。
明日预告
Day 78:Avellaneda-Stoikov 做市模型 — 经典论文 "High-frequency trading in a limit order book" (2008)。今天我们会从 HJB 方程严格推导最优 bid/ask offset 的闭式解,看到 γ(风险厌恶)、k(订单到达率)、σ(vol)三个参数如何决定报价。这是接下来 5 天做市建模的脊柱。