tradingt-algoritmico-portafoglio-etf-strategico

Portafogli di ETF strategici con pesi uguali tramite DataTrader

Sommario

SCRIVIMI SU TELEGRAM

Per informazioni, suggerimenti, collaborazioni...

Se è la prima volta che atterri su DataTrading, BENVENUTO!

Lascia che mi presenti. Sono Gianluca, ingegnere, trekker e appassionato di coding, finanza e trading. Leggi la mia storia.

Ho creato DataTrading per aiutare le altre persone ad utilizzare nuovi approcci e nuovi strumenti, ed applicarli correttamente al mondo del trading.

DataTrading vuole essere un punto di ritrovo per scambiare esperienze, opinioni ed idee.

TUTORIAL

In un precedente articolo abbiamo descritto la funzione di ribilanciamento mensile implementata nel framework di backtesting  DataTrader, effettuato dei test su un semplicistico portafoglio di ETF azioni / obbligazioni.

In questo articolo descriviamo le modifiche effettuate al framework motore in modo da consentire una gestione diretta dei pesi degli strumenti che compongono un portafoglio. In particolare descriviamo due nuovi portafogli di ETF. Il primo prevede una pesatura “strategica” degli ETF in portafoglio, mentre il secondo contiene la stessa percentuale di allocazione per ogni ETF. Entrami i portafogli sono composti dallo stesso set 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 .

In questi test sono stati utilizzati strumenti del mercato statunitense perchè hanno più dati storici e quindi permette un backtest più robusto a partire dal 2007.

Strategie di Trading

Le strategie di trading implementate sono estremamente simili e variano solamente nelle percentuali di allocazione degli strumenti che compongono il portafoglio e nelle date di inizio. Alla fine di ogni mese la strategia liquida completamente il portafoglio e quindi ribilancia (in dollari) ogni asset in base al patrimonio attualizzato. 

La prima strategia porta semplicemente una ponderazione del 60% / 40% di SPY e AGG, che rappresentano rispettivamente le azioni statunitensi a grande capitalizzazione e le obbligazioni investment grade.

La seconda strategia prevede un’allocazione del 30% 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, allocazione del 10% alle materie prime tramite DJP e infine allocazione del 10% a fondi di investimento immobiliare (REIT).

La terza strategia utilizza lo stesso set di ETF della seconda ma utilizza la stessa percentuale di allocazione al 12,5% per tutti gli assett.

Le date di inizio sono variate unicamente in base alla disponibilità dei dati. La strategia n. 1 inizia il 29 settembre 2003, mentre le strategie n. 2 e n. 3 iniziano il 4 dicembre 2007. Tutte le strategie terminano il 12 ottobre 2016.

 

Ticker# 1 – 60/40 SPY/AGG # 2 – Pesi “strategici”# 3 – Pesi Uguali
SPY60,0%25,0%12,5%
IJS0,0%5,0%12,5%
EFA0,0%20,0%12,5%
EEM0,0%5,0%12,5%
AGG40,0%20,0%12,5%
JNK0,0%5,0%12,5%
DJP0,0%10,0%12,5%
RWR0,0%10,0%12,5%

Dati

Per attuare questa strategia è necessario disporre dei dati sui prezzi OHLCV per gli ETF nel periodo coperto da questo backtest:

TickerNomePeriodoLink
SPIYSPDR S&P 500 ETF29 settembre 2003 – 12 ottobre 2016Yahoo Finanza
IJSiShares S&P Small Cap ETF 600 Value per le4 dicembre 2007 – 12 ottobre 2016Yahoo Finanza
EFAiShares MSCI EAFE ETF4 dicembre 2007 – 12 ottobre 2016Yahoo Finanza
EEMETF iShares MSCI Emerging Markets4 dicembre 2007 – 12 ottobre 2016Yahoo Finanza
AGGETF iShares Core US Aggregate Bond29 settembre 2003 – 12 ottobre 2016Yahoo Finanza
JNKSPDR Barclays Capital High Yield Bond ETF4 dicembre 2007 – 12 ottobre 2016Yahoo Finanza
DJPiPath Bloomberg Commodity Index Total Return ETN4 dicembre 2007 – 12 ottobre 2016Yahoo Finanza
RWRSPDR Dow Jones REIT ETF4 dicembre 2007 – 12 ottobre 2016Yahoo Finanza

Questi dati dovranno essere inseriti nella directory specificata nel file dei configurazione di DataTrader, se si desidera replicare i risultati.

Implementazione Python con DataTrader

La procedura per effettuare un backtest con liquidazione integrale e ribilanciamento mensile è descritta nel precedente articolo .

Per semplificare la generazione di più portafogli separati senza un’eccessiva duplicazione del codice, è stato creato un nuovo file chiamato monthly_rebalance_run.py. Contiene il codice “boilerplate” di backtesting necessario per effettuare il backtest di un ribilanciamento mensile complesso.

