← Мақалаларға оралу
July 16, 2026
5 мин оқу

Fill симуляциясы: жабық баға фантазиясынан queue-ескеретін шынайылыққа дейінгі саты

Fill симуляциясы: жабық баға фантазиясынан queue-ескеретін шынайылыққа дейінгі саты
#fill симуляциясы
#backtest
#limit ордерлер
#queue позициясы
#жартылай орындалулар
#market making
#execution
#market microstructure

Сіздің backtest-іңізде екі модель бар: альфа моделі және fill моделі. Көпшілік адамдар күш-жігердің 95%-ын біріншісіне жұмсап, екіншісін өздері қолданатын framework-тен мұраға алады. Бұл — керісінше болуы керек нәрсе. Адал fill моделі бар орташа сигнал орташа, бірақ шынайы PnL бағасын береді. fill_price = candle.close деген формуласы бар тамаша сигнал ешнәрсенің бағасы болмайтын санды береді — ол сіз ешқашан тексермеген болжамның нәтижесі ғана.

Өтімді құралдардағы taker стратегиялары үшін fill моделі — түзету мүшесі. Ал resting limit ордерлерге қатысты кез келген нәрсе үшін — market making, пассивті кіру, post-only rebate жинау — fill моделі стратегияның өзі болып табылады. Сіз fill алатын-алмайтыныңыз, қашан, қандай көлемде және қандай кейінгі баға қозғалысына байланысты болатыны PnL-дің тек шамасын емес, таңбасын да анықтайды.

Backtest пен лайв параллелизмі мақаласында біз backtest пен лайв арасындағы алшақтықтардың толық таксономиясын құрдық және execution алшақтығына 5/5 ауырлық дәрежесін бердік — бұл ең нашар класс. Сол мақала fill симуляциясына үш ірі дәлдік деңгейін берді де, әрі қарай кетті. Бұл мақала — сол терең серігі: толық саты, деңгей бойынша деңгей, ордердің өмірлік циклі мемлекет-машинасы ретінде, нақты есептей алатын fill-ықтималдық шектерімен және "пайдалы" maker стратегиясының сатының қай жерінде дәл өлетінін көрсететін өлшенген экспериментпен.

Саты

Fill симуляциясының бес деңгейлі дәлдік сатысы

Әрбір деңгей көбірек деректі және көбірек кодты талап етеді, әрі әрбір деңгей нақты жүйелі ауытқуды жояды. Деңгейлер тек құны бойынша емес, олар нені дұрыс есептемейтіні бойынша реттелген.

0-деңгей: жабық баға бойынша fill

fill_price = bar.close

Ордер сигналды тудырған bar-дың жабылуында лезде, толығымен орындалады. Сигнал дәл сол жабылу бағасынан есептелген, яғни сіз баға басылып шыққан кезде әлі болмаған деректе сауда жасайсыз: execution костюмін киген look-ahead ауытқуы. Айналымы бар кез келген стратегия осы жерде жақсы көрінеді.

1-деңгей: келесі bar-дың ашылуы

fill_price = next_bar.open

Bar-дардағы taker логикасы үшін минималды адал модель. Сигнал tt bar-ында есептеледі, ал fill t+1t+1 bar-ының алғашқы бақыланатын бағасында орындалады. Бұл look-ahead-ты жояды, бірақ әлі де нөлдік spread, нөлдік impact, ашылу баға белгісінде шексіз өтімділік және 100% fill кепілдігін болжайды. Limit ордерлер үшін бұл touch-fill логикасына айналады (неге бұл улы екені төменде түсіндіріледі).

2-деңгей: spread + тұрақты slippage

half_spread = mid * spread_bps / 2e4
slip        = mid * slippage_bps / 1e4
fill_price  = mid + side * (half_spread + slip)   # side: +1 buy, -1 sell

Енді әрбір taker сауда spread-тің жартысы мен калибрленген тұрақты шаманы төлейді. Бұл — жоғары айналымды стратегия backtest-те өлетін алғашқы деңгей, дәл осының өзі — мақсат. Қалған қате: slippage тұрақты емес. Ол көрсетілген depth-ке қатысты ордер өлшеміне пропорционалды масштабталады және дәл сіздің стратегияңыз саудаласқысы келген кезде жарылып кетеді. Тұрақты 5 bps — режимдер бойынша орташа мән; ал сіздің стратегияңыз орташа режимде сауда жасамайды.

