σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
σμimplied vol · KKmax(S−K,0)udβρᵢⱼfat tailsPnL · drawdownyield · TdSₜ = μSₜ dt + σSₜ dWₜC = S·N(d₁) − K·e⁻ʳᵀ·N(d₂)σₚ² = wᵀΣwd₁ = [ln(S/K) + (r + σ²⁄2)T] ⁄ σ√T∂V/∂t + ½σ²S²·∂²V/∂S² + rS·∂V/∂S − rV = 0Sharpe = (Rₚ − R_f) ⁄ σₚVaR₉₉ = μ − 2.33σρ = Cov(X,Y) ⁄ σₓσᵧE[Rₚ] = Σ wᵢ·E[Rᵢ]φ(x) = e^(−x²/2) ⁄ √2πE[Rᵢ] = R_f + βᵢ(E[Rₘ] − R_f)dXₜ = θ(μ − Xₜ)dt + σ dWₜσ²ₜ = ω + α·ε²ₜ₋₁ + β·σ²ₜ₋₁C − P = S − K·e⁻ʳᵀΔ = ∂V/∂S Γ = ∂²V/∂S²p = (e^(rΔt) − d) ⁄ (u − d)rₜ = ln(Pₜ ⁄ Pₜ₋₁)E[Xₜ₊₁ | ℱₜ] = Xₜf* = (bp − q) ⁄ bz = (x − μ) ⁄ σP(N=k) = λᵏe^(−λ) ⁄ k!Θ = −∂V/∂Tν = S·φ(d₁)·√Tmin wᵀΣw s.t. wᵀμ = μ*∫Σ∂∞λ
uantCraft Academy学习路线
Ctrl K
AO
+1.2k 位学习者
登录加入 ↗
学习路线
AO
+1.2k 位学习者
登录
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python 学习路线›模块 11 · 生产系统与前沿模型›主题 11.6·~1小时 30分钟专家

做市

路线进度模块 11/11 · 主题 6/13
11.6 做市
—实验得分— / 100总 XP—连续天数—

赚取价差,承担风险

做市商挂出买价和卖价然后等待。当卖家打到买价、买家吃到卖价时,做市商低买高卖,赚取价差。这就是收入。成本是库存风险:在两笔交易之间,做市商持有一个价值随价格波动的头寸,而且更常被知情者(逆向选择)而不是不知情者成交。好的做市是在两者间取得平衡:报价要足够窄,才能经常被成交;也要足够宽,才能为风险获得补偿;同时让库存保持在零附近。

# The income: buy at the bid, sell at the ask, keep the spread
bid, ask, size = 99.95, 100.05, 100
cash = -bid * size + ask * size
print(round(cash, 2))                                # 10.0    a round trip of 100 shares earned the 0.10 spread

# The risk: between the two trades the price moves against the inventory
inventory, mid_after = 100, 99.80
print(round(inventory * (mid_after - bid), 2))       # -15.0   the position lost more than the spread would have earned

Avellaneda-Stoikov:在哪里报价

Avellaneda-Stoikov 模型给出了闭式答案。第一,保留价格 r = mid - q * gamma * sigma^2 * tau 是在库存为 q 时做市商乐于成交的位置:多头做市商(q > 0)应把资产估值压到中间价之下,并报更低的价格来吸引买家,空头做市商则报更高。风险厌恶 gamma、波动率 sigma 和剩余时间 tau 决定倾斜的力度。第二,最优价差 gamma * sigma^2 * tau + (2 / gamma) * ln(1 + gamma / k) 有两部分:持仓到期限的风险费用,以及订单流项,其中 k 表示报价离中间价越远成交枯竭得有多快。报价为 r - spread/2 和 r + spread/2。

# The reservation price leans against inventory: long -> quote lower, short -> quote higher
def reservation_price(mid, inventory, gamma, sigma, tau):
    return mid - inventory * gamma * sigma ** 2 * tau

for inventory in (-50, 0, 50):
    print(inventory, round(reservation_price(100.0, inventory, 0.1, 0.2, 1.0), 4))
# -50 100.2   short: quote higher to buy back
#   0 100.0   flat: centred on the mid
#  50  99.8   long: quote lower to sell
# The optimal spread and the two quotes around the reservation price
import math

