In questo articolo trattiamo un tema cruciale nel trading quantitativo e automatico: quando “spegnere” una strategia di trading. Spieghiamo perché le strategie tendono a sottoperformare nel lungo periodo e come possiamo misurarne il decadimento nel tempo. Approfondiamo e implementiamo la metrica più utilizzata, lo Sharpe Ratio annualizzato.
Inoltre vediamo come integrare queste funzionalità nel nostro framework open-source DataTrader per il backtesting event-driven con Python. Infine applichiamo questa metodologia ad alcune strategie già presentate su TradingQuant.it per valutarne l’efficacia.
Il Decadimento di una Strategia
Il decadimento di una strategia rappresenta una delle sfide più complesse da affrontare nel trading quantitativo. Parliamo di strategie che in passato hanno funzionato bene, ma che progressivamente (o bruscamente) perdono le loro caratteristiche vincenti fino a diventare non redditizie.
Costruiamo le strategie quantitative sul concetto di previsione o su errori di valutazione statistica. Quando sempre più trader, retail o istituzionali, adottano strategie sistematiche simili, i prezzi assorbono le inefficienze, facendo scomparire il bias o edge che avevamo rilevato con le nostre analisi. L’equity della strategia si erode e tende a scendere fino al livello dei costi di transazione, rendendola non più sostenibile.
Per questo motivo non possiamo considerare il trading quantitativo un’attività “accendi e dimentica”. Dobbiamo gestire un portafoglio di strategie da ruotare gradualmente quando le opportunità di arbitraggio si riducono. Diventa essenziale condurre una ricerca continua per sviluppare nuovi “alpha” redditizi da sostituire a quelli ormai superati.
Alcune strategie sistematiche attraversano lunghi periodi di rendimenti modesti e fasi brevi di ampi drawdown. Questo accade soprattutto con le strategie giornaliere, che includono un numero limitato di “scommesse” a esito positivo. La vera sfida è capire se una strategia sta sottoperformando per l’erosione del vantaggio statistico o se stiamo vivendo solo una fase temporanea di difficoltà.
Lo Sharpe Ratio Annualizzato
Per affrontare questo problema dobbiamo implementare una metrica di trailing che confronti le performance attuali della strategia con quelle passate. Una delle più utilizzate nel mondo istituzionale è lo Sharpe Ratio annualizzato.
Lo Sharpe Ratio misura i rendimenti medi in eccesso della strategia rispetto alla volatilità che dobbiamo sopportare per ottenerli. Rappresenta un’approssimazione del rapporto rischio-rendimento. Calcoliamo lo Sharpe Ratio annualizzato utilizzando i dati dell’ultimo anno di trading, ottenendo così una stima aggiornata, anche se retrospettiva, del rapporto rischio-rendimento.
Un Sharpe Ratio basso (sotto 1,0) indica che sopportiamo molta volatilità per un rendimento medio scarso. Se il valore è negativo, significa che ci sarebbe convenuto detenere uno strumento privo di rischio, come i Treasury USA, piuttosto che usare la nostra strategia. In tal caso non solo la strategia rende meno del risk-free rate, ma presenta anche una volatilità superiore!
Possiamo quindi usare il valore dello Sharpe Ratio annualizzato progressivo per capire se è il momento di “mettere in panchina” una strategia. Se il valore scende verso zero o in territorio negativo, potremmo trovarci davanti a un chiaro segnale.
Ovviamente questo approccio non esaurisce la gestione del rischio. Lo Sharpe Ratio si limita a valutare un aspetto del rischio (il rapporto con i rendimenti in eccesso) e rimane un indicatore retrospettivo. Non ci avvisa, ad esempio, di improvvise modifiche normative o di un crash nei nostri sistemi nei giorni a venire.
Nonostante questi limiti, risulta utile per stimare la possibilità che una strategia sottoperformi in futuro. Inoltre rappresenta un buon indicatore da includere nei report di investimento, poiché uno Sharpe Ratio stabile suggerisce coerenza tra rischio e rendimento della strategia.
Calcolo dello Sharpe Ratio annualizzato progressivo
Definiamo lo Sharpe Ratio standard per i rendimenti delle strategie con la formula:
\(\begin{eqnarray}S = \frac{\mathbb{E}(r_s – r_b)}{\sqrt{\text{Var} (r_s – r_b)}}\end{eqnarray}\)
Si tratta del rapporto tra l’aspettativa dei rendimenti in eccesso della strategia e la deviazione standard di tali rendimenti. In pratica, valuta il rapporto rendimento/rischio, dove consideriamo il rischio come la volatilità dei rendimenti.
Per ottenere uno Sharpe Ratio annualizzato progressivo dobbiamo modificare questa formula in due modi. Per prima cosa consideriamo solo gli ultimi rendimenti di un periodo annualizzato (per esempio, negli studi giornalieri usiamo gli ultimi 252 rendimenti close-to-close). Poi moltiplichiamo il valore per la radice quadrata del numero di periodi (252 giorni lavorativi negli USA):
\(\begin{eqnarray}S_t = \sqrt{k} \frac{\mathbb{E}(r_{s,t-k} – r_{b,t-k})}{\sqrt{\text{Var} (r_{s,t-k} – r_{b,t-k})}}\end{eqnarray}\)
Dove \(r_{s,t-k}\) rappresenta i rendimenti della strategia nei precedenti k periodi, non l’intera serie storica.
Ricalcoliamo questo valore a ogni periodo di trading dopo aver ottenuto almeno k risultati. Non possiamo usare questo Sharpe Ratio prima di aver accumulato almeno un anno di dati, poiché i rendimenti iniziali potrebbero essere molto volatili e poco rappresentativi, generando valori irrealistici.
Dobbiamo ricordare che lo Sharpe Ratio è una misura imperfetta del “rischio”, non solo per i limiti già citati, ma anche perché penalizza anche la volatilità al rialzo. Se la varianza dei rendimenti aumenta fortemente verso l’alto, lo Sharpe Ratio diminuisce, a causa del denominatore elevato.
Non sottovalutiamo nemmeno i grandi movimenti al rialzo: anche questi indicano un comportamento imprevisto della strategia. Per esempio, un forte incremento della performance potrebbe riflettere un cambiamento normativo favorevole. In tal caso, dobbiamo considerare questo nuovo regime nei futuri sviluppi della strategia per mantenere la coerenza delle prestazioni.
Implementazione Python con DataTrader
Abbiamo incluso l’implementazione dello Sharpe Ratio annualizzato progressivo nel codice base di DataTrader nella classe TearsheetStatistics, disponibile su GitHub. Questa classe produce anche l’output visivo della strategia al termine del backtest.
Utilizziamo il metodo rolling di Pandas per calcolare lo Sharpe Ratio. La funzione dataframe.rolling() consente di effettuare operazioni su finestre mobili. In parole semplici, analizziamo una finestra di k elementi alla volta ed eseguiamo le operazioni matematiche desiderate.
Mostriamo lo Sharpe Ratio annualizzato progressivo come un grafico posto sotto la curva di equity nell’output visivo del tearSheet. Questa funzionalità è opzionale: possiamo attivarla impostando rolling_sharpe=True nell’istanza della classe TearsheetStatistics durante il backtest. La classe mostra anche quanto sia semplice implementare tutto tramite un semplice attributo booleano.
class TearsheetStatistics(AbstractStatistics):
def __init__(
self, config, portfolio_handler,
title=None, benchmark=None, periods=252,
rolling_sharpe=False
):
..
..
self.rolling_sharpe = rolling_sharpe
..
..
Calcolo dello Sharpe Ratio
Per calcolare lo Sharpe Ratio annualizzato, calcoliamo l’oggetto rolling applicando il metodo rolling alla serie dei rendimenti della strategia, utilizzando una finestra di lookback pari al numero di periodi di trading (self.periods).
Eseguiamo il calcolo moltiplicando la radice quadrata dei periodi di trading per il rapporto tra la media mobile dei rendimenti e la deviazione standard mobile. Non includiamo alcun tasso privo di rischio: consideriamo i rendimenti zero come l’alternativa priva di rischio. Adottiamo lo stesso criterio anche per il benchmark. Tutto questo lo gestiamo nel metodo get_results della classe TearsheetStatistics:
def get_results(self):
# Equity
equity_s = pd.Series(self.equity).sort_index()
# Returns
returns_s = equity_s.pct_change().fillna(0.0)
# Sharpe progressivo annualizzato
rolling = returns_s.rolling(window=self.periods)
rolling_sharpe_s = np.sqrt(self.periods) * (
rolling.mean() / rolling.std()
)
..
..
statistics["rolling_sharpe"] = rolling_sharpe_s
# Statistiche sul Benchmark se il ticker di benchmark è specificato
if self.benchmark is not None:
equity_b = pd.Series(self.equity_benchmark).sort_index()
returns_b = equity_b.pct_change().fillna(0.0)
rolling_b = returns_b.rolling(window=self.periods)
rolling_sharpe_b = np.sqrt(self.periods) * (
rolling_b.mean() / rolling_b.std()
)
..
..
statistics["rolling_sharpe_b"] = rolling_sharpe_b
..
..
return statistics
Per visualizzare il grafico dello Sharpe progressivo annualizzato abbiamo implementato il metodo _plot_rolling_sharpe. È molto simile al metodo _plot_equity. Creiamo un grafico simile alla curva equity, usando colori identici per aiutare a distinguere la performance della strategia e la performance del benchmark. L’unica differenza è una linea verticale tratteggiata posta a k periodi nella simulazione della strategia per rappresentare il primo punto in cui viene calcolato lo Sharpe Ratio:
def get_results(self):
# Equity
equity_s = pd.Series(self.equity).sort_index()
# Returns
returns_s = equity_s.pct_change().fillna(0.0)
# Sharpe progressivo annualizzato
rolling = returns_s.rolling(window=self.periods)
rolling_sharpe_s = np.sqrt(self.periods) * (
rolling.mean() / rolling.std()
)
..
..
statistics["rolling_sharpe"] = rolling_sharpe_s
# Statistiche sul Benchmark se il ticker di benchmark è specificato
if self.benchmark is not None:
equity_b = pd.Series(self.equity_benchmark).sort_index()
returns_b = equity_b.pct_change().fillna(0.0)
rolling_b = returns_b.rolling(window=self.periods)
rolling_sharpe_b = np.sqrt(self.periods) * (
rolling_b.mean() / rolling_b.std()
)
..
..
statistics["rolling_sharpe_b"] = rolling_sharpe_b
..
..
return statistics
Visualizzazione del grafico
Il codice plot_results è stato modificato per includere il grafico Sharpe progressivo se il parametro self.rolling_sharpe è impostato su True. Questa operazione viene eseguita utilizzando un offset_index. L’indice viene utilizzato per far sapere a matplotlib se ci sono cinque o sei sezioni verticali nel grafico e, in tal caso, regolare la posizione del grafico:
def plot_results(self, filename=None):
..
..
if self.rolling_sharpe:
offset_index = 1
else:
offset_index = 0
vertical_sections = 5 + offset_index
fig = plt.figure(figsize=(10, vertical_sections * 3.5))
fig.suptitle(self.title, y=0.94, weight='bold')
gs = gridspec.GridSpec(vertical_sections, 3, wspace=0.25, hspace=0.5)
stats = self.get_results()
ax_equity = plt.subplot(gs[:2, :])
if self.rolling_sharpe:
ax_sharpe = plt.subplot(gs[2, :])
ax_drawdown = plt.subplot(gs[2 + offset_index, :])
ax_monthly_returns = plt.subplot(gs[3 + offset_index, :2])
ax_yearly_returns = plt.subplot(gs[3 + offset_index, 2])
ax_txt_curve = plt.subplot(gs[4 + offset_index, 0])
ax_txt_trade = plt.subplot(gs[4 + offset_index, 1])
ax_txt_time = plt.subplot(gs[4 + offset_index, 2])
self._plot_equity(stats, ax=ax_equity)
if self.rolling_sharpe:
self._plot_rolling_sharpe(stats, ax=ax_sharpe)
self._plot_drawdown(stats, ax=ax_drawdown)
..
..
Per usare lo Sharpe progressivo annualizzato nel backtest di una strategia è sufficiente impostare a True il parametro nel file di configurazione della strategia, dove si inizializza il tearsheet:
..
..
# Uso delle statistiche Tearsheet
title = ["Example Systematic Trading Strategy"]
statistics = TearsheetStatistics(
config, portfolio_handler, title,
benchmark="SPY", rolling_sharpe=True
)
..
..
Nella prossima sezione mostriamo i tearsheet aggiornati con lo Sharpe progressivo annualizzato per varie strategie descritte negli articoli precedenti.
Risultati della strategia
Ricreiamo tutte le strategie visualizzate di seguito visitando i collegamenti agli articoli specifici. Abbiamo ristampato i risultati aggiungendo la visualizzazione dello Sharpe ratio annualizzato progressivo.
Pairs trading con i filtri di Kalman
Troviamo l’articolo originale per il Pairs Trading con i filtri di Kalman qui. Consigliamo di consultarlo per ottenere tutti i dettagli della strategia.
Iniziamo a calcolare lo Sharpe ratio annualizzato della strategia poco oltre la metà del 2010, dopo 252 periodi di trading. L’indice di Sharpe supera inizialmente quota 2,0 fino alla metà del 2011, durante una fase di forte volatilità nei rendimenti. Questo periodo culmina con una performance piatta e, in seguito, in calo tra il 2012 e il 2014, riducendo drasticamente l’indice di Sharpe in territorio negativo.
Dopo la ripresa della strategia nel 2013, lo Sharpe annualizzato risale lentamente fino a tornare vicino a 2,0. Tuttavia, non risulta chiaro se la strategia abbia iniziato nuovamente a deteriorarsi alla fine del 2016.
Dobbiamo notare quanto sia difficile, durante un’implementazione live, capire verso la fine del 2013 se fosse stato il caso di “spegnere” la strategia a causa della prolungata discesa dello Sharpe annualizzato. Questo evidenzia l’importanza di comprendere a fondo il comportamento statistico della distribuzione dei rendimenti in fase di backtest.
Strategia di cointegrazione tra asset
L’articolo originale per la strategia di cointegrazione tra asset è disponibile qui. Visitatelo per ottenere tutti i dettagli della strategia.
Poiché eseguiamo questa strategia in un arco di tempo relativamente breve (poco meno di due anni), calcoliamo lo Sharpe annualizzato progressivo solo per un periodo limitato.
All’inizio, il rapporto Sharpe risulta elevato, intorno a 2,25. Questo riflette il rapido incremento della performance nei primi mesi del 2015, ma presto osserviamo un appiattimento. Quando questi periodi di forte rendimento escono dal calcolo progressivo, l’indice di Sharpe cala rapidamente fino a -0,5 nella prima metà del 2016, e si mantiene stabile fino alla fine dello stesso anno.
Una flessione così marcata della performance aggiustata per il rischio rappresenta un chiaro segnale che dovremmo valutare la possibilità di disattivare la strategia. Questo decadimento può derivare dal fatto che qualsiasi alpha o vantaggio precedentemente legato alle sue previsioni è stato annullato dall’arbitraggio o da un cambiamento nella struttura sottostante delle relazioni di mercato.
In questo esempio, è possibile che l’azienda di raffinazione dell’alluminio abbia introdotto una propria strategia di copertura delle fusioni di alluminio, negoziando in autonomia i prezzi del gas naturale. Questo intervento ridurrebbe l’impatto del prezzo del gas naturale sulla redditività dell’azienda, rendendo quindi difficile, se non impossibile, applicare con successo questa strategia.
Strategia di analisi del sentiment
L’articolo originale per la strategia di analisi del sentiment è disponibile qui. Vi invitiamo a visitare l’articolo per consultare tutti i dettagli della strategia.
Nell’articolo sulla strategia, abbiamo effettuato tre simulazioni distinte: una sui titoli tecnologici, una sull’energia e una sulla difesa. In questo esempio, utilizziamo l’insieme di titoli del settore della difesa, che comprende BA, GD, LMT, RTN e NOC, tutti componenti dell’S&P500.
Osserviamo che la strategia ha registrato un forte rialzo nel 2013, con uno Sharpe annualizzato pari a 2,5, che ha superato quota 3,5 all’inizio del 2014. Tuttavia, la performance è rimasta stabile fino al 2014, riducendo progressivamente lo Sharpe annualizzato, poiché la volatilità dei rendimenti è rimasta pressoché invariata. All’inizio del 2015, lo Sharpe si attestava tra 0,5 e 1,0, segnalando un rischio maggiore per unità di rendimento in questa fase. Entro la fine del 2015, lo Sharpe è salito leggermente a circa 1,5, grazie soprattutto ad alcuni solidi guadagni registrati nella seconda metà dell’anno.
Dalla metà del 2014 in poi, notiamo che l’indice di Sharpe della strategia e quello del benchmark hanno mostrato un andamento sostanzialmente simile. Questo indica che, dal punto di vista rendimento/rischio, investire nella strategia ha offerto pochi vantaggi rispetto al semplice acquisto e mantenimento di SPY. Naturalmente, in questa valutazione non consideriamo i guadagni iniziali ottenuti dalla strategia nel 2013.
Conclusioni
Monitorare il decadimento delle strategie è un passaggio essenziale per mantenere un portafoglio quantitativo robusto e redditizio nel tempo. L’utilizzo dello Sharpe Ratio annualizzato progressivo ci fornisce un indicatore semplice ma efficace per valutare l’evoluzione del rapporto rischio-rendimento.
Integrare questa metrica in DataTrader consente di automatizzare la valutazione e il monitoraggio delle performance strategiche. Sebbene non esaurisca l’analisi del rischio, rappresenta un utile segnale di allerta su potenziali deterioramenti.
In un ambiente competitivo e in continua evoluzione, è fondamentale affiancare alla logica automatica una supervisione critica e costante. Solo così possiamo decidere con consapevolezza quando è il momento giusto per “spegnere” una strategia e svilupparne di nuove.
Codice Completo
Il codice completo presentato in questo articolo, basato sul framework di trading quantitativo event-driven DataTrader, è disponibile nel seguente repository GitHub: https://github.com/tradingquant-it/DataTrader.”


















