Implementation Shortfall und DIY-TCA: was Ausführung wirklich kostet
Jedes institutionelle Desk hat eine Transaction-Cost-Analysis-Pipeline. Fast niemand, der einen Krypto-Bot betreibt, hat das. Das typische Setup loggt Fills, summiert Gebühren und nennt die Differenz zwischen Backtest-PnL und Live-PnL "Slippage" — ein einziges unerklärtes Residuum, das Latenz, Spread, Impact, adverse Selektion und jede nicht ausgeführte Order absorbiert, die davongelaufen ist. Man kann eine Kostenposition nicht reparieren, die man als eine einzige Zahl misst. Das Instrumentarium, um sie korrekt zu messen, existiert seit 1988, es ist nicht proprietär, und über die eigenen Fill-Logs sind es etwa 200 Zeilen Python. Dieser Artikel baut es auf.
Der Gewinn ist kein hübscheres Dashboard. Ihr Backtest enthält ein Kostenmodell — eine Slippage-Konstante, eine Fill-Wahrscheinlichkeit, einen Impact-Koeffizienten — und jeder Parameter darin ist derzeit eine Vermutung. TCA über Live-Fills ist die einzige Ground Truth, an der diese Parameter kalibriert werden können. Die Simulationsseite dieser Schleife haben wir in Fill-Simulation: die Leiter von der Close-Price-Fantasie zur queue-bewussten Realität gebaut; dieser Artikel baut die Messseite.
Papier versus Realität: was Perold tatsächlich gemessen hat
Der grundlegende Kniff geht auf André Perold zurück (1988, "The Implementation Shortfall: Paper Versus Reality," Journal of Portfolio Management 14(3), 4–9). Man führt zwei Portfolios parallel. Das Papierportfolio exekutiert jede Entscheidung sofort, in unbegrenzter Größe, zu Nullkosten, zum Preis, der im Moment der Entscheidung galt. Das reale Portfolio ist das, was Ihr Bot tatsächlich getan hat: Teilausführungen, hinterhergejagte Quotes, stornierte Restmengen, Gebühren. Der Implementation Shortfall ist die Differenz ihrer Renditen.
Die Definition ist wichtig wegen dessen, was sie zu verstecken sich weigert. Eine Gebührenabrechnung zeigt Provisionen. Ein Fill-versus-Limit-Preis-Report zeigt gar nichts (man füllt konstruktionsbedingt nie schlechter als sein Limit). Das Papierportfolio berechnet Ihnen alles: Drift zwischen Entscheidung und Arrival, den überquerten Spread, den verursachten Impact und — entscheidend — die Orders, die nie gefüllt wurden, während der Preis davonlief. Wagner und Edwards (1993, "Best Execution," Financial Analysts Journal 49(1), 65–71) nannten sichtbare Gebühren die Spitze des Eisbergs; bei allem mit Umsatz dominiert der untergetauchte Teil.
Notation festlegen. Eine Parent-Order: Seite (Kauf/Verkauf), Größe . Der Decision Price ist der Mid, den Ihre Strategie sah, als das Signal auslöste. Fills treffen ein als mit gesamter Fill-Menge . Am Horizont (Parent abgeschlossen, storniert oder Timeout) ist der Mid . Explizite Gebühren sind . Implementation Shortfall in Währungseinheiten:
normiert auf Basispunkte durch Division durch das Papier-Nominal . Positiv bedeutet, Sie haben gezahlt. Der erste Term ist, was Ihre Fills relativ zum Papierportfolio gekostet haben; der zweite ist Perolds Opportunitätskosten — die nicht gefüllte Restmenge, bewertet zu dem Preis, zu dem sie entkommen ist; der dritte ist der einzige Teil, den Ihre Exchange-Abrechnung zugibt.
Eine Anmerkung zur Konvention: Die Branche sagt "Arrival Price", und im meisten Equity-TCA bedeutet Arrival den Mid, als die Order den Markt erreichte. Bei einem Bot unterscheiden sich Decision-Zeit und Arrival-Zeit um Ihre eigene interne Latenz plus Rate-Limit-Queueing — eine reale, messbare Kostenposition. Deshalb behalten wir beide Zeitstempel und beide Preise und lassen die Dekomposition sie trennen.
Die Dekomposition: Delay, Impact, Timing, Opportunität, Gebühren
Eine einzelne IS-Zahl sagt Ihnen, dass Ausführung teuer ist. Sie sagt Ihnen nicht, warum, und die Fixes für unterschiedliche Komponenten sind vollkommen verschieden — man löst Delay-Kosten und Impact-Kosten nicht mit derselben Änderung. Robert Kissells erweiterter Implementation Shortfall (Kissell, 2006, "The Expanded Implementation Shortfall: Understanding Transaction Cost Components," Journal of Trading 1(3), 6–16; in Buchlänge ausgearbeitet in The Science of Algorithmic Trading and Portfolio Management, Academic Press, 2013) entbündelt die Summe in Komponenten, jede einer eigenen Phase des Order-Lebenszyklus zuordenbar. Die Praktiker-Version, mit Arrival-Mid (Mid bei der ersten Exchange-Bestätigung):
Die Identität teleskopiert exakt zurück zu Perolds Definition — man expandiert die Terme, und hebt sich weg. Jedes Stück hat einen eigenen Verantwortlichen:
Delay-Kosten : Preisdrift zwischen dem Auslösen des Signals und dem Zeitpunkt, an dem Ihre erste Child-Order live an der Exchange ist. Das ist Ihre Infrastruktur — Serialisierung, Netzwerk, Rate-Limit-Queues, Risk-Checks. Wenn Ihr Signal echtes kurzfristiges Alpha hat, ist Delay-Kosten die Stelle, an der es zuerst durchsickert; ein konsistent positiver Mittelwert sagt, dass sich der Markt in Ihre Richtung bewegt, bevor Sie ankommen — abklingendes Momentum-Alpha, oder jemand Schnelleres, der dasselbe Signal handelt.
Trading-Kosten : was Ihre Fills relativ zum Arrival gekostet haben — überquerter Spread plus Market Impact plus Intra-Schedule-Drift. Das ist das Zeugnis des Ausführungsalgorithmus, und die Größe, die Ausführungsforschung tatsächlich modelliert. Almgren, Thum, Hauptmann und Li (2005, "Direct Estimation of Equity Market Impact," Risk 18(7), 58–62) haben sie über rund 700.000 US-Equity-Orders von Citigroup-Desks gemessen (Dezember 2001–Juni 2003) und fanden, dass Trading-Kosten mit der täglichen Volatilität und der Partizipationsrate skalieren — temporärer Impact folgt einem Potenzgesetz in der Handelsrate mit Exponent nahe 3/5, permanenter Impact nahe linear in der Größe. Wir werden ihre funktionale Form bei der Kalibrierung wiederverwenden.
Timing-Risiko: kein Term in der Mittelwert-Dekomposition, sondern die Varianz um ihn herum. Das Verteilen einer Parent-Order über die Zeit reduziert den erwarteten Impact und setzt Sie der Volatilität aus; für einen Schedule mit verbleibender Position skaliert die Standardabweichung der Kosten als . Das ist genau der Trade-off, den das Almgren-Chriss-Framework optimiert. In Ihrem TCA-Report zeigt sich das als die Dispersion von IS über Parent-Orders — berichten Sie die Standardabweichung neben jedem Mittelwert, sonst bekommt der Mittelwert die ganze Aufmerksamkeit und die Tails bekommen Ihr ganzes Geld.
Opportunitätskosten : die nicht gefüllte Menge, bewertet zum Terminal-Preis. Bei passiven Strategien ist dies regelmäßig die größte und am wenigsten untersuchte Komponente, und es ist der Term, der das gesamte Framework ehrlich macht — mehr dazu im Abschnitt über die Fallstricke, denn ihn wegzulassen ist die häufigste Art, sich mit TCA selbst zu belügen.
Gebühren : der explizite Teil. In Krypto Gebühren nach Rabatten loggen (VIP-Tiers, Token-Rebates umgerechnet zum Fill-Zeit-Preis) und Maker-Rebates vorzeichenbehaftet halten — eine negative Gebühr ist Daten, kein Rauschen.
Ein durchgerechnetes Beispiel
Kauf von BTC. Signal löst aus mit ; Papier-Nominal 600.000 USD. Erste Child-Order bestätigt mit Mid . In den nächsten zwei Minuten füllen 8 BTC zum VWAP 60.072; der Preis trendet weg, der Algo respektiert sein Limit, und die verbleibenden 2 BTC werden storniert mit Mid bei . Gemischte Gebühren 2,5 bps auf gefülltes Nominal.
| Komponente | Formel | USD | bps vom Papier |
|---|---|---|---|
| Delay | 120 | 2,0 | |
| Trading-Kosten | 480 | 8,0 | |
| Opportunität | 456 | 7,6 | |
| Gebühren | 120 | 2,0 | |
| Gesamt-IS | 1.176 | 19,6 |
Die Exchange-Abrechnung zeigt 120 USD. Die realen Kosten der Umsetzung der Entscheidung betrugen 1.176 USD — ein Faktor zehn, mit den zwei größten Komponenten unsichtbar für gebührenbasierte Buchhaltung. Etwa 40 % davon kamen von Menge, die nie gehandelt wurde. Ein TCA-Report, der nur Fills analysierte, würde diese Parent-Order mit 8 bps bewerten und für in Ordnung erklären.

