In un precedente articolo abbiamo descritto la funzione di ribilanciamento mensile implementata nel framework di backtesting DataTrader ed eseguito dei test su un portafoglio semplificato di ETF azionari e obbligazionari. In questo articolo vediamo come gestire direttamente i pesi tramite il backtest di portafogli strategici di ETF.
A tale scopo apportato al motore del framework per gestire direttamente i pesi degli strumenti presenti in portafoglio. Presentiamo in particolare due nuovi portafogli di ETF. Il primo prevede una pesatura “strategica” degli ETF in portafoglio, mentre il secondo assegna la stessa percentuale di allocazione a ciascun ETF. Entrambi i portafogli includono lo stesso insieme di ETF: un mix di azioni statunitensi (large e small cap), obbligazioni statunitensi (investment grade e ad alto rendimento), azioni dei mercati emergenti e “asset alternativi” come materie prime e REIT.
Per questi test utilizziamo strumenti del mercato statunitense, poiché offrono una maggiore disponibilità di dati storici, permettendoci così un backtest più robusto a partire dal 2007.
Portafogli Strategici di ETF
Per effettuare il backtest di portafogli strategici di ETF abbiamo implementato strategie di trading molto simili, che si differenziano soltanto per le percentuali di allocazione degli strumenti in portafoglio e per le date di inizio. Alla fine di ogni mese, la strategia liquida completamente il portafoglio e ribilancia (in dollari) ciascun asset in base al patrimonio aggiornato.
La prima strategia applica semplicemente una ponderazione del 60% a SPY e del 40% ad AGG, che rappresentano rispettivamente le azioni statunitensi a grande capitalizzazione e le obbligazioni investment grade.
Con la seconda strategia, destiniamo il 30% del portafoglio alle azioni statunitensi tramite SPY e IJS, il 25% alle azioni dei mercati emergenti tramite EFA ed EEM, il 25% alle obbligazioni statunitensi (investment grade e ad alto rendimento) tramite AGG e JNK, il 10% alle materie prime tramite DJP e il 10% ai fondi di investimento immobiliare (REIT).
La terza strategia utilizza lo stesso set di ETF della seconda, ma assegna a tutti gli asset la medesima percentuale di allocazione, pari al 12,5%.
Abbiamo scelto le date di inizio esclusivamente in base alla disponibilità dei dati. La strategia n. 1 parte il 29 settembre 2003, mentre le strategie n. 2 e n. 3 iniziano il 4 dicembre 2007. Tutte e tre si concludono il 12 ottobre 2016.
Ticker | # 1 – 60/40 SPY/AGG | # 2 – Pesi “strategici” | # 3 – Pesi Uguali |
---|---|---|---|
SPY | 60,0% | 25,0% | 12,5% |
IJS | 0,0% | 5,0% | 12,5% |
EFA | 0,0% | 20,0% | 12,5% |
EEM | 0,0% | 5,0% | 12,5% |
AGG | 40,0% | 20,0% | 12,5% |
JNK | 0,0% | 5,0% | 12,5% |
DJP | 0,0% | 10,0% | 12,5% |
RWR | 0,0% | 10,0% | 12,5% |
Set di dati storici
Per attuare questa strategia, dobbiamo disporre dei dati sui prezzi OHLCV degli ETF nel periodo coperto dal backtest:
Ticker | Nome | Periodo |
---|---|---|
SPIY | SPDR S&P 500 ETF | 29 settembre 2003 – 12 ottobre 2016 |
IJS | iShares S&P Small Cap ETF 600 Value | 4 dicembre 2007 – 12 ottobre 2016 |
EFA | iShares MSCI EAFE ETF | 4 dicembre 2007 – 12 ottobre 2016 |
EEM | ETF iShares MSCI Emerging Markets | 4 dicembre 2007 – 12 ottobre 2016 |
AGG | ETF iShares Core US Aggregate Bond | 29 settembre 2003 – 12 ottobre 2016 |
JNK | SPDR Barclays Capital High Yield Bond ETF | 4 dicembre 2007 – 12 ottobre 2016 |
DJP | iPath Bloomberg Commodity Index Total Return ETN | 4 dicembre 2007 – 12 ottobre 2016 |
RWR | SPDR Dow Jones REIT ETF | 4 dicembre 2007 – 12 ottobre 2016 |
Dobbiamo inserire questi dati nella directory specificata nel file di configurazione di DataTrader se vogliamo replicare i risultati.
Implementazione Python con DataTrader
Per effettuare il backtest dei portafogli strategici di ETF, applichiamo la stessa procedura di liquidazione integrale e ribilanciamento mensile descritta nel precedente articolo.
Per semplificare la generazione di più portafogli separati senza duplicare eccessivamente il codice, abbiamo creato un nuovo file chiamato monthly_rebalance_run.py
. Questo file contiene il codice “boilerplate” necessario per eseguire un backtest con ribilanciamento mensile complesso.
Per generare i backtest separati dei due portafogli descritti in questo articolo, importiamo all’interno di monthly_rebalance_run.py
la funzione run_monthly_rebalance
. Poi eseguiamo lo script, impostando tra i parametri iniziali il ticker del benchmark (SPY), il dizionario ticker_weights
con le percentuali di allocazione degli ETF, il titolo del report dei risultati, le date di inizio/fine e il capitale iniziale.
Questa struttura ci consente di modificare facilmente la composizione del portafoglio, a patto di avere i dati storici disponibili. A titolo di esempio, riportiamo il codice per il mix 60/40 tra azioni e obbligazioni USA:
# equities_bonds_60_40_etf_portfolio_backtest.py
import datetime
from datatrader import settings
from monthly_rebalance_run import run_monthly_rebalance
if __name__ == "__main__":
ticker_weights = {
"SPY": 0.6,
"AGG": 0.4,
}
run_monthly_rebalance(
settings.DEFAULT_CONFIG_FILENAME, False, "",
"SPY", ticker_weights, "Strategia ETF mix 60/40 Azioni/Obbligazioni USA",
datetime.datetime(2003, 9, 29), datetime.datetime(2016, 10, 12),
500000.00
)
Il codice per altre strategie di portfolio è riportato nel paragrafo “Codice completo” alla fine dell’articolo, sebbene siano molto simili allo snippet di cui sopra.
Per eseguire uno qualsiasi dei backtest è necessario posizionarsi nella directory dove è presente il file monthly_rebalance_run.py
e quindi digitare nella console quanto segue:
$ python equities_bonds_60_40_etf_portfolio_backtest.py
Ricordarsi di cambiare il nome del file a seconda di quale backtest si vuol eseguire.
Risultati del Backtest
I risultati del backtest di portafogli strategici di ETF qui presentati sono forniti al netto dei costi delle commissioni. I costi sono simulati utilizzando il prezzo fisso di Interactive Brokers per le azioni del Nord America . Non tengono conto delle differenti commissioni per gli ETF, ma sono ragionevolmente rappresentativi di ciò che potrebbe essere ottenuto in una vera strategia di trading.
Portafoglio ETF con mix 60/40 di azioni / obbligazioni statunitensi
La strategia stessa è identica a quella dell’articolo precedente ed è stata ripubblicata qui grazie all’utilizzo di dati storici aggiuntivi che risalgono al 2003. Di seguito viene fornito il report della strategia:

Il benchmark è formato da un portafoglio buy-and-hold (cioè nessun ribilanciamento mensile) composto esclusivamente dell’ETF SPY.
Lo Sharpe Ratio del benchmark e quello del portafoglio sono identici a 0.4. Inoltre abbiamo un drawdown maggiore investendo solo in SPY. Il CAGR della strategia è del 4,43%, inferiore al 5,92% dello SPY. Ciò è dovuto ai costi di transazione per eseguire il ribilanciamento nonché alla sottoperformance dell’ETF AGG rispetto allo SPY.
AGG ha in qualche modo attutito l’ampio drawdown del 2008 per il portafoglio, ma non in misura significativa. A causa del crollo del 2008/2009, il portafoglio è in drawdown per 1242 giorni, quasi 3 anni e mezzo, rispetto ai 1365 giorni del benchmark. Tieni presente, tuttavia, che questo periodo ha avuto un effetto drammatico su quasi tutti i portafogli ETF.
Portafoglio ETF “strategico”
Di seguito il report delle prestazioni della strategia:

Usiamo come benchmark un portafoglio buy-and-hold (cioè senza ribilanciamento mensile) composto esclusivamente dall’ETF SPY.
Nel portafoglio con allocazione strategica abbiamo provato a replicare il portafoglio descritto in questo post [1]. Tuttavia, non siamo riusciti a trovare strumenti idonei per replicare gli indici specifici di VWEXH, RPIBX, PREMX e VGSIX, che rappresentano rispettivamente obbligazioni spazzatura statunitensi, obbligazioni dei mercati esteri sviluppati, obbligazioni dei mercati emergenti e REIT statunitensi.
Abbiamo inoltre faticato a reperire dati sufficienti per gli ETF scelti per rappresentare RPIBX e PREMX, ossia VWOB (Vanguard Emerging Markets Govt Bd ETF) e BNDX (Vanguard Total International Bond ETF). Questi ultimi due sono arrivati sul mercato solo alla fine del 2013, il che ci avrebbe costretti a lavorare con un backtest limitato a pochi anni. Una durata così breve non ci permette di valutare adeguatamente le prestazioni di una strategia con ribilanciamento mensile.
Per questo motivo, abbiamo ridotto il portafoglio a otto ETF, rispetto ai dieci del post citato. Li abbiamo descritti nel paragrafo precedente, intitolato “Dati”. Il portafoglio prevede un’allocazione del 30% sulle azioni statunitensi, 25% sulle azioni dei mercati emergenti, 25% sulle obbligazioni statunitensi, 10% sulle materie prime e 10% sul settore immobiliare.
La performance di questa strategia si è rivelata nettamente inferiore rispetto al portafoglio 60/40. Abbiamo registrato un CAGR negativo. Alcune flessioni dei rendimenti derivano dalla necessità di negoziare otto titoli distinti ogni mese. Tuttavia, quasi tutti gli altri ETF presenti nel portafoglio hanno sottoperformato SPY in modo significativo, penalizzando i rendimenti complessivi.
In particolare, le materie prime e il reddito fisso non hanno ottenuto buoni risultati negli ultimi cinque anni di dati in-sample, rispetto alle azioni statunitensi. Questo ci porta a concludere che adottare una ponderazione uguale potrebbe addirittura peggiorare la situazione. Nella sezione successiva applichiamo la strategia con percentuali di allocazione uguali, per testare questa ipotesi all’interno dei nostri portafogli strategici di ETF.
Portafoglio ETF con uguale allocazione
Di seguito il report delle prestazioni della strategia:

Utilizziamo come benchmark un portafoglio buy-and-hold (cioè senza ribilanciamento mensile) composto esclusivamente dall’ETF SPY.
Come ci aspettavamo, la performance di questo portafoglio risulta significativamente peggiore rispetto all’allocazione strategica o al mix 60/40. Dal 2008 in poi, il portafoglio resta in drawdown e registra un CAGR di quasi -5%. Il drawdown massimo di questa strategia si aggira intorno al 70%.
Notiamo però chiaramente che quasi tutto il drawdown deriva dalla crisi finanziaria del 2008. Se avessimo effettuato il backtest un anno dopo, i risultati sarebbero probabilmente stati abbastanza diversi, anche se ancora inferiori rispetto allo SPY.
In sintesi, risulta chiaramente difficile costruire un portafoglio a pari allocazione o allocato in dollari che riesca a reggere eventi di mercato significativi come il crollo del 2008. Nei prossimi post esploreremo i modi per mitigare questi problemi.
Conclusioni
La volatilità diseguale, che misuriamo attraverso le deviazioni standard storiche dei rendimenti, motiva il concetto di parità di rischio, secondo cui allochiamo il capitale sulla base del “rischio” anziché sul peso dello strumento nel portafoglio.
La parità di rischio richiede calcoli storici sui flussi di rendimenti e quindi si struttura in modo fondamentalmente diverso rispetto alle strategie descritte sopra. Una versione futura di DataTrader includerà questi calcoli, così potremo costruire un’ampia varietà di portafogli a parità di rischio con l’obiettivo di aumentare i valori dello Sharpe Ratio.
Codice completo
Il codice completo per il backtest di portafogli strategici di ETF, basato sul framework di trading quantitativo event-driven DataTrader, è disponibile nel seguente repository GitHub: https://github.com/tradingquant-it/DataTrader.”
# Monthly_rebalance_run.py
from datatrader import settings
from datatrader.compat import queue
from datatrader.price_parser import PriceParser
from datatrader.price_handler.yahoo_daily_csv_bar import YahooDailyCsvBarPriceHandler
from examples.strategies.monthly_liquidate_rebalance_strategy import MonthlyLiquidateRebalanceStrategy
from datatrader.position_sizer.rebalance import LiquidateRebalancePositionSizer
from datatrader.risk_manager.example import ExampleRiskManager
from datatrader.portfolio_handler import PortfolioHandler
from datatrader.compliance.example import ExampleCompliance
from datatrader.execution_handler.ib_simulated import IBSimulatedExecutionHandler
from datatrader.statistics.tearsheet import TearsheetStatistics
from datatrader.trading_session import TradingSession
def run_monthly_rebalance(
config, testing, filename,
benchmark, ticker_weights, title_str,
start_date, end_date, equity
):
config = settings.from_file(config, testing)
tickers = [t for t in ticker_weights.keys()]
# Imposta le variabili necessarie per il backtest
events_queue = queue.Queue()
csv_dir = config.CSV_DATA_DIR
initial_equity = PriceParser.parse(equity)
# Uso di Yahoo Daily Price Handler
price_handler = YahooDailyCsvBarPriceHandler(
csv_dir, events_queue, tickers,
start_date=start_date, end_date=end_date
)
# Uso della strategia "monthly liquidate Rebalance"
strategy = MonthlyLiquidateRebalanceStrategy(tickers, events_queue)
# strategy = Strategies(strategy, DisplayStrategy())
# Uso del sizer delle posizioni con specifici pesi dei ticker
position_sizer = LiquidateRebalancePositionSizer(ticker_weights)
# Uso di un Risk Manager di esempio
risk_manager = ExampleRiskManager()
# Uso del Portfolio Handler standard
portfolio_handler = PortfolioHandler(
initial_equity, events_queue, price_handler,
position_sizer, risk_manager
)
# Uso del componente ExampleCompliance
compliance = ExampleCompliance(config)
# Uso di un IB Execution Handler simulato
execution_handler = IBSimulatedExecutionHandler(
events_queue, price_handler, compliance
)
# Uso delle statistiche standard
title = [title_str]
statistics = TearsheetStatistics(
config, portfolio_handler, title, benchmark
)
# Setup del backtest
backtest = TradingSession(
config, strategy, tickers,
initial_equity, start_date, end_date,
events_queue, price_handler=price_handler,
position_sizer=position_sizer,
execution_handler=execution_handler,
title=title, benchmark=tickers[0],
)
results = backtest.start_trading(testing=testing)
statistics.save(filename)
return results
# equities_bonds_60_40_etf_portfolio_backtest.py
import datetime
from datatrader import settings
from monthly_rebalance_run import run_monthly_rebalance
if __name__ == "__main__":
ticker_weights = {
"SPY": 0.6,
"AGG": 0.4,
}
run_monthly_rebalance(
settings.DEFAULT_CONFIG_FILENAME, False, "",
"SPY", ticker_weights, "Strategia ETF mix 60/40 Azioni/Obbligazioni USA",
datetime.datetime(2003, 9, 29), datetime.datetime(2016, 10, 12),
500000.00
)
# strategy_weight_etf_portfolio_backtest.py
import datetime
from datatrader import settings
from monthly_rebalance_run import run_monthly_rebalance
if __name__ == "__main__":
ticker_weights = {
"SPY": 0.25,
"IJS": 0.05,
"EFA": 0.20,
"EEM": 0.05,
"AGG": 0.20,
"JNK": 0.05,
"DJP": 0.10,
"RWR": 0.10
}
run_monthly_rebalance(
settings.DEFAULT_CONFIG_FILENAME, False, "",
"SPY", ticker_weights, "Strategia con Allocazione Strategica ETF",
datetime.datetime(2007, 12, 4), datetime.datetime(2016, 10, 12),
500000.00
)
# equal_weight_etf_portfolio_backtest.py
import datetime
from datatrader import settings
from monthly_rebalance_run import run_monthly_rebalance
if __name__ == "__main__":
ticker_weights = {
"SPY": 0.125,
"IJS": 0.125,
"EFA": 0.125,
"EEM": 0.125,
"AGG": 0.125,
"JNK": 0.125,
"DJP": 0.125,
"RWR": 0.125
}
run_monthly_rebalance(
settings.DEFAULT_CONFIG_FILENAME, False, "",
"SPY", ticker_weights, "Strategia con uguale percentuale di ETF",
datetime.datetime(2007, 12, 4), datetime.datetime(2016, 10, 12),
500000.00
)