Implementation shortfall ve kendi TCA'nızı kurmak: execution size gerçekte ne kadara mal oluyor
Her kurumsal masanın bir işlem maliyeti analizi (TCA) hattı vardır. Kripto botu çalıştıranların neredeyse hiçbirinde bu yoktur. Tipik kurulum fill'leri loglar, komisyonları toplar ve backtest PnL'i ile canlı PnL arasındaki farka "slippage" der — gecikmeyi, spread'i, impact'i, ters seçilimi (adverse selection) ve kaçıp giden her doldurulmamış emri içine alan, açıklanmamış tek bir kalıntı. Tek bir sayı olarak ölçtüğünüz bir maliyeti düzeltemezsiniz. Bunu doğru ölçmenin makinesi 1988'den beri var, tescilli/gizli bir şey değil ve kendi fill loglarınız üzerinde kabaca 200 satır Python'dır. Bu yazı onu inşa ediyor.
Karşılığında elde ettiğiniz şey daha güzel bir gösterge paneli değil. Backtest'iniz bir maliyet modeli içeriyor — bir slippage sabiti, bir fill olasılığı, bir impact katsayısı — ve içindeki her parametre şu an bir tahmin. Canlı fill'ler üzerinde TCA, bu parametrelerin karşısında kalibre edilebileceği tek gerçek referanstır (ground truth). Bu döngünün simülasyon tarafını Fill simülasyonu: kapanış-fiyatı hayalinden kuyruk-farkında gerçekliğe uzanan merdiven yazısında kurmuştuk; bu yazı ölçüm tarafını kuruyor.
Kağıt üstü ile gerçek: Perold aslında neyi ölçtü
Temel numara André Perold'a aittir (1988, "The Implementation Shortfall: Paper Versus Reality," Journal of Portfolio Management 14(3), 4–9). İki portföyü paralel çalıştırın. Kağıt portföy her kararı anında, sınırsız büyüklükte, sıfır maliyetle ve karar anındaki geçerli fiyattan uygular. Gerçek portföy ise botunuzun fiilen yaptığı şeydir: kısmi fill'ler, kovalanan kotasyonlar, iptal edilen kalıntılar, komisyonlar. Implementation shortfall, bunların getirileri arasındaki farktır.
Tanım önemlidir çünkü neyi gizlemeyi reddettiğiyle önemlidir. Bir komisyon ekstresi komisyonları gösterir. Fill'i limit-fiyatına karşı gösteren bir rapor ise hiçbir şey göstermez (yapı gereği limitinizden daha kötüye asla dolmazsınız). Kağıt portföy sizi her şey için ücretlendirir: karar ile varış arasındaki drift, geçtiğiniz spread, yarattığınız impact ve — en kritiği — fiyat kaçarken hiç dolmayan emirler. Wagner ve Edwards (1993, "Best Execution," Financial Analysts Journal 49(1), 65–71) görünür komisyonları buzdağının görünen ucu olarak nitelendirdi; devir hızı olan herhangi bir şeyde su altındaki kısım baskındır.
Notasyonu sabitleyelim. Bir ana emir (parent order): yön (al/sat), büyüklük . Karar fiyatı , sinyal tetiklendiğinde stratejinizin gördüğü mid'dir. Fill'ler olarak gelir, toplam dolan miktar . Horizon 'de (ana emir tamamlandı, iptal edildi veya zaman aşımına uğradı) mid 'dir. Açık komisyonlar 'dir. Para birimi cinsinden implementation shortfall:
kağıt notional 'ye bölünerek baz puana normalize edilir. Pozitif değer ödediğiniz anlamına gelir. İlk terim, fill'lerinizin kağıt portföye göre maliyetidir; ikincisi Perold'un fırsat maliyetidir — kaçan fiyattan işaretlenmiş dolmamış kalıntı; üçüncüsü ise borsa ekstrenizin kabul ettiği tek kısımdır.
Bir konvansiyon notu: sektör "arrival price" (varış fiyatı) der ve çoğu hisse senedi TCA'sında arrival, emrin piyasaya ulaştığı andaki mid demektir. Bir bot için karar zamanı ile varış zamanı, kendi iç gecikmeniz artı rate-limit kuyruklaması kadar farklıdır — gerçek, ölçülebilir bir maliyet. Bu yüzden her iki zaman damgasını ve her iki fiyatı da tutar, ayrıştırmanın onları birbirinden ayırmasına izin veririz.
Ayrıştırma: delay, impact, timing, opportunity, komisyon
Tek bir IS sayısı size execution'ın pahalı olduğunu söyler. Size neden olduğunu söylemez ve farklı bileşenlerin çözümleri tamamen farklıdır — delay maliyetiyle impact maliyetini aynı değişiklikle çözemezsiniz. Robert Kissell'in genişletilmiş implementation shortfall'ı (Kissell, 2006, "The Expanded Implementation Shortfall: Understanding Transaction Cost Components," Journal of Trading 1(3), 6–16; kitap uzunluğunda The Science of Algorithmic Trading and Portfolio Management, Academic Press, 2013 içinde geliştirilmiştir) toplamı bileşenlerine ayırır; her biri emir yaşam döngüsünün ayrı bir aşamasına atfedilebilir. Uygulayıcının versiyonu, arrival mid 'ı kullanır (ilk borsa onayındaki mid):
Bu özdeşlik tam olarak Perold'un tanımına geri katlanır (telescope) — terimleri açın ve sadeleşir. Her parçanın ayrı bir sahibi vardır:
Delay maliyeti : sinyalin tetiklenmesi ile ilk alt emrinizin (child order) borsada canlı hale gelmesi arasındaki fiyat drifti. Bu sizin altyapınızdır — serileştirme, ağ, rate-limit kuyrukları, risk kontrolleri. Sinyalinizin gerçek bir kısa-horizon alpha'sı varsa, delay maliyeti bunun ilk sızdığı yerdir; tutarlı biçimde pozitif bir ortalama, siz varmadan piyasanın sizin lehinize hareket ettiğini söyler — sönümlenen momentum alpha'sı ya da aynı sinyali sizden hızlı işleyen biri.
Trading maliyeti : fill'lerinizin varışa göre ne ödediği — geçilen spread artı piyasa impact'i artı program-içi drift. Bu, execution algoritmasının karnesidir ve execution araştırmasının fiilen modellediği niceliktir. Almgren, Thum, Hauptmann ve Li (2005, "Direct Estimation of Equity Market Impact," Risk 18(7), 58–62) bunu Citigroup masalarından kabaca 700.000 ABD hisse senedi emri üzerinde ölçtü (Aralık 2001–Haziran 2003) ve trading maliyetinin günlük volatilite ile katılım oranına (participation rate) göre ölçeklendiğini buldu — geçici impact, trade oranında üssü 3/5'e yakın bir kuvvet yasasını izliyor, kalıcı impact ise büyüklükte neredeyse doğrusal. Kalibre ederken onların fonksiyonel formunu yeniden kullanacağız.
Timing riski: ortalama ayrıştırmasındaki bir terim değil, onun etrafındaki varyanstır. Bir ana emri zamana yaymak beklenen impact'i azaltır ve sizi volatiliteye maruz bırakır; kalan pozisyonu olan bir program için maliyetin standart sapması olarak ölçeklenir. Bu tam olarak Almgren–Chriss çerçevesinin optimize ettiği ödünleşmedir. TCA raporunuzda bu, IS'in ana emirler arasındaki dağılımı olarak ortaya çıkar — her ortalamanın yanına standart sapmayı yazın, yoksa tüm dikkati ortalama alır ve tüm paranızı kuyruklar götürür.
Fırsat maliyeti : terminal fiyatta işaretlenmiş dolmamış miktar. Pasif stratejiler için bu rutin olarak en büyük ve en az incelenen bileşendir ve tüm çerçeveyi dürüst kılan terimdir — bu konuda tuzaklar bölümünde daha fazlası var, çünkü bunu atlamak, insanların TCA ile kendilerini kandırmasının en yaygın tek yoludur.
Komisyonlar : açık kısım. Kriptoda komisyonları indirimlerden sonra loglayın (VIP kademeleri, fill anındaki fiyattan çevrilmiş token iadeleri) ve maker iadelerini işaretli tutun — negatif bir komisyon gürültü değil, veridir.
İşlenmiş bir örnek
BTC al. Sinyal ile tetikleniyor; kağıt notional $600.000. İlk alt emir mid ile onaylandı. Sonraki iki dakikada 8 BTC, VWAP 60.072'den doluyor; fiyat uzaklaşarak trend ediyor, algoritma limitine saygı gösteriyor ve kalan 2 BTC, mid iken iptal ediliyor. Karma komisyonlar dolan notional üzerinde 2,5 bps.
| Bileşen | Formül | USD | kağıdın bps'i |
|---|---|---|---|
| Delay | 120 | 2.0 | |
| Trading maliyeti | 480 | 8.0 | |
| Opportunity | 456 | 7.6 | |
| Komisyonlar | 120 | 2.0 | |
| Toplam IS | 1,176 | 19.6 |
Borsa ekstresi $120 gösteriyor. Kararı uygulamanın gerçek maliyeti $1.176 idi — on kat, üstelik iki en büyük bileşen komisyon tabanlı muhasebeye görünmez. Bunun kabaca %40'ı hiç işlem görmeyen miktardan geldi. Yalnızca fill'leri analiz eden bir TCA raporu bu ana emri 8 bps olarak puanlar ve "sorun yok" derdi.