Markouts: adverse Selektion bepreisen
Implementation Shortfall bewertet die Parent-Order. Es sagt nichts über die Qualität einzelner Fills aus — konkret, ob Sie systematisch mit Gegenparteien handeln, die etwas wissen, das Sie nicht wissen. Das wird durch Markouts gemessen: jeder Fill wird zum Mid an einem festen Horizont nach dem Ereignis bewertet.
wobei der Fill-Preis zum Zeitpunkt ist und der Mid zum Zeitpunkt . Das ist der Pro-Einheit-Mark-to-Market-PnL des Fills am Horizont , und es hat eine saubere Anatomie bei : ein Maker-Fill startet bei halber Spread (Sie haben zum Bid gekauft, der Mid liegt über Ihnen); ein Taker-Fill startet bei halber Spread. Was passiert, wenn wächst, ist der Informationsgehalt des Trades:
- s isoliert Sniping und Stale-Quote-Pickoff. Wenn Ihre Maker-Fills schon eine Sekunde nach dem Fill unter Wasser sind, greifen schnellere Teilnehmer Ihre Quotes genau in dem Moment, in dem sie fehlbepreist werden — Ihre Quote-Update-Schleife ist langsamer als deren Trigger-Schleife. Dieser Markout ist eine Latenz-Diagnose, keine Strategie-Diagnose.
- s misst klassische adverse Selektion: Fills, gefolgt von fortgesetzter Bewegung durch Ihren Preis hindurch. Für einen Market Maker sind das die Kosten, die die Spread-Vereinnahmung übertreffen muss; die Equity-Mikrostruktur nennt die verwandte Größe realized spread, institutionalisiert im SEC-Rule-605-Reporting bei einem 5-Minuten-Horizont. Krypto bewegt sich schneller; 10–60s ist das äquivalente Band.
- s sagt Ihnen, ob Fills über den Mikrostruktur-Horizont hinaus Momentum gegen Sie tragen — bei Taker-Strategien, ob das Alpha Ihres Signals bei 60s den Spread-plus-Impact übersteigt, den Sie beim Einstieg gezahlt haben. Eine Taker-Markout-Kurve, die bei bps startet und nie die Nullachse kreuzt, ist eine Strategie, die für Einstiege zahlt, die ihr Alpha nicht finanzieren kann.
Die Maker/Taker-Asymmetrie ist die gesamte Ökonomie des passiven Tradings in zwei Kurven. Eine Maker-Kurve, die bei bps (halber Spread) startet und bis 60s auf bps abklingt, sagt: Sie vereinnahmen den Spread und geben mehr zurück, und keine Rebate-Stufe repariert das. Dieselbe Kurve, die sich bei bps einpendelt, sagt, dass die Quoting-Engine ihr Salz wert ist. Genau das setzen auch naive Touch-Fill-Backtests als gegeben voraus — ein Simulator, der Sie füllt, sobald der Preis Ihr Limit berührt, ignoriert, dass gefüllt zu werden mit falsch zu liegen korreliert ist, weshalb die queue-bewusste Sprosse der Fill-Simulations-Leiter gemessene Markouts als Eingabe braucht statt als Annahme.

