Simulazione dei fill: la scala dal fantasy del close-price alla realtà queue-aware
Il tuo backtest contiene due modelli: un modello della tua alpha e un modello dei tuoi fill. La maggior parte delle persone spende il 95% dei propri sforzi sul primo ed eredita il secondo da qualunque framework capiti di usare. Questo è al contrario. Un segnale mediocre con un modello di fill onesto produce una stima di PnL mediocre ma reale. Un segnale eccellente con fill_price = candle.close produce un numero che non è la stima di niente — è l'output di un'assunzione che non hai mai esaminato.
Per strategie taker su strumenti liquidi, il modello di fill è un termine correttivo. Per qualunque cosa coinvolga ordini limite passivi — market making, entrate passive, raccolta di rebate post-only — il modello di fill è la strategia. Se vieni riempito, quando, in che quantità, e condizionatamente a quale mossa di prezzo successiva determina il segno del PnL, non solo la sua magnitudine.
In Backtest-live parity abbiamo mappato la tassonomia completa delle divergenze backtest-live e valutato la divergenza di esecuzione con severità 5/5 — la classe peggiore. Quell'articolo assegnava alla simulazione dei fill tre livelli di accuratezza grossolani e proseguiva. Questo è il compagno di approfondimento: la scala completa, gradino per gradino, con il ciclo di vita dell'ordine come macchina a stati, limiti di probabilità di fill effettivamente calcolabili, e un esperimento misurato che mostra esattamente in quale punto della scala muore una strategia maker "profittevole".
La scala

