Medias Móviles 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 en general 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 qué 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 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í.

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

Recursos

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

Hasta la próxima!

Medias Móviles con Python

Un pensamiento en “Medias Móviles con Python

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Deslizar arriba