Vai al contenuto

Oltre il Backtest

Lavorando nell’ambito del trading sistematico da oltre vent’anni, ci siamo misurati con diversi approcci di validazione con un unico e comune obiettivo: aumentare la probabilità che un modello sviluppato sul passato potesse continuare a funzionare nel futuro.

Ma quali sono gli elementi dirimenti di questo progetto e in che modo un semplice Performance Report può aiutarci a questo scopo?

Una prima distinzione, di matrice statistica, ha a che fare con l’orizzonte operativo. Se il backtest viene effettuato su una strategia di HFT (che operi a brevissimo termine, ache sotto il secondo) ci affideremo a determinate metriche e utilizzeremo specifiche contromisure, per aumentare la robustezza dei risultati. Se viceversa desideriamo progettare un sistema di investimento sul multiday, dovremo dare maggiore risalto a metriche differenti e proporremo criteri di validazione più adatti ad un orizzonte temporale di medio lungo termine.

In ogni caso dovremo partire da una matrice di valori opportunamente organizzata in un Performance Report che sia, al contempo, il più sintetico ed esaustivo possibile. Dunque a quale affidarsi?

Esistono decine di suite commerciali (a partire da Tradestation, Multicharts, Amibroker, Metatrader, Trade Navigator ecc.) che propongono la propria versione di Performance Report. Tutte hanno dei pregi, ma a nostro avviso anche diversi difetti, legati all’impossibilità di personalizzare le maschere e le metriche.

La nostra soluzione è stata la costruzione di un codice Python “aperto”, che è cresciuto nel tempo e che risulta personalizzabile da parte di ciascun utente (il codice delle funzioni e del motore di backtest è fornito all’interno della Python Academy). Stiamo parlando dell’ultima versione del motore “Wintermute” accelerato in Numba (libreria JIT per Python).

Ciò si declina nel fatto che non sia più necessario avere una piattaforma specifica per fare backtest, ma si possa lavorare i dati direttamente con Python senza ulteriori “intermediari”. E non è una cosa da poco.

Scrivere una strategia in Python è molto più semplice che farlo in Easylanguage o Powerlanguage (dopo che aver appeso come semplificare il codice mediante funzioni e aver razionalizzato le dipendenze). Un pò di lavoro a monte per conquistare una libertà insperata negli anni precedenti.

Ma facciamo un esempio che ci permetta di vedere all’opera la nuova versione del motore Wintermute (versione 20231114).

Prendiamo il Gold Future Continuous a 60 minuti, uno strumento che, con qualche eccezione (ad esempio nel 2018) ha sempre risposto a logiche di level e volatility breakout.

Iniziamo impostando le costanti di lavoro (che in Python per convenzione sono scritte in maiuscolo).

In testa troviamo le impostazioni generali di servizio: impostiamo il nome del Trading System (“NAME”), disabilitiamo la scrittura del log di dettaglio (“WRITELOG”), utile nel caso volessimo fare una analisi di dettaglio e impostiamo il motore di backtest in modalità “single run” (“OPTIMIZATION).

A questo punto impostiamo il funzionamento del motore di backtest:

EXIT_ON_ENTRY_BAR = True

Permette al motore di concludere un trade sulla stessa barra di entrata. Escluderlo permette di compiere simulazioni specifiche su strategie che tengano conto di una certa inerzia temporale o, in altre parole, di alcune non idealità.

CONSECUTIVE_TRADES = True

Permette di iniziare un trade sulla stessa barra su cui si sia concluso il trade precedente.

MAX_INTRADAY_OPERATIONS = 1

Limita ad 1 il massimo numero di trade per sessione giornaliera. Una modalità personalizzabile per diminuire il numero di falsi segnali.

INSTABILITY_FACTOR = 0

