English Deutsch 日本語
preview
Desarrollando un cliente MQTT para MetaTrader 5: un enfoque TDD - Final

Desarrollando un cliente MQTT para MetaTrader 5: un enfoque TDD - Final

MetaTrader 5Integración | 16 agosto 2024, 11:22
53 0
Jocimar Lopes
Jocimar Lopes
"Nuestra meta es siempre trabajar en el nivel más alto de abstracción posible, dado un problema y las restricciones en su solución." (Bjarne Stroustrup, Programming Principles and Practice Using C++)

Introducción

Dado que éste es el último artículo de esta serie, quizá sería útil, o al menos conveniente, hacer un breve resumen.

En el primer artículo de esta serie, vimos que MQTT es un protocolo de intercambio de mensajes basado en el modelo de interacción publicar/suscribir (pub/sub) que puede ser útil en un entorno de trading al permitir al usuario compartir cualquier tipo de datos en tiempo real: transacciones de trading, información de cuentas, datos estadísticos para ingestión por un pipeline de machine learning, en texto plano, XML, JSON, o datos binarios, incluyendo imágenes. MQTT es ligero, resistente a las inestabilidades o interrupciones de la red e independiente del contenido. Además, el protocolo es maduro, está probado y es una norma abierta mantenida por OASIS. Sus dos versiones más utilizadas, la anterior 3.1.1 y la actual 5.0, figuran entre los protocolos más utilizados para conectar un número prácticamente ilimitado de dispositivos en la llamada Internet de las Cosas. MQTT puede utilizarse en cualquier situación en la que sea necesario compartir datos en tiempo real entre máquinas desacopladas.

Hay muchos brokers MQTT disponibles para entornos de desarrollo, pruebas y producción, tanto de código abierto como comerciales, y un montón de clientes MQTT para prácticamente cualquier lenguaje de programación moderno. Puedes consultarlas en esta lista de software MQTT que incluye brokers, librerías y herramientas.

En el segundo artículo de esta serie, describimos la organización de nuestro código para el desarrollo de este cliente y comentamos algunas decisiones de diseño iniciales, como el uso del paradigma orientado a objetos. La mayor parte de ese código cambió en nuestra primera gran refactorización, pero la funcionalidad sigue siendo la misma. En ese artículo, también describimos algunas conexiones que hicimos con un broker local corriendo en el Subsistema Windows para Linux (WSL) sólo para darnos cuenta de que nuestra clase CONNECT estaba generando paquetes malos. Lo mejoramos y lo comunicamos en el siguiente artículo.

El tercer artículo de esta serie estuvo dedicado a tomar algunas notas sobre la parte de comportamiento operativo del protocolo y su relación con las banderas CONNECT. Describimos la semántica de esas banderas y cómo las configuramos. También tomamos algunas notas sobre la práctica del desarrollo basado en pruebas que estamos utilizando para este proyecto. Por último, explicamos cómo estamos probando los métodos protegidos de nuestras clases.

Profundizamos en la importancia de las propiedades de MQTT 5.0 en el cuarto artículo de esta serie. Comentamos cada uno de ellos y sus respectivos tipos de datos, o representación de datos en jerga MQTT. Allí tomamos algunas notas sobre cómo las propiedades de MQTT 5.0, en particular la(s) propiedad(es) de usuario, pueden ser utilizadas para extender el protocolo.

El paquete de control PUBLISH y sus banderas de cabecera fijas únicas (no reservadas) fueron el tema del quinto artículo de esta serie. Hemos dedicado mucho espacio a mostrar cómo funcionan estas banderas de cabecera fija PUBLISH a nivel de bits. Marcamos las características de los diferentes niveles de Calidad de Servicio (Quality of Service) MQTT (QoS 0, QoS 1 y QoS 2) con algunos diagramas ilustrativos que muestran el intercambio de paquetes entre el cliente y el broker en cada una de estas QoS.

Un intermedio fue descrito en nuestro sexto artículo de esta serie. Fue nuestra primera gran refactorización de código. Hemos cambiado el plano de nuestras clases de paquetes de control y eliminado funciones duplicadas y código de prueba obsoleto. Ese artículo es principalmente una documentación de estos cambios con algunas notas sobre el Paquete de Control PUBACK. La semántica de PUBACK Reason Codes con sus respectivas Reason Strings están anotadas en ese artículo.

Finalmente, en esta séptima y última parte queremos compartir con ustedes un código de trabajo que pretende resolver una necesidad muy común de los traders cuando se trata de la construcción de señales de indicadores para ser usadas en Asesores Expertos: la falta de un símbolo requerido para el indicador en la cuenta de trading.

