Tradiar Blog

Medias Móviles en Python

April 24, 2019 • 7 Minute Read • by Damián Fanaro

Aprende cómo una estrategia básica de trading puede ser automatizada con Python.

En el post anterior hemos hablado sobre algoritmos, medias móviles y trading automático. Mencionamos una estrategia de trading sencilla que consistía en tomar posiciones de compra o venta cuando las medias móviles se cruzaban.

En esta entrada, vamos a utilizar el lenguaje de programación Python para implementar la estrategia y así conseguir nuestro primer sistema de trading semi-automático. Decimos que será “semi” ya que el algoritmo nos indicará si comprar o vender en un momento dado pero no enviará las ordenes al mercado automáticamente. En última instancia, el inversor decide qué hacer con las señales generadas por el sistema.

Les recordamos que si bien este es un ejemplo básico, se requieren conocimientos elementales de programación y de Python en especial.

¿Comenzamos?

Lo primero que tenemos que hacer es utilizar 3 librerías muy poderosas que nos permitirán manipular y visualizar datos financieros:

Una vez instalados estos paquetes, procederemos a crear un archivo con extensión .py donde escribiremos nuestro algoritmo completo.

Abrimos el archivo y ponemos el siguiente fragmento de código:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from enum import Enum

Lo que estamos haciendo es indicarle al intérprete de Python que queremos incluir las librerías mencionadas para que estén disponibles al momento de usarlas en nuestro programa.

Luego vamos a definir dos clases, ActionColorMapping y ActionPricePoint:

class ActionColorMapping(Enum):
    SELL = 'red'
    BUY = 'green'

class ActionPricePoint:
    def __init__(self, price, date, action):
        self.price = price
        self.date = date
        self.action = action

Usaremos ActionColorMapping solamente para propósitos de visualización que veremos más adelante. Por otro lado, instancias del tipo ActionPricePoint encapsularán los datos sobre posiciones a tomar en momentos específicos del tiempo.

A continuación vamos a escribir dos funciones de orden superior (funciones que retornan funciones, comúnmente llamado expresiones lambda), una para evaluar si estamos en condiciones de vender y otra que evalúa si estamos en condiciones de comprar:

def sell():
    return lambda left, right: left < right

def buy():
    return lambda left, right: left >= right

En ambas funciones, las variables left y right serán las medias móviles de 20 y 100 días respectivamente, para un día específico.

También vamos a definir una función para visualizar el precio, las medias móviles y los puntos de compra y venta que se hayan manifestado en el pasado:

def plot(price, ma_20, ma_100, action_price_points):
    ax = price.plot()

    ma_20.plot(label='Moving Average 20 Days', ax=ax)
    ma_100.plot(label='Moving Average 100 Days', ax=ax)

    ax.set_xlabel('Date')
    ax.set_ylabel('Closing Price')
    ax.set_title('Caterpillar Inc. Closing Price')
    ax.legend(loc='upper left')

    for position in action_price_points:
        plt.scatter(position.date, position.price, s=600, c=position.action.value)

    plt.show()

Aquí es donde hacemos uso de la librería Matplotlib. Cabe destacar que, para que un gráfico sea visualizado al momento de ejecutar el programa, es necesario llamar a la función show() (ver la última línea de código).

Ahora que ya tenemos definidas las funciones de apoyo, veamos finalmente la implementación del algoritmo de cruces de medias móviles:

def retrieve_closing_price(symbol):
    df = pd.read_csv('data/{}.csv'.format(symbol))
    return df['Close']

def data_not_available(price):
    return np.isnan(price)

def calculate_moving_average_crossovers(symbol):
    closing_price = retrieve_closing_price(symbol)

    rm_20 = closing_price.rolling(window=20).mean()
    rm_100 = closing_price.rolling(window=100).mean()

    action = ActionColorMapping.SELL
    signal_detected = sell()
    signals = []

    for index in range(closing_price.size):
        if data_not_available(rm_20[index]) or data_not_available(rm_100[index]):
            continue

        if signal_detected(rm_20[index], rm_100[index]):
            mean_price = (rm_20[index] + rm_100[index]) / 2
            action = ActionPricePoint(mean_price, index, action)
            signals.append(action)

        if rm_20[index] >= rm_100[index]:
            action = ActionColorMapping.SELL
            signal_detected = sell()
        else:
            action = ActionColorMapping.BUY
            signal_detected = buy()

    plot(closing_price, rm_20, rm_100, signals)


if __name__ == '__main__':
    calculate_moving_average_crossovers('CAT')

Nuestro programa comienza con la ejecución del método main. Ahí es donde llamamos a la función calculate_moving_average_crossovers pasando como parámetro el símbolo por el cual queremos operar. En este caso hemos pasado CAT, por la empresa americana Caterpillar Inc.

Luego, el algoritmo llama a una función que lee los datos históricos desde un archivo con formato CSV (comma-separated value, del inglés) y retorna el precio de cierre. El archivo contiene múltiples valores tales como la fecha, el precio de apertura y cierre, el precio máximo y mínimo del día, el precio de cierre ajustado y el volumen operado; pero en nuestro caso solo nos interesa el precio de cierre. La fuente de estos datos es de Yahoo Finance.

Una vez que tenemos los datos del precio para cada día, procederemos a calcular las medias móviles utilizando la función rolling de Pandas Dataframe.

Por último, iteramos sobre cada precio de cierre yendo en orden cronológico y comprobamos si se produce un cruce de medias móviles y en que dirección sucede. Recordemos que si la media de 20 días se cruza por debajo de la de 100 entonces tendremos una señal de venta, mientras que si lo hace por encima, tendremos una señal de compra.

A modo de ejercicio, dejamos los detalles del algoritmo a interpretación del lector.

Resultado final

El gráfico resultante luego de ejecutar nuestro programa es el siguiente:

Puntos de cruce de medias móviles, 20 y 100 días.
Puntos de cruce de medias móviles de 20 y 100 días.

Como se puede observar, hay 3 señales que se manifestaron en el pasado. Dos para vender (círculos rojos) y uno para comprar (círculo verde).

El código completo junto con los datos históricos lo puedes encontrar aquí.

Recursos

En el próximo post haremos algunos cálculos matemáticos para determinar cuanto hubiese sido nuestra ganancia o pérdida si hubiésemos operado en el mercado siguiendo nuestra estrategia de cruce de medias móviles.

Si tienes alguna pregunta o sugerencia, no dudes en comentar!

Hasta la próxima!