3-деңгей: market ордерлер үшін L2 depth-walk

Order book снапшоттарымен сіз slippage-ті болжамай, есептейсіз. QQ өлшеміндегі market buy ask жағын деңгей бойынша деңгей аралайды; fill бағасы тұтынылған деңгейлер бойынша көлем-салмақталған орташа болып табылады:

Pfill(Q)=1Qipimin ⁣(qi,  Qj<iqj)P_{\text{fill}}(Q) = \frac{1}{Q}\sum_{i} p_i \cdot \min\!\left(q_i,\; Q - \textstyle\sum_{j<i} q_j\right)

def depth_walk(levels: list[tuple[float, float]], qty: float) -> tuple[float, float]:
    """levels: [(price, size), ...] sorted best-first. Returns (vwap, filled_qty)."""
    remaining, cost = qty, 0.0
    for price, size in levels:
        take = min(size, remaining)
        cost += take * price
        remaining -= take
        if remaining <= 0:
            break
    filled = qty - remaining
    return (cost / filled if filled > 0 else float("nan"), filled)

Мұны едәуір адал ететін екі түзету бар. Біріншісі — латентность: book-ты сіздің шешім қабылдау уақыт белгісінен Δt\Delta t кейін тұрған күйінде аралаңыз, мұндағы Δt\Delta t — сіздің өлшенген сигналдан биржаға дейінгі латентность; сіз көрген book — сіз соққан book емес. Екіншісі — жартылай marketable fill-дер: егер сіздің limit-through бағаңыз шегіндегі көрсетілген depth QQ-дан аз болса, модель жартылай fill қайтаруы және resting қалдықты қалдыруы керек, бұл мәселені төмендегі мемлекет-машинасына тапсырады.

3-деңгейдегі қалған қате — impact пен refill: сіз book-ты статикалық объект ретінде тұтынасыз, бірақ нақты book-тар жартылай толығады (әрі нақты контрагенттер реакция білдіреді). Top-of-book depth-тің бірнеше пайызынан аз clip өлшемдері үшін бұл қате аз; үлкенірек clip-тер үшін үстінен impact моделі қажет (Almgren-Chriss 2001).

4-деңгей: limit ордерлер үшін ықтималдыққа негізделген queue-позиция fill-дері

0-3 деңгейлер "менің агрессивті ордерім қандай бағада орындалады" деген сұраққа жауап береді. 4-деңгей қиынырақ сұраққа жауап береді: менің пассивті ордерім жалпы орындала ма — және бұл maker стратегиясын бағалай алатын жалғыз деңгей. pp бағасында resting тұрған limit ордер, сол бағада сауда жасалған кумулятивті көлем алдында тұрған queue көлемінен асып кеткенде орындалады. Бұл сіз тікелей бақылай алмайтын FIFO queue-дегі позицияңызды қадағалауды талап етеді.

Queue-позицияны бағалау механизмі — бастапқы позиция, сауда мен cancel-дерге қатысты жаңарту ережелері, бақыланбайтын cancel-дерді бөлуге арналған f(x)f(x) ықтималдық отбасы — біз Дуал ішіндегі queue мақаласында құрған примитив. Мен мұны осы жерде қайта шығармаймын; симулятор оны тұтынады. 4-деңгей оған қоятыны — бағалаудың үстіне қойылатын fill шешім ережесі, ол төмендегі fill-ықтималдық бөлімінде қамтылған.

Бұл деңгейдің неге маңызды екені әдебиетте сандық түрде көрсетілген: Moallemi және Yuan (2016), "A Model for Queue Position Valuation in a Limit Order Book" еңбегінде үлкен-тик құралдары үшін queue басындағы позицияның экономикалық құны queue соңындағы позицияға қарағанда жарты-spread-пен салыстырмалы — бұл market-making стратегиясының бүкіл теориялық артықшылығымен бірдей шама реті екенін көрсетеді. Queue позициясын елемейтін fill моделі maker-дің PnL-ін дұрыс бағаламай қоймайды; ол мүлдем басқа стратегияның PnL-ін бағалайды.

