← Макалаларга кайтуу
July 20, 2026
5 мүн окуу

Implementation shortfall жана өз алдынча TCA: аткаруу чын-чынына канчага түшөрүн өлчөө

Implementation shortfall жана өз алдынча TCA: аткаруу чын-чынына канчага түшөрүн өлчөө
#execution
#tca
#implementation-shortfall
#markouts
#транзакция-чыгымдары
#algotrading
#backtest
#market-microstructure

Ар бир институционалдык деск транзакция чыгымдарын талдоо (TCA) түтүгүнө ээ. Крипто-бот иштеткендердин ичинен муну колдонгондор дээрлик жок. Адаттагы схема fill'дерди жазат, комиссияларды кошот жана backtest PnL менен live PnL ортосундагы айырманы жөн эле "slippage" деп атайт — латенцияны, спредди, таасирди (impact), терс тандоону (adverse selection) жана качып кеткен ар бир аткарылбаган заказды өзүнө сиңирген бир түшүндүрүлбөгөн калдык. Бир сан менен өлчөгөн чыгымды оңдой албайсыз. Муну туура өлчөө үчүн керектүү машинерия 1988-жылдан бери бар, ал жабык-менчик эмес, жана өз fill журналдарыңыз боюнча болжол менен 200 сап Python талап кылат. Бул макалада биз аны түзөбүз.

Пайда — сулуураак dashboard эмес. Сиздин backtest'иңизде чыгым модели бар — slippage константасы, толуу ыктымалдыгы, таасир коэффициенти — жана ал жердеги ар бир параметр азыр болжолдоо гана. Live fill'дер боюнча TCA — ушул параметрлерди калибрлеп болгон жалгыз чыныгы негиз (ground truth). Биз бул циклдин симуляция бөлүгүн Fill simulation: жабылуу баасынын фантазиясынан кезектерди эске алган чындыкка чейинки тепкич макаласында түзгөнбүз; бул макала өлчөө бөлүгүн түзөт.

Кагаз менен чындык: Perold чын-чынында эмнени өлчөгөн

Негиздөөчү ыкма André Perold-дуку (1988, "The Implementation Shortfall: Paper Versus Reality," Journal of Portfolio Management 14(3), 4–9). Эки портфелди параллель жүргүзүү. Кагаз портфели ар бир чечимди дароо, чексиз көлөмдө, нөлдүк чыгымда, чечим кабыл алынган учурдагы баада аткарат. Чыныгы портфель — боттун чын-чынында эмне кылганы: жарым-жартылай fill'дер, кубалаган котировкалар, жокко чыгарылган калдыктар, комиссиялар. Implementation shortfall — алардын кирешелери ортосундагы айырма.

Бул аныктама эмнени жашыруудан баш тартары үчүн маанилүү. Комиссия отчёту комиссияларды көрсөтөт. Fill-менен-лимит-баа отчёту эч нерсе көрсөтпөйт (сиз лимитиңизден начаррак эч качан толбойсуз — түзүлүшү боюнча). Кагаз портфели баары үчүн эсеп талап кылат: чечим менен arrival ортосундагы дрейф, кесип өткөн спред, жараткан таасир, жана — эң маанилүүсү — баа качып кеткенде толбой калган заказдар. Wagner жана Edwards (1993, "Best Execution," Financial Analysts Journal 49(1), 65–71) көрүнгөн комиссияларды айсбергдин учу деп аташкан; жогорку жүгүртүүсү бар нерсе үчүн суу астындагы бөлүгү басымдуулук кылат.

Белгилөөнү бекители. Ата-заказ (parent order): тарап s{+1,1}s \in \{+1, -1\} (сатып алуу/сатуу), көлөм XX. Чечим баасы PdP_d — сигнал иштеген учурда стратегия көргөн mid. Fill'дер (pj,xj)(p_j, x_j) түрүндө келет, толгон жалпы көлөм x=jxjXx = \sum_j x_j \le X. Горизонт TT (ата-заказ аяктаган, жокко чыгарылган же убакыты бүткөн учур) учурунда mid — PTP_T. Ачык комиссиялар FF. Валюта түрүндөгү implementation shortfall:

