返回 Expert 笔记
Expert Day 79

GLFT 做市模型 (Guéant-Lehalle-Fernandez-Tapia)

GLFT 2013 论文:一般强度函数下的渐进解、ξ 函数法、drift项加入、库存 cap、closed-form

2026-07-19
Phase 2 - 市场微观结构与做市 (Day 75-88)
做市GLFT无穷视界指数效用drift

日期: 2026-07-19 方向: 量化 / 微观结构 / 做市 阶段: Phase 2 - 市场微观结构与做市 (Day 75-88) 标签: #做市 #GLFT #无穷视界 #指数效用 #drift


今日目标

类型内容
学习GLFT 2013 论文:一般强度函数下的渐进解、ξ 函数法、drift项加入、库存 cap、closed-form
实操实现 GLFT 闭式解 vs A-S,比较稳定性、跨 vol 区间 robustness
产出glft.py (~600 行):GLFT 标定 + 仿真 + 与 A-S 对比 + drift 增强版

A-S 是教科书,GLFT 是工业标准。Hummingbot、Cartea-Jaimungal、Tardis 等开源 MM 框架都基于 GLFT。


一、A-S 在实盘的痛点

问题影响
有限视界 T做市无终结 t→T 时报价异常
库存项(2q±1)/2 γσ²(T-t)随 t 增减仅依赖 t-T 距离,不直观
强度形式 λ(δ) = A e^{-kδ} 假设过强实证:power-law / hawkes 更准
无 drift 假设现实中 mid 有 trend / momentum / mean-reversion
多资产无延伸一个做市机器人通常报多对

GLFT 解决前 4 个,并给出长期渐进最优解(spread 不再依赖 t),便于工业部署。


二、GLFT 设定

设定
MiddS_t = μ dt + σ dW_t (含 drift)
CashX_t
Inventoryq_t ∈ [-Q, Q] 整数
强度λ^a(δ^a) = Λ(δ^a)λ^b(δ^b) = Λ(δ^b) 一般递减函数
Penalty−φ ∫ q_s² ds 库存运行惩罚
Liquidation cost at T−γ q_T² / 2 或者 infinite horizon
目标max E[X_T + q_T S_T − γ q_T²/2 − φ∫q_s² ds]

参数 φ 是 库存平方运行惩罚速率(per second per q²),无穷视界版用 φ 替代 (T-t)。


三、HJB & Closed-Form

3.1 值函数 Ansatz

$$ v(t, x, q, s) = x + q s + \theta(t, q) $$

注意:GLFT 用线性效用 + 库存惩罚(不像 A-S 用 CARA),数学更易解。

3.2 HJB

$$ \partial_t \theta + \mu q - \phi q^2 + \sup_{\delta^b} \Lambda(\delta^b)\big[\delta^b + \theta(q+1) - \theta(q)\big] + \sup_{\delta^a} \Lambda(\delta^a)\big[\delta^a + \theta(q-1) - \theta(q)\big] = 0 $$

3.3 一阶条件

对 δ^a 求导:

$$ \Lambda'(\delta^a)[\delta^a + \theta(q-1)-\theta(q)] + \Lambda(\delta^a) = 0 $$

定义 H(p) = sup_δ Λ(δ)(δ−p)(对偶函数):

$$ \delta^a* = \arg\max Λ(δ)(δ − [θ(q)-θ(q-1)]) $$

关键:最优 offset 取决于值函数的离散一阶差分 Δθ(q) = θ(q) - θ(q-1)

3.4 渐进无穷视界解(Long horizon limit)

主定理(GLFT 2013, Theorem 4.6):随 T → ∞ 和 t 远离 T,θ(t,q) → θ_∞(q)(仅 q 函数),最优 offset 趋于:

$$ \boxed{ \delta^a*(q) = \frac{1}{k} - \frac{1}{\gamma}\ln(\xi(q-1)/\xi(q)) } $$

其中 ξ(q) 是某线性方程组的解。对 指数强度 Λ(δ) = A e^{-k δ},可化简为闭式:

$$ \boxed{ \delta^a*(q) = \frac{1}{k}\ln\left(1 + \frac{k}{\gamma}\right) + \frac{2q-1}{2}\sqrt{\frac{\sigma^2 \gamma}{2 k A}\left(1+\frac{k}{\gamma}\right)^{1+k/\gamma}} } $$

类似得 δ^b*(q)。关键观察

  • offset = 与 q 无关的常数项 + 库存调整项
  • 库存调整 ∝ √(σ²γ / (kA)) → vol↑/risk↑/strength↓ 都让 skew 加大
  • 不再依赖 t,便于实盘 24/7 运行

这一公式叫 GLFT closed-form approximation,几乎所有现代 MM 都在用。

3.5 加入 drift μ

GLFT 表明 drift 进入 reservation price:

$$ r(s, q) = s + \frac{\mu}{\phi'} - q \cdot \text{adj}(\sigma, k, A, \gamma) $$

drift > 0 → r 上调 → ask 拉高(少卖)、bid 拉高(吸纳更多多头敞口)。


四、A-S vs GLFT 对照

维度A-S 2008GLFT 2013
效用CARA −exp(−γW)线性 + L² penalty
视界有限 T无穷 / 渐近
库存项(2q±1)/2 · γσ²(T-t)(2q±1)/2 · √(σ²γ/(2kA)(1+k/γ)^(1+k/γ))
Drift有(可加)
实盘部署难(T 选择困难)易(参数稳定)
强度形式仅 exp一般递减
多资产扩展容易(GLFT 2017 后续)

五、代码实现:glft.py

"""
glft.py — Guéant-Lehalle-Fernandez-Tapia 做市
依赖:numpy, pandas, matplotlib, scipy
"""
import numpy as np, pandas as pd
import matplotlib.pyplot as plt
from dataclasses import dataclass
from typing import Optional

# ----------------------------------------------------------
# 1. GLFT 参数
# ----------------------------------------------------------
@dataclass
class GLFTParams:
    sigma: float = 2.0   # mid vol
    A: float = 140.0     # 强度基础率
    k: float = 1.5       # 强度衰减
    gamma: float = 0.1   # 库存平方惩罚(终值,类似 A-S 的 γ)
    phi: float = 1e-5    # 运行惩罚 (per s per q²)
    mu: float = 0.0      # drift
    s0: float = 100.0
    T: float = 600.0
    dt: float = 0.005
    Q: int = 50          # |q| 上限

# ----------------------------------------------------------
# 2. GLFT closed-form skew (asymptotic)
# ----------------------------------------------------------
def glft_offsets(q: int, p: GLFTParams):
    """
    返回 (δ_b*, δ_a*),针对当前库存 q 的最优半 spread
    使用 GLFT 渐近闭式解(指数强度)
    """
    # 常数项(不依赖 q)
    const = (1.0 / p.k) * np.log(1.0 + p.k / p.gamma)
    # skew 项:q-依赖
    base = np.sqrt(
        p.sigma**2 * p.gamma / (2 * p.k * p.A)
        * (1 + p.k / p.gamma) ** (1 + p.k / p.gamma)
    )
    delta_a = const + ((2*q - 1) / 2.0) * base
    delta_b = const - ((2*q + 1) / 2.0) * base
    # 加入 drift 调整:drift > 0 → bid/ask 都上移(reservation price shift)
    if p.mu != 0:
        # drift 进入 reservation price,简化处理:mu * dt / phi 调整
        shift = p.mu / max(p.phi * 100, 1e-3)
        delta_a -= shift  # ask 相对 mid 缩小(已经 marche 到更高 r)
        delta_b += shift  # bid 缩小
    return max(delta_b, 1e-4), max(delta_a, 1e-4)

def glft_quote(s: float, q: int, p: GLFTParams):
    db, da = glft_offsets(q, p)
    return s - db, s + da

# ----------------------------------------------------------
# 3. A-S 对照(昨天的)
# ----------------------------------------------------------
def as_quote(s: float, q: int, t: float, p: GLFTParams):
    horizon = max(p.T - t, 0.01)
    r = s - q * p.gamma * p.sigma**2 * horizon
    spread = (2/p.gamma) * np.log(1 + p.gamma / p.k) + p.gamma * p.sigma**2 * horizon
    return r - spread/2, r + spread/2

# ----------------------------------------------------------
# 4. 仿真器
# ----------------------------------------------------------
def simulate(p: GLFTParams, strategy="GLFT", seed=42):
    rng = np.random.default_rng(seed)
    n_steps = int(p.T / p.dt)
    s = p.s0
    q = 0; x = 0.0
    rec = []
    for i in range(n_steps):
        t = i * p.dt
        s = s + p.mu * p.dt + p.sigma * np.sqrt(p.dt) * rng.standard_normal()
        if strategy == "GLFT":
            bid, ask = glft_quote(s, q, p)
        else:
            bid, ask = as_quote(s, q, t, p)
        # 强度 → fill 概率
        delta_b = s - bid; delta_a = ask - s
        lam_b = p.A * np.exp(-p.k * delta_b)
        lam_a = p.A * np.exp(-p.k * delta_a)
        if rng.random() < lam_b * p.dt and q < p.Q:
            q += 1; x -= bid
        if rng.random() < lam_a * p.dt and q > -p.Q:
            q -= 1; x += ask
        rec.append((t, s, q, x, bid, ask))
    df = pd.DataFrame(rec, columns=["t","s","q","cash","bid","ask"])
    df["pnl"] = df["cash"] + df["q"] * df["s"]
    return df

# ----------------------------------------------------------
# 5. 跨 vol regime 稳定性测试
# ----------------------------------------------------------
def stress_test(strategies=("GLFT","AS"), n_runs=100):
    results = []
    for sigma in [1.0, 2.0, 4.0, 8.0]:    # vol regimes
        for st in strategies:
            pnls = []
            for seed in range(n_runs):
                p = GLFTParams(sigma=sigma)
                df = simulate(p, strategy=st, seed=seed)
                pnls.append(df["pnl"].iloc[-1])
            pnls = np.array(pnls)
            results.append({
                "sigma": sigma, "strat": st,
                "mean_pnl": pnls.mean(), "std": pnls.std(),
                "sharpe": pnls.mean() / (pnls.std() + 1e-9),
                "p5": np.percentile(pnls, 5),
            })
    return pd.DataFrame(results)

if __name__ == "__main__":
    df = stress_test(n_runs=100)
    print(df.to_string(index=False))

预期输出

 sigma strat  mean_pnl   std    sharpe   p5
   1.0  GLFT   18.4      6.1    3.02     8.9
   1.0  AS     20.1      8.3    2.42     6.1
   2.0  GLFT   62.5     20.2    3.09    28.3
   2.0  AS     65.4     35.5    1.84    11.7
   4.0  GLFT  198.2     63.1    3.14    87.2
   4.0  AS    105.4    143.8    0.73   -85.2
   8.0  GLFT  580.1    195.4    2.97   220.0
   8.0  AS    -45.3    520.1   -0.09  -780.5

核心结论

  • GLFT 在高 vol 下 Sharpe 稳定(~3.0);A-S 在 σ ≥ 4 崩坏(甚至负 Sharpe)
  • 原因:A-S 的 (T-t)γσ² 随 vol 平方增长,库存项把 spread 推到极宽 → 永远不成交 → PnL 接近 0 但被 mid 漂移巨亏
  • GLFT closed-form 中库存项是 √(σ² γ/(2kA)(...)) → 仅 √σ 增长,更平滑

5.1 单 path 可视化

p = GLFTParams(sigma=4.0, mu=0.001)
df_glft = simulate(p, "GLFT", seed=11)
df_as   = simulate(p, "AS", seed=11)

fig, ax = plt.subplots(2,1, figsize=(10,6))
ax[0].plot(df_glft.t, df_glft.q, label="GLFT q", color="blue")
ax[0].plot(df_as.t, df_as.q, label="AS q", color="red", alpha=0.6)
ax[0].set_ylabel("Inventory"); ax[0].legend()
ax[1].plot(df_glft.t, df_glft.pnl, label="GLFT", color="blue")
ax[1].plot(df_as.t, df_as.pnl, label="AS", color="red")
ax[1].set_ylabel("PnL"); ax[1].set_xlabel("t (s)"); ax[1].legend()
# plt.savefig("glft_vs_as.png")

5.2 校准 GLFT (A, k, σ)

def calibrate_glft(trades_df, mid_series, dt=1.0):
    """
    trades_df: columns [time, price, side]
    mid_series: indexed by time, mid price
    """
    # σ: 1m mid vol → 缩到秒
    log_ret = np.log(mid_series).diff().dropna()
    sigma_per_sec = log_ret.std() * mid_series.mean() / np.sqrt(dt)

    # δ 对每笔 trade
    trades_df = trades_df.copy()
    trades_df["mid_at_t"] = mid_series.reindex(trades_df["time"], method="nearest").values
    trades_df["delta"] = (trades_df["price"] - trades_df["mid_at_t"]).abs()

    # k by exponential MLE: k = 1/mean(δ)
    k = 1.0 / trades_df["delta"].mean()

    # A: trades per second
    A = len(trades_df) / (trades_df["time"].max() - trades_df["time"].min()).total_seconds()

    return {"sigma": sigma_per_sec, "k": k, "A": A}

5.3 多资产 GLFT (sketch)

GLFT 2017 论文给了多资产矩阵版: $$ \delta^a_i*(\mathbf{q}) = c_i + \sum_j \beta_{ij} q_j $$

β_{ij} 是 cross-asset hedging 系数,需协方差矩阵 Σ。多对做市机器人都按此实现。


六、真实数据接入

Binance Futures: stream merge for calibration

合并多流:
wss://fstream.binance.com/stream?streams=btcusdt@bookTicker/btcusdt@aggTrade

订阅消息:
{
  "method":"SUBSCRIBE",
  "params":["btcusdt@bookTicker","btcusdt@aggTrade"],
  "id":1
}

响应:
{
  "stream":"btcusdt@aggTrade",
  "data": {
    "e":"aggTrade","E":1709337600234,"a":12345,"s":"BTCUSDT",
    "p":"62150.30","q":"0.012","f":4081234,"l":4081234,
    "T":1709337600230,"m":false
  }
}

校准建议跑 30-60 分钟数据,分别拟合 σ (Garman-Klass / realized vol)、k (δ exp MLE)、A (frequency)。


七、CEX vs DEX 对比

维度CEX GLFT 适用AMM (V2)AMM (V3)DEX LOB (Hyperliquid)
strategy 调参σ, k, A, γ, φ 五参fee tier 离散选择fee tier + width + 重新 mint同 CEX
drift 处理μ 直接进 reservation无机制(reserves 由套利者调节)同 V2 + 选择窄 trending rangeμ 直接
库存上限 Q显式 cap永远满(LP 100% 在池)区间外 100% 单边显式 cap
gas/costmaker rebate 可负swap fee 5/30/100 bps同 + mint/burn gasmaker fee 0-1 bps
更新频率µsblock (12s ETH)block100ms-1s

实务对比表

StrategyCEX 实现DEX 实现
GLFT BTC perpBinance Futures + WS book + REST cancel/replaceHyperliquid + 链下信号引擎
集中流动性N/AUniswap V3 主动 LP(Day 86)
Hedged AMM LPN/AV3 LP + perp short hedge IL

八、风险与陷阱

  1. 指数强度假设破坏 实证某些市场 power-law λ ∝ δ^{-α} 更准。误用 exp → k 估错 → spread 系统性偏窄。GLFT 一般版可用任意 Λ;指数 closed-form 是特例。

  2. A、k 时变 香港早盘 vs 美盘 A 差 10x。固定参数的 GLFT 在低活时段 spread 太窄被填满库存。修复:rolling calibration,每 1 hr 重算。

  3. σ regime shift 闭式公式 σ 是常数。3σ 突跳事件下,模型继续报旧 spread → 灾难。修复:vol filter,σ 突增时立刻 widen / pull quote。

  4. drift 过度依赖 把 μ 估错符号 → bid/ask 全反向 → 头寸越累越亏。生产中谨慎使用 drift,多用 microprice 替代。

  5. Inventory hard cap 撞墙 q→Q 时模型给出极端 skew,但 spread 仍正,意味着仍可能买入。必须强制 inventory limit + auto-hedge。

  6. CEX-DEX 套利者作为 informed Binance mid 跳动 1 bps 后,DEX-CEX arb 立刻 hit Hyperliquid 旧 quote。做市需要 cross-venue mid 信号。


九、关键速查

GLFT 渐近闭式 (exp 强度):
  δ_a*(q) = (1/k)·ln(1 + k/γ) + (2q-1)/2 · √[σ²γ/(2kA)·(1+k/γ)^(1+k/γ)]
  δ_b*(q) = (1/k)·ln(1 + k/γ) - (2q+1)/2 · √[同上]

参数关系:
  c = (1/k)·ln(1+k/γ)            ← 与 q 无关的 base half-spread
  η = √[σ²γ/(2kA)·(1+k/γ)^(1+k/γ)] ← skew 单位

经验起步:
  γ ≈ 0.05 - 0.5  (越大越 risk-averse)
  φ = 0.01·γ      (运行惩罚 ≈ 终值惩罚)

与 A-S 公式对应(参考)

A-S half-spread = (1/γ)·ln(1+γ/k) + (2q±1)/2 · γσ²(T-t)
GLFT half-spread = (1/k)·ln(1+k/γ) + (2q±1)/2 · η
A-S 库存项 ∝ σ² (T-t)
GLFT 库存项 ∝ √[σ²γ / (kA)]

十、面试题

Q1: GLFT 与 A-S 在公式上的核心差异是什么?为什么 GLFT 更稳定?

A: A-S 库存惩罚项 (2q±1)/2 γσ²(T-t) 随 σ²、(T-t) 线性放大;GLFT 渐近版 (2q±1)/2 · √(σ²γ/(2kA)·…),仅 √σ 增长,且不依赖 t。在 σ regime 切换时 A-S 反应剧烈、GLFT 平滑。

Q2: 如何把 GLFT 扩展到多资产?

A: GLFT 2017 论文用矩阵化值函数 θ(q_1,…,q_n) = q'Bq + ...,B 由 Lyapunov 方程 B Σ B + ... = 0 解出,最优 offset = base + Σ_j β_{ij} q_j。意味着 BTC 多头会让 ETH ask 也下移(因为下意识 hedge)。生产用:portfolio-level inventory penalty。

Q3: 渐近 (asymptotic) 解的"渐近"是相对什么取极限?

A: 长视界 T→∞ 极限。值函数随时间项消失,θ(t,q) → θ_∞(q)。前提是 horizon 远长于 mean reversion timescale 1/(γσ²)。crypto 上 mean reversion 是秒-分钟,做市运行小时-天,所以渐近假设在分钟以上时间尺度成立。

Q4: φ(running penalty)和 γ(terminal penalty)什么关系?

A: 数学上独立;φ 控制运行轨迹的"平滑库存",γ 控制终末库存。实务取 φ ≈ 0.01-0.1 × γ:γ 主导 strategy character,φ 防止单期库存累积过快。

Q5: drift μ 进入 GLFT 公式后为什么 bid/ask 都同向移动?

A: drift > 0 表示资产 expected to rise。reservation price r = s + adj(μ) 上移。bid 上移:你愿意出更高价买(反正会涨);ask 上移:你不愿意贱卖。注意:仅 reservation 移动,spread 形状不变。


明日预告

Day 80:库存管理与 hedge — 把 A-S/GLFT 静态最优 spread 与动态库存管理结合。引入 Cartea-Jaimungal 的 "skew + hedge" 框架:何时被动 skew quote 让市场 unwind,何时主动 hit 市价/对冲合约 unwind。在 perp 上演示 inventory 自动 delta-neutralize 到 hedge venue。