def optimal_spread(gamma, sigma, tau, k):
    return gamma * sigma ** 2 * tau + (2 / gamma) * math.log(1 + gamma / k)

def make_quotes(mid, inventory, gamma, sigma, tau, k):
    centre = mid - inventory * gamma * sigma ** 2 * tau
    half = optimal_spread(gamma, sigma, tau, k) / 2
    return centre - half, centre + half

print(round(optimal_spread(0.1, 0.2, 1.0, 1.5), 4))                       # 1.2948
bid, ask = make_quotes(100.0, 0, 0.1, 0.2, 1.0, 1.5)
print(round(bid, 4), round(ask, 4))                  # 99.3526 100.6474   symmetric around the mid when flat
bid, ask = make_quotes(100.0, 50, 0.1, 0.2, 1.0, 1.5)
print(round(bid, 4), round(ask, 4))                  # 99.1526 100.4474   long 50: both quotes move down by 0.2

库存、成交与盈亏

用两个数字跟踪状态:库存和现金。买价被打到时你买入了:库存上升,现金减少 price * quantity;卖价被吃到时你卖出了:反之。任一时刻的财富是现金加上按中间价估值的库存,cash + inventory * mid,它的变化就是你的盈亏。盈亏来自赚到的价差,以及持有库存期间的价格波动;围绕保留价格报价会把库存拉回零,因为能减少库存的那一边变得更有吸引力。实践中限制很重要:库存的硬上限、快市时暂停,以及每次报价时检查买价低于卖价。

# Booking fills and marking the book to market
def apply_fill(state, side, price, quantity):
    inventory, cash = state
    if side == "bid":
        return inventory + quantity, cash - price * quantity
    return inventory - quantity, cash + price * quantity

state = (0, 0.0)
state = apply_fill(state, "bid", 99.95, 100)         # our bid is hit: we buy 100
print(state)                                         # (100, -9995.0)
state = apply_fill(state, "ask", 100.05, 100)        # our ask is hit: we sell 100
print(state)                                         # (0, 10.0)   flat again, and the spread is the profit
inventory, cash = apply_fill(state, "bid", 99.9, 50)
print(round(cash + inventory * 99.9, 2))             # 10.0   marked at the price we just paid, the equity is unchanged
模型只是起点该模型假设中间价为随机游走、波动率恒定,以及平滑的指数型订单到达曲线。真实市场有排队、最小报价单位、手续费与返佣、跳空,还有会吃掉过期报价的知情交易者。把这些公式当作思考各个效应方向的方式(多头库存:报更低;波动更大:更宽),然后在投入资金之前,用记录下来的数据在模拟器里校准和测试,并设置任何模型都无法突破的硬性限制。
  • 做市商赚取价差,但要承担库存风险和逆向选择;目标是足够窄以成交、足够宽以获得补偿,并让库存接近零。
  • 保留价格 mid - q * gamma * sigma^2 * tau 逆着库存倾斜;价差 gamma * sigma^2 * tau + (2/gamma) ln(1 + gamma/k) 随风险厌恶加权的波动率和时间而变宽。
  • 跟踪库存和现金,按中间价估值,强制执行硬性库存限制,并在实盘使用前用记录数据在模拟器里测试策略。

下一节:Heston 模型,本身也是随机的波动率。

实验:评分内容

  • 用 reservation_price 逆库存调整25 分
  • 用 optimal_spread 设定价差宽度25 分
  • 用 make_quotes 双边报价25 分
  • 用 apply_fill 记录一笔成交25 分
资源管理器 · lab-135-1
▾ lab-135-1
…
bash · 浏览器内
learner@quantcraft:~/lab-135-1$
Python 尚未加载PythonLn 1, Col 1Spaces: 4UTF-8

要重置此主题吗?

lab-135-1 中的所有内容将恢复为初始文件,终端也会被清空。你当前的解答将丢失,且无法撤销。

将被重置
  • main.py → 初始代码
  • 终端历史与测试输出
  • 未提交的更改
将被保留
  • 实验最高得分
  • 你的 XP 与连续天数
  • 讲解进度与笔记

登录后即可运行你的代码。

所有讲解均可免费阅读。创建账户或登录后,即可运行代码、运行测试并提交此实验。

  • 浏览器内 IDE 与 bash 终端,无需安装
  • 即时评分、XP 以及你在学习路线中的位置
创建账户登录
或
使用 GitHub 继续