IS=sjxj(pjPd)аткаруу чыгымы  +  s(Xx)(PTPd)мүмкүнчүлүк чыгымы  +  F,\mathrm{IS} = \underbrace{s \sum_j x_j\,(p_j - P_d)}_{\text{аткаруу чыгымы}} \;+\; \underbrace{s\,(X - x)(P_T - P_d)}_{\text{мүмкүнчүлүк чыгымы}} \;+\; F,

кагаз номиналы XPdX \cdot P_d бөлүнүп базистик пунктарга (bps) нормалдаштырылат. Оң мааниси — сиз төлөгөнүңүздү билдирет. Биринчи мүчө — fill'дериңиз кагаз портфелине салыштырмалуу канчага түшкөнү; экинчиси — Perold-дун мүмкүнчүлүк чыгымы (opportunity cost) — толбой калган калдык качып кеткен баада белгиленет; үчүнчүсү — биржа отчётуңуз тааныган жалгыз бөлүк.

Бир эскертүү конвенция боюнча: тармак "arrival price" деп атайт, жана көпчүлүк акция TCA'сында arrival — заказ рынокко жеткен учурдагы mid дегенди билдирет. Бот үчүн чечим убактысы менен arrival убактысы сиздин өз ички латенцияңыз жана rate-limit кезеги менен айырмаланат — чыныгы, өлчөнүүчү чыгым. Ошондуктан биз эки убакыт белгисин тең жана эки баа тең сактайбыз, декомпозиция аларды өз алдынча бөлүп берсин.

Декомпозиция: delay, impact, timing, opportunity, комиссиялар

Бир жалгыз IS саны аткаруунун кымбат экенин айтат. Ал эмне үчүн экенин айтпайт, ал эми ар түрдүү компоненттерди оңдоо толугу менен ар башка — delay чыгымын жана impact чыгымын бир эле өзгөртүү менен чечпейсиз. Robert Kissell-дин кеңейтилген implementation shortfall теориясы (Kissell, 2006, "The Expanded Implementation Shortfall: Understanding Transaction Cost Components," Journal of Trading 1(3), 6–16; толук эмгекте иштелип чыккан: The Science of Algorithmic Trading and Portfolio Management, Academic Press, 2013) жалпы сумманы заказдын жашоо циклинин ар бир этабына таандык компоненттерге бөлөт. Практиктин версиясы, arrival mid P0P_0 (биринчи биржа тастыктоосундагы mid) колдонулуп:

IS=sX(P0Pd)delay  +  sjxj(pjP0)соода чыгымы  +  s(Xx)(PTP0)мүмкүнчүлүк  +  F.\mathrm{IS} = \underbrace{s\,X\,(P_0 - P_d)}_{\text{delay}} \;+\; \underbrace{s \sum_j x_j\,(p_j - P_0)}_{\text{соода чыгымы}} \;+\; \underbrace{s\,(X - x)(P_T - P_0)}_{\text{мүмкүнчүлүк}} \;+\; F.

Бул тождество Perold-дун аныктамасына так айланат — мүчөлөрдү ачсаңыз, P0P_0 жоюлат. Ар бир бөлүктүн өз ээси бар:

Delay чыгымы sX(P0Pd)sX(P_0 - P_d): сигнал иштеген учур менен биринчи бала-заказыңыз (child order) биржада активдүү болгон учур ортосундагы баа дрейфи. Бул сиздин инфраструктураңыз — сериялаштыруу, тармак, rate-limit кезектери, тобокел текшерүүлөрү. Эгер сигналыңызда чыныгы кыска мөөнөттүү альфа болсо, delay чыгымы — ал биринчи агып чыгуучу жер; туруктуу оң орточо мааниси рынок сиз жеткенче сиздин жагыңызга кыймылдап жатканын билдирет — азайып бараткан momentum-альфа, же ошол эле сигнал боюнча тезирээк соода кылган башка бирөө.

