English Русский Deutsch 日本語 Português
preview
Acceso a la información de ticks de MetaTrader desde los servicios de MQL5 a una aplicación de Python mediante sockets

Acceso a la información de ticks de MetaTrader desde los servicios de MQL5 a una aplicación de Python mediante sockets

MetaTrader 5Sistemas comerciales |
35 5
Ramesh Maharjan
Ramesh Maharjan

Introducción

A veces no todo se puede programar en el lenguaje MQL5. E incluso si fuera posible convertir las bibliotecas avanzadas existentes en MQL5, llevaría mucho tiempo. Una mejor opción es integrar o utilizar las bibliotecas existentes para llevar a cabo una tarea. Por ejemplo, hay numerosas bibliotecas de aprendizaje automático en Python. Para nosotros, crear copias idénticas en formato MQL5 solo para realizar tareas de aprendizaje automático aplicadas al trading no es la mejor opción. Es mejor exportar los datos necesarios para esa biblioteca de aprendizaje automático de Python, realizar los procesos necesarios en el entorno de Python e importar el resultado de nuevo al programa MQL5. Este artículo trata sobre cómo transferir esos datos desde la terminal MetaTrader al entorno de Python.

Existe un paquete de Python llamado MetaTrader 5 que permite acceder a los datos de los gráficos de MetaTrader, como la información de las barras, la información de los ticks, la información de los usuarios, la información de las operaciones, etc. Sin embargo, este paquete solo está disponible para Windows, lo que significa que, si quieres utilizarlo, debes tener el sistema operativo Windows. En este artículo, te voy a enseñar cómo puedes acceder a los datos de ticks de MetaTrader desde un programa de Python utilizando sockets.

Con la introducción de los sockets de Berkeley en el sistema operativo Unix 4.2BSD en 1983, la comunicación entre máquinas se hizo más fácil y generalizada. Todas las bibliotecas y protocolos más avanzados de la capa de aplicación dependen de los protocolos de socket de la capa de transporte, que es lo que vamos a utilizar en este artículo.

A modo de demostración, voy a transferir únicamente datos de ticks (precio bid, precio ask y hora de los datos de ticks) a la aplicación de Python a través de puertos de socket para su uso en la aplicación de Python. Podemos exportar todo tipo de datos de gráficos, como información sobre barras, información de usuarios, información sobre operaciones, etc., utilizando la idea que se presenta en este artículo. Y para ello podemos utilizar no solo servicios, sino también scripts, indicadores o asesores expertos.


Flujo del programa

Este artículo se centra en el uso del programa de servicios de MetaTrader 5 para enviar información de cotización —como el precio bid, precio ask y hora— a un servidor Python, y en cómo dicho servidor difunde la información a todos los sockets de los clientes conectados al servidor. Esto se ve más claramente en la siguiente figura.

Flujo de datos por socket

Como se puede ver en la imagen, el programa de servicio MetaTrader está conectado a un servidor Python que escucha en el puerto 9070. Todos los datos de ticks de los gráficos abiertos en la terminal MetaTrader 5 se enviarán al servidor Python en el puerto 9070. A continuación, el servidor Python analiza los datos recibidos de MetaTrader 5, realiza los análisis necesarios sobre dichos datos y distribuye —o, mejor dicho, difunde— la información de ticks a los clientes conectados. A continuación, los clientes pueden utilizar los datos recibidos para realizar las tareas necesarias o aplicar algoritmos con el fin de obtener el resultado deseado, que puede volver a transmitirse al programa de servicios de MetaTrader para su posterior procesamiento.


¿Por qué los servicios y Python?

No hay ninguna razón concreta para elegir los servicios de MetaTrader 5 para enviar información de ticks. Los scripts, los indicadores y los asesores expertos también se pueden utilizar para realizar este tipo de tareas, y lo explicaré en mis próximos artículos. Solo quería demostrar que los datos de los gráficos y otra información relacionada con ellos se pueden transferir a otras aplicaciones mediante los servicios de MetaTrader y la programación de sockets. Y, en lugar de utilizar un servidor y clientes en Python, también podemos recurrir a otras plataformas y marcos de trabajo para desarrollar el servidor y los clientes. Python me resultaba muy práctico.


Programa de servicios

Como todos sabemos, los servicios de MetaTrader 5 solo tienen una función, OnStart, y todo debe realizarse dentro de esa función. Los servicios no están vinculados a ninguna ventana, y no es posible acceder a las funciones integradas como _Symbol, _Period, _Point y otras en los servicios, a diferencia de lo que ocurre en los scripts, los indicadores y los asesores expertos. Además, la ejecución y la gestión de los servicios solo pueden realizarse a través de la ventana Navegador. Los servicios no se pueden arrastrar a las ventanas de gráficos abiertas para ejecutarlos. Puedes ver cómo ejecutar los servicios en la sección de demostración que aparece a continuación, donde he incluido un archivo GIF.