Sugerimos una posible solución utilizando símbolos personalizados y un par de clientes MQTT ejecutándose como servicios en el terminal Metatrader 5. Aunque el código de la demostración está simplificado en exceso y se ejecuta en una única instancia de terminal, debido a la principal característica del propio protocolo MQTT -que es el desacoplamiento entre el emisor y el receptor mediante una mediación «broker»-, esta solución puede ampliarse para dar cabida a cualquier número de instancias de dispositivos y símbolos.

Al final del artículo, indicamos el estado actual de la biblioteca, nuestras prioridades de desarrollo con una posible hoja de ruta, y dónde puede seguir y contribuir al proyecto.

En las descripciones que siguen, utilizamos los términos MUST y MAY tal y como los utiliza el estándar OASIS, que a su vez los utiliza tal y como se describen en IETF RFC 2119.

Asimismo, a menos que se indique lo contrario, todas las citas proceden de la Norma OASIS.


Necesidades del Trader

Supongamos que usted, como Trader, está especializado en criptomonedas. Aprendiste que existe una correlación negativa consistente entre el S&P 500 y una memecoin oscura y casi desconocida llamada AncapCoin. Has observado que cuando el S&P 500 sube, AncapCoin baja, y viceversa. Este conocimiento le da una ventaja en los mercados y ha estado ganando dinero operando con AncapCoin siguiendo su correlación negativa con S&P 500. Ahora, para maximizar sus ganancias, desea automatizar sus operaciones, eventualmente ejecutando su Asesor Experto 24/7 en un VPS. Todo lo que necesitas es un indicador básico del S&P 500 para respaldar tus decisiones de compra y venta en tu EA (Asesor Experto).

Pero AncapBroker también se especializa en cripto. Entonces, no tienen el S&P 500 entre sus símbolos. Y los brokers que ofrecen S&P 500 no ofrecen tu adorable AncapCoin. ¿Cómo podría tener su indicador S&P 500 funcionando en su cuenta de trading proporcionada por AncapBroker?

Aunque AncapBroker y AncapCoin son nombres inventados, este escenario ficticio no lo es en absoluto. Comúnmente, un operador que opera con índices apreciaría tener un indicador hecho de un compuesto de acciones seleccionadas que no son ofrecidas por el corredor que ofrece los índices y viceversa. Considere las materias primas que están disponibles para su negociación en una bolsa centralizada y sus respectivos CFD disponibles para su negociación en los corredores de CFD y tendrá un desajuste típico en el que el operador tiene acceso legítimo a los datos, pero los datos provienen de diferentes proveedores. En el caso de la negociación manual y discrecional, no hay problema. Con un monitor separado, o incluso con una ventana separada en el mismo monitor, puedes seguir el mercado del símbolo que falta. Pero cuando se trata de automatización, incluso cuando ambos proveedores ofrecen MetaTrader 5, es muy difícil, si no prácticamente imposible para el operador minorista medio, disponer de las cotizaciones en tiempo real en el lugar donde se necesitan para tomar la decisión de operar: la cuenta de trading.

Como desarrollador, si tomas este escenario como un requisito del cliente, podrías considerar el patrón de mensajería de publicación/suscripción como una opción para abordar este requisito. Entre las especificaciones de publicación/suscripción disponibles que son abiertas y bien mantenidas, MQTT es probablemente una de las más simples, económicas y robustas. Para ser absolutamente claro, MQTT fue diseñado precisamente para abordar el tipo de requisito descrito anteriormente en nuestro escenario ficticio, es decir, para recopilar y distribuir datos de varias fuentes, posiblemente dispersas geográficamente, en tiempo real y con un mínimo de carga en la red.

En las secciones siguientes veremos cómo puede implementar esta solución en MetaTrader 5, utilizando nuestro cliente en su estado actual, hasta el punto de que tendrá las cotizaciones de su cuenta fuente de datos fluyendo en tiempo real a su cuenta de trading. Allí estará disponible para construir ese indicador requerido.

En la cuenta de trading, vamos a:

  1. Crear un símbolo personalizado para representar el S&P 500 que nos falta.
  2. Escriba un servicio MQTT SUBSCRIBE para recibir las cotizaciones de S&P 500 del broker MQTT.
  3. Actualiza nuestro símbolo personalizado que representa el S&P 500 utilizando funciones de MQL5.

En la cuenta de origen de datos, vamos a:

  1. Escribir un servicio MQTT PUBLISH para recoger las cotizaciones del S&P 500 y enviarlas a un broker MQTT.