Соода чыгымы sjxj(pjP0)s\sum_j x_j(p_j - P_0): fill'дериңиз arrival'га салыштырмалуу канчага түшкөнү — кесилген спред плюс рынок таасири плюс графиктин ичиндеги дрейф. Бул аткаруу алгоритминин баа баракчасы, жана аткаруу изилдөөлөрү чын-чынында моделдеген чоңдук. Almgren, Thum, Hauptmann жана Li (2005, "Direct Estimation of Equity Market Impact," Risk 18(7), 58–62) муну Citigroup дескиндеги болжол менен 700 000 АКШ акция заказы боюнча (2001-жылдын декабры – 2003-жылдын июну) өлчөшкөн жана соода чыгымы күндүк волатилдүүлүккө жана катышуу ылдамдыгына (participation rate) масштабданарын аныкташкан — убактылуу таасир соода ылдамдыгынан даражалык мыйзам боюнча, көрсөткүчү 3/5-ке жакын; туруктуу таасир көлөмгө болжол менен сызыктуу. Калибрлеген учурда биз алардын функционалдык түрүн кайра колдонобуз.

Timing тобокелдиги: орточо мааниге кирбеген, бирок анын айланасындагы дисперсия. Ата-заказды убакыт боюнча таратуу күтүлгөн таасирди азайтат, бирок волатилдүүлүккө дуушар кылат; калган позициясы q(t)q(t) болгон график үчүн чыгымдын стандарттык четтөөсү σ0Tq(t)2dt\sigma \sqrt{\int_0^T q(t)^2\,dt} түрүндө масштабданат. Бул Almgren–Chriss фреймворкунун так оптималдаштырган тегиздиги. TCA отчётуңузда бул ата-заказдар боюнча IS'тин дисперсиясы катары көрүнөт — ар бир орточо маани менен катар стандарттык четтөөнү да көрсөтүңүз, болбосо бардык көңүл орточо маанге кетет да, бардык акчаңыз куйруктарга (tails) кетет.

Мүмкүнчүлүк чыгымы s(Xx)(PTP0)s(X - x)(P_T - P_0): терминалдык баада белгиленген толбой калган көлөм. Пассивдүү стратегиялар үчүн бул адатта эң чоң жана эң аз изилденген компонент, жана бул мүчө бүтүндөй фреймворкту чынчыл кылат — трэптер бөлүмүндө муну кеңири талкуулайбыз, анткени муну калтырып кетүү — адамдардын TCA менен өздөрүн алдоосунун эң кеңири тараган ыкмасы.

Комиссиялар FF: ачык бөлүк. Криптодо комиссияларды дисконттордон кийин (VIP деңгээлдер, fill убагындагы баада конвертацияланган токен-рибейттер) жазып жүрүңүз жана maker-рибейттерди белгиси менен сактаңыз — терс комиссия — маалымат, шум эмес.

Иштелген мисал

10 BTC сатып алуу (X=10X = 10). Сигнал Pd=60,000P_d = 60{,}000 баада иштейт; кагаз номиналы $600,000. Биринчи бала-заказ mid P0=60,012P_0 = 60{,}012 баада тастыкталат. Кийинки эки мүнөттүн ичинде 8 BTC VWAP 60,072 баасы менен толот; баа алыстап баратат, алгоритм өз лимитин сактайт, жана калган 2 BTC PT=60,240P_T = 60{,}240 mid'де жокко чыгарылат. Толгон номинал боюнча аралашма комиссиялар 2.5 bps.

Компонент Формула USD Кагаздын bps
Delay 10×(60,01260,000)10 \times (60{,}012 - 60{,}000) 120 2.0
Соода чыгымы 8×(60,07260,012)8 \times (60{,}072 - 60{,}012) 480 8.0
Мүмкүнчүлүк 2×(60,24060,012)2 \times (60{,}240 - 60{,}012) 456 7.6
Комиссиялар 8×60,072×2.5e-48 \times 60{,}072 \times 2.5\text{e-}4 120 2.0
Жалпы IS 1,176 19.6

Биржа отчёту $120 көрсөтөт. Чечимди аткаруунун чыныгы наркы — $1,176 — он эсе көп, эң чоң эки компонент комиссияга негизделген эсепке көрүнбөйт. Анын болжол менен 40% пайызы эч качан соода кылынбаган көлөмдөн келген. Fill'дерди гана талдаган TCA отчёту бул ата-заказды 8 bps деп бааламак жана баары жакшы деп аймак.