El programa de servicios comienza definiendo las variables de socket, servidor y puerto.

int socket; string server = "localhost"; int port = 9070;

La función SocketInit() crea un identificador de socket mediante el método SocketCreate(). El identificador del socket, junto con la dirección y el puerto del servidor, se pasa a la función SocketConnect() para conectarse al servidor, la cual devuelve «true» si la conexión se establece correctamente.

void SocketInit() {    socket=SocketCreate();    bool connect = SocketConnect(socket, server, port, 1000);    if(connect) {       Print("socket is connected", " ", server, " port ", port);    } }

Cuando añadimos y ejecutamos el programa de servicios desde la ventana Navegador en la terminal MetaTrader, se invoca la función OnStart, en la que llamamos a la función SocketInit().

void OnStart() {    SocketInit();

A continuación, se definen tres variables: la primera para almacenar la información de los ticks del gráfico abierto, la segunda para almacenar el ID del gráfico y la tercera para almacenar la información de los ticks que se enviará al servidor.

MqlTick latestTick; long next; string payload;

Dado que tenemos que enviar constantemente información de ticks al servidor, se define un bucle «while» con «true» y, a continuación, se comprueba si el servidor está conectado. Si la conexión del socket no se ha establecido correctamente, la función SocketIsConnected(identificador del socket) devolverá «false» y el servicio se detendrá. 

while(true) {
   if(!SocketIsConnected(socket)){
      Print("socket is not initialized yet so stopping the service");
      break;
   }

Las variables «next» y «payload» se inicializan con el primer ID del gráfico y una cadena vacía.

next = ChartFirst();
payload = "";

A continuación, se ejecuta otro bucle «while» para recorrer todas las ventanas de gráficos y obtener el símbolo del gráfico.

while (next != -1) {
   string chartSymbol = ChartSymbol(next);

Se extraen los datos más recientes de los ticks, como el precio bid, precio ask y hora, para el gráfico. 

SymbolInfoTick(chartSymbol, latestTick);
double bid = latestTick.bid;
double ask = latestTick.ask;
string tickTime = TimeToString(latestTick.time, TIME_SECONDS);

Se genera una carga útil para enviarla al servidor, utilizando los valores de símbolo, hora, precio bid / precio ask. He intentado crear un valor de cadena JSON para la carga útil, con el fin de poder utilizar la biblioteca JSON del servidor Python para descodificar la cadena JSON y convertirla en un objeto JSON, y así poder extraer los datos. Puedes utilizar el formato que te resulte más cómodo.

bool stringAdded = StringAdd(payload, StringFormat("{\"pair\": \"%s\", \"time\": \"%s\", \"bid\": %f, \"ask\": %f}", chartSymbol, tickTime, bid, ask));

Si hay más ventanas de gráficos abiertas, se añade la información de los ticks de la siguiente ventana de gráfico utilizando #@# como separador.

next = ChartNext(next);
if (next != -1 && stringAdded) {
   stringAdded = StringAdd(payload, "#@#");
}

Una vez que se han analizado todas las ventanas de gráficos abiertas en busca de datos de ticks y se ha completado la carga útil, esta está lista para enviarse al servidor. El método `SocketSend` envía los datos al puerto del socket definido en el identificador del socket. Asegúrate de que el valor del campo «longitud» sea correcto; de lo contrario, se enviarán caracteres adicionales junto con los datos, lo que supondría una carga extra para el servidor a la hora de procesar los datos.

uchar data[];
int len = StringToCharArray(payload, data);
SocketSend(socket, data, len-1);

Y el bucle continúa, reiniciándose desde la primera ventana de gráfico abierta, hasta que la conexión con el servidor esté abierta.


Programa de servidor en Python

Tal y como se describe en el flujo del programa, ese servidor debe estar a la escucha para recibir información de ticks en el puerto 9070 desde la terminal MetaTrader, y también a la escucha de los clientes para enviarles la información de ticks recibida en el puerto 9071. Por tanto, se crean dos sockets vinculados a esos puertos y se mantienen a la escucha.

host = '127.0.0.1'
MT5_RECIEVING_PORT = 9070
CLIENT_SENDING_PORT = 9071
mt5Socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
mt5Socket.bind((host, MT5_RECIEVING_PORT))
mt5Socket.listen()
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientSocket.bind((host, CLIENT_SENDING_PORT))
clientSocket.listen()

He programado el servidor de tal manera que solo se permite la conexión y la comunicación de un único MetaTrader 5. Esto se hace para evitar problemas de gestión y situaciones caóticas que puedan surgir al establecer varias conexiones a MetaTrader 5. Por otra parte, los clientes recibirán los datos de ticks para su posterior procesamiento; esos mismos datos pueden ser utilizados por distintos clientes para aplicar diferentes algoritmos con el fin de obtener el resultado deseado. Por lo tanto, es necesario gestionar las conexiones de los clientes.

El siguiente diccionario se utiliza para almacenar los clientes conectados activos con el fin de gestionar dichos clientes desde el lado del servidor.

connectedClients = {}

MetaTrader y el flujo de clientes se inician como subprocesos independientes, de modo que puedan ejecutarse de forma independiente entre sí.

if __name__ == "__main__":

   thread = threading.Thread(target=acceptFromMt5)
   thread.start()
   thread = threading.Thread(target=acceptFromClients)
   thread.start()

El subproceso de MetaTrader 5 sigue a la espera de que el servicio de MetaTrader se conecte a él. Una vez aceptada la conexión, se invoca la función processMt5Data, donde los datos se reciben, se procesan y se envían a los clientes correspondientes. Hablaré más sobre esto más adelante.

Si se produce un error al aceptar una conexión del servicio MetaTrader, se esperará a la siguiente conexión.

def acceptFromMt5():
   try:
       print("Server is listening for mt5")
       mt5Client, address = mt5Socket.accept()
       print(f"mt5 service is connected at {str(address)}")
       processMt5Data(mt5Client)

   except Exception as ex:
       print(f"error in accepting mt5 client {ex}")
       acceptFromMt5()

Antes de abordar el análisis de la información de ticks, es fundamental comprender cómo se conectan y gestionan los clientes. Al igual que en el servicio MetaTrader 5, el servidor espera a que se conecte un nuevo cliente. El cliente debe enviar su identificación para que pueda gestionarse en el servidor. La identificación es el par de símbolos de la ventana del gráfico. Esta identificación se utiliza como clave del objeto «dict», y el cliente se añade a ese diccionario. Se utiliza un bucle «while» para que puedan conectarse muchos clientes.

def acceptFromClients():
   try:
       while True:

           print("Server is listening for clients")
           client, address = clientSocket.accept()
           pair = client.recv(7).decode("ascii")
           print(f"{pair} client service is connected at {str(address)}")
           clients = connectedClients[pair] if connectedClients and pair in connectedClients.keys() else []
           clients.append(client)
           connectedClients[pair] = clients

   except Exception as ex:
       print(f"error in accepting other clients {ex}")
       acceptFromClients()

Ahora que ya sabemos cómo se registran los nuevos clientes conectados, resulta fácil comprender cómo se distribuye a todos los clientes conectados. Los datos de los ticks se convierten en un objeto JSON y se extrae el símbolo de este. Además, la información de ticks se transmite a los clientes en función de la clave de identificación que estos envían al servidor al conectarse. 

Los datos de ticks se dividen utilizando el mismo separador que se emplea en el programa de servicios de MetaTrader 5. Es necesario realizar una división adicional si se envían varios datos de ticks de un gráfico de una sola vez. La segunda división se llevó a cabo porque la biblioteca de Python para el análisis de JSON no era capaz de procesar la información de los ticks si el servicio de MetaTrader enviaba varios datos de ticks a la vez al servidor.

def processMt5Data(mt5Client):
   data = "mt5 client connected"
   repeatativeEmpty = 0
   while(len(data) > 0):

       try:
          data = mt5Client.recv(1024000).decode("ascii")

           if len(data) > 0:
               for jsn in data.split("#@#"):

                   if "}{" in jsn:
                       splittedTickData = jsn.split("}{")
                       jsn = splittedTickData[0] + "}"

                   jsonTickData = json.loads(jsn)
                   pair = jsonTickData["pair"]

                   if pair in connectedClients.keys():
                       broadcastToClients(pair, jsonTickData)
           repeatativeEmpty = repeatativeEmpty + 1 if len(data) == 0 else 0

           if repeatativeEmpty > 10:
               print(f"data is not recieved for 10 times in a row {data}")
               break

       except Exception as ex:
           print(f"error in processing mt5 data {ex}")
           break

       time.sleep(0.1)
   acceptFromMt5()

Enviar datos a los clientes es muy sencillo. Si se produce un error al enviar datos, el cliente se elimina de la lista de clientes conectados, dando por hecho que ya no está conectado, y se actualiza el diccionario.

def broadcastToClients(pair, message):
   for client in connectedClients[pair]:

       try:
           client.send(str(message).encode("ascii"))

       except Exception as ex:
           print(f"error while sending {message} to {client} for {pair}")
           client.close()
           clients = connectedClients[pair]
           clients.remove(client)
           connectedClients[pair] = clients
Y con esto queda completado el programa del servidor.


Programa cliente en Python

El programa cliente se conecta al servidor en el puerto 9071, tal y como ya se ha comentado en el flujo del programa. Y, como ya se ha mencionado anteriormente, para gestionar correctamente los clientes en el servidor, es necesario disponer en el servidor de una clave que represente el par de símbolo/divisa. Por lo tanto, necesitamos opciones que permitan mostrar la lista de claves disponibles para seleccionar la identificación que se enviará al servidor.

CURRENCY_PAIRS = [

   "AUDUSD",
   "AUDJPY",
   "AUDCAD",
   "AUDNZD",
   "AUDCHF",
   "CADJPY",
   "CADCHF",
   "CHFJPY",
   "EURUSD",
   "EURJPY",
   "EURGBP",
   "EURCAD",
   "EURAUD",
   "EURNZD",
   "EURCHF",
   "GBPUSD",
   "GBPJPY",
   "GBPCAD",
   "GBPAUD",
   "GBPNZD",
   "GBPCHF",
   "NZDUSD",
   "NZDJPY",
   "NZDCAD",
   "USDCHF",
   "USDJPY",
   "USDCAD",
   "NZDCHF"
]
server = "127.0.0.1"
port = 9071

Como se puede ver, una vez establecida la conexión, se selecciona una opción de la lista anterior y se envía al servidor, tras lo cual se espera a que lleguen los datos del servidor. 

if __name__ == "__main__":
   print(f"please choose from the currency pairs given below as name \n {', '.join(CURRENCY_PAIRS)}")
   name = input("enter the name for the client : ")

   if name in CURRENCY_PAIRS:
       client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

       if client.connect_ex((server, port)) == 0:
           client.send(name.encode("ascii"))
           receiveData(client)

       else:
           print("server could not be connected.")

   else:
       print("you didn't choose from the above list")

El programa cliente que he escrito solo muestra los datos de ticks, pero podemos hacer lo que queramos con ellos según nuestras necesidades, como procesar los datos más a fondo utilizando los marcos y bibliotecas disponibles, aplicar bibliotecas de aprendizaje automático, etc.

def receiveData(client):
   repeatativeEmpty = 0

   while True:
       data = client.recv(1024).decode("ascii")
       print("data received ", data)
       repeatativeEmpty = repeatativeEmpty + 1 if len(data) == 0 else 0

       if repeatativeEmpty > 10:
           print(f"data is not recieved for 10 times in a row {data}")

           break

Y con esto queda completado el programa cliente.


Demostración del flujo de datos

Para que la transformación de datos se realice sin problemas, es necesario iniciar primero el servidor; de lo contrario, ninguno de los servicios funcionaría. A efectos de prueba, el servidor Python se ejecuta en un entorno virtual, como se puede ver en la siguiente figura.

El servidor se ha iniciado

Necesitamos que el servidor reciba los datos de ticks y los transmita a los clientes, por lo que la segunda tarea consiste en iniciar el programa de servicios de MetaTrader, tal y como se muestra en la siguiente captura de pantalla.

Se ha iniciado el servicio MetaTrader

Si el servicio MetaTrader no está en ejecución, los clientes conectados tendrían que esperar a recibir los datos. En cuanto un cliente envía las claves de identificación al servidor y si se reciben datos de ticks procedentes de los servicios de MetaTrader, por el momento esos datos se muestran en la terminal del cliente. Esto se hace con el EURUSD y el GBPUSD, ya que en ese momento ambas ventanas de gráficos están abiertas en la plataforma MetaTrader 5. Esto se puede ver claramente en las siguientes capturas de pantalla.

Se ha iniciado el cliente EURUSD

Se ha iniciado el cliente GBPUSD

He intentado plasmar todo el proceso que se muestra en las capturas de pantalla anteriores en un GIF para que resulte más claro.

Proceso completo de transferencia de datos de ticks desde MetaTrader a Python


Conclusión

En este artículo se explica cómo utilizar la programación de sockets para enviar datos de ticks desde MetaTrader 5 a una aplicación de Python mediante los servicios de MetaTrader y un servidor y cliente de Python. Dado que podemos transferir datos de ticks tal y como se ha hecho en este artículo, también podemos transferir otra información relacionada con los gráficos. He utilizado servicios, pero también podemos recurrir a scripts, indicadores o asesores expertos. Y, al igual que Python, también podemos utilizar otros lenguajes de programación.

Además, este artículo también busca sortear la dependencia de Windows. MetaTrader y las bibliotecas de Python, como MetaTrader 5, dependen todas del sistema operativo Windows. En lugar de utilizar la biblioteca Python de MetaTrader 5, podemos recurrir al protocolo de sockets para la transferencia de datos desde MetaTrader a una biblioteca más adecuada que permita un procesamiento sencillo y avanzado de los datos de MetaTrader.

Nombre del archivoDescripción
TickSocketService.mq5
Archivo MQL que contiene códigos para conectarse al servidor de sockets en el puerto 9070 y luego enviar datos de tick.
tick_server.pyEl servidor Python abre el puerto 9070 para MetaTrader y el puerto 9071 para otros clientes.
tick_client.pyEl cliente Python se conecta mediante socket al puerto 9071 y recibe los datos enviados por el servidor.

Traducción del inglés realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/en/articles/18680

Archivos adjuntos |
tick_server.py (2.86 KB)
tick_client.py (1.32 KB)
Yevgeniy Koshtenko
Yevgeniy Koshtenko | 5 ago 2025 en 16:51
Interesante trabajo. ¿Es posible crear de esta forma un servidor de gestión de riesgos para una red de terminales que lo escuchen?
Alain Verleyen
Alain Verleyen | 5 ago 2025 en 23:02
Buen y útil artículo, enhorabuena.
Ramesh Maharjan
Ramesh Maharjan | 8 ago 2025 en 17:23
Alain Verleyen #:
Bonito y útil artículo, enhorabuena.

Gracias

Ramesh Maharjan
Ramesh Maharjan | 8 ago 2025 en 17:26
Yevgeniy Koshtenko #:
Interesante trabajo. ¿Es posible crear de este modo un servidor de gestión de riesgos para una red de terminales que lo escuchen?

sí, es posible

Delane Tendai Nyaruni
Delane Tendai Nyaruni | 8 ago 2025 en 21:32
Yevgeniy Koshtenko #:
Interesante trabajo. ¿Es posible crear de esta forma un servidor de gestión de riesgos para una red de terminales que lo escuchen?
Sí, es posible, depende de la latencia que pueda tolerar.
Procesos gaussianos en el aprendizaje automático (Parte 1): Modelo de clasificación en MQL5 Procesos gaussianos en el aprendizaje automático (Parte 1): Modelo de clasificación en MQL5
En este artículo, analizaremos el modelo de clasificación de procesos gaussianos. Comenzaremos estudiando sus principios teóricos y luego pasaremos al desarrollo práctico de la biblioteca GP en MQL5.
Redes neuronales en el trading: Modelo multivariado de extremo a extremo para la predicción de series temporales (GinAR) Redes neuronales en el trading: Modelo multivariado de extremo a extremo para la predicción de series temporales (GinAR)
Le invitamos a explorar un enfoque innovador para la previsión de series temporales con datos faltantes usando el framework GinAR. El artículo muestra la implementación de componentes clave en OpenCL, lo que garantiza un alto rendimiento. En este artículo, analizaremos con detalle la integración de estas soluciones en MQL5. Esto nos permitirá comprender cómo aplicar el método en la práctica en el trading.
Particularidades del trabajo con números del tipo double en MQL4 Particularidades del trabajo con números del tipo double en MQL4
En estos apuntes hemos reunido consejos para resolver los errores más frecuentes al trabajar con números del tipo double en los programas en MQL4.
De novato a experto: Noticias animadas utilizando MQL5 (VII) Estrategia para el trading de noticias tras el impacto De novato a experto: Noticias animadas utilizando MQL5 (VII) Estrategia para el trading de noticias tras el impacto
El riesgo de que se produzcan movimientos bruscos es extremadamente alto durante el primer minuto tras la publicación de una noticia económica de gran repercusión. En ese breve lapso de tiempo, los movimientos de los precios pueden ser erráticos y volátiles, lo que a menudo activa ambos lados de las órdenes pendientes. Poco después de la publicación —normalmente en menos de un minuto—, el mercado tiende a estabilizarse, reanudando o corrigiendo la tendencia predominante con una volatilidad más habitual. En esta sección, analizaremos un enfoque alternativo al trading basado en noticias, con el objetivo de evaluar si puede convertirse en una herramienta útil dentro del conjunto de recursos del trader. Sigue leyendo para conocer más información y detalles sobre este tema.