Por favor, tenga en cuenta que para simplificar y abreviar, estamos hablando de una cuenta de negociación y una cuenta de fuente de datos, pero el número de estas cuentas es teóricamente ilimitado. En la práctica, el número de cuentas conectadas en ambos lados sólo está limitado por los límites físicos de los dispositivos (memoria, CPU, ancho de banda de la red, etc.) y por los límites impuestos por tu broker MQTT.

Además, tenga en cuenta que nuestro objetivo no es ofrecerle una solución lista para la producción. En su lugar, nuestro objetivo es mostrarle los principales pasos necesarios para una posible implementación y, por supuesto, impulsar su interés en los usos potenciales del patrón pub/sub de MQTT para sus operaciones comerciales o para ofrecer aplicaciones personalizadas a sus clientes. Estamos convencidos de que va mucho más allá de copiar citas de una memecoin.


Crear un símbolo personalizado

Un símbolo personalizado es un símbolo que usted crea con sus especificaciones deseadas o requeridas y lo actualiza con las cotizaciones que usted proporciona. Dado que usted tiene el control sobre sus especificaciones y cotizaciones, los símbolos personalizados son útiles para crear índices compuestos y para probar Asesores Expertos e indicadores.

Puede crear un símbolo personalizado utilizando la interfaz gráfica de usuario del editor de MetaTrader 5, o mediante programación a través de funciones MQL5 específicas. La documentación contiene instrucciones detalladas sobre cómo crear, personalizar y utilizar símbolos personalizados mediante programación o a través de la interfaz gráfica de usuario. Para nuestros propósitos aquí, la interfaz gráfica de usuario es suficiente.

En el MetaTrader 5 que aloja su cuenta de trading, haga clic en Ver > Símbolos > Crear símbolo personalizado.

MetaTrader 5 - Creación de un símbolo personalizado utilizando la interfaz gráfica de usuario

Fig. 01 - MetaTrader 5 - Creación de un símbolo personalizado mediante la interfaz gráfica de usuario

En la ventana que aparecerá a continuación, en el campo «Copiar de:», elija el símbolo S&P 500 para crear un símbolo personalizado basado en el índice real. Introduzca un nombre y una breve descripción para su símbolo personalizado.

Establecer las especificaciones de un símbolo personalizado mediante la interfaz gráfica de usuario

Fig. 02 - MetaTrader 5 - Establecimiento de las especificaciones de un símbolo personalizado mediante la interfaz gráfica de usuario

ADVERTENCIA: El lector atento habrá observado que estamos creando nuestro símbolo personalizado en la cuenta de trading, pero según nuestro ejemplo, esta cuenta no tiene el símbolo S&P 500 que se utilizará como modelo para nuestro símbolo personalizado. Deberíamos estar en la cuenta fuente de datos. Al fin y al cabo, lo que estamos haciendo aquí es precisamente porque no tenemos el S&P 500 disponible en la cuenta de trading. ¡Y tienes razón! En una situación real, necesitarás completar estas especificaciones de símbolos de acuerdo a tus necesidades, probablemente copiando manualmente las especificaciones del S&P 500. Estamos simplificando demasiado porque no nos centramos en la creación de símbolos personalizados. En su lugar, nos interesa conectar las cuentas a través de MQTT. Si necesita adaptar el símbolo a su situación real, consulte la documentación a la que se hace referencia más arriba.

Tras hacer clic en Aceptar, el símbolo personalizado recién creado debería aparecer en la lista de árbol de la izquierda.

Comprobación del árbol de símbolos tras la creación de un símbolo personalizado mediante la interfaz gráfica de usuario

Fig. 03 - MetaTrader 5 - Comprobación del árbol de símbolos tras la creación de un símbolo personalizado mediante la interfaz gráfica de usuario

A continuación, añádalo a su Observación del Mercado para que esté disponible para las visualizaciones de gráficos. Este paso es necesario para las actualizaciones de ticks. 

«La función CustomTicksAdd sólo funciona para símbolos personalizados abiertos en la ventana Observación del Mercado. Si el símbolo no está seleccionado en Observación del Mercado, entonces debe añadir ticks usando CustomTicksReplace.» (Documentación de referencia MQL5)<

MetaTrader 5 - Comprobación de la ventana de Observación del Mercado tras la creación de un símbolo personalizado mediante la interfaz gráfica de usuario

Fig. 04 - MetaTrader 5 - Comprobación de la ventana de Observación del Mercado tras la creación de un símbolo personalizado mediante la interfaz gráfica de usuario

En la ventana Observación del Mercado, observará que aún no tiene cotizaciones. Así que, vamos a suscribir esta cuenta al broker MQTT que enviará cotizaciones en tiempo real para actualizar nuestro recién creado símbolo personalizado MySPX500.