Implementation shortfall'дун кагаз портфелинен чыныгы портфелге чейинки waterfall декомпозициясы

Markout'тор: терс тандоону баалоо

Implementation shortfall ата-заказды баалайт. Ал жеке fill'дердин сапаты жөнүндө эч нерсе айтпайт — тагыраак айтканда, сиз системалуу түрдө сизден көбүрөөк билген контрагенттер менен соода кылып жатасызбы. Муну markout'тор өлчөйт: ар бир fill'ди ал болгондон белгилүү убакыт өткөндөн кийинки mid'ге белгилейт.

πτ=s(mtf+τpf)mtf×104 bps,\pi_\tau = \frac{s\,(m_{t_f + \tau} - p_f)}{m_{t_f}} \times 10^4 \ \text{bps},

бул жерде pfp_ftft_f убагындагы fill баасы, mtm_ttt убагындагы mid. Бул τ\tau горизонтундагы fill'дин бирдик-баада mark-to-market PnL'и, жана τ=0\tau = 0 учурунда таза анатомиясы бар: maker fill'и ++жарым-спредден баштайт (сиз bid'ден сатып алдыңыз, mid сизден жогору); taker fill'и -жарым-спредден баштайт. τ\tau өскөн сайын эмне болору — сооданын маалыматтык мазмуну:

  • τ=1\tau = 1s снайпингди жана эскирген котировкаларды "терип алууну" (stale-quote pickoff) бөлүп көрсөтөт. Эгер maker fill'дериңиз толгондон бир секунд өткөндөн эле суу астында болсо, тезирээк катышуучулар котировкаларыңыз туура эмес бааланган учурда так согуп жатышат — котировка жаңылоо циклиңиз алардын триггер циклинен жайыраак. Бул markout — стратегия эмес, латенция диагностикасы.
  • τ=10\tau = 10s классикалык терс тандоону (adverse selection) өлчөйт: fill'ден кийин баа сиздин баагыз аркылуу улана кыймылдайт. Маркет-мейкер үчүн бул спред-камтуу жеңип чыгышы керек чыгым; акция микроструктурасында мунун байланыштуу чоңдугун realized spread деп аташат, ал SEC Rule 605 отчёттуулугунда 5 мүнөттүк горизонтто институционалдаштырылган. Крипто тезирээк кыймылдайт; 10–60s — эквиваленттик тилке.
  • τ=60\tau = 60s fill'дер микроструктура горизонтунан ары карай сизге каршы momentum алып жүрөбү дегенди көрсөтөт — taker стратегиялары үчүн, сигналыңыздын 60s'тагы альфасы киргенде төлөгөн спред-плюс-таасирден ашабы дегенди. 4-4 bps'тан баштап эч качан нөлдөн өтпөгөн taker markout ийриги — киришин альфасы каржылай албаган стратегияны билдирет.

Maker/taker асимметриясы — пассивдүү соодалоонун бүтүндөй экономикасы, эки ийрикте берилген. +2+2 bps'тан (жарым-спред) баштап 60s'ке чейин 1-1 bps'ке чейин азайган maker ийриги мунун маанисин билдирет: сиз спредди камтыйсыз, бирок андан көбүрөөгүн кайра бересиз, жана бул эч кандай рибейт деңгээли менен оңолбойт. Ошол эле ийрик +0.7+0.7 bps'де турукташса, котировка кылуучу кыймылдаткыч өз наркын актайт. Бул так эле наивдуу touch-fill backtest'тер эске албаган нерсе — сиздин лимитиңизге баа тийген сайын толтуруучу симулятор толтурулуу туура эмес болуу менен корреляцияда экенин эске албайт, ошондуктан fill-simulation тепкичинин кезекти эске алган баскычы божомол катары эмес, кирүү маалымат катары өлчөнгөн markout'торду талап кылат.

Maker жана taker fill'дери үчүн терс тандоонун азаюусун көрсөткөн horizon'го жараша markout ийриктери

Эсептөө — өз mid агымыңыз боюнча merge_asof:

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)

