La settimana scorsa mi hai mandato il notebook: stesso segnale, stesso universo, gli stessi 14 mesi di dati BTCUSDT perp. L'unico cambiamento riguardava l'esecuzione. Hai smesso di attraversare lo spread e hai iniziato a lasciare bid un tick all'interno, e lo Sharpe è passato da 0.42 a 2.14. Mi hai chiesto se fosse un risultato reale o se avessi rotto qualcosa.
Hai rotto qualcosa. Voglio mostrarti esattamente dove, perché il bug è in una riga e la lezione che ne ricaviamo è molto più ampia.
Ecco la tua regola di esecuzione, copiata dal motore:
if bar.low <= limit_price: fill(limit_price)
Dice: se durante questo minuto il mercato ha registrato uno scambio a un prezzo pari o inferiore al mio, il mio ordine è stato eseguito al mio prezzo. In realtà, quello che codifica è che il prezzo ha toccato il tuo livello. Toccare non significa eseguire. Fra le due cose c'è una coda, che nel tuo modello hai trattato come vuota.
Cosa hai davanti
Quando inserisci un bid a 84,120.0 sul BTCUSDT perp, ti metti in fondo alla coda degli ordini già presenti a quel prezzo. Al primo livello del book di quel contratto, di solito ci sono dai 4 ai 30 BTC, a seconda dell'ora; la mediana nella finestra del tuo campione è circa 12 BTC. Il tuo ordine è di 0.4 BTC. Perché venga eseguito, i venditori devono colpire quel livello con una quantità aggregata sufficiente a superare tutti quelli arrivati prima di te, e devono farlo prima che il livello venga cancellato da sotto il tuo ordine o che il mercato risalga.
Quindi la domanda che il tuo backtest dovrebbe porsi non è «il prezzo ha raggiunto 84,120.0?», ma «sono stati eseguiti almeno 12.4 BTC di vendite a mercato a 84,120.0 mentre il mio ordine era lì?». Sono eventi molto diversi. Nei tuoi dati, differiscono di circa un fattore tre.
Tre regole di esecuzione, tre strategie diverse
Ho rilanciato il tuo segnale con gli stessi ingressi e tre modelli di esecuzione. Stesso alpha, stesse commissioni, stesso funding. È cambiata solo la logica di esecuzione.
| Regola di esecuzione | Esecuzioni | Edge medio a 60s dopo l'esecuzione | Sharpe |
|---|---|---|---|
Tocco: low <= limit | 4,180 | +2.6 bp | 2.14 |
Superamento rigoroso: low < limit - 1 tick | 1,712 | +0.4 bp | 0.61 |
| Simulazione della coda basata sul volume al livello | 1,306 | +0.9 bp | 0.77 |
La riga centrale è la correzione approssimativa a cui tutti ricorrono per prima: considerare un'esecuzione solo se il mercato ha scambiato a un prezzo che è andato decisamente oltre il tuo, con l'idea che, se il prezzo ti ha superato, deve averti consumato. La direzione è quella giusta e gran parte dell'illusione sparisce. Ma c'è anche un bias fastidioso, di cui parlerò tra poco.
La riga in fondo è il modello che dovresti costruire. Non serve un feed L3 né ricostruire ogni singolo ordine. Hai già quasi tutto quello che serve.
Una simulazione della coda costruibile da aggTrades
Usa il flusso degli scambi aggregati invece delle klines. Ogni transazione contiene prezzo, quantità, timestamp e il flag del lato maker, che indica se l'aggressore stava comprando o vendendo. Basta per una simulazione funzionale del tuo ordine passivo:
- Quando viene inserito il tuo ordine, registra la quantità già presente al tuo prezzo. Se hai solo un book feed da 100ms, usa l'ultimo snapshot: l'errore è piccolo rispetto a quello che stai correggendo.
- Imposta
queue_ahead = resting_size. Sii pessimista e presumi di essere ultimo in coda. È così, a meno che non sia tu a creare il livello. - Scorri il flusso degli scambi. Ogni vendita aggressiva al tuo prezzo o a un prezzo inferiore riduce
queue_aheaddella relativa quantità. Quando il valore scende sotto zero, il tuo ordine viene eseguito al tuo prezzo e al timestamp di quello scambio. - Se il prezzo si sposta di un tick, non azzerare la coda. Riducila gradualmente. Alcuni ordini davanti a te vengono cancellati quando il livello diventa inattivo, altri restano. Una riduzione del 30-40% per ogni secondo intero fuori dal livello ha corrisposto alle mie ricostruzioni meglio di entrambe le ipotesi estreme.
- Se la tua strategia cancellerebbe l'ordine per reinserirlo, modella il nuovo inserimento come un ordine nuovo in fondo alla coda del nuovo livello. Questo passaggio viene saltato spesso, ed è qui che si nasconde l'illusione residua.
È sul passaggio 2 che vorrai obiettare. Sì, a volte ti trovi vicino alla testa della coda perché hai inserito l'ordine appena si è formato il livello. Va bene: misuralo, non darlo per scontato. Registra la quantità presente al momento dell'inserimento e lascia che siano i dati a dirti quale frazione dei tuoi ordini arriva davvero presto. Nella tua strategia era l'11%, perché il segnale scatta dopo un movimento: il livello a cui ti accodi esiste già e ha già una coda.
Le esecuzioni che ottieni sono quelle che avresti preferito evitare
Ora veniamo alla parte che conta davvero e al motivo per cui la regola del superamento rigoroso è distorta.
Pensa a quando il tuo bid viene consumato per intero. Succede quando la pressione di vendita è sufficiente a svuotare tutto il livello. Per definizione, è il momento in cui il mercato scende oltre il tuo prezzo. Le esecuzioni di cui sei più certo sono quelle in cui ti trovi subito dalla parte sbagliata del mercato.
Suddividi le esecuzioni in base a come sono avvenute e misura il mark-out a 60 secondi:
| Tipo di esecuzione | Quota delle esecuzioni | Mark-out a 60s |
|---|---|---|
| Livello scambiato, prezzo rimbalzato verso l'alto | 38% | +3.1 bp |
| Livello scambiato, prezzo invariato | 21% | +0.2 bp |
| Prezzo sceso di 2+ tick oltre il tuo | 41% | −2.4 bp |
Il tuo backtest ingenuo includeva tutti e tre i gruppi, trattandoli come se fossero esecuzioni gratuite. La regola del superamento rigoroso include quasi solo il terzo gruppo, ed è per questo che il suo edge è calato più di quello della simulazione della coda. Nessuno dei due modelli è corretto. La simulazione della coda ti dà un mix realistico, e il mix è tutto: l'esecuzione passiva ti fa guadagnare lo spread e ti presenta il conto sotto forma di selezione avversa; il rapporto fra i due è la tua vera strategia.
La vecchia metafora del desk azionario è ancora valida: un ordine maker è un'opzione gratuita che hai venduto al mercato. Qualcuno la esercita quando gli conviene. Il tuo backtest incassava il premio e ignorava che l'opzione comportava un pagamento.
Le operazioni che non hai ottenuto cambiano la strategia, non solo i costi
È questo il punto su cui vorrei che riflettessi di più. Se modelli male l'esecuzione taker, ottieni le operazioni giuste al prezzo sbagliato, e una correzione delle commissioni sistema gran parte del problema. Se modelli male l'esecuzione maker, ottieni un insieme di operazioni del tutto sbagliato. Circa 2,900 dei tuoi 4,180 ingressi non sono mai avvenuti. Alcuni erano i tuoi segnali migliori, su candele schizzate in alto per poi invertirsi: proprio il tipo di movimento in cui il mercato è scappato senza di te.
Quindi il ramo dei mancati riempimenti deve avere una logica reale. Cosa fa la strategia se l'ingresso non viene eseguito entro il momento in cui il segnale scade? Insegue il prezzo con un ordine taker, pagando spread e impatto? Reinserisce l'ordine più in basso, accettando una base di ingresso diversa? Salta l'operazione e resta flat? Ogni scelta produce una curva del capitale sostanzialmente diversa, e nessuna equivale a «presumi che l'ordine sia stato eseguito». Nei nostri test, aggiungere una regola di inseguimento realistica — passaggio a taker dopo 20 secondi senza esecuzione, con slippage limitato a 3 bp — ha recuperato circa un terzo delle operazioni perse e all'incirca metà del divario fra lo Sharpe ingenuo e quello della simulazione della coda. È un risultato davvero interessante, che emerge solo quando il modello di esecuzione è abbastanza realistico da rendere sensata la domanda.
Controllo rapido di coerenza, dieci minuti: confronta il registro del tuo paper trading live con il backtest sulla stessa finestra temporale. Confronta il tasso di esecuzione, non il PnL. Se il backtest esegue il 100% degli ordini passivi e il paper trading il 34%, non stai confrontando strategie: hai un bug nel modello di esecuzione. Correggilo prima di guardare un solo dato sui rendimenti.
Altre due cose, già che ci siamo
Rifiuti degli ordini post-only. Se usi post-only per assicurarti la fascia commissionale maker e il book si sposta tra la tua decisione e l'ack dell'exchange, l'ordine viene rifiutato invece di restare nel book. Nei nostri log di paper trading, durante le ore normali succede al 3-6% dei tentativi su BTCUSDT, e a oltre il 15% nel minuto successivo alla pubblicazione di un dato CPI statunitense. Un ordine rifiutato non è un ordine eseguito, né un ordine passivo rimasto in attesa: è un'operazione mai esistita. Se il tuo backtest non contempla questo stato, il numero di operazioni è gonfiato proprio nei regimi che più ti interessano.
Prevenzione dell'autoesecuzione e impatto dei tuoi ordini. Con 0.4 BTC non muovi BTCUSDT, quindi puoi ignorare questo aspetto. Ma dicevi di voler provare la strategia su un perp altcoin a media capitalizzazione, dove il valore nozionale al primo livello del book è spesso inferiore a $15k. In quel caso, il tuo ordine è una quota significativa della coda, e la quantità che registri davanti a te include l'effetto della presenza del tuo ordine precedente. Quando la tua quantità supera circa il 10% del livello in attesa, la simulazione deve considerare che gli altri partecipanti reagiscono alla tua presenza. A quel punto, onestamente, mi fiderei più del paper trading che di qualunque simulazione che potremmo scrivere io o te.
Rilancialo con la simulazione della coda e mandami la tabella dei mark-out suddivisa per tipo di esecuzione. Se il gruppo dei rimbalzi conserva ancora gran parte dell'edge e quello dei superamenti non se lo mangia tutto, potresti avere qualcosa che vale la pena mettere alla prova in paper trading. Se tutto dipendeva dalle 2,900 esecuzioni che non avresti mai ottenuto, meglio scoprirlo ora che dopo quattro settimane passate a guardare un conto paper fare qualcosa di diverso da quanto promesso dal notebook.
← Tutti gli articoli