Escribir un servicio MQTT SUBSCRIBE

En la fase actual, nuestro cliente puede suscribirse con QoS 0 y QoS 1, pero para actualizar cotizaciones/ticks pensamos que QoS 0 es suficiente porque un tick eventualmente perdido no es crítico en este contexto. Estamos enviando un tick cada 500ms, por lo que si uno u otro se pierde, su posición será ocupada inmediatamente por el siguiente.

Comenzamos nuestro código para el servicio de suscripción con parámetros de entrada para el host y el puerto del broker. Recuerde sustituir esto por los datos reales de su corredor. Vea en la siguiente sección algunas notas sobre cómo configurar su entorno de desarrollo/pruebas con un broker local.

#property service
//--- input parameters
input string   host = "172.20.106.92";
input int      port = 80;

A continuación, declaramos algunas variables globales para nuestros objetos de las clases Connect y Subscribe. Necesitaremos que estas variables estén disponibles para su borrado (limpieza) cuando se detenga o vuelva de un error.

//--- global vars
int skt;
CConnect *conn;
CSubscribe *sub;

“Todos los objetos creados mediante la expresión object_pointer=new Class_name deben ser eliminados posteriormente con el operador delete(object_pointer).” (Documentación de referencia de MQL5)

En el método OnStart() de nuestro servicio, configuramos un objeto de conexión para que sea una conexión de Inicio Limpio (Clean Start), lo que significa que no se utilizará ninguna sesión previa con el broker. Además, establecemos un valor de Keep Alive generoso para evitar la necesidad de enviar solicitudes de ping periódicas mientras estamos en desarrollo. Luego, configuramos el identificador de nuestro cliente y, finalmente, construimos nuestro paquete CONNECT. Asegúrese de establecer un identificador de cliente distinto del utilizado en su servicio de publicación.

   uchar conn_pkt[];
   conn = new CConnect(host, port);
   conn.SetCleanStart(true);
   conn.SetKeepAlive(3600);
   conn.SetClientIdentifier("MT5_SUB");
   conn.Build(conn_pkt);

En el mismo método OnStart(), también configuramos y construimos nuestro paquete de suscripción (subscribe packet) con su Filtro de Tópico (Topic Filter). Para ser claros e intuitivos, estamos utilizando el nombre que elegimos para nuestro símbolo personalizado como el Filtro de Tópico (Topic Filter). Esto es arbitrario, siempre y cuando utilices el mismo Filtro de Tópico (Topic Filter) en tu servicio de publicación, por supuesto.

  uchar sub_pkt[];
  sub = new CSubscribe();
  sub.SetTopicFilter("MySPX500");
  sub.Build(sub_pkt);

Finalmente, llamamos a las funciones para enviar ambos paquetes en secuencia, delegando el manejo de errores a esas funciones y devolviendo -1 si algo sale mal con nuestra solicitud de suscripción.

if(SendConnect(host, port, conn_pkt) == 0)
     {
      Print("Client connected ", host);
     }
   if(!SendSubscribe(sub_pkt))
     {
      return -1;
     }

La función SendConnect tiene solo unas pocas líneas de código relacionado con MQTT. La mayor parte está relacionada con redes/sockets, y no entraremos en detalles aquí. En su lugar, consulta la documentación sobre Funciones de Red. Si quieres profundizar en el conocimiento de las Funciones de Red MQL5, te recomendamos encarecidamente los capítulos relacionados en el AlgoBook, donde encontrarás explicaciones detalladas y ejemplos útiles tanto para redes simples como seguras (TLS) con MQL5.

En el AlgoBook encontrará información como el siguiente extracto, que nos ayudó a identificar y hacer una solución para un comportamiento intermitente y no determinista en nuestra función (ver el código comentado en los archivos adjuntos).

"Los programadores familiarizados con las API del sistema de sockets de Windows/Linux saben que un valor 0 también puede ser un estado normal cuando no hay datos entrantes en el búfer interno del socket. Sin embargo, esta función se comporta de manera diferente en MQL5. Con un búfer de socket del sistema vacío, devuelve especulativamente 1, aplazando la comprobación real de la disponibilidad de datos hasta la siguiente llamada a una de las funciones de lectura. En particular, esta situación con un resultado ficticio de 1 byte se produce, por regla general, la primera vez que se llama a una función en un socket cuando el búfer interno de recepción aún está vacío.» (AlgoBook)

Para el lado MQTT, todo lo que hacemos en el SendConnect es comprobar la respuesta CONNACK del broker, así como el valor del Reason Code asociado.