5-деңгей де бар — нарық сіздің ордерлеріңізге реакция білдіретін толық агент-негізделген симуляция (Huang, Lehalle және Rosenbaum 2015 мағынасындағы queue-reactive модельдер; ABIDES сияқты мульти-агентті framework-тер, Byrd т.б. 2020). Тарихи replay, тіпті queue-ескергенде де, сіздің ордеріңіз басқалардың мінез-құлқында ешнәрсені өзгертпейді деп болжайды. Бұл болжам retail көлемінде дұрыс, ал сіздің квоталарыңыз деңгейдің көрінетін бөлігіне айналған сайын дұрыс болмай кетеді. 5-деңгей бұл жерде қамтылмайды; сатының 4-те аяқталмайтынын білу керек.

Мемлекет-машинасы ретіндегі жартылай орындалулар

0-2 деңгейлер ордерді функция шақыруы ретінде елестете алады: жіберу, баға алу, дайын. 3-деңгейден бастап ордер — өмірлік циклі бар процесс, және симулятор оны мемлекет-машинасы ретінде модельдеуі керек, әйтпесе ол ең маңызды жағдайларды үнсіз дұрыс өңдемейді.

Queue-позиция аннотациялары бар ордердің өмірлік циклінің мемлекет-машинасы

enum OrderState {
    PendingNew,                                  // sent, not yet acked (latency window)
    Resting     { remaining: f64, q_ahead: f64 }, // in book, queue position estimated
    PartialFill { remaining: f64, q_ahead: f64 }, // some qty done, rest still queued
    PendingAmend,                                 // amend in flight
    PendingCancel,                                // cancel in flight
    Filled,
    Canceled    { filled_qty: f64 },              // may be partially filled at cancel time
    Rejected,
}

Ауысулар экономиканы алып жүреді:

  • PendingNewResting: ордер queue-ге шешім қабылдау сәтінде емес, ack алынған сәтте бар нәрсенің артында қосылады. Сіздің queue позицияңыз t+Δtackt + \Delta t_{\text{ack}} уақытындағы деңгей көлемімен себіледі. Шешім қабылдау сәтінде себетін симуляторлар queue приоритетін жүйелі түрде асыра бағалайды — дәл сіздің латентность терезеңізде келген көлем мөлшерінде, ал бұл — жарылыс кездерінде ең көп көлем.
  • RestingPartialFill: сіздің деңгейіңізде алдыңдағы queue-ден үлкен сауда сізді жартылай толтырады. Қалдық өзінің (енді queue басындағы) позициясын сақтайды. Жартылай fill-дер шу емес — олар ақпарат: 1.0-ден 0.3 fill алып, бағаның кейін секіргенін көру — толық fill-мен салыстырғанда басқа PnL оқиғасы, ал maker-дің қор процесі осы фрагменттерден құрылады.
  • RestingPendingAmendResting: тұзақ. Іс жүзінде барлық крипто платформаларда amend — cancel/replace: Binance-тің cancelReplace қосарланған орындауға қарсы атомды, бірақ queue соңында жаңа ордер ID қайтарады. Тіпті нативті modify семантикасы бар платформалар (CME Globex) уақыт приоритетін тек көлемді азайту кезінде сақтайды; баға өзгерісі немесе көлемді ұлғайту оны жоғалтады. Сондықтан симуляторда: кез келген баға amend-і q_ahead-ты ағымдағы деңгей көлемінің толық мәніне қайта орнатады. Әрбір 500мс сайын қайта позицияланатын quoting engine "квотаны сақтап тұрған" жоқ — ол queue соңына үздіксіз қайта кіріп отыр, ал оның шынайы fill профилі дерлік таза жағымсыз селекция болып табылады. Moallemi-Yuan нәтижесімен біріктірсек: қайта квота беруде баға бар, және ол баға — сіздің queue позицияңыз.
  • PendingCancelPartialFillCanceled: cancel-дер де латентность алады. Квотаны алып тастау туралы шешім мен cancel matching engine-ге жеткен уақыт арасындағы терезеде сіз әлі де fill ала аласыз — және бұл fill-дер сіз алатын ең нашар fill-дер болады, себебі квотаны алып тастап жатқан себебіңіз — нарықтың сізді басып өтуге дайын болғаны. Cancel латентносы жоқ симулятор сіздің тарихыңыздан дәл ең улы fill-дерді жойып тастайды.