Per generare backtest separati dei due portafoglio per questo articolo, all’interno di monthly_rebalance_run.py viene importata la funzione run_monthly_rebalance. Quindi si esegue lo script inserendo tra i parametri iniziali il ticker del benchmark (SPY), il dizionario ticker_weights contenente le percentuali degli ETF, il testo del titolo del report dei risultati, le date di inizio / fine e il capitale iniziale.

Ciò rende estremamente semplice modificare la composizione del portafoglio, in caso di disponibilità dei dati storici. A titolo di esempio il codice per il mix 60/40 di azioni / obbligazioni USA è il seguente da:

            # 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 della strategia

Costo delle commissioni

I risultati della strategia 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 del post precedente ed è stata ripubblicata qui grazie all’utilizzo di dati storici aggiuntivi che risalgono al 2003.

Di seguito viene fornito il report della strategia:

trading-algoritmico-datatrader-etf-strategy-60-40-tearsheet

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:

trading-algoritmico-datatrader-etf-strategy-strategic-tearsheet

Il benchmark è formato da un portafoglio buy-and-hold (cioè nessun ribilanciamento mensile) composto esclusivamente dall’ETF SPY.

Nel portafoglio con allocazione strategica abbiamo fatto un tentativo per replicare il portafoglio in questo post [1]. Tuttavia, non è stato possibile 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.

Inoltre è stato difficile individuare dati sufficienti per gli ETF destinati a rappresentare RPIBX e PREMX, vale a dire VWOB (Vanguard Emerging Markets Govt Bd ETF) e BNDX (Vanguard Total International Bond ETF). Gli ultimi due sono stati quotati nel mercato solo alla fine del 2013, che significava avere un backtest di pochi anni. Tale durata non è sufficientemente lunga per produrre prestazioni rappresentative di una strategia di ribilanciamento mensile.

Quindi il portafoglio è stato ridotto a otto ETF rispetto a dieci nel post di cui sopra. Sono descritti nel precedente paragrafo “Dati”. Il portafoglio è invece allocato per il 30% sulle azioni statunitensi, il 25% per le azioni dei mercati emergenti, il 25% per le obbligazioni statunitensi, il 10% per le materie prime e il 10% per il settore immobiliare.

La performance di questa strategia è di gran lunga peggiore rispetto al portafoglio 60/40. Ha un CAGR negativo. Alcune flessioni dei rendimenti sono una conseguenza della negoziazione di otto titoli separati ogni mese. Tuttavia, quasi tutti gli altri ETF in portafoglio hanno sottoperformato SPY per un ampio margine, trascinando i rendimenti in modo significativo.

In particolare le materie prime e il reddito fisso non hanno registrato una buona performance negli ultimi cinque anni dei dati in-sample rispetto alle azioni statunitensi.

Ciò suggerisce che una ponderazione uguale probabilmente peggiorerà le cose. Nella sezione successiva viene eseguita la strategia con uguali percentuali di allocazione.

Portafoglio ETF con uguale allocazione

Di seguito il report delle prestazioni della strategia:

trading-algoritmico-datatrader-etf-strategy-egual-weight-tearsheet

Il benchmark è formato da un portafoglio buy-and-hold (cioè nessun ribilanciamento mensile) composto esclusivamente dall’ETF SPY.

Come previsto, la performance di questo portafoglio è significativamente peggiore rispetto all’allocazione strategica o al mix 60/40. Il portafoglio rimane in drawdown dal 2008 in poi e registra un CAGR di quasi -5%. Il drawdown massimo di questa strategia è intorno al 70%.

Tuttavia è anche chiaro che quasi tutto il drawdown è una conseguenza della crisi finanziaria del 2008. Se il backtest fosse stato effettuato un anno dopo, i risultati sarebbero stati probabilmente abbastanza diversi, anche se ancora sottoperformanti rispetto allo SPY.

In sintesi, è chiaramente relativamente difficile costruire un portafoglio di pari allocazione o allocato in dollari che possa resistere a significativi eventi di mercato come il crollo del 2008. Nei prossimi post esamineremo i modi per mitigare tali problemi.

Prossimi Passi

La volatilità diseguale, misurata dalle deviazioni standard storiche dei rendimenti, delle varie classi di attività motiva il concetto di parità di rischio, per cui il capitale è allocato sulla base del “rischio” piuttosto che sul peso dello strumento nel portafoglio

La parità di rischio richiede calcoli storici sui flussi di rendimenti e quindi ha una struttura fondamentalmente diversa dalle strategie sopra descritte. Una versione futura di DataTrader consentirà calcoli di questo tipo, in modo tale da poter costruire un’ampia varietà di portafogli di parità di rischio nel tentativo di aumentare i Sharpe Ratio.

Codice Completo

Per il codice completo riportato in questo articolo, utilizzando il framework open-source di backtesting event-driven DataTrader si può consultare il seguente repository di github:
https://github.com/datatrading-info/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
    )
				
			
Torna su