if(rsp[0] >> 4 != CONNACK)
     {
      Print("Not Connect acknowledgment");
      CleanUp();
      return -1;
     }
   if(rsp[3] != MQTT_REASON_CODE_SUCCESS)  // Connect Return code (Connection accepted)
     {
      Print("Connection Refused");
      CleanUp();
      return -1;
     }

Como puedes ver, en ambos devolvemos -1 por error después de limpiar esos punteros dinámicos a los objetos de nuestra clase.

La misma proporción de código relacionado con redes/MQTT se aplica a la función SendSubscribe. Tras comprobar la respuesta SUBACK del broker y su respectivo Reason Code, realizamos el borrado de los punteros dinámicos de esos objetos de clase si se produce algún error.

if(((rsp[0] >> 4) & SUBACK) != SUBACK)
     {
      Print("Not Subscribe acknowledgment");
     }
   else
      Print("Subscribed");
   if(rsp[5] > 2)  // Suback Reason Code (Granted QoS 2)
     {
      Print("Subscription Refused with error code %d ", rsp[4]);
      CleanUp();
      return false;
     }

En un bucle infinito, esperamos los mensajes del broker y los leemos con la ayuda de un método estático de la clase Publish.

msg += CPublish().ReadMessageRawBytes(inpkt);
               //printf("New quote arrived for MySPX500: %s", msg);
               //UpdateRates(msg);
               printf("New tick arrived for MySPX500: %s", msg);
               UpdateTicks(msg);

Notarás que dejamos un código de desarrollo comentado para actualizar precios en lugar de ticks. Puedes descomentar estas líneas si deseas probar de esta manera. Ten en cuenta que, al actualizar solo precios y no ticks, es posible que alguna información no esté presente en la ventana de Observación del Mercado ni en el gráfico. Pero es una alternativa razonable si quieres reducir el consumo de RAM, CPU y ancho de banda y te interesan más los datos para la automatización del trading que los visuales.

La función UpdateRates trabaja en tándem con su homóloga en el servicio de publicación. Estamos pagando el precio de conversión de cadena de ida y vuelta en ambos lados mientras desarrollamos nuestra(s) propiedad(es) de usuario MQTT y tenemos un intercambio de datos binarios más fiable. Es la máxima prioridad de nuestra cuasi hoja de ruta.

void UpdateTicks(string new_ticks)
  {
   string new_ticks_arr[];

   StringSplit(new_ticks, 45, new_ticks_arr);

   MqlTick last_tick[1];

   last_tick[0].time          = StringToTime(new_ticks_arr[0]);
   last_tick[0].bid           = StringToDouble(new_ticks_arr[1]);
   last_tick[0].ask           = StringToDouble(new_ticks_arr[2]);
   last_tick[0].last          = StringToDouble(new_ticks_arr[3]);
   last_tick[0].volume        = StringToInteger(new_ticks_arr[4]);
   last_tick[0].time_msc      = StringToInteger(new_ticks_arr[5]);
   last_tick[0].flags         = (uint)StringToInteger(new_ticks_arr[6]);
   last_tick[0].volume_real   = StringToDouble(new_ticks_arr[7]);

   if(CustomTicksAdd("MySPX500", last_tick) < 1)
     {
      Print("Update ticks failed: ", _LastError);
     }
  }

Al iniciar el servicio de suscripción debería ver algo como esto en su pestaña de registro Expertos.

MetaTrader 5 - Salida de registro en la pestaña Expertos mostrando error 5270

Fig. 05 - MetaTrader 5 - Salida de registro en la pestaña Expertos mostrando error 5270

Nuestro servicio de suscripción está hablando solo en el desierto. Arreglemos esto ejecutando nuestro broker MQTT.


Configurar un broker local para desarrollo y pruebas

El entorno que estamos utilizando para nuestro desarrollo utiliza el Subsistema Windows para Linux (WSL, Windows Subsystem For Linux) en una máquina Windows. Si lo único que quieres es ejecutar los ejemplos, puedes ejecutar tanto el cliente como el broker en un loopback en la misma máquina, siempre que utilices diferentes identificadores de cliente para los servicios Publish y Subscribe. Pero si más que ejecutar los ejemplos quieres configurar un entorno de desarrollo, te recomendamos que configures una máquina independiente para ellos. Como probablemente sabes, al desarrollar aplicaciones cliente/servidor, y dado que el patrón pub/sub puede incluirse en esta arquitectura, se considera una buena práctica que cada lado (cliente y servidor) se ejecute en su propio host. De este modo, podrá solucionar antes los problemas de conexión, autenticación y otros problemas de red.

