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.0Gegen 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 sellerDie 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- 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_pricedie Ausführung bepreisen25 Pkt. - Mit
slippage_bpsdie Abweichung messen25 Pkt. - Mit
average_fillTeilausführungen mitteln25 Pkt. - Mit
sqrt_impact_bpsdie Wirkung vorhersagen25 Pkt.