Markout'lar: ters seçilimi fiyatlamak
Implementation shortfall ana emri notlandırır. Bireysel fill'lerin kalitesi hakkında — özellikle sizin bilmediğiniz bir şey bilen karşı taraflarla sistematik olarak işlem yapıp yapmadığınız hakkında — hiçbir şey söylemez. Bu, markout'larla ölçülür: her fill'i, gerçekleştikten sonra sabit bir horizonda mid'e işaretleyin.
burada , anındaki fill fiyatı ve , anındaki mid'dir. Bu, fill'in horizonundaki birim başına mark-to-market PnL'idir ve 'da temiz bir anatomisi vardır: bir maker fill yarım-spread'de başlar (bid'den aldınız, mid sizin üzerinizde); bir taker fill yarım-spread'de başlar. büyüdükçe olan şey, trade'in bilgi içeriğidir:
- s sniping ve stale-quote pickoff'u (bayat kotasyon toplama) izole eder. Maker fill'leriniz dolduktan bir saniye sonra zaten su altındaysa, daha hızlı katılımcılar kotasyonlarınız yanlış fiyatlandığı anda onlara vuruyor demektir — kotasyon güncelleme döngünüz onların tetik döngüsünden yavaş. Bu markout bir gecikme (latency) teşhisidir, bir strateji teşhisi değil.
- s klasik ters seçilimi ölçer: fill'leri, fiyatınızın ötesine devam eden hareketin izlemesi. Bir piyasa yapıcı için bu, spread yakalamanın yenmesi gereken maliyettir; hisse senedi mikroyapısı ilgili niceliği realized spread (gerçekleşen spread) diye adlandırır, 5 dakikalık horizonda SEC Rule 605 raporlamasında kurumsallaşmıştır. Kripto daha hızlı hareket eder; 10–60s eşdeğer banttır.
- s fill'lerin, mikroyapı horizonunun ötesinde size karşı momentum taşıyıp taşımadığını söyler — taker stratejileri için, sinyalinizin 60s'deki alpha'sının, girmek için ödediğiniz spread-artı-impact'i aşıp aşmadığı. bps'de başlayıp sıfırı hiç geçmeyen bir taker markout eğrisi, alpha'sının finanse edemeyeceği girişler için ödeme yapan bir stratejidir.
Maker/taker asimetrisi, pasif işlemin tüm ekonomisidir — iki eğride özetlenmiş halde. bps'de (yarım-spread) başlayıp 60s'de bps'e sönümlenen bir maker eğrisi şunu söyler: spread'i yakalıyor ve fazlasını geri veriyorsunuz ve hiçbir iade kademesi bunu düzeltmez. Aynı eğri bps'de yerleşiyorsa, kotasyon motoru masrafını çıkarıyor demektir. Bu, saf touch-fill backtest'lerinin varsayarak yok saydığı şeydir de — fiyat limitinize her değdiğinde sizi dolduran bir simülatör, dolmanın yanılmakla ilişkili olduğunu göz ardı eder; fill-simülasyon merdiveninin kuyruk-farkında basamağının, bir varsayım yerine ölçülen markout'lara girdi olarak ihtiyaç duymasının nedeni budur.