Esta configuración con WSL es bastante sencilla. Detallamos la instalación, activación y configuración de WSL en otro artículo publicado hace un año. A continuación encontrará algunos consejos específicos para el uso del corredor Mosquitto en la WSL. Se trata de pequeños detalles que nos han facilitado la vida y que quizá a usted también le resulten útiles.

  • Si activas el WSL con sus valores por defecto, e instalas el Mosquitto de la forma fácil y recomendada, probablemente lo instales usando el gestor de paquetes y lo ejecutes como un servicio de Ubuntu. Es decir, Mosquitto se iniciará automáticamente cuando inicie el intérprete de comandos WSL. Esto es bueno y conveniente para el uso regular, pero para el desarrollo, recomendamos que detengas el servicio de Mosquitto y lo relances manualmente a través de la línea de comandos con la opción verbose (-v). Esto evitará la necesidad de utilizar el comando tail para seguir los logs porque Mosquitto se ejecutará en primer plano y redirigirá todos los logs a STDOUT. Además, los registros no incluyen toda la información que obtendrás al iniciarlo con la opción verbose.

Subsistema Windows para Linux - Detener el servidor Mosquitto y reiniciarlo con el indicador Verbose

Fig. 06 - Subsistema Windows para Linux - Detener el servidor Mosquitto y reiniciarlo con el indicador Verbose

  • Recuerde que debe incluir el nombre de host WSL en las URL permitidas para la conexión en red en el terminal Metatrader 5.

Subsistema Windows para Linux - Obtención del nombre de host WSL

Fig. 07 - Subsistema Windows para Linux - Obtención del nombre de host WSL

MetaTrader 5 - Incluir URLs permitidas en el menú de opciones del terminal

Fig. 08 - MetaTrader 5 - Incluir URLs permitidas en el menú de opciones del terminal

  • En un esfuerzo por aumentar la seguridad, las versiones más recientes de Mosquitto sólo permiten conexiones locales por defecto, es decir, conexiones desde la misma máquina. Para conectarte desde otra máquina, y tu máquina con Windows se considera otra máquina en este contexto, debes incluir una línea de escucha para el puerto 1883 (u otro puerto de tu elección) en tu archivo Mosquitto.conf.

Subsistema Windows para Linux - Configuración del servidor Mosquitto para escuchar en el puerto 1883

Fig. 09 - Subsistema Windows para Linux - Configuración del servidor Mosquitto para escuchar en el puerto 1883

  • Por último, recuerde que Mosquitto se ejecutará en el puerto 1883 por defecto para conexiones no TLS. El terminal MetaTrader 5 sólo permite conexiones a los puertos 80 (HTTP) y 443 (HTTPS). Así que necesita redirigir el tráfico del puerto 80 al puerto 1883. Esto puede hacerse con un breve comando de una línea si instala una utilidad de Linux llamada redir. También puedes instalarlo con el gestor de paquetes.

Subsistema Windows para Linux - Redirección de puertos con la utilidad Redir

Fig. 10 - Subsistema Windows para Linux - Realización de la redirección de puertos mediante la utilidad Redir.

  • Si te olvidas de incluir el nombre de host WSL en la lista de URLs permitidas o de redirigir los puertos, terminarás con una conexión denegada y probablemente aparecerá un error como este en tu registro de la pestaña Expertos

MetaTrader 5 - Salida de registro que muestra el error 5273

Fig. 11 - MetaTrader 5 - Salida de registro que muestra el error 5273 - Error al enviar/recibir datos del socket

Incluso si no estás familiarizado con Linux, esta configuración no debería llevarte más de diez o quince minutos, si todo va bien. Una vez hecho esto, podrás comprobar si tu servicio de suscripción funciona como es debido.

MetaTrader 5 Salida de registro que muestra el servicio MQTT Subscribe iniciado y suscrito

Fig. 12 - MetaTrader 5 Salida de registro que muestra el servicio MQTT Subscribe iniciado y suscrito



Escribir un servicio MQTT PUBLISH

El servicio de publicación sigue la misma estructura que el servicio de suscripción, por lo que le ahorraremos tiempo y no nos repetiremos aquí. Excepto para recordarle de nuevo que utilice un identificador de cliente diferente para los servicios de publicación y suscripción. (Hacemos hincapié en este identificador de cliente porque al ignorar este pequeño detalle en la habitual rutina de copiar y pegar de los desarrolladores perezosos hemos perdido algo de tiempo depurando un «no-bug» que provocaba que Mosquitto no entregara nuestros mensajes adecuadamente).

   uchar conn_pkt[];
   conn = new CConnect(host, port);
   conn.SetCleanStart(true);
   conn.SetKeepAlive(3600);
   conn.SetClientIdentifier("MT5_PUB");
   conn.Build(conn_pkt);

