En esta entrega vamos a tratar de manera simple la idea de explotar la estacionalidad en Python mediante portfolios, como de costumbre.Esta vez, tratamos de batir al indice de referencia, en la anterior entrega, basada en las ideas de Ray Dalio,no se consiguió, no obstante esta nueva entrega, eliminará ese mal sabor de boca que nos produjo la estrategia(para todos los publicos) que ofrecia el CEO de Bridgewater Associates.
La premisa principal es muy básica. Por razones que desconozco, y que considero que enumerarlas, es caer en un sesgo narrativo fehaciente, no voy a tunelar sobre las mismas.
Realizando estudios estacionales sobre la renta variable americana, y el activo refugio por excelencia, el oro ,se puede comprobar que los meses, que historicamente hacen retroceder a la renta variable americana, son los mismos que hacen avanzar al oro. Por dicho motivos buscamos encontrar la estacionalidad via python para dichos activos.
En la siguiente imagen, de un gráfico normalizado de la estacionalidad del indice SPX de los últimos 20 años, podemos observar que hay unas zonas criticas (bajo mi sesgo), que podríamos delimitar entre mitades de diciembre(El fin del rally de navidad) hasta mitades de marzo, y agosto, el mes donde todos los grandes gestores están de vacaciones, y la liquidez se desvanece.



En el caso del oro es algo distinto, podemos ver que desde mitades de diciembre hasta finales de febrero, es alcista, y ademas el verano le sienta muy bien, desde mitades de julio hasta mitades de septiembre, tiene un avance importante. Y es un gran logro encontrado gracias a la estacionalida en python






Una vez encontrada la fuente de valor, puede ser una casualidad espuria, un sesgo del autor de estas lineas, o una ventaja real. Pero hay algo, que se debe analizar en profundidad, y a por ello vamos.
Vamos a hacer la ventaja mas solida, mediante la anomalia de la baja volatilidad, que se puede apreciar en la siguiente imagen.



Estacionalidad en Python, creando el portfolio
Como se puede apreciar, los ETFs que replican los indices de baja volatilidad, están batiendo sistematicamente desde su creación a el indice convencional, tanto en rentabilidad, como en volatilidad anualizada, y drawdowns. Por consecuencia, vamos a utilizar un etf que replique un indice de baja volatilidad, en lugar de utilizar un etf que replique directamente
Unas vez explicadas, las dos ventajas fundamentales que vamos a explotar en este portfolio, vamos a definir las reglas que trabajaremos:
- Universo de activos : $SPLV & $GLD
- Fechas Clave $GLD : 20-12 hasta el 20-2 y del 1-8 al 31-8
- Fechas Clave $SPLV : desde el 21-2 hasta el 30-7, y desde el 1-9 hasta el 20-12
- Tamaño de la posicion : 100%
- TP & SL : No aplica
Una vez justificadas las ventajas y establecidas las reglas, a crear Portfolios estacionales en Python
Cargamos las librerias basicas¶
#Basicos para todo
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
#Herramientas Financieras
import yfinance as yf
import quantstats as qs
import ffn
import pyfolio as pf
#Limpieza de avisos
import warnings
warnings.filterwarnings(action='ignore')
#Configuraciones Basicas
%matplotlib inline
plt.style.use('ggplot')
plt.rcParams["figure.figsize"] = [20,9]
Descargamos los tickers y los añadimos en un nuevo DataFrame¶
splv = yf.download("SPLV")
gld = yf.download("GLD")
spy = yf.download("SPY")
dataframe =pd.DataFrame()
dataframe['SPLV'] = splv['Adj Close']
dataframe['GLD'] = gld['Adj Close']
dataframe['SPY'] = spy['Adj Close']
Creamos las señales que nos indicaran en que activo debemos estar invertidos¶
Vamos a crear dos señales, 1 para el oro, y 0 para el indice de renta variable,
y mediante .loc vamos a acceder a las fechas del indice con condiciones, para asignarlo a una colunmna.
dataframe.loc[(dataframe.index.month==12) & (dataframe.index.day>=20), 'signal'] = 1
dataframe.loc[(dataframe.index.month==1),'signal'] = 1
dataframe.loc[(dataframe.index.month==2) & (dataframe.index.day<=20), 'signal'] = 1
dataframe.loc[(dataframe.index.month==8) ,'signal'] = 1
dataframe['signal'] = np.where(dataframe['signal']==1,1,0)
Calculamos los retornos porcentuales de los activos¶
dataframe['gold_pct'] = dataframe['GLD'].pct_change()
dataframe['splv_pct'] = dataframe['SPLV'].pct_change()
dataframe['spy_pct'] = dataframe['SPY'].pct_change()
Creamos el retorno del portfolio¶
Le indicamos que cuando signal == 1, utilice los retornos del oro, y cuando no, que utilice los de la renta variable
dataframe['returns'] = np.where(dataframe['signal']==1,dataframe['gold_pct'],dataframe['splv_pct'])
Ploteamos los retornos de los tickers utilizados¶
plt.figure(figsize=(14,7))
plt.plot(dataframe['spy_pct'].cumsum(), color='r', label='$SPY')
plt.plot(dataframe['splv_pct'].cumsum(),color='b', label='$SPLV')
plt.plot(dataframe['gold_pct'].cumsum(),color='g', label='$GLD')
plt.legend()
plt.show()
Observavamos como SPY y SPLV van siempre en una pelea constante entre ellos, y el oro esta en otro universo paralelo, comprobamos que no es muy buena idea almacenar posiciones en oro durante largos peridos de tiempo, pues conlleva un coste de oportunidad muy alto
Comparacion de la curva de resultados¶
Creamos unos cuantos resultados aleatorios (siempre hay que compararse con el random walk, o analisis tecnico como lo llaman ahora 😉 )
dataframe['random1'] = np.random.randint(2, size=len(dataframe))
dataframe['random1'] = np.where(dataframe['random1'] == True, dataframe['spy_pct'], -0)
dataframe['random2'] = np.random.randint(2, size=len(dataframe))
dataframe['random2'] = np.where(dataframe['random2'] == True, dataframe['spy_pct'], -0)
plt.figure(figsize=(14,7))
plt.plot(dataframe['returns'].cumsum(), color='r', label='Portfolio')
plt.plot(dataframe['splv_pct'].cumsum(),color='b', label='$SPLV')
plt.plot(dataframe['spy_pct'].cumsum(),color='g', label='$SPY')
plt.plot(dataframe['random1'].cumsum(),color='y', label='Random1')
plt.plot(dataframe['random2'].cumsum(),color='grey', label='Random2')
plt.legend()
plt.show()
a simple vista, batimos al indice, batimos al random walk y batimos a nuestro activo de renta variable. Parece que hemos conseguido el objetivo de batir al indice, de una forma sencilla y aburrida, pero rentable.
Ahora damos paso a las metricas de la cartera para tener algo mas de informacion, y poder evaluar un poco mejor.
qs.reports.full(dataframe['returns'],'SPY')
pf.create_interesting_times_tear_sheet(dataframe['returns'],dataframe['SPLV'].pct_change())