σμ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 AcademySkills
Ctrl K
AO
+1,2 Tsd. Lernende
AnmeldenBeitreten ↗
Skills
AO
+1,2 Tsd. Lernende
Anmelden
  • English
  • Français
  • Español
  • العربية
  • Português
  • Deutsch
  • 日本語
  • 中文
Python-Skill›Modul 6 · Backtesting und Handelskosten›Thema 6.5·~45 Min.Fortgeschritten

Slippage

Position in der SkillModul 6 von 11 · Thema 5 von 6
6.5 Slippage
—Lab-Punkte— / 100XP gesamt—Serie—

Der Preis, den Sie nicht bekommen

Ein Backtest handelt zum Preis in seinen Daten; eine echte Order wird woanders ausgeführt. Slippage ist dieser Unterschied: Man kauft etwas höher als der gesehene Preis und verkauft etwas tiefer. Sie hat zwei Quellen. Den Spread: Eine Market-Order kauft zum Ask und verkauft zum Bid, man zahlt also in jede Richtung den halben Spread relativ zum Mittelkurs. Und die Marktwirkung: Eine große Order verbraucht die besten Quotes und schiebt den Preis gegen sich.

def fill_price(mid, side, slippage_bps):
    if side == "buy":
        return mid * (1 + slippage_bps / 10000)
    if side == "sell":
        return mid * (1 - slippage_bps / 10000)
    raise ValueError("side must be 'buy' or 'sell'")

print(round(fill_price(50.0, "buy", 4), 4))        # 50.02   pays more
print(round(fill_price(50.0, "sell", 4), 4))       # 49.98   receives less
# Crossing the spread: a market buy pays the ask, a market sell receives the bid
bid, ask = 49.98, 50.02
mid = (bid + ask) / 2
half_spread_bps = (ask - bid) / 2 / mid * 10000
print(mid, round(half_spread_bps, 2))                     # 50.0 4.0   each way, relative to the mid price
print(round((ask - mid) / mid * 10000, 2), round((mid - bid) / mid * 10000, 2))   # 4.0 4.0

Gegen einen Maßstab messen

Um Slippage zu messen, braucht man einen Maßstab: den Ankunftspreis, den Mittelkurs bei der Orderentscheidung. Die Abweichung hat ein Vorzeichen, sodass positiv immer Kosten bedeutet: beim Kauf fill - arrival, beim Verkauf arrival - fill, in Basispunkten des Ankunftspreises. Dieselbe Ausführung, die für den Käufer schlecht ist, ist für den Verkäufer gut, daher hängt das Vorzeichen von der Seite ab. Große Orders werden in Teilen ausgeführt, die zu berichtende Zahl ist also der volumengewichtete durchschnittliche Ausführungspreis.

import numpy as np

def slippage_bps(fill, arrival, side):
    if side == "buy":
        return (fill - arrival) / arrival * 10000
    if side == "sell":
        return (arrival - fill) / arrival * 10000
    raise ValueError("side must be 'buy' or 'sell'")

def average_fill(prices, quantities):
    prices, quantities = np.asarray(prices, dtype=float), np.asarray(quantities, dtype=float)
    return float(np.sum(prices * quantities) / np.sum(quantities))

# Buying 600 shares in three pieces; the price at arrival was 50.00
avg = average_fill([50.0, 50.1, 50.3], [100, 200, 300])
print(round(avg, 4))                                      # 50.1833
print(round(slippage_bps(avg, 50.0, "buy"), 2))           # 36.67 bps: it cost us
print(round(slippage_bps(avg, 50.0, "sell"), 2))          # -36.67: the same fills would have helped a seller

Die Wirkung wächst mit der Größe

Der Spread ist eine feste Gebühr; die Marktwirkung hängt davon ab, wie viel man handelt. Eine gut getestete Faustregel ist das Quadratwurzelgesetz: Die erwartete Wirkung als Anteil des Preises beträgt etwa k * volatility * sqrt(quantity / ADV), mit ADV als durchschnittlichem Tagesvolumen und k einer Konstante nahe 1. Vervierfacht man die Order, verdoppelt sich die Wirkung nur, das Aufteilen einer großen Order über die Zeit hilft also, aber weniger als erhofft.

import math

def sqrt_impact_bps(volatility, quantity, adv, k=1.0):
    return k * volatility * math.sqrt(quantity / adv) * 10000

# A stock with 2% daily volatility and 1,000,000 shares of average daily volume
for shares in (10_000, 50_000, 200_000, 800_000):
    print(shares, round(sqrt_impact_bps(0.02, shares, 1_000_000), 1), "bps")
# 10,000: 20.0, 50,000: 44.7, 200,000: 89.4, 800,000: 178.9
Fügen Sie jedem Backtest Slippage hinzuSelbst ein grobes Modell ist besser als keines: Berechnen Sie bei jedem Handel den halben quotierten Spread und einen mit der Ordergröße wachsenden Wirkungsterm und prüfen Sie, ob die Strategie überlebt. Funktioniert sie nur ohne Slippage, funktioniert sie nicht. Die ehrliche Prüfung vergleicht die angenommene Slippage mit echten Ausführungen und senkt die Annahme nur, wenn die Daten es erlauben.
  • Ein Kauf wird über, ein Verkauf unter dem Mittelkurs ausgeführt: mid * (1 +/- bps / 10000); der Spread allein kostet in jede Richtung die halbe Breite.
  • Messen Sie gegen den Ankunftspreis, mit Vorzeichen, sodass positiv Kosten heißt; Teilausführungen werden volumengewichtet gemittelt.
  • Die Wirkung folgt dem Quadratwurzelgesetz k * volatility * sqrt(quantity / ADV): vierfache Größe, doppelte Wirkung.

Als Nächstes: realistische Orderausführung, simulieren, wie Orders tatsächlich gegen ein Orderbuch und Bar-Daten ausgeführt werden.

Lab: Was bewertet wird

  • Mit fill_price die Ausführung bepreisen25 Pkt.
  • Mit slippage_bps die Abweichung messen25 Pkt.
  • Mit average_fill Teilausführungen mitteln25 Pkt.
  • Mit sqrt_impact_bps die Wirkung vorhersagen25 Pkt.
Explorer · lab-91-1
▾ lab-91-1
…
bash · im Browser
learner@quantcraft:~/lab-91-1$
Python noch nicht geladenPythonLn 1, Col 1Spaces: 4UTF-8

Dieses Thema zurücksetzen?

Alles in lab-91-1 wird auf die Startdateien zurückgesetzt und das Terminal geleert. Ihre aktuelle Lösung geht verloren – das lässt sich nicht rückgängig machen.

Wird zurückgesetzt
  • main.py → Startcode
  • Terminal-Verlauf und Testausgabe
  • Nicht eingereichte Änderungen
Bleibt erhalten
  • Beste Lab-Punktzahl
  • Ihre XP und Ihre Serie
  • Vorlesungsfortschritt und Notizen

Melde dich an, um deinen Code auszuführen.

Alle Lektionen kannst du kostenlos lesen. Erstelle ein Konto oder melde dich an, um Code auszuführen, Tests zu starten und dieses Lab abzugeben.

  • IDE im Browser mit Bash-Terminal — nichts zu installieren
  • Sofortige Bewertung, XP und dein Platz auf der Skill
Konto erstellenAnmelden
ODER
Weiter mit GitHub