La publicación se ejecuta en un bucle continuo hasta que se detiene. Recuerda configurar el mismo "Topic Filter" que estás utilizando en tu servicio de publicación, por supuesto, y si deseas actualizar las cotizaciones, en lugar de los ticks, descomentar la asignación de la carga útil a GetRates (y comentar la asignación a GetLastTick) debería ser suficiente. 

   do
     {
      uchar pub_pkt[];
      pub = new CPublish();
      pub.SetTopicName("MySPX500");
      //string payload = GetRates();
      string payload = GetLastTick();
      pub.SetPayload(payload);
      pub.Build(pub_pkt);
      delete(pub);
     //ArrayPrint(pub_pkt);
      if(!SendPublish(pub_pkt))
        {
         return -1;
         CleanUp();
        }
      ZeroMemory(pub_pkt);
      Sleep(500);
     }
   while(!IsStopped());

Las observaciones que hicimos sobre la proporción de código de red y código específico de MQTT en el servicio de suscripción también se aplican aquí. Ni siquiera necesitamos comprobar el PUBACK porque no recibiremos ninguno, ya que estamos usando QoS 0. Por tanto, sólo es cuestión de construir los paquetes, conectarse y enviarlos.

Cabe señalar que en el servicio de publicación también estamos pagando el precio de la conversión de strings, al menos hasta que nuestra(s) propiedad(es) de usuario esté(n) totalmente implementada(s) y podamos intercambiar datos binarios con confianza.

string GetLastTick()
  {
   MqlTick last_tick;
   if(SymbolInfoTick("#USSPX500", last_tick))
     {
      string format = "%G-%G-%G-%d-%I64d-%d-%G";
      string out;
      out = TimeToString(last_tick.time, TIME_SECONDS);
      out += "-" + StringFormat(format,
                                last_tick.bid, //double
                                last_tick.ask, //double
                                last_tick.last, //double
                                last_tick.volume, //ulong
                                last_tick.time_msc, //long
                                last_tick.flags, //uint
                                last_tick.volume_real);//double
      Print(last_tick.time,
            ": Bid = ", last_tick.bid,
            " Ask = ", last_tick.ask,
            " Last = ", last_tick.last,
            " Volume = ", last_tick.volume,
            " Time msc = ", last_tick.time_msc,
            " Flags = ", last_tick.flags,
            " Vol Real = ", last_tick.volume_real
           );
      Print(out);
      return out;
     }
   else
      Print("Failed to get rates for #USSPX500");
   return "";
  }

Al iniciar el servicio de publicación en el terminal de MetaTrader 5 debería ver algo como lo siguiente en su registro de la pestaña Expertos.

MetaTrader 5 Salida de registro que muestra MQTT Publish, servicio iniciado y conectado

Fig. 13 - MetaTrader 5 Salida de registro que muestra MQTT Publish, servicio iniciado y conectado

Puede suscribirse al tema en el broker Mosquitto y comprobar la salida verbose del broker.

Subsistema de Windows para Linux Mostrando el Registro del Servidor Mosquitto con la Opción Verbose

Fig. 14 - Subsistema de Windows para Linux Mostrando el Registro del Servidor Mosquitto con la Opción Verbose

Si el mensaje se ha recibido correctamente, deberías verlo en la pestaña de suscripción.

Subsistema Windows para Linux que muestra la salida de la utilidad mosquitto_sub

Fig. 15 - Subsistema Windows para Linux mostrando la salida de la utilidad mosquitto_sub


Actualizar el símbolo personalizado

Con ambos servicios instalados y con cada uno de ellos contrastado con su broker, es el momento de ejecutarlos en el terminal MetaTrader 5 y ver los resultados de su arduo trabajo.

Navegador MetaTrader 5 con servicios MQTT Publish y Subscribe Iniciados

Fig. 16 - Navegador MetaTrader 5 con servicios MQTT Publish y Subscribe iniciados

Si todo va bien, debería ver algo como lo siguiente en la pestaña de ticks de sus ventanas de Observación del Mercado.

 Pestaña de ticks en Observación del Mercado de MetaTrader 5 con actualizaciones de ticks para un símbolo personalizado

Fig. 17 - Pestaña de ticks en Observación del Mercado de MetaTrader 5 con actualizaciones de ticks para un símbolo personalizado

En su gráfico de símbolos personalizado, también deberían reflejarse las actualizaciones de los ticks.

Gráfico de Mercado en MetaTrader 5 con Actualizaciones de Ticks para un Símbolo Personalizado