Отчётту символ, суткалык саат жана mid'ден котировка аралыгы боюнча андан ары бөлүктөргө бөлүңүз. Maker стратегиясы үчүн эң иш-аракетке жарамдуу кесим — markout-by-queue-position-at-fill: жаңы кезектин алдыңкы бөлүгүндөгү fill'дер деңгээл сиз аркылуу сыпырылып өткөн fill'дерден таптакыр башкача баа алат.

Түтүк: эмнени журналдоо керек

TCA логика деңгээлинде эмес, журналдоо деңгээлинде өлүп калат. Жогорудагы математика көпчүлүк боттор ыргытып жиберген сандарды талап кылат, жана алардын эч бирин кийин биржа тарыхынан кайра курууга болбойт. Талашсыз шарттар:

  • Чечим mid'и, өз агымыңыздан, сигнал берилген учурда. Биржа шамдын жабылуу баасы эмес, кийинчерээк кайра курулган нерсе эмес. Эталон — "стратегиям чечим кабыл алганда ишенген баа" — муну ошол учурдагы процессиңиз гана билет.
  • Эки убакыт белгиси тең: чечим убактысы жана биринчи тастыктоо убактысы, болбосо delay чыгымы өлчөнгүс болуп калат жана унчукпастан соода чыгымына аралашып кетет.
  • Биржа убакыт белгиси, комиссия жана maker/taker флагы менен ар бир fill — жергиликтүү кабыл алуу убактыңыз өзүңүздүн кирүүчү латенцияңыз менен булганган.
  • Жокко чыгарылган жана мөөнөтү өткөн ата-заказдар, бардыгы сыяктуу журналдалган. Толуусу нөл болгон ата-заказдар — таблицадагы эң кымбат саптар.
  • Туруктуу mid агымы (же L1 агымы) 100–250ms майдалыкта, markout'торду эсептеп чыгууга жетишерлик убакытка сакталган. Эгер сиз fill симуляторуна арналган order book'торду мурунтан жазып жаткан болсоңуз, бул текке.

Эки жалпак таблица жетиштүү:

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",
}

Атрибуция функциясы декомпозициянын түз транскрипциясы:

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")

Жана атрибуция сурамдары — TCA бухгалтерия болуудан токтоп, изилдөө боло баштаган жер:

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)}))

4-сурам — акылдуу order router'дун оптималдаштырган кирүүчү таблицасы (маршрутизацияны ар бир venue боюнча TCA'сыз жүргүзүү — комиссия графиги боюнча маршрутизациялоо, б.а. эң кичине чыгым компоненти боюнча; Криптодогу Smart Order Routing ушул таблицаны баштапкы чекит катары алат). Markout модулу менен бирге бул — убада берилген ~200 сап.

Циклди жабуу: backtest чыгым моделин калибрлөө

Мына так ушул жерде түтүк өзүн актайт. Backtest'иңиз сандарды бекитет: slippage_bps = 5, толуу ыктымалдык ийриги, таасир коэффициенти. Алардын ар бири live аткаруу жөнүндөгү дооматтар, ал эми TCA таблицасы — live аткаруунун өзү. Цикл: TCA менен өлчө, чыгым моделин ыңгайлаштыр, ыңгайлаштырылган модель менен backtest жүргүз, соода кыл, кайра өлч.

Live fill'дерден TCA атрибуциясы аркылуу backtest чыгым моделине жабылган калибрлөө циклы

Taker чыгымы үчүн функционалдык түрдү өзүңүз ойлоп табуунун ордуна Almgren жана башкалардан (2005) карызга алыңыз. Алардын жыйынтыгы — чыгым күндүк волатилдүүлүккө көбөйтүлгөн катышуу ылдамдыгынын даражасына пропорционалдуу — эки параметрлүү модель берет:

trade_bpsσbps=a(xV)b,\frac{\text{trade\_bps}}{\sigma_{\text{bps}}} = a \cdot \left(\frac{x}{V}\right)^{b},

акция баасы боюнча болжолдук маани b0.6b \approx 0.6. Аны чийки ата-заказдарда эмес, топторго бириктирилген орточо мааниде ыңгайлаштырыңыз — жеке ата-заказдардын чыгымдары шумга көбүрөөк көз каранды (бул timing-тобокелдик мүчөсү), жана чийки маалыматтагы log-log регрессия ыракат менен шумга ыңгайлашат:

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