Die Berechnung ist ein merge_asof über Ihren eigenen Mid-Stream:
import pandas as pd
def markouts(fills: pd.DataFrame, mids: pd.DataFrame,
horizons=("1s", "10s", "60s")) -> pd.DataFrame:
"""fills: [ts, price, qty, side, liquidity]; mids: [ts, mid].
Both UTC-indexed and sorted. Mid stream must be from YOUR captured
feed, not candles reconstructed later."""
fills = fills.sort_values("ts").reset_index(drop=True)
mids = mids.sort_values("ts")
out = fills.copy()
for h in horizons:
probe = fills[["ts"]].copy()
probe["ts"] = probe["ts"] + pd.Timedelta(h)
m = pd.merge_asof(probe, mids, on="ts", direction="backward")
out[f"mo_{h}"] = (fills["side"] * (m["mid"].values - fills["price"])
/ fills["price"] * 1e4)
return out
def markout_report(mo: pd.DataFrame) -> pd.DataFrame:
"""Qty-weighted markouts by liquidity flag. Weighting matters:
a 0.001 BTC fill and a 2 BTC fill are not equal evidence."""
cols = [c for c in mo.columns if c.startswith("mo_")]
def agg(g):
w = g["qty"] / g["qty"].sum()
return pd.Series({c: (g[c] * w).sum() for c in cols}
| {"n": len(g), "qty": g["qty"].sum()})
return mo.groupby("liquidity").apply(agg)
Schneiden Sie den Report weiter nach Symbol, Tagesstunde und Quote-Distanz vom Mid auf. Der handlungsrelevanteste Schnitt für eine Maker-Strategie ist Markout-nach-Queue-Position-beim-Fill: Fills am Anfang einer frischen Queue bepreisen sich sehr anders als Fills, bei denen das Level durch Sie hindurch weggefegt wurde.
Die Pipeline: was zu loggen ist
TCA stirbt auf der Logging-Ebene, nicht auf der Mathe-Ebene. Die obige Mathematik braucht Zahlen, die die meisten Bots wegwerfen, und keine davon lässt sich im Nachhinein aus der Exchange-Historie rekonstruieren. Die nicht verhandelbaren Punkte:
- Decision-Mid, aus Ihrem eigenen Feed, zum Signalzeitpunkt. Nicht der Exchange-Candle-Close, keine spätere Rekonstruktion. Die Benchmark ist "der Preis, an den meine Strategie glaubte, als sie entschied" — nur Ihr Prozess in diesem Moment kennt ihn.
- Beide Zeitstempel: Decision-Zeit und First-Ack-Zeit, sonst ist Delay-Kosten unmessbar und verschmilzt lautlos mit Trading-Kosten.
- Jeder Fill mit Exchange-Zeitstempel, Gebühr und Maker/Taker-Flag — Ihre lokale Empfangszeit ist durch Ihre eigene Inbound-Latenz verunreinigt.
- Stornierte und abgelaufene Parent-Orders, geloggt wie alles andere. Die Parent-Orders mit null Fills sind die teuersten Zeilen in der Tabelle.
- Ein persistenter Mid-Stream (oder L1-Stream) mit 100–250ms-Granularität, lange genug aufbewahrt, um Markouts zu berechnen. Wenn Sie bereits Orderbücher für den Fill-Simulator aufzeichnen, ist das kostenlos.
Zwei flache Tabellen genügen:
PARENTS = {
"parent_id": "str",
"strategy": "str",
"symbol": "str",
"venue": "str",
"algo": "str", # twap | pov | sniper | quote | ...
"side": "int8", # +1 buy, -1 sell
"qty": "float64", # parent size, base units
"limit_px": "float64", # NaN for unconstrained
"decision_ts": "datetime64[ns, UTC]",
"decision_mid": "float64", # mid your feed showed at decision_ts
"arrival_ts": "datetime64[ns, UTC]", # first exchange ack
"arrival_mid": "float64",
"end_ts": "datetime64[ns, UTC]", # filled / cancelled / expired
"terminal_mid": "float64",
"mkt_volume": "float64", # market volume over [arrival_ts, end_ts]
"sigma_bps": "float64", # realized vol estimate at decision time
}
FILLS = {
"parent_id": "str",
"ts": "datetime64[ns, UTC]", # exchange timestamp
"price": "float64",
"qty": "float64",
"fee": "float64", # quote ccy, post-discount, signed
"liquidity": "str", # maker | taker
"venue": "str",
}
Die Attributionsfunktion ist eine direkte Transkription der Dekomposition:
import numpy as np
def is_decomposition(p: pd.Series, fills: pd.DataFrame) -> dict:
"""Expanded implementation shortfall for one parent, bps of paper
notional. Sign convention: positive = cost."""
s, X = p["side"], p["qty"]
paper = X * p["decision_mid"]
x = fills["qty"].sum()
delay = s * X * (p["arrival_mid"] - p["decision_mid"])
trade = s * ((fills["price"] - p["arrival_mid"]) * fills["qty"]).sum()
oppty = s * (X - x) * (p["terminal_mid"] - p["arrival_mid"])
fees = fills["fee"].sum()
bps = lambda v: 1e4 * v / paper
return {
"parent_id": p["parent_id"],
"delay_bps": bps(delay), "trade_bps": bps(trade),
"oppty_bps": bps(oppty), "fees_bps": bps(fees),
"is_bps": bps(delay + trade + oppty + fees),
"fill_ratio": x / X,
"participation": x / max(p["mkt_volume"], x),
}
def tca_table(parents: pd.DataFrame, fills: pd.DataFrame) -> pd.DataFrame:
fg = dict(tuple(fills.groupby("parent_id")))
empty = fills.iloc[0:0]
rows = [is_decomposition(p, fg.get(p["parent_id"], empty))
for _, p in parents.iterrows()]
return parents.merge(pd.DataFrame(rows), on="parent_id")
Und die Attributions-Queries, an denen TCA aufhört, Buchhaltung zu sein, und anfängt, Forschung zu sein:
tca = tca_table(parents, fills)
def wavg(g: pd.DataFrame, col: str) -> float:
w = g["qty"] * g["decision_mid"] # notional weights
return (g[col] * w).sum() / w.sum()
comp = ["delay_bps", "trade_bps", "oppty_bps", "fees_bps", "is_bps"]
by_strat = tca.groupby("strategy").apply(
lambda g: pd.Series({c: wavg(g, c) for c in comp}
| {"is_std": g["is_bps"].std(), "n": len(g)}))
done = tca[tca["fill_ratio"] > 0.99]
done["pov_bin"] = pd.qcut(done["participation"], 6)
impact_curve = done.groupby("pov_bin").apply(lambda g: wavg(g, "trade_bps"))
tca["hour"] = tca["arrival_ts"].dt.hour
by_hour = tca.groupby("hour").apply(lambda g: wavg(g, "is_bps"))
by_venue = tca.groupby("venue").apply(
lambda g: pd.Series({"is_bps": wavg(g, "is_bps"),
"is_std": g["is_bps"].std(),
"fill_ratio": g["fill_ratio"].mean(), "n": len(g)}))
Query 4 ist die Eingabetabelle, über die ein Smart Order Router optimiert — Routing ohne Per-Venue-TCA ist Routing nach Gebührenschema, also nach der kleinsten Kostenkomponente (Smart Order Routing in Krypto nimmt diese Tabelle als Ausgangspunkt). Zusammen mit dem Markout-Modul sind das die versprochenen ~200 Zeilen.
Die Schleife schließen: das Backtest-Kostenmodell kalibrieren
Hier zahlt sich die Pipeline aus. Ihr Backtest behauptet Zahlen: slippage_bps = 5, eine Fill-Wahrscheinlichkeitskurve, einen Impact-Koeffizienten. Jede davon ist eine Behauptung über Live-Ausführung, und die TCA-Tabelle ist Live-Ausführung. Die Schleife: mit TCA messen, das Kostenmodell fitten, den Backtest mit dem gefitteten Modell laufen lassen, handeln, neu messen.

