Estrategia: MACD + Backtest

Gerard Sánchez - 25 de Junio de 2021 a las 12:50 - Estrategias




En este artículo vamos a basarnos en el código realizado en un vídeo anterior sobre el MACDque explicaremos a continuación, para posteriormente dar paso a la estrategia y a la comprobación de resultados.

Primero debemos importar las librerías:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import pandas_datareader.data as wb
import seaborn as sns

A continuación realizamos el cálculo del indicador técnico, del que hablamos específicamente en el artículo sobre indicadores técnicos.

Originalmente trata de una resta de dos medias exponenciales (12-26) y una de 9 que servirá de corte. Individualmente podemos escoger el número de periodos discrecionalmente si consideramos que queremos tener un MACD más o menos agresivo, cambiando los números del span.

data = wb.DataReader('TSLA', 'yahoo', '2020-1-1')['Adj Close']

m_rap = data.ewm(span=12, adjust=False).mean()
m_lenta = data.ewm(span=26, adjust=False).mean()

macd = m_rap - m_lenta
ema9 = macd.ewm(span=9, adjust=False).mean()
histograma = macd - ema9


A continuación ya entraríamos en el apartado de diseño y realización de la estrategia.

Vamos a programar una estrategia con precios de ejecución que serán los mismos que los de cierre , el mismo día en que se dan las señales, y no vamos a tener en cuenta comisiones.

También vamos a hacer que cada vez que haya una señal de compra, se compren acciones por una cantidad fija que no variará (a fin de simplificar el ejemplo) y que a la hora de vender, se venda toda la posición.

En primer lugar hay que calcular los cortes, tanto al alza (señal de compra) como a la baja (señal de venta) de la variable MACD con la ema9.

Con numpy.where() podemos determinar cuándo uno está por encima del otro (1 o 0) y después determinando el momento exacto de los cortes con .diff(), siendo 1 cuando el macd cruza por encima de la ema9 y -1 cuando cruza por debajo.

signal = pd.DataFrame(index=data.index)
signal['signal'] = np.where(macd > ema9, 1, 0)
signal['position'] = signal['signal'].diff()

Generamos la variable capital (nuestro capital inicial) y otra con el número de acciones que se comprarán o venderán en la señal de compra o venta. Para este ejemplo se venderá todo en la señal de venta, no sobreponderaremos ni infraponderaremos.

Debemos tener en cuenta que en acciones con un nominal reducido, la cartera prácticamente no se moverá, ya que el capital invertido será muy bajo y la mayor parte de nuestra posición será efectivo.

capital = 100000
stocks = int(1000)

Vamos a crear la variable positions con el número de acciones en cartera cuando estemos comprados, para a continuación escribir la variable portfolio con la ponderación del precio por el número de acciones en el momento de la compra, simulando la cartera.

positions = stocks*signal['signal']
portfolio = positions.multiply(data)

Necesitamos tener el efectivo, que bajará cuando hagamos una operación de compra y subirá cuando realicemos una venta, esto podemos hacerlo con la siguiente fórmula:

pos_diff = positions.diff()
cash = capital - (pos_diff.multiply(data).cumsum())

Calculamos el total:

total = cash + portfolio

Calculamos los retornos y eliminamos los 0 que se producen entre el momento de la venta y la siguiente compra:

retornos = total.pct_change()[1:]
retornos = retornos[retornos != 0]

Podemos hacer un print() con el valor final de la cartera:

print("Valor total bruto de la cartera al final del periodo:", round(total.iloc[-1],2))

Pasamos a graficar el precio del activo con el indicador MACD y las señales de compra/venta en el precio. Utilizaremos gridspec para generar nuestras tablas. 

grafico = plt.figure(figsize=(20,10))
tabla = gridspec.GridSpec(nrows=2, ncols=1, figure=grafico, height_ratios=[3,1])

graf_sup = plt.subplot(tabla[0,0])
graf_inf = plt.subplot(tabla[1,0])

graf_sup.plot(data, label='Cierre')
graf_sup.plot(data[signal['position'] == 1], '^', markersize=9, color='g')
graf_sup.plot(data[signal['position'] == -1], 'v', markersize=9, color='r')
graf_sup.set_title("Estrategia MACD ")