Мемлекет-машинасы сонымен қатар симулятордың есеп жүргізуін адал етеді: комиссиялар әрбір fill оқиғасы бойынша есептеледі, қор әрбір fill оқиғасы бойынша жаңарады, ал уақыт-мемлекетте статистикасы (ордерлер орындалғанға дейін немесе cancel болғанға дейін қанша уақыт resting тұрады) лайв журналдармен тікелей салыстырылатын болады — соңында калибрлеу циклы дәл осымен қоректенеді.

Limit-fill ықтималдығы: үш модель және жақша

pp бағасында resting тұрған ордер берілгенде (айталық, bid), симулятор qашан fill жариялайды? Адалдық артуы бойынша үш шешім ережесі:

1. Touch-fill (наивты). Баға сіздің деңгейіңізге тисе, fill: lowtp\text{low}_t \le p. Бұл — бірінші-өту-уақыты ережесі, оның сәтсіздігі ширек ғасыр бұрын өлшенген: Lo, MacKinlay және Zhang (2002), "Econometric Models of Limit-Order Executions" (Journal of Financial Economics 65), нақты limit ордер деректеріне survival модельдерін бейімдеп, бірінші-өту уақыттарынан құрылған гипотетикалық орындаулар "нақты limit-order орындауларына өте нашар прокси болып табылады" деген қорытындыға келеді. Сәтсіздік режимі құрылымдық: баға сіздің деңгейіңізге тиіп, кейін секіргенде, touch queue-дің басын тұтынды — сізден бұрын квота берген трейдерлерді. Touch-fill сізге олардың fill-дерін береді. Одан да жаманы, ол дәл жақсы fill-дерді береді (touch-and-bounce maker үшін пайдалы сценарий), ал сіздің нақты fill жиынтығыңыз touch-and-run-through-ке — жағымсыз селекцияланғандарға — қарай ауытқыған.

2. Trade-through (консервативті шек). Тек баға сіздің деңгейіңізден толығымен өтсе ғана fill: lowt<pϵ\text{low}_t < p - \epsilon, немесе tick деректерінде, pp бағасында сауда жасалған кумулятивті көлем бүкіл деңгейден асып кетсе. Егер баға толығымен өтсе, pp-дегі бүкіл queue тұтынылды, сондықтан сіздің позицияңызға қарамастан fill алдыңыз. Бұл сізге алмаған fill-ді ешқашан бермейді. Оның ауытқуы touch-fill-дікінің айна кескіні: ол queue-нің толық енбей сіздің позицияңызға дейін таусылған әрбір fill-ден бас тартады, ал ол беретін fill-дер тым жиі run-through (жағымсыз селекцияланған) болады. Trade-through астындағы maker backtest-і — бағалау емес, стресс-тест.

3. Queue-depletion бағалауы. Trade tape-тен pp бағасында сауда жасалған кумулятивті көлем MtM_t мен L2 delta-дан шығарылған cancel болған көлем CtC_t-ны қадағалаңыз. Сіздің бағаланған queue-ңіз:

Q^ahead(t)=max ⁣(Q0MtϕCt,  0)\hat{Q}_{\text{ahead}}(t) = \max\!\Big(Q_0 - M_t - \phi \cdot C_t,\; 0\Big)

мұндағы Q0Q_0 — (ack уақытындағы) ордер кіру кезіндегі деңгей көлемі, ал ϕ[0,1]\phi \in [0,1] — сізден алдыда деп есептелетін cancel-дер үлесі — оның қағидатты формасы (f(x)f(x) отбасы) queue-позиция мақаласында шығарылған реттеу тетігі. Fill Q^ahead=0\hat{Q}_{\text{ahead}} = 0 болғанда басталады; сіздің орындалған көлеміңіз одан асатын сауда көлемі, бұл жартылай fill-дерді табиғи түрде береді:

filled(t)=min ⁣(S,  max(MtQ0+ϕCt,0))\text{filled}(t) = \min\!\Big(S,\; \max(M_t - Q_0 + \phi\, C_t,\, 0)\Big)

ϕ=1\phi = 1 орнату (барлық cancel-дер сізден алда) осы модельдің оптимистік шетін береді; ϕ=0\phi = 0 — пессимистік шетін. Тек сирек L2 снапшоттарыңыз болып, деңгейде trade tape-іңіз жоқ кезде — 100мс-пен шектелген крипто feed-терде жиі кездеседі — модельге негізделген априорға сүйенуге болады: Cont, Stoikov және Talreja (2010), "A Stochastic Model for Order Book Dynamics" (Operations Research 58), әр баға деңгейін birth-death queue ретінде модельдейді және Laplace түрлендірулерін пайдаланып, ағымдағы queue өлшемдеріне байланысты, bid-тегі ордердің mid қозғалғанға дейін орындалу ықтималдығын есептейді. Бұл — аналитикалық fill-ықтималдық оракулы: tape replay-ге қарағанда дөрекі, touch-fill-ден әлдеқайда жақсы, әрі ыстық backtest циклінің ішінде есептеу үшін жеткілікті арзан.

Жақша тәртібі

Үш ереже — бәсекелестер емес, олар — реттелу:

fillstrade-through    fillsqueue    fillstouch\text{fills}_{\text{trade-through}} \;\subseteq\; \text{fills}_{\text{queue}} \;\subseteq\; \text{fills}_{\text{touch}}

бұл PnL жақшасын тудырады. Әрбір maker backtest-ін үш рет жүргізіп, интервалды хабарлаңыз:

PnLtrade-through    PnLlivePnLtouch\text{PnL}_{\text{trade-through}} \;\le\; \text{PnL}_{\text{live}} \lesssim \text{PnL}_{\text{touch}}

(жоғарғы теңсіздік жуықталған — touch-fill стратегияларды жай ғана үлкейтіп қана қоймай, дұрыс емес дәрежелей де алады, себебі ол сізге контрфактілі жақсы fill-дерді береді). Осыдан шығатын шешім ережесі: maker стратегиясы тек жақшаның консервативті шетінде аман қалса ғана орналастыруға жарамды, әрі жақша нүктелік бағаның мағынасы болатындай тар болуы керек. +9ktouch/9k touch / -3k trade-through көрсетіп тұрған стратегияның 12kкежуықенібаржақшасыбар;сізсимуляторыңыздыңболжамыальфаңыздыбасымететініненбасқаешнәрсебілмейсіз.+12k-ке жуық ені бар жақшасы бар; сіз симуляторыңыздың болжамы альфаңызды басым ететінінен басқа ешнәрсе білмейсіз. +2.1k / +$0.4k көрсетіп тұрған стратегия сізге шынайы бірнәрсе айтады.

Эксперимент: бір maker стратегиясын сатымен төмен жүргізу

Maker стратегиясының PnL-і fill-модель сатысы бойынша деңгей сайын құлдырауы

Әдейі қарапайым maker алайық — best bid/ask-та симметриялы квоталар, тұрақты 0.05 BTC clip-тер, ±0.5 BTC-те шектелген қор, шекте taker flattening-мен. BTCUSDT perpetual деректерінің бір айы: төменгі деңгейлер үшін 1м bar-лар, жоғарғы деңгейлер үшін 100мс L2 diff-тер плюс trade tape; maker комиссиясы 1.0 bps, taker 4.0 bps. Әр деңгейде бірдей сигнал коды (ортақ ядро, сондықтан жалғыз айнымалы — fill моделі). Төмендегі сандар — біздің бір репрезентативті жүргізуіміз; сіздің шамаларыңыз платформа, ай және өлшем бойынша ерекшеленеді; пішіні ерекшеленбейді:

