Vai al contenuto

Tre bug, tre test superati, nessuno individuato

Grafico a 4 ore del perpetual ETHUSDT con due finestre di lookback disegnate affiancate, una di 23 barre e una di 22, ossia la lunghezza che calcola il sorgente Pine e la lunghezza che ha calcolato la ricostruzione Python dalla stessa formula.
Lo stesso lookback, calcolato due volte. Pine arrotonda 22,5 allontanandosi dallo zero e ottiene 23. Python 3 lo arrotonda al numero pari e ottiene 22. In nessuno dei due linguaggi qualcosa vi avverte che la convenzione è cambiata.

Se vi è mai capitato di portare un indicatore in Python per fargli un backtest, o di fidarvi di uno script perché arrivava con una verifica, qui si parla del vuoto che quelle due cose lasciano aperto.

Abbiamo ricostruito tre dei nostri indicatori perché un backtest potesse girarci sopra. Tutte e tre le ricostruzioni contenevano una costante sbagliata. Tutte e tre hanno superato la loro validazione. Nemmeno una è stata intercettata da un test.

Perché ricostruire un indicatore, poi

Uno script Pine gira sul vostro grafico. Non percorre sei anni di storico in un ciclo, e non risponde alla domanda se un segnale paghi davvero. Per porre quella domanda lo si ricostruisce in qualcosa che si possa eseguire migliaia di volte.

È in quella ricostruzione che un indicatore smette silenziosamente di essere lo stesso indicatore.

Il bug che è costato di più, ed è una barra

Una delle lunghezze dentro Volume Oracle è derivata dal timeframe:

target bars   = 36 hours × 60 / 240 minutes = 9
regime length = round(9 × 2.5) = round(22.5)

Il math.round di Pine risolve i pareggi allontanandosi dallo zero. Dà 23. Il round() di Python 3 usa l'arrotondamento bancario, che risolve i pareggi verso il numero pari. Dà 22.

Entrambe sono implementazioni corrette dell'arrotondamento. Sono semplicemente convenzioni diverse, e nulla vi avverte che siete passati dall'una all'altra.

Ogni lookback derivato da quel numero si è spostato con lui. Il risultato sulla riga meglio supportata di una scansione di 36 segnali:

versionevantaggio a 48 htasso di successon
ricostruito, lunghezza 22+0,423%511216
corretto, lunghezza 23+0,260%501223

Un taglio del 38% dovuto a una convenzione di arrotondamento.

Poi abbiamo messo a verbale la lezione sbagliata

La nota che avevamo scritto diceva che accadeva solo su 4H. Era stata scritta dall'unico timeframe che in quel momento era sotto test, ed è rimasta così per due mesi.

Facendo girare la stessa aritmetica su ogni timeframe:

timeframelunghezza di regimelunghezza di flussorisultato
5m45 contro 4523 contro 22diverge
2H45 contro 4523 contro 22diverge
4H23 contro 2212 contro 11diverge
8H53 contro 5227 contro 26diverge
1D18 contro 189 contro 9pareggia, concorda
3D, 1W15 contro 158 contro 8pareggia, concorda

Quattro su sedici, non uno.

Da quella tabella cadono fuori due cose che nessun ragionamento aveva prodotto.

Un pareggio non basta. 1D, 3D e 1W cadono tutti esattamente su ,5 e concordano. L'arrotondamento bancario sposta soltanto un pareggio la cui risposta arrotondata per eccesso è dispari, quindi round(17.5) fa 18 in entrambi i casi. Circa metà di tutti i pareggi sono innocui, ed è proprio per questo che controllarne uno a campione non dimostra nulla.

La divergenza può arrivare un passo più tardi. Su 5m e 2H la prima lunghezza concorda a 45. La riga successiva, round(45 × 0.5), è di nuovo 22,5, e quella si divide. Se controllate la prima costante e tirate dritto, date il via libera a entrambi i timeframe a torto.

Gli altri due, in breve

Harmonic normalizza un oscillatore ritagliandolo su una banda mobile. Nel sorgente ogni barra viene ritagliata dalla propria banda. La ricostruzione ritagliava un'intera finestra di 200 barre usando la banda della barra corrente, che è una funzione diversa. Differenza misurata su 11.497 barre: 0,47 punti in media. Un vero errore di trascrizione senza effetto materiale.

Quello lì, per giunta, lo abbiamo gonfiato prima di misurarlo. È stato annunciato, alla sola lettura del codice, come quasi certamente la deriva del 9%. Il numero era 0,47. Un bug vero e un bug rilevante sono due affermazioni diverse, e l'ordine di grandezza si misura prima di dare un nome all'impatto.

Plutus Flow media il volume su 50 barre per individuare i picchi. La ricostruzione ne usava 20, perché era il numero che credevamo di sapere. Effetto: sotto un decimo di punto percentuale, nessun cambio di segno. Il verdetto non si è mosso, ma il numero era sbagliato e ora è giusto, che è l'unico metro che conta.

La parte che è davvero la lezione

Ecco che cosa riportava allora la validazione di ciascuna ricostruzione.

ricostruzioneil suo stesso controlloche cosa significava
Volume Oracle3 su 3 prima, 3 su 3 doponon è riuscito a distinguerle
Harmonic6 su 8 prima, 5 su 8 dopoha dato un punteggio più alto alla versione con il bug
Plutus Flowmai eseguito

È sulla riga di mezzo che vale la pena fermarsi. La validazione ha preferito il codice sbagliato. Se avessimo lasciato decidere il controllo, ci avrebbe riportati dentro il bug a forza di argomenti.

Una validazione che confronta un solo valore in uscita non può rilevare una costante in ingresso sbagliata. L'output è un setaccio troppo grossolano. Molti insiemi di parametri sbagliati e diversi producono oggi la stessa etichetta di regime, lo stesso stato di incrocio, lo stesso colore sul grafico. Differiscono su barre che in questo momento non state guardando, ed è esattamente lì che vive un backtest.

Che cosa fare invece

Stampate ogni parametro derivato. Ogni lunghezza, ogni soglia, ogni moltiplicatore. Confrontateli con il sorgente riga per riga, prima di dare un voto a qualsiasi cosa.

Tenete il controllo sull'output. Intercetta un'altra classe di errori ed è necessario. È soltanto ben lontano dall'essere sufficiente, e tutti e tre gli sono passati davanti indisturbati.

L'ambito onesto

Il codice Pine era corretto in tutti e tre i casi. Ciò che era sbagliato era l'apparato costruito per testarlo, che è la versione più scomoda del problema: lo strumento è andato alla deriva, e l'autocontrollo dello strumento stesso diceva di no.

La cifra del 38% è una riga, una scansione, un timeframe, dentro il campione, ed è citata dal nostro registro anziché ricalcolata. Il risultato sull'arrotondamento è riproducibile con un solo comando, e correggendolo è emerso che la nostra stessa nota in proposito era sbagliata da due mesi.


Misurazione passata dei nostri stessi strumenti, descritta a posteriori. Nulla di quanto sopra è una raccomandazione o un'indicazione di risultati futuri.