Für Taker-Kosten leihen Sie sich die funktionale Form von Almgren et al. (2005), statt eine eigene zu erfinden. Ihr Ergebnis — Kosten proportional zur täglichen Volatilität mal einer Potenz der Partizipationsrate — ergibt ein Zwei-Parameter-Modell:
mit dem Equity-Schätzwert als Prior. Fitten Sie ihn auf gebinnte Mittelwerte, nicht auf rohe Parent-Orders — individuelle Parent-Kosten werden vom Rauschen dominiert (das ist der Timing-Risiko-Term), und eine Log-Log-Regression auf Rohdaten fittet bereitwillig das Rauschen:
done = tca[(tca["fill_ratio"] > 0.99) & (tca["participation"] > 0)]
done["norm_cost"] = done["trade_bps"] / done["sigma_bps"]
bins = done.groupby(pd.qcut(done["participation"], 8)).agg(
pov=("participation", "mean"), cost=("norm_cost", "mean"))
bins = bins[bins["cost"] > 0] # can't log a negative bin
b, log_a = np.polyfit(np.log(bins["pov"]), np.log(bins["cost"]), 1)
def taker_cost_bps(participation, sigma_bps, a=np.exp(log_a), b=b):
return a * sigma_bps * participation ** b
Seien Sie dann ehrlich zu den Fehlerbalken. Almgrens Team hatte 700.000 Orders und berichtete den Temporary-Impact-Exponenten trotzdem als ; ein Bot mit 2.000 Parent-Orders bekommt nicht das Privileg, frei pro Symbol zu schätzen. Das praktische Regime: über Symbole poolen nach Normierung durch , hart in Richtung 0,6 schrumpfen (oder einfach fixieren und nur fitten), und monatlich neu fitten. Wenn Ihr gefittetes nach oben driftet, ist Ihr Footprint gewachsen, oder der Markt ist dünner geworden — so oder so musste der Backtest es wissen.
Für Maker-Strategien sind die Kalibrierungsziele anders und kommen aus den anderen beiden Modulen:
- Fill-Wahrscheinlichkeit: Das Queue-Modell des Fill-Simulators prognostiziert pro (Spread-Distanz, Queue-Position)-Zelle; Ihr Parent-Log liefert realisierte Fill-Ratios pro Zelle. Abweichungen sind ein Simulator-Bug mit angehängter Gitterreferenz.
- Adverse Selektion: Ersetzen Sie die implizite Annahme des Simulators "Fills sind austauschbar" durch die gemessene Markout-Tabelle — ein simulierter Maker-Fill bei Distanz zur Stunde trägt den gemessenen als sofortigen Mark-to-Market-Abschlag. Diese eine Änderung ist der Unterschied zwischen einem Maker-Backtest, der halluziniert, und einem, der trackt; es war der fehlende Kalibrierungsinput, der im Fill-Simulations-Artikel angemerkt wurde.
- Kosten-Dispersion: Speisen Sie die Verteilung von IS, nicht ihren Mittelwert, in den Backtest ein. Ein Kostenmodell, das nur den Mittelwert verschiebt, kann die Drawdowns, die Timing-Risiko erzeugt, nicht reproduzieren; selbst eine lognormale Verteilung, gefittet auf Per-Parent-IS, schlägt eine Konstante.
Die vollständige Behandlung der Slippage-Kurve — funktionale Formen, Regime-Konditionierung, wann das Potenzgesetz bricht — ist ein eigener Artikel: Slippage-Kurven und Kostenmodelle für Backtests. Der Punkt hier ist architektonisch: Die Modelle jenes Artikels sind ohne die Tabellen dieses Artikels nicht fittbar.
Fallstricke
Die TCA-Literatur ist Jahrzehnte alt, und ebenso die Wege, sie zu manipulieren. Drei Fehlermodi verantworten den Großteil der Selbsttäuschung.
Benchmark-Gaming. Jede andere Benchmark als der Arrival Price lässt sich umarmen. Der Klassiker ist VWAP: ein Algorithmus, bewertet gegen Interval-VWAP, kann diesen auf einen Basispunkt genau tracken, während die Position gegen den Arrival zwanzig verliert, weil die Benchmark mit dem Preis driftet, den Sie selbst schieben — und bei nennenswerter Partizipation sind Ihre eigenen Prints der VWAP, sodass ihn zu tracken selbstbenotete Hausaufgaben sind. Wir haben die Benchmark-Politik in TWAP vs VWAP vs POV seziert; die TCA-seitige Regel ist einfacher: Benchmarks werden vor dem Handel gewählt, und IS-versus-Arrival wird immer berechnet, auch wenn ein Scheduler gegen seine eigene Scheduling-Benchmark bewertet wird. Die subtilere Variante ist Arrival-Gaming: Wenn die Komponente, die decision_ts setzt, kurzfristiges Momentum sehen kann, kann sie "Entscheidungen" so timen, dass der Delay-Term geschönt wird. Decision-Zeitstempel gehören zur Signalschicht, geloggt bevor irgendeine Ausführungslogik läuft.
Survivorship-Bias in Filled-Only-Analysen. Konditionieren Sie Ihre Kostenanalyse auf Fills, und passive Ausführung sieht kostenlos aus. Konkret: 100 passive Buy-Parent-Orders, ein Tick unter dem Mid. Sechzig füllen und — weil passiv — zu Preisen, die im Schnitt 3 bps besser als der Arrival sind: gemessene "Kosten" bps, ein Report, auf den man stolz sein könnte. Die vierzig, die nie gefüllt haben, waren genau die, bei denen der Preis wegzog; bewerten Sie sie mit 25 bps adverser Bewegung bei Stornierung, und die ehrliche Zahl ist bps. Der Filled-Only-Report und der wahre Report unterscheiden sich um 11 bps und im Vorzeichen. Das ist kein Randfall — es ist der Mechanismus passiven Tradings: Gefüllt zu werden korreliert damit, dass der Preis durch Sie hindurchgeht, dieselbe Konditionierung, die auch Touch-Fill-Backtests zur Fantasie macht. Opportunitätskosten sind keine optionale Verfeinerung von IS; sie sind der Term, der die gesamte Messung gegen Selektion verteidigt. Derselbe Bias hat eine Taker-Variante: IOC-Orders, die verfehlt haben, Orders, die von Rate-Limits oder Risk-Checks abgelehnt wurden — wenn Verfehlungen nicht geloggt werden, sind die Kosten des Verfehlens ungemessen, und sie sind gerade in den schnellen Märkten am größten, in denen Ihre Strategie den Trade am meisten wollte.
Diverse Fußangeln, kurz: Arrival-Mids, rekonstruiert aus Exchange-Candles (Ihr Feed und die Candle sind genau dann uneins, wenn es zählt); Mittelung von Per-Parent-bps ohne Nominal-Gewichte (tausend Staubfills überstimmen eine echte Order); Gebühren geloggt vor Rabatt; Markout-Mids aus einer anderen Venue als der Fill (Cross-Venue-Basis, die sich als adverse Selektion tarnt); und bei Perps, Funding-Accrual ins Ausführungsfenster bluten zu lassen — Funding ist eine Kostenposition, aber keine Ausführungskostenposition, und beides zu mischen vergiftet beide Analysen.
Was diese Woche zu tun ist
Fügen Sie Ihrem Bot die beiden Log-Tabellen hinzu — das obige Schema ist eine Spaltenliste, kein Projekt. Nichts nachträglich rekonstruieren; zwei Wochen ehrlicher Logs schlagen ein Jahr Rekonstruktionen. Führen Sie die Dekomposition und die drei Markout-Horizonte aus. Sie werden lernen, welche Komponente dominiert (fast nie Gebühren), ob Ihre passiven Fills über ihre Spread-Vereinnahmung hinaus adversely selektiert sind, und wie weit die Kostenkonstante Ihres Backtests von der gemessenen Kurve entfernt liegt. Verdrahten Sie dann die gefittete Kurve zurück in den Simulator und lassen Sie den Backtest erneut laufen, der Ihnen ursprünglich sagte, diese Strategie zu fahren. Perolds Papierportfolio ist, achtunddreißig Jahre später, immer noch der einzige ehrliche Gegner, den Ihr reales hat.
Authors
Trading-systems engineer
Trading-systems engineer building bots since 2017: cross-exchange arbitrage (connected up to 30 venues), cointegration-based pairs arbitrage across spot and futures, scalping, news and sentiment-driven strategies, trend algorithms, and portfolio management and balancing algorithms. Also builds sub-millisecond order execution, big-data warehouses, backtesting engines, AI agents, and trading interfaces (incl. open-source profitmaker.cc). Stack: JS/TS, Python, Rust/Zig/Go, DevOps, backend, frontend, architecture.