Ogni gradino richiede più dati e più codice, e ogni gradino rimuove un bias sistematico specifico. I gradini sono ordinati in base a cosa sbagliano, non solo in base al costo.
Gradino 0: fill al close-price
fill_price = bar.close
L'ordine viene riempito istantaneamente, completamente, al close della barra che ha generato il segnale. Il segnale è stato calcolato da quello stesso close, quindi stai facendo trading su dati che non esistevano quando il prezzo è stato stampato: look-ahead bias travestito da esecuzione. Qualunque strategia con turnover sembra buona qui.
Gradino 1: apertura della barra successiva
fill_price = next_bar.open
Il modello minimo onesto per la logica taker su barre. Il segnale è calcolato sulla barra , il fill avviene al primo prezzo osservabile della barra . Questo elimina il look-ahead ma assume ancora spread zero, impatto zero, liquidità infinita al prezzo di apertura, e certezza di fill del 100%. Per gli ordini limite degenera in una logica di touch-fill (più avanti sul perché questo sia veleno).
Gradino 2: spread + slippage fisso
half_spread = mid * spread_bps / 2e4
slip = mid * slippage_bps / 1e4
fill_price = mid + side * (half_spread + slip) # side: +1 buy, -1 sell
Ora ogni trade taker paga metà dello spread più una costante calibrata. Questo è il primo gradino in cui una strategia ad alto turnover può morire nel backtest — che è il punto. L'errore residuo: lo slippage non è costante. Scala con la dimensione dell'ordine relativa alla profondità visualizzata ed esplode esattamente quando la tua strategia vuole di più fare trading. Un valore fisso di 5 bps è una media su vari regimi; la tua strategia non fa trading sul regime medio.
Gradino 3: depth-walk L2 per ordini di mercato
Con gli snapshot dell'order book smetti di indovinare lo slippage e lo calcoli. Un ordine di acquisto a mercato di dimensione percorre il lato ask livello per livello; il prezzo di fill è la media ponderata per volume sui livelli consumati:
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)
Due correzioni rendono questo materialmente più onesto. Primo, la latenza: percorri il book come si presentava dopo il timestamp della tua decisione, dove è la tua latenza misurata segnale-verso-exchange — il book che hai visto non è il book che hai colpito. Secondo, i fill parziali marketable: se la profondità visualizzata entro il tuo prezzo limite-attraverso è inferiore a , il modello deve restituire un fill parziale e lasciare un residuo passivo, il che passa il problema alla macchina a stati qui sotto.
L'errore residuo al gradino 3 è impatto e refill: consumi il book come un oggetto statico, ma i book reali si ricostituiscono parzialmente (e le controparti reali reagiscono). Per dimensioni di clip inferiori a qualche punto percentuale della profondità al top-of-book questo errore è piccolo; per clip più grandi serve un modello di impatto (Almgren-Chriss 2001) sovrapposto.
Gradino 4: fill probabilistici basati sulla posizione in coda per ordini limite
I gradini 0-3 rispondono a "a quale prezzo si riempie il mio ordine aggressivo". Il gradino 4 risponde alla domanda più difficile: il mio ordine passivo si riempie affatto — ed è l'unico gradino in grado di prezzare una strategia maker. Un ordine limite passivo al prezzo si riempie quando il volume scambiato cumulativo a supera il volume in coda che lo precedeva. Questo richiede di tracciare la tua posizione in una coda FIFO che non puoi osservare direttamente.
Il meccanismo di stima della posizione in coda — posizione iniziale, regole di aggiornamento su trade vs. cancellazioni, la famiglia di probabilità per allocare le cancellazioni non osservate — è la primitiva che abbiamo costruito in Queue inside the wall. Non la ri-deriverò qui; il simulatore la consuma. Ciò che il gradino 4 aggiunge è la regola decisionale di fill sopra la stima, trattata nella sezione sulla probabilità di fill qui sotto.
Perché questo gradino conta è quantificato in letteratura: Moallemi e Yuan (2016), "A Model for Queue Position Valuation in a Limit Order Book," mostrano che per strumenti a tick largo il valore economico di una posizione a inizio coda rispetto a fine coda è paragonabile al mezzo spread — lo stesso ordine di grandezza dell'intero edge teorico di una strategia di market making. Un modello di fill che ignora la posizione in coda non stima male il PnL di un maker; stima il PnL di una strategia diversa.
Esiste un gradino 5 — la simulazione completa basata su agenti in cui il mercato reagisce ai tuoi ordini (modelli queue-reactive nel senso di Huang, Lehalle e Rosenbaum 2015; framework multi-agente come ABIDES, Byrd et al. 2020). Il replay storico, anche se queue-aware, assume che il tuo ordine non cambi nulla nel comportamento di tutti gli altri. Questa assunzione va bene a dimensioni retail e diventa sempre più sbagliata man mano che le tue quote diventano una frazione visibile del livello. Il gradino 5 è fuori scope qui; sappi che la scala non finisce al 4.
I fill parziali come macchina a stati
I gradini 0-2 possono fingere che un ordine sia una chiamata di funzione: invia, ottieni il prezzo, fine. Dal gradino 3 in su, un ordine è un processo con un ciclo di vita, e il simulatore deve modellarlo come una macchina a stati o gestirà silenziosamente male i casi che contano di più.

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,
}
Le transizioni portano con sé l'economia:
PendingNew→Resting: l'ordine si unisce alla coda dietro a tutto ciò che è presente al momento dell'ack, non al momento della decisione. La tua posizione in coda viene seminata con il volume del livello al tempo . I simulatori che seminano al momento della decisione sovrastimano sistematicamente la priorità in coda — esattamente del volume arrivato durante la tua finestra di latenza, che è la maggior parte del volume durante i burst.Resting→PartialFill: un trade al tuo livello più grande della coda che ti precede ti riempie parzialmente. Il residuo mantiene la sua posizione (ora a inizio coda). I fill parziali non sono rumore — sono informazione: essere riempiti per 0,3 su 1,0 e vedere il prezzo rimbalzare via è un evento di PnL diverso da un fill completo, e il processo di inventario di un maker è costruito su questi frammenti.Resting→PendingAmend→Resting: la trappola. Praticamente su ogni venue crypto, un amend è un cancel/replace — ilcancelReplacedi Binance è atomico contro la doppia esecuzione ma restituisce un nuovo order ID in fondo alla coda. Persino i venue con semantiche di modifica native (CME Globex) preservano la priorità temporale solo per una riduzione di quantità; un cambio di prezzo o un aumento di quantità la fa perdere. Quindi nel simulatore: qualunque amend di prezzo azzeraq_aheadal volume completo attuale del livello. Un motore di quotazione che si ri-posiziona ogni 500ms non sta "mantenendo una quota" — si sta re-inserendo perpetuamente in fondo alla coda, e il suo profilo di fill realistico è quasi pura selezione avversa. Combinato con il risultato di Moallemi-Yuan: ri-quotare ha un prezzo, e il prezzo è la tua posizione in coda.PendingCancel→PartialFill→Canceled: anche le cancellazioni richiedono latenza. Nella finestra tra la decisione di ritirare una quota e l'arrivo della cancellazione al matching engine, puoi comunque essere riempito — e quei fill sono i peggiori fill che riceverai mai, perché la ragione per cui stavi ritirando la quota è che il mercato stava per travolgerti. Un simulatore senza latenza di cancellazione elimina esattamente i fill più tossici dalla tua storia.
La macchina a stati è anche ciò che rende onesta la contabilità del simulatore: le commissioni maturano per evento di fill, l'inventario si aggiorna per evento di fill, e le statistiche di tempo-in-stato (quanto a lungo gli ordini restano prima di riempirsi rispetto a essere cancellati) diventano direttamente comparabili con i log live — che è ciò su cui si nutre il loop di calibrazione alla fine.
Probabilità di fill per ordini limite: tre modelli e un bracket
Dato un ordine passivo al prezzo (diciamo un bid), quando il simulatore dichiara un fill? Tre regole decisionali, in ordine crescente di onestà:
1. Touch-fill (ingenuo). Fill se il prezzo tocca il tuo livello: . Questa è una regola di first-passage-time, e il suo fallimento è stato misurato un quarto di secolo fa: Lo, MacKinlay e Zhang (2002), "Econometric Models of Limit-Order Executions" (Journal of Financial Economics 65), adattano modelli di sopravvivenza a dati reali di ordini limite e concludono che le esecuzioni ipotetiche costruite da first-passage time "sono proxy molto scadenti delle esecuzioni reali di ordini limite." La modalità di fallimento è strutturale: quando il prezzo tocca il tuo livello e rimbalza, il tocco ha consumato la testa della coda — i trader che avevano quotato prima di te. Il touch-fill ti assegna i loro fill. Peggio, assegna esattamente i fill buoni (touch-and-bounce è lo scenario profittevole per un maker), mentre il tuo insieme reale di fill è sbilanciato verso il touch-and-run-through — quelli avversamente selezionati.
2. Trade-through (limite conservativo). Fill solo se il prezzo scambia strettamente attraverso il tuo livello: , o su dati tick, il volume scambiato cumulativo a supera l'intero livello. Se il prezzo è passato attraverso, l'intera coda a è stata consumata, quindi sei stato riempito indipendentemente dalla posizione. Questo non assegna mai un fill che non avresti ottenuto. Il suo bias è l'immagine speculare di quello del touch-fill: nega ogni fill in cui la coda si è esaurita fino alla tua posizione senza penetrazione completa, e i fill che concede sono sproporzionatamente quelli di run-through (avversamente selezionati). Un backtest maker sotto trade-through è uno stress test, non una stima.
3. Stima di esaurimento della coda. Traccia il volume scambiato cumulativo al tuo prezzo dal trade tape e il volume cancellato inferito dai delta L2. La tua coda stimata che ti precede:
dove è il volume del livello all'ingresso dell'ordine (al momento dell'ack) e è la frazione di cancellazioni assunta provenire da davanti a te — il parametro la cui forma principiata (la famiglia ) è derivata nell'articolo sulla posizione in coda. Il fill inizia quando ; la tua quantità riempita è il volume scambiato in eccesso rispetto ad essa, il che genera fill parziali naturalmente:
Impostare (tutte le cancellazioni davanti a te) dà il limite ottimistico di questo modello; il limite pessimistico. Quando disponi solo di snapshot L2 sparsi e nessun trade tape al livello — comune con feed crypto throttled a 100ms — puoi ricorrere a un prior basato su modello: Cont, Stoikov e Talreja (2010), "A Stochastic Model for Order Book Dynamics" (Operations Research 58), modellano ogni livello di prezzo come una coda birth-death e calcolano, tramite trasformate di Laplace, la probabilità che un ordine al bid esegua prima che il mid si muova, condizionatamente alle dimensioni attuali della coda. È un oracolo analitico di probabilità di fill: rozzo rispetto al replay del tape, molto migliore del touch-fill, e abbastanza economico da valutare dentro un loop di backtest ad alta frequenza.
La disciplina del bracket
Le tre regole non sono competitor — sono un ordinamento:
che induce un bracket di PnL. Esegui ogni backtest maker tre volte e riporta l'intervallo:
(la disuguaglianza superiore è approssimata — il touch-fill può riclassificare erroneamente le strategie, non solo gonfiarle, perché ti assegna fill controfattuali buoni). La regola decisionale che ne segue: una strategia maker è deployabile solo se sopravvive al limite conservativo del bracket, e il bracket è abbastanza stretto da rendere significativa la stima puntuale. Una strategia che mostra +9k trade-through ha un bracket largo 12k / +0,4k$ ti sta dicendo qualcosa di reale.
L'esperimento: portare una strategia maker giù per la scala

Prendi un maker deliberatamente semplice — quote simmetriche a best bid/ask, clip fissi di 0,05 BTC, inventario limitato a ±0,5 BTC con flattening taker al limite. Un mese di dati perpetual BTCUSDT: barre a 1m per i gradini bassi, diff L2 a 100ms più il trade tape per i gradini alti; commissione maker 1,0 bps, taker 4,0 bps. Stesso codice del segnale a ogni gradino (core condiviso, quindi l'unica variabile è il modello di fill). I numeri sotto sono una nostra run rappresentativa — le tue magnitudini differiranno per venue, mese e dimensione; la forma no:
| Gradino | Modello di fill | Tasso di fill delle quote | Fill | PnL mensile | Verdetto |
|---|---|---|---|---|---|
| 0 | close-fill | 98% | 41.200 | +14.800$ | fantasy |
| 1 | next-bar-open / touch su barre 1m | 89% | 37.400 | +9.600$ | fantasy con latenza |
| 2 | touch + spread & slippage fisso sui flatten taker | 89% | 37.400 | +7.100$ | costi modellati, fill ancora fittizi |
| 3 | + depth-walk L2 sui flatten taker | 89% | 37.400 | +6.400$ | uscite oneste, entrate ancora fittizie |
| 4a | trade-through (conservativo) | 21% | 8.900 | -3.900$ | limite di stress |
| 4b | esaurimento coda, calibrato | 37% | 15.600 | -700$ | migliore stima |
| 4c | esaurimento coda, (ottimistico) | 44% | 18.700 | +1.900$ | limite superiore |
| — | run shadow live, stesso mese | 35% | 14.100 | -1.150$ | realtà |
Leggi la tabella dall'alto verso il basso e osserva dove muore la strategia. Non è il gradino 2 — commissioni e slippage tolgono il 26% e la strategia sembra ancora robustamente profittevole. Muore tra il gradino 3 e il gradino 4, e muore per una ragione che nessun modello di costo può catturare: la selezione dei fill. Il touch-fill ha concesso 37.400 fill di cui la maggioranza era touch-and-bounce — pura cattura dello spread. Il modello queue-aware ha eliminato il 58% di quei fill, e i fill che ha eliminato erano sproporzionatamente quelli profittevoli: quando il livello viene toccato leggermente, la coda davanti a un maker che ri-quota con latenza retail assorbe tutto. I fill che sopravvivono fino al gradino 4 sono sbilanciati verso gli sweep che ripuliscono il livello — i fill in cui il prezzo sta già passando attraverso di te. Il tasso di fill è sceso di 2,4x; il PnL ha invertito segno. Quell'asimmetria — perdere i fill buoni, tenere quelli cattivi — è la selezione avversa resa meccanicamente esplicita, ed è invisibile a ogni gradino sotto il 4.
Nota anche cosa ha fatto il bracket: [-3.900] a cavallo dello zero con la stima calibrata a -700. Il simulatore non ha azzeccato il PnL live al dollaro — ha ottenuto il segno, la magnitudine, e il tasso di fill entro 2 punti. Ecco a cosa serve un modello di fill. Il backtest al gradino 1 ha mancato il PnL live di 10.750$ su una strategia il cui intero edge lordo mensile era di poche migliaia di dollari: l'errore del modello di fill era circa 3 volte la dimensione dell'alpha. Da qui la tesi: il tuo modello di fill è un'assunzione più grande della tua alpha.
Un avvertimento per i gradini bassi: se devi vivere su dati a barre (gradini 0-2), come minimo risolvi l'ambiguità intrabarra con l'adaptive drill-down — scendendo da 1m a 1s/100ms/trade dove SL, TP o i livelli di quota cadono dentro il range della barra. Il drill-down risolve gli errori di sequenza (quale livello è stato colpito per primo) ma non può risolvere gli errori di coda; è il compagno sulla risoluzione dei dati di questo articolo, non un sostituto del gradino 4.
Il loop di calibrazione: chiudere il cerchio contro i fill live
Un simulatore al gradino 4 ha parametri liberi — , latenza , latenza di cancellazione, l'assunzione di refill del livello. Senza calibrazione, è solo una supposizione con una forma diversa. Il loop che lo trasforma in uno strumento:
1. Registra tutto in live. Ogni evento d'ordine con timestamp dell'exchange: submit, ack, ogni fill parziale, ack degli amend, ack delle cancellazioni. Più lo stato L2 al momento del submit. Questa è la stessa disciplina di logging che backtest-live parity richiede per il suo DivergenceMonitor — la calibrazione del modello di fill è lo strato più profondo di quel monitor.
2. Rigioca gli stessi ordini attraverso il simulatore. Alimenta i dati di mercato registrati e le istruzioni d'ordine registrate (non i fill) nel simulatore. Ora hai esiti appaiati: per ogni ordine live, un destino simulato.
3. Confronta le distribuzioni, non le medie, in bucket. Un singolo match del tasso di fill globale può nascondere errori compensativi (troppo ottimistico in regimi calmi, troppo pessimistico nei burst — che si compensano risultando "calibrati"). Suddividi in bucket per i driver:
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
Due statistiche per bucket: il gap del tasso di fill con segno (simulatore meno live) e un test KS sulle distribuzioni di time-to-fill tra gli ordini riempiti. Il confronto del time-to-fill è quello acuto — un simulatore può eguagliare i tassi di fill riempiendo però a momenti sistematicamente sbagliati, il che corrompe ogni statistica downstream di inventario e selezione avversa. Questa è precisamente la lezione dell'inquadramento tramite analisi di sopravvivenza di Lo-MacKinlay-Zhang: l'esecuzione è un problema di time-to-event, quindi validalo come tale.
4. Adatta i parametri, in ordine di identificabilità. Prima la latenza (misurata direttamente dai timestamp di ack — non fittata). Poi minimizzando il gap del tasso di fill attraverso i bucket di profondità della coda. Poi controlla i bucket di regime di volatilità: un gap ottimistico persistente concentrato nei bucket di burst di solito significa che il tuo simulatore sotto-modella i fill tossici da latenza di cancellazione o il refill del livello, non .
5. Riesegui il bracket. Dopo la calibrazione, la stima di esaurimento della coda dovrebbe posizionarsi dentro il bracket vicino al live, e — il vero test di accettazione — il ranking delle varianti di strategia sotto il simulatore dovrebbe corrispondere al loro ranking in modalità shadow. Poi congela i parametri e ricalibra a scadenza fissa; le dinamiche dei fill derivano con i fee tier del venue, i cambi di tick-size e la popolazione HFT, e un calibrato a marzo è un'ipotesi a luglio.
Aspettative di convergenza dalle nostre run: un simulatore al gradino 4 non calibrato tipicamente si posiziona entro ±10-15 punti dal tasso di fill live; dopo un passaggio di calibrazione, ±3-5 punti, con i p-value del test KS sul time-to-fill che non rifiutano più uniformemente. Non farai meglio di così con il replay storico — il residuo è la reazione del mercato a te, che è il problema del gradino 5.
Cosa portare a casa
- Nomina il tuo gradino. Ogni backtest si trova su questa scala, che tu abbia scelto il gradino o che lo abbia scelto il tuo framework al posto tuo. Se non riesci a nominare il gradino del tuo modello di fill e i suoi bias noti, il tuo numero di PnL ha barre d'errore che non hai visto.
- I taker possono fermarsi al gradino 3. Depth-walk più latenza misurata prezza onestamente l'esecuzione aggressiva a dimensione retail. Spendi lo sforzo risparmiato sulla qualità dei dati.
- I maker iniziano al gradino 4. Sotto di esso, la logica di fill degli ordini limite non è approssimata — seleziona un insieme controfattuale di fill con inclinazione di selezione avversa opposta. I backtest touch-fill di strategie maker sono il generatore singolo più affidabile di strategie che muoiono al contatto con la produzione.
- Modella il ciclo di vita, non il fill. Fill parziali, reset della coda da amend, latenza di cancellazione — la macchina a stati è dove vivono i fill tossici, e i fill tossici sono dove muore il PnL del maker.
- Riporta il bracket. I limiti conservativo e ottimistico costano due run di backtest extra e convertono "il mio backtest dice +7k, +1,9k$]" — che è una decisione diversa, e migliore.
- Calibra contro i fill live in bucket. Un modello di fill validato solo in aggregato è un modello di fill con errori compensativi nascosti. Gap del tasso di fill suddivisi in bucket più test KS sul time-to-fill, ricalibrati trimestralmente.
I gradini della scala non sono gradazioni accademiche — ognuno è una bugia specifica che il tuo backtest smette di raccontarti. Sali finché le bugie non sono più piccole del tuo edge.
Link utili
- Moallemi, C., Yuan, K. — A Model for Queue Position Valuation in a Limit Order Book (2016)
- Cont, R., Stoikov, S., Talreja, R. — A Stochastic Model for Order Book Dynamics, Operations Research 58(3), 549-563 (2010)
- Lo, A., MacKinlay, C., Zhang, J. — Econometric Models of Limit-Order Executions, Journal of Financial Economics 65(1), 31-71 (2002)
- Huang, W., Lehalle, C.-A., Rosenbaum, M. — Simulating and Analyzing Order Book Data: The Queue-Reactive Model, JASA 110(509), 107-122 (2015)
- Almgren, R., Chriss, N. — Optimal Execution of Portfolio Transactions (2001)
- Byrd, D., Hybinette, M., Balch, T. — ABIDES: Towards High-Fidelity Multi-Agent Market Simulation (2020)
- Binance API — Cancel-Replace order semantics
- CME Globex — Order modification and time priority rules
Citazione
@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.}
}
Autori
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.