Si tratta dell’inibizione di un fattore di instabilità che attiveremo invece a valle del primo backtest. Parametro cruciale nei criteri di validazione per aumentare la robustezza dei modelli e aumentare la probabilità che una strategia possa continuare a performare dopo il backtest su dati sconosciuti (tutta la teoria che sta dietro questa parte è presa in prestito dal mondo del machine learning.

Entriamo dunque nel vivo delle impostazioni legate alla parte operativa:

INSTRUMENT = 2

Decidiamo di operare su un future la macchina si porterà dietro tutte le impostazioni relative.

TICK = 0.1

Definiamo il valore del minimo movimento di prezzo (granularità dello strumento). Esiste anche un modo di leggere in automatico questo valore.

BIGPOINTVALUE = 100

Si tratta dei dollari (USD) che guadagneremo o perderemo per ogni movimento di un punto del sottostante.

DIRECTION = “long”

Impostiamo un’operatività soltanto al rialzo (ingressi con ordini di acquisto). Spesso dividere l’analisi sul fronte long da quella short può essere utile per caratterizzare e correggere alcuni comportamenti peculiari sui due fronti.

ORDER_TYPE = “stop”

Decidiamo di utilizzare ordini “stop” che si attivino cioè, al superamento di un certo livello di prezzo. Un ordine stop viene tramutato in un ordine market al verificarsi di una condizione.

QUANTITY = 1

Utilizziamo un unico contratto sul Gold Future.

MARGIN_PERCENT = 10

Impostiamo una marginazione 10 (tipica di molti broker intermediari). Questa impostazione serve a tenere conto di quanto capitale venga immobilizzato durante il trade per ciascun contratto.

INITIAL CAPITAL = 20000

Ipotizziamo di partire con un capitale di 20000 $. Questa soglia ci servirà per calcolare alcune metriche come il drawdown.

RISK_FREE_RATE = 0

Impostiamo un interesse annuale “risk free” nullo. Questa voce influenzerà il calcolo di molti indicatori metrici come ad esempio il Sortino Ratio.

COSTS_FIXED = 20
COSTS_VARIABLE = 0
COSTS_PERSHARES = 0

Dal punto di vista dei costi impostiamo un regime fisso di 20 $ ad operazione per contratto (una prima ipotesi). Ciò si traduce in 40$ “round turn” per aprire e chiudere un trade.

Non utilizziamo nessuna delle altre modalità di uscita codificate.

Passiamo al cuore del sistema.

In questo caso definiamo un “enter_level” che permetta l’acquisto al superamento dell’ultimo massimo a 23 ore. Come filtro utilizziamo un “blastoff” di sessione alla Larry Williams sul time frame superiore (come parametro abbiamo scelto 0.3, quindi piuttosto stringente sul Gold).
Usciamo dal trade allo scoccare della mezzanotte (sessione americana).
Nessun’altra impostazione utilizzata.

Non ci rimane che invocare la funzione del motore Wintermute “apply_trading_system” e popolare i seguenti elementi: tradelist, open_equity, closed_equity, operation_equity

Una volta prodotti questi elementi, che caratterizzano tutto ciò di cui abbiamo bisogno, possiamo far partire le stampe del performance report invocando la funzione “performance_report”.

Non ci rimane che visionare il report nella versione che presenteremo nella prossima live della Python Academy di giovedì 23 novembre 2023.

In testa una notifica ci dice che l’ultimo trade è ancora aperto ed è stato chiuso fittiziamente sull’ultima chiusura di barra (per tenerne conto).

Il primo blocco pone l’attenzione sul profitto atteso, esplicitando CAGR e Annual Return. La differenza principale tra il CAGR (Compound Annual Growth Rate, Tasso di Crescita Annuo Composto) e il rendimento annuo (Annual Return) è nella gestione del reinvestimento degli utili nel caso del CAGR (ognuna delle funzioni relative è codificata e spiegata nel dettaglio all’interno del percorso, ma potete reperire informazioni da diverse fonti ufficiali).

Il secondo fornisce una visione sintetica delle prestazioni secondo le metriche più utilizzate sia in ambito investing che trading:

Sharpe Ratio: è una misura di performance che calcola il rendimento in eccesso di un investimento (rispetto al rendimento senza rischio) per unità di rischio assunto. Questa metrica aiuta gli investitori a valutare se il rendimento ottenuto da un investimento sia adeguato rispetto al livello di rischio sostenuto.

Sortino Ratio: è un indicatore di performance che misura il rendimento di un investimento rispetto al rischio al di sotto di un determinato livello minimo accettabile. A differenza del più comune Sharpe Ratio, che considera la deviazione standard di tutti i rendimenti (positivi e negativi), lo Sortino Ratio si concentra solo sulla deviazione standard dei rendimenti negativi. In breve, possiamo dire che valuta la capacità di un investimento di generare rendimenti positivi in rapporto al rischio negativo, fornendo una misura più focalizzata sulla gestione del rischio al ribasso.

Calmar Ratio: è un indicatore che valuta le performance di un investimento in rapporto al rischio assunto. In questa versione base è calcolato come il rendimento annualizzato diviso per il max drawdown. In sostanza, il Calmar Ratio fornisce una misura del rendimento in relazione alla volatilità o al rischio, aiutando gli investitori a valutare l’efficienza di una strategia o di un fondo, dove valori più alti indicano una migliore performance corretta per il rischio assunto.

Calmar Ratio Mean: versione media del Calmar Ratio calcolata anno su anno. Ha lo scopo di rendere indipendente dal tempo il valore del Calmar Ratio classico (che aumenta, per strategie profittevoli, man mano che ci si allontani dall’inizio della messa in opera.

Omega Ratio: utilizzato per valutare il rendimento di un investimento in relazione al rischio assunto. Si tratta di una misura di performance che tiene conto non solo del rendimento ma anche della distribuzione degli rendimenti, specialmente focalizzandosi sulle perdite e su una soglia minima di sussistenza (che nel caso del Profit Factor è zero).

Kestner Ratio: misura di regolarità della curva dei rendimenti, che si basa sullo scarto quadratico medio associato alla retta di regressione lineare.

Treynor Ratio: è una misura di rendimento rispetto al rischio che valuta quanto guadagno un investimento ha generato in rapporto al rischio di mercato che ha assunto. A denominatore abbiamo in questo caso il Beta dell’investimento.

Information Ratio: è una misura del rendimento di un portafoglio in eccesso rispetto a un benchmark, regolato per il rischio assunto. Essenzialmente, l’Information Ratio valuta quanto valore aggiunto un gestore di portafoglio ha generato rispetto a un benchmark in relazione alla volatilità del portafoglio.

Beta: è una misura della sensibilità di un titolo rispetto alle variazioni del mercato nel suo complesso. Rappresenta la relazione statistica tra il rendimento di un titolo e il rendimento di un indice di mercato di riferimento. Il beta è comunemente utilizzato nella teoria del portafoglio e nell’analisi dei rischi finanziari.

Nel blocco successivo troviamo l’analisi di dettaglio. Ciò significa che i primi due blocchi consentono di avere una prima visione di insieme sulla bontà o meno della strategia, mentre i successivi permettono di dettagliare pregi e difetti.

Operations: il numero dei trade.

Profit: il profitto al netto dei costi fissi (slippage e commissioni).

Average Trade: la media del profit/loss della strategia. Una delle voci più importanti per comprendere se la strategia sia sufficientemente capiente per essere messa in opera.

Profit Factor: è una misura utilizzata per valutare la redditività di un sistema o strategia di trading. È un rapporto che confronta il totale dei profitti generati da un sistema rispetto alle perdite. Un PF inferiore ad 1 non sarà in grado di essere sostenibile. E’ calcolato come rapporto tra Gross Profit e Gross Loss.

Gross Profit: la somma di tutte le operazioni positive.
Gross Loss: la somma di tutte le operazioni negative.

Percent Winning Trades: la percentuale dei trade positivi calcolato su “Operations”. Un valore superiore al 50% ha generalmente delle implicazioni psicologiche positive, al contrario sotto il 50%.

Reward Risk Ratio: si tratta del rapporto tra la media dei guadagni per operazioni profittevoli e la media delle perdite per operazioni perdenti. Questa metrica ha generalmente un rapporto di inversa proporzionalità con il Reward Risk Ratio.

Sustainability: combinazione lineare del prodotto tra (Percent Winning Trades x Avg Winning Trades) e (Percent Losing Trades x Ava Losing Trades). Più alto è il valore positivo e più capiente è statisticamente la strategia ad assorbire outliers negativi.

Trading Fees: si tratta della cumulata dei costi fissi sostenuti.

Avg Gain: la media delle operazioni positive.
Max Gain: la massima occorrenza positiva tra le operazioni.

Avg Loss: la media delle operazioni negative.
Max Loss: la massima occorrenza negativa tra le operazioni.

Avg Win Trade Length: la media del numero di operazioni consecutive positive.
Max Win Trade Length: il massimo numero di operazioni consecutive positive.

Avg Losing Trade Length: la media del numero di operazioni consecutive negative.
Max Losing Trade Length: il massimo numero di operazioni consecutive negative.

Prima di passare al prossimo blocco desideriamo sottolineare come la strategia di test presentata (che va chiarito è una strategia di base) al netto dei costi (che potremmo maggiorare) risulta sostenibile (137 $ di Avg Trade ed 1.47 di Profit Factor) con una buona confidenza psicologica (52.74% di Percent Winning Trades ed un Reward Risk Ratio di 1.33).

La seconda parte del Performance Report è incentrata maggiormente sul rischio atteso della strategia:

Avg Delay Between Peaks: la media dei ritardi (in barre) prima di un nuovo massimo di equity line.
Max Delay Between Peaks: il massimo dei ritardi (in barre) prima di un nuovo massimo di equity line.

Avg Time in Trade: la media della durata in barre per trade.
Max Time in Trade: la massima lunghezza in barre di un trade.
Min Time in Trade: la minima lunghezza in barre di un trade.

Trades Standard Deviation: la deviazione standard calcolata sui trades. Misura della varianza e quindi del rischio atteso.
Equity Standard Deviation: la deviazione standard calcolata sull’open equity. Misura della varianza e quindi del rischio atteso.

Correlation: la correlazione, espressa in punti percentuali, tra la serie aggregata daily dei profitti e quella delle variazioni delle chiusure dello strumento di lavoro (nel nostro caso il Gold Future).

Siamo quindi giunti alla sezione dedicata al drawdown:

Avg Open Drawdown: ricorrezione monetaria media della curva di open equity da un nuovo massimo.
Avg Open Drawdown %: ricorrezione percentuale media della curva di open equity da un nuovo massimo.

Max Open Drawdown: massima ricorrezione monetaria della curva di open equity da un nuovo massimo.
Max Open Drawdown %: massima ricorrezione percentuale della curva di open equity da un nuovo massimo.

Nel nostro caso verifichiamo un max open draw down per contratto di -16300 $ (registrata in data 20 novembre 2008) ed un valor medio di -2313 $. Per essere conservativi consigliamo di visionare sempre questa voce e non quella, che trovate di seguito, ad operazioni chiuse.

Avg Closed Drawdown: ricorrezione monetaria media della curva di closed equity da un nuovo massimo.
Avg Closed Drawdown %: ricorrezione percentuale media della curva di closed equity da un nuovo massimo.

Max Closed Drawdown: massima ricorrezione monetaria della curva di closed equity da un nuovo massimo.
Max Closed Drawdown %: massima ricorrezione percentuale della curva di closed equity da un nuovo massimo.

Questi valori risultano invece estremamente utili se la nostra strategia non prevede stop loss o comunque criteri di arresto durante la sessione.

A seguire il profilo percentile del drawdown, utile per commisurare eventuali stopposo statici o adattivi.

Alla fine troviamo il sunto dei motivi di uscita che, in questo caso, sono tutti annoverabili all’exit rule (non leggiamo il 100% dei trade generati perchè l’ultimo trade risultava ancora aperto).

Passiamo dunque alle tavole grafiche.

Vediamo l’equity line al netto dei costi fissi con evidenziati (in colore “lime”) i nuovi massimi di equity line. Il profitto finale tocca i 140000 $ per contratto in 17 anni.

La tavola successiva mostra la stessa curva sovrapposta all’andamento della close del Gold Future (lo strumento di lavoro). Si notino le due differenti scale verticali. Uno strumento molto utile per apprezzare il livello di correlazione tra i due strumenti.

A seguire vediamo la dinamica del drawdown monetario, che ci mostra come dopo il 2008 non sia stato più registrato un livello paragonabile.

Il profilo percentuale racconta una storia differente, enfatizzando il quasi -50% di drawdown (i -16000 $ della tavola precedente) registrato all’inizio dell’operatività. Come tutte le scale percentuali sono inficiate dal valore nominale raggiunto nel tempo.

A seguire il profilo del controvalore per contratto (in verde) e la porzione di denaro immobilizzata (in “lime”), tenendo conto della leva utilizzata derivante dal margine).

Troviamo quindi l’aggregato annuale dei profitti e delle perdite. In questo caso specifico notiamo come, al netto dei costi fissi, si sia guadagnato quasi tutti gli anni ad eccezione del 2008 e del 2018.

Per gli appassionati dei sistemi Bias possiamo osservare il profit/loss medio per mese di calendario. Si evidenzia una certa sofferenza a febbraio, maggio e ottobre. Dato che le dinamiche tendono a spostarsi, in questo caso sconsigliamo di filtrare i trade su questa base.

La “heatmap” dei profitti e delle perdite rappresenta un mosaico ordinato per mese e per anno. Consente di evidenziare aree anomale con consistenza periodica.

La tavola del MAE (“Maximum Adverse Excursion”) rappresenta il profit/loss di ciascun trade in rapporto alla massima ricorrezione negativa subita durante la vita del trade. Se il trade termina con un profitto avremo un triangolino verde orientato verso l’alto, se termina con una perdita uno rosso orientato verso il basso. Nel nostro caso è evidente come uno stop loss di 4000 $ taglierebbe fuori 6 trade negativi.

Ribaltando la logica otteniamo il grafico del MFE (“Maximum Favorable Excursion”) che mostra il massimo run up durante la vita di ciascun trade. Anche in questo caso un take profit di 6000 $ (minor tempo a mercato si traduce in un minor rischio) taglierebbe i profitti di un’unico trade.

Quella della “Trade Duration” è un’informazione da correlare al MAE per comprendere se esista la possibilità di tagliare temporalmente i trade in modo ancora più efficiente. Nel nostro caso questa possibilità sembra non esserci, essendo i trade equi distribuiti da 1 a 23 barre di vita.

Di seguito la visualizzazione di tre trade consecutivi.

Ora, abbiamo appena introdotto la possibilità di introdurre una Montecarlo Analysis che in realtà non è propriamente una Montecarlo. Si basa sull’Instability Factor e permette di comprendere se la strategia appena coniata sia tendenzialmente “ottimista” o meno (i dettagli li spieghiamo all’interno della Python Academy).

Iniziamo definendo i seguenti due parametri:

IF_SAMPLE: definisce il numero di resample (non Montecarlo) da utilizzare per la simulazione.
INSTABILITY_FACTOR: ha a che fare con un fattore di instabilità che ha lo scopo di rendere più difficile l’overfitting.

Lo sciame di curve qui sopra rappresenta le 1000 repliche (che ripeto non sono una ricombinazione dei trade come nella classica Montecarlo) dell’equity line originale (colorata in “lime”). Da un punto di vista qualitativo si nota come la curva originale si collochi nei percentili più elevati della distribuzione.

Più difficile il giudizio sul profilo del drawdown. Ci facciamo aiutare dall’analisi quantitativa:

Da un punto di vista squisitamente legato al profitto, la curva originale si attesta all’89° percentile della distribuzione. Dato che un posizionamento neutro corrisponde al 50° percentile, che uno pessimista vada considerato al di sotto di tale livello ed uno ottimista al di sopra, ciò ci dice che abbiamo creato una strategia i cui risultati sono da considerarsi fortemente ottimisti (+39.2 sopra la neutralità).

Per quanto riguarda il drawdown dobbiamo invertire la scala (trattandosi di valori intrinsecamente negativi): un valore sopra il 50° percentile si traduce in una previsione pessimistica, al contrario al di sotto. Nel nostro caso, quindi abbiamo una proiezione pessimista del +12.7.

In sintesi abbiamo una curva che probabilmente genererà un profitto inferiore a quello di backtest ma una max drawdown probabilmente inferiore.

Il nuovo motore “Wintermute” (versione 20231114) verrà consegnato a tutti i corsisti (che saranno liberi di modificarlo a propio piacere) domani, durante la diretta della nuova Live della Python Academy 2023.

Buon divertimento!

Giovanni Trombetta

Founder – Head of R&D
Gandalf Project

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *