Cómo operar en MetaTrader 5 desde Python – Parte 3

por

Apreciado trader,

¡Llegamos al último artículo de la serie! Finalmente, vamos a centrarnos en cómo operar en MetaTrader 5 desde Python. Concretamente, veremos cómo enviar órdenes pendientes, cómo cancelarlas y cómo cerrar todas las posiciones que tengas abiertas.

Durante este artículo vamos a apoyarnos en todo lo que construimos en los dos anteriores artículos, por lo que si aún no los has leído, te recomiendo comenzar por ellos antes de seguir con lo que veremos a continuación. Puedes encontrarlos aquí y aquí.

Si eres de los que prefiere seguir este tipo de tutoriales en formato de vídeo, también hemos preparado una lista de reproducción con todos los pasos en nuestro canal de YouTube.

Por otro lado, puedes obtener también el Jupyter Notebook que hemos preparado con todo el código y sus explicaciones por si quieres ir directamente al grano.

¡Vamos a ello!

Cómo operar en MetaTrader 5 desde Python

Como ya sabes, la capacidad de automatizar el envío y la gestión de órdenes de manera rápida y precisa es esencial para poder ejecutar la lógica de una estrategia de trading algorítmico. Por esta razón, en esta sección veremos en detalle cómo operar en MetaTrader 5 desde Python.

Para facilitar los ejemplos, durante todo el artículo usaremos un mismo símbolo que guardaremos en la variable ‘symbol’. Al mismo tiempo, tal y como habíamos visto en los artículos anteriores, añadiremos dicho símbolo al MarketWatch. Recuerda que, si no lo hacemos, no podremos enviar ninguna órden.

# Primero, definimos el símbolo en el que queremos abrir nuestras operaciones
symbol = 'EURUSD'

# Y nos aseguramos de que dicho símbolo esté añadido al MarketWatch
if mt5.symbol_select(symbol, True):
    print("Símbolo añadido correctamente")
else:
    print(f"Ha ocurrido un error añadiendo el símbolo: {mt5.last_error()}")

Estructurar un Trade Request para una Market Order

Antes de poder enviar una órden al mercado, deberemos estructurar un trade request con toda la información que el servidor de MetaTrader 5 necesita para poder ejecutar dicha órden.

Por ello, comenzaremos viendo cómo estructurar un trade request para una órden a mercado:

# Creamos el trade request necesario
request = {
    'action': mt5.TRADE_ACTION_DEAL,
    'magic': 1010101010,
    'symbol': symbol,
    'volume': 1.25,
    'sl': 0.0,
    'tp': 0.0,
    'deviation': 50,
    'type': mt5.ORDER_TYPE_BUY,
    'type_filling': mt5.ORDER_FILLING_FOK,
    'type_time': mt5.ORDER_TIME_GTC,
    'comment': "Primera orden desde Python"
}

Si no entiendes para qué sirven todos estos campos del trade request no te preocupes; vamos a repasarlos todos para que no te quede ninguna duda sobre su funcionamiento:

  • Action: queremos una ejecución instantánea (market order), por eso ponemos la acción como TRADE_ACTION_DEAL.
  • Magic: utilizamos el magic number para identificar una estrategia, como si fuera su DNI. Así podremos, por ejemplo, cerrar solamente las posiciones que tengan ese mismo magic number y no interferir con otras posiciones abiertas por otras estrategias.
  • Symbol: el instrumento donde queremos ejecutar la órden definina en el trade request.
  • Volume: la cantidad de contratos que queremos negociar en el instrumento.
  • SL: precio donde colocar el Stop Loss. Si es 0, no se coloca.
  • TP: precio donde colocar el Take Profit. Si es 0, no se coloca.
  • Deviation: desviación máxima permitida respecto al precio solicitado, en puntos.
  • Type: tipo de orden a colocar, es decir, compra/venta a mercado, stop, limit, o stop-limit.
  • Type_filling: política de ejecución del volumen total de la orden. Puede ser FOK, IOC y RETURN.
  • Type_time: política de expiración para las órdenes pendientes.
  • Comment: comentario identificativo que quieras darle a la orden. Importante: limitado a 32 caracteres.

En MetaTrader 5, las órdenes de Stop Loss y Take Profit cumplen con la política OCO (One Cancels the Other) por defecto.

Puedes obtener más información acerca de los trade requests en Python aquí. Para información más genérica sobre la estructura de los requests, puedes recurrir a este enlace a la documentación oficial de MQL5.

También te dejo el enlace a la documentación sobre los distintos trade actions, fillings y expiraciones aquí, así como la lista completa de los distintos tipos de órden disponibles aquí.

Ejecución de una Market Order con control de errores

Una vez que tenemos definido el trade request, estaremos en disposición de enviarlo al mercado utilizando la función order_send(). También aprovecharemos para gestionar los códigos de retorno de dicha función y verificar si se ha producido algún error:

# Y enviamos el request para poder ejecutar la orden
order = mt5.order_send(request)


if order.retcode == mt5.TRADE_RETCODE_DONE:
    print(f"La orden se ha ejecutado correctamente: {order}")
else:
    print(f"Ha ocurrido un error al ejecutar la orden: {mt5.last_error()}")

También tienes toda la información acerca de los códigos de retorno fruto de enviar un trade request al servidor de MetaTrader 5 aquí.

Función para cerrar todas las posiciones abiertas

Es habitual que te encuentres en la situación de necesitar cerrar más de una posición a la vez. Para ello, tendrías que crear un trade request específico para poder cerrar cada posición abierta, lo que en la práctica resulta ser muy poco eficiente e inaceptable. Por esta razón, vamos a crear una función que generará automáticamente el trade request adecuado para poder cerrar cada posición.

El objetivo es que puedas reutilizar esta base de código para poder crear tus propias variantes según tus necesidades, como solamente cerrar las posiciones que tengan un magic number específico o que estén abiertas en un instrumento (símbolo) en concreto.

Para conseguirlo, lo primero que haremos será crear una función llamada cerrar_posicion() que generará el trade request adecuado para cerrar una posición en concreto, devolviéndonos la información sobre dicha ejecución. Al hacerlo de esta manera, podremos reutilizar esta función en el futuro:

# Definimos una función con el request pertinente y que recibe una posición como argumento

def cerrar_posicion(position):
    """Esta función cierra la posición que recibe como argumento"""
   
    request = {
        'action': mt5.TRADE_ACTION_DEAL,
        'position': position.ticket,
        'magic': position.magic,
        'symbol': position.symbol,
        'volume': position.volume,
        'deviation': 50,
        'type': mt5.ORDER_TYPE_BUY if position.type == 1 else mt5.ORDER_TYPE_SELL,
        'type_filling': mt5.ORDER_FILLING_FOK,
        'type_time': mt5.ORDER_TIME_GTC,
        'comment': "mi primera orden desde Python"
    }

    return mt5.order_send(request)

Posteriormente, crearemos otra función que usará la que acabamos de definir para efectivamente cerrar todas las posiciones abiertas. Fíjate en que la lógica que permite cerrarlas todas estará definida en esta segunda función y no en la anterior.

# Ahora definimos una nueva función que sirva para cerrar TODAS las posiciones abiertas
def cerrar_todas_las_posiciones():
    """Esta función cierra TODAS las posiciones abiertas y gestiona posibles errores"""
   
    positions = mt5.positions_get()

    for position in positions:
        if cerrar_posicion(position).retcode == mt5.TRADE_RETCODE_DONE:
            print(f"Posición {position.ticket} cerrada correctamente.")
        else:
            print(f"Ha ocurrido un error al cerrar la posición {position.ticket}: {mt5.last_error()}")

Finalmente, solo necesitaremos ejecutar la función para efectivamente cerrar todas las posiciones abiertas.

# Ahora ejecutamos la función para cerrar todas las operaciones
cerrar_todas_las_posiciones()

Cómo gestionar órdenes pendientes

En caso de querer enviar una órden pendiente al mercado, también utilizaremos la combinación de un trade request y la función order_send(). Sin embargo, el trade request tendrá diferencias significativas respecto al que habíamos definido anteriormente para la Market Order.

Creación del trade request para una órden pendiente

Vamos ahora a crear el trade request para una órden pendiente:

# Creamos un request para la orden pendiente
pending_request = {
    'action': mt5.TRADE_ACTION_PENDING,
    'magic': 1010101010,
    'symbol': symbol,
    'volume': 1.25,
    "price": 1.12390,
    'sl': 0.0,
    'tp': 0.0,
    'type': mt5.ORDER_TYPE_SELL_LIMIT,
    'type_filling': mt5.ORDER_FILLING_FOK,
    'type_time': mt5.ORDER_TIME_GTC,
    'comment': "mi primera orden pendiente"
}

Como puedes ver, en este caso hemos modificado la ‘action’ por un mt5.TRADE_ACTION_PENDING. También hemos tenido que definir el precio donde colocar la órden pendiente, así como escoger el tipo de orden. En MetaTrader 5 hay 3 tipos de órden pendientes:

  • Órdenes Stop
  • Órdenes Limit
  • Órdenes Stop-Limit