Hesaplama, kendi mid akışınız üzerinde bir merge_asof'tur:
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)
Raporu ayrıca sembole, günün saatine ve mid'den kotasyon mesafesine göre dilimleyin. Bir maker stratejisi için en eyleme dönük kesit, fill anındaki-kuyruk-pozisyonuna göre markout'tur: taze-kuyruğun-önündeki fill'ler, seviyenin sizin üzerinizden süpürüldüğü fill'lerden çok farklı fiyatlanır.
Hat: neyi loglamalı
TCA, matematik katmanında değil, loglama katmanında ölür. Yukarıdaki matematik, çoğu botun çöpe attığı sayılara ihtiyaç duyar ve bunların hiçbiri sonradan borsa geçmişinden yeniden oluşturulamaz. Pazarlığa kapalı olanlar:
- Karar mid'i, kendi akışınızdan, sinyal anında. Borsa mumunun kapanışı değil, sonraki bir yeniden oluşturma değil. Referans "stratejimin karar verirken inandığı fiyat"tır — bunu yalnızca o andaki süreciniz bilir.
- Her iki zaman damgası: karar zamanı ve ilk-onay (first-ack) zamanı, yoksa delay maliyeti ölçülemez ve sessizce trading maliyetine karışır.
- Her fill; borsa zaman damgası, komisyonu ve maker/taker bayrağıyla — kendi yerel alım zamanınız kendi gelen gecikmenizle kirlenmiştir.
- İptal edilmiş ve süresi dolmuş ana emirler, her şey gibi loglanmış. Sıfır fill'li ana emirler tablodaki en pahalı satırlardır.
- Kalıcı bir mid akışı (veya L1 akışı), 100–250ms çözünürlükte, markout'ları hesaplamaya yetecek kadar uzun süre saklanmış. Fill simülatörü için zaten kitapları kaydediyorsanız, bu bedavadır.
İki düz tablo yeterlidir:
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",
}
Atıf (attribution) fonksiyonu, ayrıştırmanın doğrudan bir transkripsiyonudur:
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")
Ve atıf sorguları — ki TCA'nın muhasebe olmayı bırakıp araştırma olmaya başladığı yer burasıdır:
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)}))
Sorgu 4, bir smart order router'ın üzerinde optimize ettiği girdi tablosudur — venue başına TCA olmadan yönlendirme, komisyon cetveline göre, yani en küçük maliyet bileşenine göre yönlendirmedir (Kriptoda smart order routing bu tabloyu başlangıç noktası olarak alır). Markout modülüyle birlikte, işte bu, vaat edilen ~200 satırdır.
Döngüyü kapatmak: backtest maliyet modelini kalibre etmek
Hattın kendini amorti ettiği yer burasıdır. Backtest'iniz sayılar öne sürer: slippage_bps = 5, bir fill olasılığı eğrisi, bir impact katsayısı. Her biri canlı execution hakkında bir iddiadır ve TCA tablosu canlı execution'ın ta kendisidir. Döngü şu: TCA ile ölç, maliyet modelini fit et, fit edilmiş modelle backtest çalıştır, işlem yap, yeniden ölç.