Fig. 18 - Gráfico de Mercado en MetaTrader 5 con Actualizaciones de Ticks para un Símbolo Personalizado

En la pestaña de registro de Expertos, deberías encontrar una salida algo detallada. Esto se debe a que dejamos algo de información de depuración por ahora. Allí se puede ver, entre otra información, la salida de la función PrintArray para los paquetes que se están intercambiando. Esta salida puede evitar la necesidad de aplicar un analizador de paquetes, como Wireshark, para comprobar el contenido de los paquetes.

Salida del Registro de MetaTrader 5 en la Pestaña de Expertos con Registro de Servicios MQTT para Desarrollo y Depuración

Fig. 19 - Salida del Registro de MetaTrader 5 en la Pestaña de Expertos con Registro de Servicios MQTT para Desarrollo y Depuración



Conclusión

Este artículo presenta un código funcional para compartir cotizaciones en tiempo real entre brokers y cuentas vía MQTT. Aunque la demostración se ejecuta en la misma instancia y máquina de MetaTrader 5, muestra la flexibilidad y robustez de MQTT. Nuestro cliente MQTT nativo aún está en desarrollo durante nuestro tiempo libre limitado. Nuestro objetivo es disponer de una versión totalmente conforme en junio, en la que se aborden retos como la implantación de QoS_2 y las propiedades de los usuarios. Tenemos previsto perfeccionar el código y hacerlo público en GitHub a finales de abril.

Damos la bienvenida a los colaboradores de nuestro proyecto de código abierto, independientemente de sus conocimientos de MQL5. Nuestro enfoque de desarrollo basado en pruebas garantiza un progreso sin errores, incluso para los no expertos. Empezando por las pruebas básicas, hemos ido avanzando poco a poco en la documentación de MQL5. Nos comprometemos a perfeccionar nuestro cliente hasta que cumpla todos los estándares MQTT.

Únete a nosotros, incluso con conocimientos básicos. Su aportación es valiosa. ¡Bienvenido a bordo!

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

Archivos adjuntos |
mqtt-headers.zip (23.78 KB)
mqtt-services.zip (3.51 KB)
mqtt-tests.zip (19.52 KB)
Redes neuronales: así de sencillo (Parte 78): Detector de objetos basado en el Transformer (DFFT) Redes neuronales: así de sencillo (Parte 78): Detector de objetos basado en el Transformer (DFFT)
En este artículo, le propongo abordar la creación de una estrategia comercial desde una perspectiva diferente. Hoy no pronosticaremos los movimientos futuros de los precios, sino que trataremos de construir un sistema comercial basado en el análisis de datos históricos.
Características del Wizard MQL5 que debe conocer (Parte 15): Máquinas de vectores de soporte utilizando el polinomio de Newton Características del Wizard MQL5 que debe conocer (Parte 15): Máquinas de vectores de soporte utilizando el polinomio de Newton
Las máquinas de vectores de soporte clasifican los datos en función de clases predefinidas explorando los efectos de aumentar su dimensionalidad. Se trata de un método de aprendizaje supervisado bastante complejo dado su potencial para tratar datos multidimensionales. Para este artículo consideramos cómo su implementación muy básica de datos bidimensionales puede hacerse más eficientemente con el polinomio de Newton al clasificar precio-acción.
Formulación Genérica de Optimización (GOF, Generic Optimization Formulation) utilizando el `Criterio máximos del usuario` (Custom Max) con múltiples restricciones en el Probador de Estrategias Formulación Genérica de Optimización (GOF, Generic Optimization Formulation) utilizando el `Criterio máximos del usuario` (Custom Max) con múltiples restricciones en el Probador de Estrategias
En este artículo presentaremos una forma de implementar problemas de optimización con múltiples objetivos y restricciones al seleccionar «Custom Max» en la pestaña Setting del terminal MetaTrader 5. Como ejemplo, el problema de optimización podría ser: Maximizar el Factor de Beneficio, el Beneficio Neto y el Factor de Recuperación, de forma que la reducción sea inferior al 10%, el número de pérdidas consecutivas sea inferior a 5 y el número de operaciones por semana sea superior a 5.
Multibot en MetaTrader (Parte II): Plantilla dinámica mejorada Multibot en MetaTrader (Parte II): Plantilla dinámica mejorada
Desarrollando el tema del artículo anterior sobre el multibot, hemos decidido crear una plantilla más flexible y funcional, que tenga grandes posibilidades, y que se pueda utilizar eficazmente en freelance, además de como base para desarrollar asesores de divisa y periodo múltiple con posibilidad de integración con soluciones externas.