graf_inf.plot(data.index, macd, 'b', label="MACD")
graf_inf.plot(data.index, ema9, 'r--', label="Signal")
graf_inf.bar(data.index, histograma, color=(histograma>0).map({True:'g', False:'r'}))
graf_inf.set_title("MACD")
plt.grid()

En un tabla aparte, vamos a generar el gráfico de nuestro valor de la cartera y los retornos de la estrategia. Con ax1 creamos la figura de la izquierda con el valor de la cartera (total) y en ax2 los retornos con seaborn.histplot()

fig = plt.figure(figsize=(20,10))
ax1 = fig.add_subplot(121)
ax1.set_title("Valor de la cartera con " + str(capital)+ " euros y " + str(stocks)+ " acciones")
total.plot(ax=ax1, lw=2.)
ax1.plot(total[signal['position'] == 1], '^', markersize=8, color='g')
ax1.plot(total[signal['position'] == -1], 'v', markersize=8, color='r')

ax2 = fig.add_subplot(122)
ax2.set_title("Frecuencia de los retornos")
sns.histplot(retornos, kde=True, ax=ax2)
plt.show()


Aquí os dejo el código completo:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import pandas_datareader.data as wb
import seaborn as sns

data = wb.DataReader('TSLA', 'yahoo', '2020-1-1')['Adj Close']

m_rap = data.ewm(span=12, adjust=False).mean()
m_lenta = data.ewm(span=26, adjust=False).mean()

macd = m_rap - m_lenta
ema9 = macd.ewm(span=9, adjust=False).mean()
histograma = macd - ema9

signal = pd.DataFrame(index=data.index)
signal['signal'] = np.where(macd > ema9,1,0)
signal['position'] = signal['signal'].diff()

#################################################################
#################################################################
#################################################################

capital = 100000
stocks = int(1000)

positions = stocks*signal['signal']
portfolio = positions.multiply(data)

pos_diff = positions.diff()

cash = capital - (pos_diff.multiply(data).cumsum())
total = cash + portfolio

returns = total.pct_change()[1:]
returns = returns[returns != 0]

print("\n Valor total bruto de la cartera al final del periodo:", round(total.iloc[-1],2))

fig= plt.figure(figsize=(20,10))
ax1 = fig.add_subplot(121)
ax1.set_title("Valor bruto de la cartera con " + str(capital) + " euros y " + str(stocks) + " acciones")
total.plot(ax=ax1, lw=2.)
ax1.plot(total[signal['position'] == 1], '^', markersize=9, color='g')
ax1.plot(total[signal['position'] == -1], 'v', markersize=9, color='r')

ax2 = fig.add_subplot(122)
ax2.set_title("Frecuencia de los retornos")
sns.histplot(returns, kde=True, ax=ax2)

grafico = plt.figure(figsize=(20,10))
tabla = gridspec.GridSpec(nrows=2, ncols=1, figure=grafico, height_ratios=[3,1])

graf_sup = plt.subplot(tabla[0,0])
graf_inf = plt.subplot(tabla[1,0])

graf_sup.plot(data, label='Cierre')
graf_sup.plot(data[signal['position']== 1], '^', markersize=9, color='g')
graf_sup.plot(data[signal['position']== -1], 'v', markersize=9, color='r')
graf_sup.set_title("Precio")

graf_inf.plot(data.index, macd, 'b', label="MACD")
graf_inf.plot(data.index, ema9, 'r--', label="Signal")
graf_inf.bar(data.index, histograma, color=(histograma>0).map({True:'g', False:'r'}))
graf_inf.set_title("MACD")
plt.grid()
plt.show()

 


624 visitas
4    Login to like
Categorías:
 Estrategias   Estadísticas   Random   Gestión pasiva   Análisis técnico   Modelos   CEO   Mapas mentales   Liberalismo   Python   Growth   Niusleta   Ahorro   Recursos humanos   Inmobiliario   Fiscalidad   Value investing   Dividendos   Contabilidad   Marketing   Riesgo   IF   Cursos   Opciones   Bolsa