Taker maliyeti için, birini icat etmek yerine fonksiyonel formu Almgren ve ark. (2005)'ten ödünç alın. Sonuçları — maliyetin, günlük volatilite çarpı katılım oranının bir kuvveti ile orantılı olması — iki parametreli bir model verir:
hisse senedi tahmini bir prior olarak. Bunu ham ana emirler üzerinde değil, binlenmiş ortalamalar üzerinde fit edin — bireysel ana emir maliyetleri gürültüye baskındır (timing-risk terimi budur) ve ham veri üzerindeki bir log-log regresyonu gürültüye seve seve fit olur:
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
Sonra hata payları konusunda dürüst olun. Almgren'in ekibinin 700.000 emri vardı ve yine de geçici-impact üssünü olarak raporladı; 2.000 ana emirli bir bot, sembol başına 'yi serbestçe tahmin etme lüksüne sahip değildir. Pratik rejim: ile normalize ettikten sonra semboller arasında havuzlayın, 'yi 0.6'ya doğru sertçe shrink edin (ya da sadece sabitleyip yalnızca 'yı fit edin) ve aylık olarak yeniden fit edin. Fit ettiğiniz yukarı drift ediyorsa, ayak iziniz büyüdü ya da piyasa inceldi — her iki durumda da backtest'in bunu bilmesi gerekiyordu.
Maker stratejileri için kalibrasyon hedefleri farklıdır ve diğer iki modülden gelir:
- Fill olasılığı: fill simülatörünün kuyruk modeli, (spread-mesafesi, kuyruk-pozisyonu) hücresi başına 'i öngörür; ana emir loglarınız hücre başına gerçekleşen fill oranlarını sağlar. Uyuşmazlık, bir grid referansı iliştirilmiş bir simülatör hatasıdır.
- Ters seçilim: simülatörün örtük "fill'ler değiştirilebilir (exchangeable)" varsayımını ölçülen markout tablosuyla değiştirin — saatinde mesafesindeki bir simüle maker fill, ölçülen 'i anlık bir mark-to-market kesintisi olarak taşır. Bu tek değişiklik, halüsinasyon gören bir maker backtest'i ile onu takip eden bir maker backtest'i arasındaki farktır; fill-simülasyon yazısında işaret edilen eksik kalibrasyon girdisi buydu.
- Maliyet dağılımı: backtest'e IS'in ortalamasını değil, dağılımını besleyin. Yalnızca ortalamayı kaydıran bir maliyet modeli, timing riskinin yarattığı drawdown'ları yeniden üretemez; ana emir başına IS'e fit edilmiş bir lognormal bile bir sabitten iyidir.
Slippage-eğrisinin tam ele alışı — fonksiyonel formlar, rejim koşullandırması, kuvvet yasasının ne zaman kırıldığı — kendi başına bir yazıdır: Backtest'ler için slippage eğrileri ve maliyet modelleri. Buradaki nokta mimaridir: o yazının modelleri, bu yazının tabloları olmadan fit edilemez.
Tuzaklar
TCA literatürü onlarca yıllıktır ve onu oyunlamanın (gaming) yolları da öyle. Üç başarısızlık modu, kendini kandırmanın çoğunu açıklar.
Benchmark oyunlama. Arrival price dışındaki herhangi bir benchmark'a sarılınabilir (hugged). Klasiği VWAP'tır: aralık VWAP'ına göre notlanan bir algoritma, onu bir baz puan içinde takip ederken pozisyon arrival'a karşı yirmi puan kanayabilir, çünkü benchmark ittiğiniz fiyatla drift eder — ve anlamlı katılımda kendi prints'leriniz VWAP'ın ta kendisidir, dolayısıyla onu takip etmek kendini notlandıran bir ödevdir. Benchmark politikasını TWAP vs VWAP vs POV yazısında incelemiştik; TCA tarafındaki kural daha basittir: benchmark'lar işlemden önce seçilir ve bir scheduler kendi planlama benchmark'ına göre notlanırken bile IS-arrival'a-karşı her zaman hesaplanır. Daha ince varyant arrival oyunlamasıdır: decision_ts'i belirleyen bileşen kısa-vadeli momentumu görebiliyorsa, delay terimini pohpohlamak için "kararları" zamanlayabilir. Karar zaman damgaları sinyal katmanına aittir, herhangi bir execution mantığı çalışmadan önce loglanır.
Yalnızca-dolan analizinde survivorship bias. Maliyet analizinizi fill'ler üzerine koşullandırın, pasif execution bedava görünür. Somut olarak: 100 pasif alım ana emri, mid'in bir tick altında. Altmışı doluyor ve — pasif oldukları için — arrival'dan ortalama 3 bps daha iyi fiyatlardan doluyor: ölçülen "maliyet" bps, gurur duyulacak bir rapor. Hiç dolmayan kırkı, fiyatın kaldırıp uzaklaştığı tam da o emirlerdi; onları iptalde 25 bps ters işaretleyin ve dürüst sayı bps olur. Yalnızca-dolan raporu ile gerçek rapor 11 bps ve işaret olarak farklıdır. Bu bir köşe durumu değil — pasif işlemin mekanizmasının ta kendisidir: dolmak, fiyatın sizin üzerinizden gelmesiyle ilişkilidir, ki bu, touch-fill backtest'lerini hayal ürünü yapan aynı koşullandırmadır. Fırsat maliyeti, IS'in isteğe bağlı bir incelmesi değildir; tüm ölçümü seçilime (selection) karşı savunan terimdir. Aynı bias'ın bir taker varyantı vardır: ıskalayan IOC emirleri, rate limit veya risk kontrolleriyle reddedilen emirler — ıskalamalar loglanmazsa, ıskalamanın maliyeti ölçülmez ve tam da stratejinizin trade'i en çok istediği hızlı piyasalarda en büyüktür.
Çeşitli ayak-vuruşları (foot-guns), kısaca: borsa mumlarından yeniden oluşturulan arrival mid'leri (akışınız ile mum tam da önemli olduğu anda uyuşmaz); ana emir başına bps'i notional ağırlıkları olmadan ortalamak (bir gerçek emri geride bırakan bin toz fill); indirim-öncesi loglanan komisyonlar; fill'den farklı bir venue'dan alınan markout mid'leri (ters seçilim kılığına giren cross-venue basis); ve perp'lerde, funding tahakkukunun execution penceresine sızmasına izin vermek — funding bir maliyettir ama bir execution maliyeti değildir ve ikisini karıştırmak her iki analizi de zehirler.
Bu hafta ne yapmalı
İki log tablosunu botunuza ekleyin — yukarıdaki şema bir kolon listesidir, bir proje değil. Hiçbir şeyi geriye doğru doldurmayın; iki haftalık dürüst log, bir yıllık yeniden oluşturmadan iyidir. Ayrıştırmayı ve üç markout horizonunu çalıştırın. Hangi bileşenin baskın olduğunu (neredeyse hiçbir zaman komisyonlar değil), pasif fill'lerinizin spread yakalamalarının ötesinde ters seçilmiş olup olmadığını ve backtest'inizin maliyet sabitinin ölçülen eğriden ne kadar uzakta durduğunu öğreneceksiniz. Sonra fit edilmiş eğriyi simülatöre geri bağlayın ve size ilk etapta bu stratejiyi çalıştırmanızı söyleyen backtest'i yeniden çalıştırın. Perold'un kağıt portföyü, otuz sekiz yıl sonra bile, gerçek portföyünüzün sahip olduğu tek dürüst rakip olmayı sürdürüyor.
Yazarlar
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.