Деңгей Fill моделі Квота fill қарқыны Fill саны Ай PnL Қорытынды
0 close-fill 98% 41,200 +$14,800 фантазия
1 next-bar-open / touch 1м bar-да 89% 37,400 +$9,600 латентносы бар фантазия
2 touch + spread & тұрақты slippage taker flatten-де 89% 37,400 +$7,100 шығындар модельделген, fill-дер әлі де ойдан шығарылған
3 + L2 depth-walk taker flatten-де 89% 37,400 +$6,400 шығулар адал, кірулер әлі де ойдан шығарылған
4a trade-through (консервативті) 21% 8,900 -$3,900 стресс шегі
4b queue-depletion, ϕ\phi калибрленген 37% 15,600 -$700 ең жақсы баға
4c queue-depletion, ϕ=1\phi = 1 (оптимистік) 44% 18,700 +$1,900 жоғарғы шек
лайв көлеңке жүргізу, сол ай 35% 14,100 -$1,150 шындық

Кестені жоғарыдан төменге оқып, стратегия қай жерде өлетінін бақылаңыз. Ол 2-деңгейде емес — комиссиялар мен slippage 26%-ды алып тастайды, ал стратегия әлі де сенімді пайдалы болып көрінеді. Ол 3-деңгей мен 4-деңгей арасында өледі, әрі ешбір шығын моделі түсіре алмайтын себеппен өледі: fill селекциясы. Touch-fill 37,400 fill берді, олардың көпшілігі touch-and-bounce болды — таза spread capture. Queue-ескеретін модель сол fill-дердің 58%-ын жойды, әрі ол жойған fill-дер үйлесімсіз пайдалылар болды: деңгей жеңіл соғылғанда, қайта квота беретін retail-латентносы бар maker-дің алдындағы queue бәрін жұтады. 4-деңгейге дейін аман қалатын fill-дер деңгейді тазалайтын sweep-терге қарай ауытқыған — баға сіздің арқылы қазірдің өзінде қозғалып жатқан fill-дер. Fill қарқыны 2.4 есе төмендеді; PnL таңбасы өзгерді. Бұл асимметрия — жақсы fill-дерді жоғалту, жаманын сақтау — жағымсыз селекцияны механикалық түрде айқын етеді, әрі ол 4-тен төмен әрбір деңгейде көрінбейді.

Сондай-ақ жақшаның не істегеніне назар аударыңыз: [-3,900,+3,900, +1,900] нөлді қамтиды, калибрленген баға -700,аллайв700, ал лайв -1,150. Симулятор лайв PnL-ді долларға дейін дәл түсірген жоқ — ол таңбаны, шаманы және fill қарқынын 2 пункт ішінде дұрыс алды. Fill моделі дәл осы үшін керек. 1-деңгейлі backtest лайв PnL-ден жалпы айлық gross артықшылығы бірнеше мың доллар болатын стратегияда $10,750-ге жаңылды: fill-модель қатесі альфаның мөлшерінен шамамен 3 есе үлкен болды. Демек тезис: сіздің fill моделіңіз альфаңыздан үлкенірек болжам.

Төменгі деңгейлер үшін бір ескерту: егер сіз bar деректерінде өмір сүруге мәжбүр болсаңыз (0-2 деңгейлер), кем дегенде адаптивті ажыратымдылық терендету арқылы bar ішіндегі белгісіздікті шешіңіз — SL, TP немесе квота деңгейлері bar диапазонының ішіне түскенде 1м-ден 1с/100мс/сауда деректеріне дейін терендету. Drill-down реттілік қателерін (қай деңгей бірінші тиді) түзетеді, бірақ queue қателерін түзете алмайды; ол бұл мақаланың деректер-ажыратымдылық серігі, 4-деңгейдің алмастырғышы емес.

Калибрлеу циклы: лайв fill-дерге қарсы жабу

4-деңгейлі симулятордың еркін параметрлері бар — ϕ\phi, латентность Δt\Delta t, cancel-латентность, деңгей-refill болжамы. Калибрленбеген, ол — түрлі пішінде болжам ғана. Оны құралға айналдыратын цикл:

1. Бәрін лайвте журналдаңыз. Биржа уақыт белгілерімен әрбір ордер оқиғасы: жіберу, ack, әрбір жартылай fill, amend ack, cancel ack. Плюс жіберу сәтіндегі L2 күйі. Бұл — backtest-live параллелизмі мақаласы оның DivergenceMonitor-ы үшін талап ететін журналдау тәртібінің дәл өзі — fill-модель калибрлеуі сол монитордың ең терең қабаты.

