Impacto del factor estacional sobre los bonos americanos.

Analisis estacional sobre los bonos americanos en Python, segun Jonathan Hartley y Krista Schawarz

Impacto del factor estacional sobre los bonos americanos.

Analisis del Research

En este articulo, vamos a estudiar el impacto del factor estacional sobre los bonos americanos. En el paper Predictable End-of-Month Treasury Returns de Jonathan Hartley y Krista Schwarz (NOV-2019) conluimos que:

  • Los rendimientos medios son significativamente postivios en los ultimos dias del mes, y son muy cercanos a cero en otros momentos.
  • Segun el paper, una posicion larga en el tesoro en los ultimos dias del mes, proporciona un sharpe sobre 1.

Atribuyen el efecto, a las grandes aseguradoras. Dado que manejan fondos y planes de pensiones de millonoes de norteamericanos, que cuando reciben su salario, automaticamente les descuentan las aportaciones a sus planes, y por consecuencia, las gestoras que gestionan dichos planes, estan en la obligacion en tomar posiciones en sus carteras. Y por consecuencia a impactar en el precio.Ademas los bonos del tesoro americano, son el activo financiero con mayor tamaño creado por unn unico emisor, el departamento del tesoro de los estados unidos con 16 trillones de bonos operables

En el paper, para analizar el factor, utilizan emisiones directas por parte del departamento del tesoro, para simplificar este modelo, vamos a elegir unos activos, que se les atribuyan unas caracteristicas similares.

Seleccion de Activos

Para seleccionar los activos que vamos a utilizar, screapearemos mediante pandas y su funcion read_html la web de etfdb, seleccionando el asset class de bond. Seleccionaremos los tickers que contengan el termino Treasury, y realizaremos ciertos calculos adicionales, como añadirle metadata, o calcular el porcentaje de peso total sobre nuestro indice sintetico de referencia.

tickers = pd.read_html('https://etfdb.com/etfs/asset-class/bond/')[0]['Symbol Symbol'][:25].tolist()
meta = pd.read_html('https://etfdb.com/etfs/asset-class/bond/')[0]
meta = meta.iloc[:, :4][:25]
cols = {meta.columns[0]: 'ticker',
                    meta.columns[1]: 'Name',
                    meta.columns[2]: 'AUM',
                    meta.columns[3]: 'YTD'}
meta.rename(columns=cols, inplace=True)
meta['AUM'] = meta['AUM'].str.replace('[^\d.]', '', regex=True).astype(float)
t  = meta[meta['Name'].str.contains('Treasury')]
del meta
t['w'] = t['AUM']/ t['AUM'].sum()
tickers = t['ticker'].tolist()

Descargar Activos Necesarios

Una vez t enemos seleccionados en la variable tickers, los tickers que necesitamos para nuestro estudio, vamos a descargarlo de forma sencilla utilizando la libreria de yfinance, donde descargara la informacion de yahoo finance mediante el siguiente codigo

tickers = ['TLT','IEF','GOVT']
raw = yf.download(tickers)['Adj Close'].dropna()
insambple = raw['2018':'2022']

Generacion de Señales

La generacion de señales, dado la taxonomia de la ventaja, que se basa unicamente en un factor mensual recurrente, deberiamos filtrar los dias donde queramos estar dentro, generando una columna llamada signal donde 1 = esta comprado, 0 = esta vendido.

bt = pd.DataFrame()
ultimo_dia_del_mes = insample.index.to_series().dt.daysinmonth
dias_hasta_que_acabe_el_mes = ultimo_dia_del_mes - insample.index.day
bt['dias_hasta_eom'] = dias_hasta_que_acabe_el_mes
bt['signal'] = (bt['dias_hasta_eom'] <= 2).astype(int)

Modeling

Para modelar esta ventaja, vamos a utilizar la forma mas sencilla posible. Cuando es el dia concreto, estamos dentro, y si no... fuera.

pct = insample.pct_change()
bt['results'] = np.where(bt['signal'] == 1,pct.sum(axis=1),0)
bt['cum'] = bt['results'].cumsum()

fig, ax = plt.subplots()
plt.plot(bt['cum'], color=colors[0], label='Model')
plt.title('TLT - 3DTE to 0DTE - 2018:2020')
plt.show()

Ajustando el Modelo

Ajustamos el modelo a una funcion, para en proximos articulos ir investigando diferentes intervalos y activos de forma sencilla. Encapsulamos la estrategia dentro de una funcion

def modelling_bonds(df,dias_anticipo):
    ultimo_dia_del_mes = df.index.to_series().dt.daysinmonth
    dias_hasta_que_acabe_el_mes = ultimo_dia_del_mes - df.index.day
    df['dias_hasta_eom'] = dias_hasta_que_acabe_el_mes
    df['signal'] = (df['dias_hasta_eom'] <= dias_anticipo).astype(int)
    results = np.where(df['signal'] == 1,pct.sum(axis=1),0) 
    eq =  pd.DataFrame(results,index=pct.index ,columns=['close'])
    ratios = {...}
    return ratios,eq
eqs = []
opt = []
for x in range(0,8,1):
    r,g = modelling_bonds(df,x)
    opt.append(r)
    eqs.append(g)
fig, ax = plt.subplots()
for n, data in enumerate(eqs):
        cumsum = np.cumsum(eqs[n] * 100)
        plt.plot(cumsum, label=f'N={n}')
plt.show()

Proximamente...

Backtesting de la estrategia utilizando Zipline y VectorBT