Ten en cuenta que si estás operando en CFDs con MetaTrader 5, las órdenes limite no te permiten proveer de liquidez, sino que cuando el precio llega a ellas se “activan” en forma de market orders. En otras palabras, no son órdenes limitadas reales y pagarás siempre el spread.

Envío de una órden pendiente al mercado

Con el trade request listo, podemos enviarlo utilizando de nuevo la función order_send(). En este caso, el servidor de MetaTarder 5 colocará nuestra órden pendiente en el instrumento indicado:

# Y enviamos el request para poder colocar la orden pendiente
order = mt5.order_send(pending_request)

if order.retcode == mt5.TRADE_RETCODE_DONE:
    print(f"La orden pendiente se ha enviado correctamente: {order}")
else:
    print(f"Ha ocurrido un error al enviar la orden: {mt5.last_error()}")

Función para cancelar todas las órdenes pendientes

De manera similar al caso anterior, te podrías encontrar también con múltiples órdenes pendientes esperando a ser activadas que quisieras cancelar.

Por ejemplo, imagínate la situación en la que tienes órdenes pendientes de tipo buy-stop, muy cerca del precio actual, y está a punto de publicarse una notícia relevante como un cambio en los tipos de interés. Para evitar que tus órdenes se activaran con, probablemente, un gran slippage, podrías querer cancelarlas todas a la vez.

Para ello, vamos a crear la función cancelar_orden_pendiente():

def cancelar_orden_pendiente(order):
    """Esta función cancela la orden pendiente que recibe como argumento"""
   
    cancel_request = {
        'action': mt5.TRADE_ACTION_REMOVE,
        "order": order.ticket,
        'symbol': order.symbol,
    }

    return mt5.order_send(cancel_request)

Fíjate como en este caso, el tamaño del trade request ha disminuido notablemente, siendo solamente necesarios tres campos.

Siendo capaces de cancelar órdenes pendientes individuales, llega el momento de utilizar la función anterior para poder crear un bucle for con el que, efectivamente, cancelar todas las órdenes pendientes que estuvieran latentes en el mercado:

def cancelar_todas_las_ordenes_pendientes():
    """Esta función cancela TODAS las órdenes pendientes y gestiona posibles errores"""

    orders = mt5.orders_get()

    for order in orders:
        if cancelar_orden_pendiente(order).retcode == mt5.TRADE_RETCODE_DONE:
            print(f"Orden {order.ticket} cancelada correctamente.")
        else:
            print(f"Ha ocurrido un error al cancelar la orden {order.ticket}: {mt5.last_error()}")

Ahora, solo necesitaremos ejecutar la función para efectivamente cancelar todas las órdenes pendientes:

# Finalmente ejecutamos la función para cancelar todas las órdenes pendientes
cancelar_todas_las_ordenes_pendientes()

Conclusión

Y con éste último artículo finalizamos la serie sobre cómo conectar Metatrader 5 con Python. En el primer artículo, descubrimos cómo conectar MetaTrader 5 con Python, permitiéndonos acceder a datos y funcionalidades de la plataforma desde Python.

En el segundo artículo, vimos cómo obtener datos OHLC y Tick directamente desde MetaTrader 5, lo que nos permite poder acceder a los datos de los precios en tiempo real para que nuestras estrategias puedan consumirlos y tomar las decisiones adecuadas.

Finalmente, en este último artículo, hemos aprendido cómo operar en MetaTrader 5 desde Python, enviando y cerrando órdenes, así como pudiendo gestionar órdenes pendientes, lo que nos brinda la capacidad de automatizar la ejecución de nuestra lógica.

Combinando todo lo aprendido durante los tres articulos, estarás preparado para poder ejecutar en MetaTrader 5 la lógica de tu estrategia codificada en Python.

Como siempre, si tienes cualquier duda, puedes ponerte en contacto con nostros a través de nuestro formulario o en la comunidad de Discord.

Gracias por tu lectura.

Un saludo,

Martí

Escrito por Marti Castany

Martí Castany es Quant Researcher, Portfolio Manager y Co-Fundador de KomaLogic, una firma de trading algorítmico. Con más de una década de experiencia en el sector de la inversión sistemática, Martí se ha destacado en el desarrollo de modelos estadísticos, análisis y limpieza de datos y gestión cuantitativa del riesgo.

27 Jul, 2023

También puede interesarte…