In questo articolo descriviamo come analizzare le performance storiche degli ETF a leva (o Leveraged ETF) in Python, oltre a simulare il loro rendimento in periodi di tempo precedenti al loro lancio.
In questo articolo introduciamo gli ETF a leva ed analizziamo le performance storiche tramite le librerie di Python. Nel successivo articolo descriviamo il backtest di un portafoglio di ETF a leva con Backtrader.
I Leveraged ETF
Molte persone sconsigliano di mantenere aperta una posizione in un ETF con leva a causa del beta slippage. Diamo un’occhiata alla performance di SPY, un ETF S&P 500, rispetto a UPRO, un ETF S&P 500 con leva 3x.
Per prima cosa importiamo alcune librerie.
import pandas as pd
import yfinance as yf
import datetime
Implementiamo alcune funzioni per evitare duplicazioni del codice.
def returns(prices):
"""
Calcola la crescita di 1 dollaro investito in un titolo con determinati prezzi
"""
return (1 + prices.pct_change(1)).cumprod()
def drawdown(prices):
"""
Calcola il drawdown di un titolo con determinati prezzi
"""
rets = returns(prices)
return (rets.div(rets.cummax()) - 1) * 100
def cagr(prices):
"""
Calcola il Compound Annual Growth Rate (CAGR) di un titolo con determinati prezzi
"""
delta = (prices.index[-1] - prices.index[0]).days / 365.25
return ((prices[-1] / prices[0]) ** (1 / delta) - 1) * 100
Passiamo quindi a visualizzare il grafico dei prezzi di chiusura aggiustati di UPRO rispetto a SPY.
start = "2009-01-01"
end = "2020-01-01"
spy = yf.download("SPY", start, end)["Adj Close"]
upro = yf.download("UPRO", start, end)["Adj Close"]
spy_returns = returns(spy).rename("SPY")
upro_returns = returns(upro).rename("UPRO")
spy_returns.plot(title="Growth of $1: SPY vs UPRO", legend=True, figsize=(10,6))
upro_returns.plot(legend=True)
plt.show()
print("CAGRs")
print(f"SPY: {cagr(spy):.2f}%")
print(f"UPRO: {cagr(upro):.2f}%")
CAGRs
SPY: 14.25%
UPRO: 38.07%
Come possiamo vedere, l’ETF con leva 3x offre prestazioni migliori della sua controparte senza leva, ma con un compromesso di rischio maggiore. Diamo un’occhiata ai drawdown.
spy_drawdown = drawdown(spy)
upro_drawdown = drawdown(upro)
print("Max Drawdown")
print(f"SPY: {spy_drawdown.idxmin()} {spy_drawdown.min():.2f}%")
print(f"UPRO: {upro_drawdown.idxmin()} {upro_drawdown.min():.2f}%")
upro_drawdown.plot.area(color="red", title="UPRO drawdown", figsize=(10,6))
plt.show()
Max Drawdown
SPY: 2009-03-09 00:00:00 -27.13%
UPRO: 2011-10-03 00:00:00 -51.73%
Un investitore che deteneva SPY durante il periodo di tempo analizzato avrebbe subito un drawdown massimo di poco inferiore al 20%, mentre un investitore long su UPRO avrebbe dovuto sopportare di perdere più volte oltre la metà del proprio portafoglio!
Simulazione delle prestazioni storiche
Fino ad ora abbiamo visto che UPRO effettivamente supera SPY sin dal suo inizio. Tuttavia, UPRO ha visto solamente un mercato rialzista, quindi non sorprende che abbia avuto una buona crescita annuale. Per simulare come si sarebbe comportato UPRO in passato, inclusa la crisi finanziaria, possiamo applicare il rapporto di leva e spesa 3x di UPRO (0,92%) a VFINX, un ETF su S&P 500 che esiste dal 1976.
Scriviamo una funzione helper. La variazione percentuale giornaliera viene calcolata prendendo la variazione percentuale giornaliera del proxy, sottraendo il rapporto di spesa giornaliera, quindi moltiplicando per la leva finanziaria.
def sim_leverage(proxy, leverage=1, expense_ratio = 0.0, initial_value=1.0):
pct_change = proxy.pct_change(1)
pct_change = (pct_change - expense_ratio / 252) * leverage
sim = (1 + pct_change).cumprod() * initial_value
sim[0] = initial_value
return sim
Per testare la nostra simulazione, confrontiamo un UPRO simulato con tutto lo storico disponibile per UPRO.
vfinx = yf.download("VFINX", start, end)["Adj Close"]
upro_sim = sim_leverage(vfinx, leverage=3).rename("UPRO Sim")
upro_sim.plot(title="Growth of $1: UPRO vs UPRO Sim", legend=True, figsize=(10,6))
upro_returns.plot(legend=True)
plt.show()
Le serie sono quasi identiche! Simuliamo ora le ipotetiche prestazioni di UPRO dall’inizio di VFINX.
start = '1976-08-31'
vfinx = yf.download("VFINX", start, end)["Adj Close"]
upro_sim = sim_leverage(vfinx, leverage=3, expense_ratio=0.00).rename("UPRO Sim")
upro_sim.plot(title="Growth of $1: VFINX vs UPRO Sim", legend=True, figsize=(10,6))
vfinx_returns = returns(vfinx).rename("VFINX")
vfinx_returns.plot(legend=True)
plt.show()
print("CAGRs")
print(f"VFINX: {cagr(vfinx):.2f}%")
print(f"UPRO Sim: {cagr(upro_sim):.2f}%")
CAGRs
VFINX: 10.87%
UPRO Sim: 20.46%
La simulazione UPRO supera ancora la sua controparte senza leva, ma diamo un’occhiata ai drawdown.
upro_sim_drawdown = drawdown(upro_sim)
vfinx_drawdown = drawdown(vfinx)
print("Max Drawdown")
print(f"VFINX: {vfinx_drawdown.idxmin()} {vfinx_drawdown.min():.2f}%")
print(f"UPRO Sim: {upro_sim_drawdown.idxmin()} {upro_sim_drawdown.min():.2f}%")
upro_sim_drawdown.plot.area(color="red", title="UPRO Sim drawdown", figsize=(10,6))
plt.show()
Max Drawdown
VFINX: 2009-03-09 00:00:00 -55.25%
UPRO Sim: 2009-03-09 00:00:00 -97.11%
VFINX ha un drawdown abbastanza consistente di oltre il 55%, ma un investitore che detiene l’UPRO simulato incontrerebbe molti grandi drawdown tra cui uno superiore al 97% durante la crisi finanziaria del 2008!
Conclusione
L’UPRO simulato ha avuto un tasso di crescita annuo composto medio del 18,76% rispetto al 10,39% dell’S&P 500. Sebbene i rendimenti siano più elevati, i drawdown vicini al 100% lo rendono un investimento estremamente rischioso da avare da solo. Nel prossimo articolo vediamo la performance storica simulata di altri ETF a leva e esaminiamo alcune strategie di allocazione multi-asset.
Codice completo
In questo articolo abbiamo descritto come analizzare le performance storiche degli ETF a leva (o Leveraged ETF) in Python. Per il codice completo riportato in questo articolo, si può consultare il seguente repository di github:
https://github.com/datatrading-info/AnalisiDatiFinanziari