2. Сол ордерлерді симулятор арқылы replay жасаңыз. Жазылған нарық деректерін және жазылған ордер нұсқауларын (fill-дерді емес) симуляторға беріңіз. Енді сізде жұптасқан нәтижелер бар: әрбір лайв ордер үшін — симуляцияланған тағдыр.

3. Орташаларды емес, таралымдарды bucket-терде салыстырыңыз. Жалғыз глобалды fill-қарқыны сәйкестігі теңгерімделетін қателерді жасыра алады (тыныш режимдерде тым оптимистік, жарылыс кездерінде тым пессимистік — "калибрленген" деп есептеледі). Драйверлер бойынша bucket-терге бөліңіз:

import numpy as np
from scipy.stats import ks_2samp

def calibration_report(pairs, bucket_key):
    """pairs: [{'bucket':…, 'live_filled':bool, 'sim_filled':bool,
                'live_ttf':float|None, 'sim_ttf':float|None}, …]"""
    out = {}
    for b in sorted({p['bucket'] for p in pairs}):
        grp = [p for p in pairs if p['bucket'] == b]
        live_fr = np.mean([p['live_filled'] for p in grp])
        sim_fr  = np.mean([p['sim_filled']  for p in grp])
        live_ttf = [p['live_ttf'] for p in grp if p['live_ttf'] is not None]
        sim_ttf  = [p['sim_ttf']  for p in grp if p['sim_ttf']  is not None]
        ks = ks_2samp(live_ttf, sim_ttf) if len(live_ttf) > 20 and len(sim_ttf) > 20 else None
        out[b] = {
            'n': len(grp),
            'fill_rate_live': live_fr,
            'fill_rate_sim':  sim_fr,
            'fill_rate_gap':  sim_fr - live_fr,      # signed: + means sim optimistic
            'ttf_ks_pvalue':  ks.pvalue if ks else None,
        }
    return out

Bucket бойынша екі статистика: таңбаланған fill-қарқыны алшақтығы (симулятор минус лайв) және орындалған ордерлер арасындағы time-to-fill таралымдарына KS тесті. Time-to-fill салыстыруы — өткір нәрсе: симулятор fill қарқынын сәйкестендіре алады, бірақ жүйелі түрде дұрыс емес уақыттарда толтырады, бұл әрбір кейінгі қор мен жағымсыз-селекция статистикасын бүлдіреді. Бұл дәл Lo-MacKinlay-Zhang-тың survival-анализ тұжырымдамасының сабағы: execution — уақыт-оқиға есебі, сондықтан оны сол ретінде тексеру керек.

4. Ажыратылу қабілеті ретімен параметрлерді бейімдеңіз. Алдымен латентность (ack уақыт белгілерінен тікелей өлшенеді — бейімделмейді). Содан кейін queue-тереңдік bucket-тері бойынша fill-қарқыны алшақтығын минимизациялау арқылы ϕ\phi. Содан кейін волатильділік-режим bucket-терін тексеріңіз: жарылыс bucket-терінде шоғырланған тұрақты оптимистік алшақтық әдетте ϕ\phi-ге емес, симуляторыңыздың cancel-латентность улы fill-дерін немесе деңгей refill-ін жеткіліксіз модельдеуін білдіреді.

5. Жақшаны қайта жүргізіңіз. Калибрлеуден кейін queue-depletion бағасы жақшаның ішінде лайвке жақын орналасуы керек, әрі — нақты қабылдау сынағы — симулятор астындағы стратегия нұсқаларының дәрежесі көлеңке режиміндегі дәрежесіне сәйкес келуі керек. Содан кейін параметрлерді бекітіп, кестеге сай қайта калибрлеңіз; fill динамикасы платформаның комиссия деңгейлерімен, tick-өлшем өзгерістерімен және HFT популяциясымен бірге дрейф жасайды, ал наурызда калибрленген ϕ\phi шілдеде гипотеза ғана.

Біздің жүргізулерден конвергенция күтулері: калибрленбеген 4-деңгейлі симулятор әдетте лайв fill қарқынынан ±10-15 пункт шамасында орналасады; бір калибрлеу циклінен кейін — ±3-5 пункт, ал time-to-fill KS p-мәндері енді біркелкі бас тартылмайды. Тарихи replay-мен бұдан жақсырақ ешқашан бола алмайсыз — қалдық — нарықтың сізге реакциясы, бұл 5-деңгейдің мәселесі.