Андан кийин ката тилкелери жөнүндө чынчыл болуңуз. Almgren тобунда 700 000 заказ болгон жана алар дагы эле убактылуу-таасир көрсөткүчүн 0.600±0.0380.600 \pm 0.038 деп билдиришкен; 2000 ата-заказы бар бот bb'ни ар бир символ боюнча эркин баалай албайт. Практикалык режим: σ\sigma'га нормалдаштыргандан кийин символдор боюнча бириктир, bb'ни катуу 0.6'га карай тарт (же аны жөн эле бекитип, aa'ны гана ыңгайлаштыр), жана ай сайын кайра ыңгайлаштыр. Эгер ыңгайлаштырылган aa өсүп жатса, изиңиз чоңойгон же рынок сийрекдешкен — экөө тең backtest билиши керек болгон нерсе.

Maker стратегиялары үчүн калибрлөө максаттары башкача жана башка эки модулдан келет:

  • Толуу ыктымалдыгы: fill симуляторунун кезек модели (спред-аралык, кезек-позиция) уячасы боюнча P(fill)P(\text{fill})'ди болжолдойт; ата-заказ журналыңыз ар бир уяча боюнча чыныгы толуу катышын берет. Айырма — тор шилтемеси биркиткен симулятор багы.
  • Терс тандоо: симулятордун "fill'дер алмашылуучу" деген жабык жоромолун өлчөнгөн markout таблицасы менен алмаштырыңыз — dd аралыгындагы, hh саатындагы симуляцияланган maker fill'и дароо mark-to-market чегерүү катары өлчөнгөн π10s(d,h)\pi_{10s}(d, h)'ди алып жүрөт. Бул бирдиктүү өзгөртүү — галлюцинация көргөн maker backtest менен чындыкты кадаган backtest ортосундагы айырма; бул fill-simulation макаласында белгиленген жетишпеген калибрлөө кирүүсү болчу.
  • Чыгымдын дисперсиясы: backtest'ке IS'тин орточо маанисин эмес, таралышын берип туруңуз. Орточо маанини гана жылдырган чыгым модели timing-тобокелдиги жараткан drawdown'дорду кайра чагылдыра албайт; ар бир ата-заказ боюнча IS'ке ыңгайлаштырылган lognormal да константадан жакшыраак.

Slippage-ийриктерин толук иштетүү — функционалдык түрлөр, режимге көз каранды шарттар, даражалык мыйзам кайда бузулат — бул өзүнчө макала: Backtest'тер үчүн slippage ийриктери жана чыгым моделдери. Бул жердеги маани — архитектуралык: ошол макаланын моделдерин бул макаланын таблицаларысыз ыңгайлаштырууга мүмкүн эмес.

Тузактар

TCA боюнча адабият он жылдыктарга созулган, аны алдоо ыкмалары да ошончо эски. Үч ийгиликсиздик режими өзүн-өзү алдоонун көпчүлүгүн түшүндүрөт.

Эталонду алдоо (benchmark gaming). Arrival price'дан башка каалаган эталонду "кучактап" болот. Классикалык мисал — VWAP: интервалдык VWAP боюнча бааланган алгоритм arrival'га салыштырмалуу позиция жыйырма bps кансырап жатканда, эталонду бир базистик пункттун ичинде такыр так кайталай алат, анткени эталон сиз түртүп жаткан баа менен бирге дрейфтейт — маанилүү катышуу деңгээлинде сиздин өз принт'ериңиз VWAP'тын өзү, ошондуктан аны кадоо — өз үй тапшырмаңызды өзүң баалоо. Биз эталон саясатын TWAP vs VWAP vs POV макаласында талдаганбыз; TCA жагындагы эреже жөнөкөй: эталондор соодага чейин тандалат, жана планировщик өз графигинин эталону боюнча бааланганда да IS-versus-arrival дайыма эсептелет. Дагы бир назик түрү — arrival'ды алдоо: эгер decision_ts'ти орнотуучу компонент кыска мөөнөттүү momentum'ду көрө алса, ал delay мүчөсүн жакшыртуу үчүн "чечимдердин" убактысын тандап ала алат. Чечим убакыт белгилери — сигнал катмарына таандык, эч кандай аткаруу логикасы иштебей турганда журналдалышы керек.

Толгон-гана талдоодогу survivorship bias. Чыгым талдооңузду fill'дерге шарттасаңыз, пассивдүү аткаруу текке көрүнөт. Так айтканда: 100 пассивдүү сатып алуу ата-заказы, mid'ден бир тик төмөн. Алтымышы толот, жана — пассивдүү болгондуктан — arrival'дан орто эсеп менен 3 bps жакшыраак баада толот: өлчөнгөн "чыгым" 3-3 bps, сыймыктанарлык отчёт. Толбой калган кырк ата-заказ так эле баа жогору көтөрүлүп кеткендер болгон; аларды жокко чыгарылганда 25 bps терс деп белгилесеңиз, чынчыл сан 0.6×(3)+0.4×25=+8.20.6 \times (-3) + 0.4 \times 25 = +8.2 bps болот. Толгон-гана отчёт менен чыныгы отчёт 11 bps'ке жана белгиге да айырмаланат. Бул четки учур эмес — бул пассивдүү соодалоонун механизми: толуп калуу баа сиз аркылуу өтүп кетүү менен корреляцияда, ал эми бул так эле touch-fill backtest'терди фантазияга айландырган шарттуулук. Мүмкүнчүлүк чыгымы — IS'тин милдеттүү эмес тактоосу эмес; бул бүтүндөй өлчөөнү тандоо эффектисинен коргогон мүчө. Ушул эле bias'тын taker версиясы бар: аткарылбай калган IOC заказдар, rate limit же тобокел текшерүүлөрү тарабынан четке кагылган заказдар — эгер өткөрүп жиберүүлөр журналдалбаса, өткөрүп жиберүүнүн чыгымы өлчөнбөй калат, жана бул так эле стратегияңыз сооданы эң көп каалаган тез рыноктордо эң чоң болот.

Ар кыл кичине катачылыктар, кыскача: биржа шамдарынан кайра курулган arrival mid'дер (сиздин агымыңыз менен шам так маанилүү учурда келишпейт); ата-заказ боюнча bps'ти номинал салмагысыз орточолоо (миң майда fill бир чыныгы заказды үстөмдөп кетет); дисконтко чейин журналдалган комиссиялар; fill'ден башка venue'дон алынган markout mid'дери (venue аралык базис терс тандоо катары маскировкаланат); жана perp'терде funding чегерүүсүн аткаруу терезесине аралашууга жол берүү — funding чыгым, бирок ал аткаруу чыгымы эмес, аларды аралаштыруу эки талдоону тең уулантат.

Бул жума эмне кылуу керек

Ботуңузга эки журнал таблицасын кошуңуз — жогорудагы схема — колонкалар тизмеси, долбоор эмес. Эч нерсени backfill кылбаңыз; эки жумалык чынчыл журналдар бир жылдык кайра куруудан жакшыраак. Декомпозицияны жана үч markout горизонтун иштетиңиз. Кайсы компонент басымдуулук кыларын (комиссиялар дээрлик эч качан эмес), пассивдүү fill'дериңиз спред-камтуудан ары терс тандоого дуушар болобу, жана backtest'иңиздин чыгым константасы өлчөнгөн ийрикден канчалык алыс турганын билип аласыз. Андан кийин ыңгайлаштырылган ийрикти симуляторго кайра туташтырыңыз жана бул стратегияны иштетүүгө сизге буйрук берген backtest'ти кайра жүргүзүңүз. Perold-дун кагаз портфели, отуз сегиз жылдан кийин, дагы эле сиздин чыныгы портфелиңиздин жалгыз чынчыл атаандашы бойдон калууда.

blog.disclaimer

Authors

Eugen Soloviov
Eugen Soloviov

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.

Newsletter

Рынктан бир кадам алдыда болуңуз

AI соода аналитикасы, рынок талдоолору жана платформа жаңылыктары үчүн биздин жаңылыктар бюллетенине жазылыңыз.

Биз сиздин купуялыгыңызды урматтайбыз. Каалаган убакта жазылымдан чыга аласыз.