Не есте сақтау керек

  1. Деңгейіңізді атаңыз. Әрбір backtest осы сатыда тұрады, сіз деңгейді таңдасаңыз да, framework-іңіз таңдаса да. Сіз fill моделіңіздің деңгейін және оның белгілі ауытқуларын атай алмасаңыз, PnL санында сіз көрмеген қателік жолақтары бар.
  2. Taker-лер 3-деңгейде тоқтай алады. Depth-walk плюс өлшенген латентность retail көлемінде агрессивті execution-ды адал бағалайды. Үнемделген күш-жігерді деректер сапасына жұмсаңыз.
  3. Maker-лер 4-деңгейден бастайды. Одан төмен, limit-ордер fill логикасы жуықталған емес — ол қарама-қарсы жағымсыз-селекция қисаюы бар контрфактілі fill жиынын таңдайды. Maker стратегияларының touch-fill backtest-тері production-мен байланысқа түскенде өлетін стратегияларды тудыратын ең сенімді генератор.
  4. Fill-ді емес, өмірлік циклді модельдеңіз. Жартылай fill-дер, amend-queue-ды қайта орнатады, cancel латентносы — мемлекет-машинасы улы fill-дер тұратын жер, ал улы fill-дер — maker PnL-і өлетін жер.
  5. Жақшаны хабарлаңыз. Консервативті және оптимистік шектер екі қосымша backtest жүргізуін алады және "менің backtest-ім +7kдейді"дегенді"шындық[7k дейді" дегенді "шындық [-3.9k, +$1.9k] аралығында бір жерде" дегенге айналдырады — бұл басқа, әрі жақсырақ шешім.
  6. Bucket-терде лайв fill-дерге қарсы калибрлеңіз. Тек жиынтықта расталған fill моделі — жасырын теңгерімделетін қателері бар fill моделі. Bucket-теулі fill-қарқыны алшақтықтары плюс time-to-fill KS тесттері, тоқсан сайын қайта калибрленеді.

Сатының деңгейлері академиялық градациялар емес — әрқайсысы сіздің backtest-іңіз сізге айтуды тоқтататын нақты өтірік. Өтіріктер артықшылығыңыздан кіші болғанша көтеріліңіз.

Пайдалы сілтемелер

  1. Moallemi, C., Yuan, K. — A Model for Queue Position Valuation in a Limit Order Book (2016)
  2. Cont, R., Stoikov, S., Talreja, R. — A Stochastic Model for Order Book Dynamics, Operations Research 58(3), 549-563 (2010)
  3. Lo, A., MacKinlay, C., Zhang, J. — Econometric Models of Limit-Order Executions, Journal of Financial Economics 65(1), 31-71 (2002)
  4. Huang, W., Lehalle, C.-A., Rosenbaum, M. — Simulating and Analyzing Order Book Data: The Queue-Reactive Model, JASA 110(509), 107-122 (2015)
  5. Almgren, R., Chriss, N. — Optimal Execution of Portfolio Transactions (2001)
  6. Byrd, D., Hybinette, M., Balch, T. — ABIDES: Towards High-Fidelity Multi-Agent Market Simulation (2020)
  7. Binance API — Cancel-Replace order semantics
  8. CME Globex — Order modification and time priority rules

Дәйексөз

@article{soloviov2026fillsimulation,
  author = {Soloviov, Eugen},
  title = {Fill simulation: the ladder from close-price fantasy to queue-aware reality},
  year = {2026},
  url = {https://marketmaker.cc/blog/fill-simulation-partial-fills-backtest},
  description = {Five rungs of fill simulation fidelity — from close-price fills to probabilistic queue-position models. Partial fills as a state machine, limit-fill probability bounds as a PnL bracket, and a calibration loop against live fills.}
}
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 сауда талдаулары, нарық аналитикасы және платформа жаңалықтары үшін біздің ақпараттық бюллетеньге жазылыңыз.

Біз сіздің жекелігіңізді құрметтейміз. Кез келген уақытта жазылымнан шығуға болады.