English Русский 中文 Deutsch 日本語 Português
preview
Cómo conectar MetaTrader 5 a PostgreSQL

Cómo conectar MetaTrader 5 a PostgreSQL

MetaTrader 5Integración | 3 agosto 2023, 16:37
454 0
Jocimar Lopes
Jocimar Lopes

Introducción

El desarrollo de software ha cambiado mucho en los últimos diez años. Entre otras cosas, hemos sido testigos de la popularización de la computación en la nube. Abreviaturas como IASS, PASS y SAAS son ahora herramientas clave a tener en cuenta en cualquier proyecto de desarrollo de software, y todo se ha vuelto más fácil tanto para el usuario final como para el desarrollador.

El equipo de MetaQuotes ya estaba al tanto de estos cambios y desde 2014 tenemos nuestro propio WebRequest para MetaTrader 5.

Una de las áreas que más ha cambiado es la gestión de bases de datos. Las soluciones que solían ser difíciles o incluso "extrañas" desde un punto de vista práctico se han convertido ahora no solo en factibles, sino en la solución preferida para muchos casos de uso. Esto también se aplica al acceso a bases de datos usando la API REST.

Hace un par de años, acceder a una base de datos a través de una API REST se habría percibido como algo demasiado complicado. Hoy en día, una búsqueda rápida de "base de datos administrada por rest API" arrojará docenas de proveedores con precios que van desde unos pocos dólares al mes para tarifas básicas hasta soluciones empresariales personalizadas. Muchos de estos proveedores ofrecen una completa variedad de servicios para la creación de prototipos, la realización de pruebas e incluso la implementación de capacidades a pequeña escala.

El presente artículo analizará cinco opciones estándar para conectar una base de datos de Postgres a MetaTrader 5, sus requisitos, sus ventajas y sus desventajas. También configuraremos el entorno de desarrollo, instalaremos la base de datos Postgres como una base de datos remota y nos conectaremos a ella, además, añadiremos y obtendremos datos que serán utilizados por el script MQL5 o EA.

Cualquier DBMS puede replicar fácilmente esta configuración del entorno de desarrollo y los procedimientos relacionados porque la API REST supone un nivel de abstracción entre el sistema de la base de datos y el código del cliente.


MetaTrader 5 y las bases de datos

MetaTrader 5 ya dispone de las funciones que podríamos necesitar para trabajar con , y tiene además las funciones necesarias para conectarse a una base de datos a través de .

La plataforma ofrece integración SQLite nativa desde 2020, y puede usar funciones de base de datos para interactuar con ella desde el código. Además, puede interactuar con sus bases de datos a través de una interfaz gráfica especial en MetaEditor, lo cual facilita la creación y edición de tablas, así como la realización de operaciones CRUD (Crear, Leer, Actualizar, Borrar) sin necesidad de software adicional.

Esto ha mejorado enormemente la experiencia del usuario final, sumándose al instrumental de los desarrolladores de MQL5.

Entre las docenas de DBMS disponibles, muchos de los cuales tienen licencia de código abierto, SQLite parece ser la opción más inteligente para los desarrolladores de MetaTrader 5. Aunque es una base de datos completamente funcional con índices de varias columnas, disparadores, vistas, transacciones ACID, búsquedas de texto completo, funciones añadidas, etc., sigue siendo liviana, basada en archivos, escalable y sin necesidad de mantenimiento. Como se menciona en su sitio web, "más de un billón (1e12) de bases de datos SQLite probablemente estén en uso activo".

A pesar de sus características impresionantes, SQLite está limitada en su construcción a un solo usuario y no está pensada para el acceso simultáneo en implementaciones web. El gran número de publicaciones en el foro y los artículos del sitio web de MQL5 sobre la conexión de MetaTrader 5 y MySQL demuestran la necesidad de una solución más robusta para otros casos de uso.

Este artículo tratará sobre la configuración de un entorno de desarrollo para estos casos de uso mediante Postgres.

¿Por qué Postgres?

He elegido Postgres en primer lugar porque la otra alternativa popular de código abierto, MySQL, ya se ha cubierto con detalle por aquí.

En segundo lugar, Postgres es un proyecto multiplataforma de código abierto con documentación detallada y buen soporte. Es muy popular: en Internet existen muchos ejemplos de código, así como tutoriales y manuales. Además, hay muchos proveedores de nube disponibles para todas las necesidades y presupuestos.

Postgres es de clase corporativa, sin embargo, un solo usuario es capaz de gestionarlo fácilmente trabajando en una computadora doméstica.

Y, por supuesto, he elegido Postgres porque confío en él: he estado usando Postgres para varios proyectos durante más de una década.

Por último, pero no menos importante, actualmente estoy implementando la solución que comparto aquí para mi entorno comercial personal, por lo que estoy personalmente interesado en el éxito de esta empresa y en probar el producto por mí mismo.


Cuatro formas de interactuar con Postgres desde MQL5

En mi opinión, existen cuatro enfoques principales para llamar a Postgres desde MQL5:

  1. La biblioteca/driver especial de MQL5
  2. Una .dll desde la interfaz de cliente de C++
  3. A través del driver .NET Npgsql
  4. REST API

Veamos los requisitos, las ventajas y las desventajas de cada uno. Estoy casi seguro de que la gran comunidad de desarrolladores de MQL5 experimentados ofrecerá formas sencillas de eliminar las desventajas, así como de señalar las desventajas que no he podido ver en las ventajas. Cualquier comentario de los miembros de la comunidad MQL5 será bienvenido. Creo que tanto los desarrolladores novatos como los usuarios que no se dedican al desarrollo se beneficiarán de esta discusión.


1. Driver/biblioteca especial de MQL5

La biblioteca todavía no existe. Su desarrollo requerirá muchas horas de trabajo minucioso por parte de un desarrollador de MQL5 experimentado. No saldrá barato. También deberemos considerar los costes de mantenimiento. Postgres es un proyecto maduro, pero de ninguna forma estático. Es un proyecto en desarrollo activo con lanzamientos regulares, algunos de los cuales (y posiblemente muchos) requerirán actualizaciones del código del cliente.

En el momento en que escribo este artículo, la última versión de Postgres (15) requiere que los usuarios habituales de la base de datos tengan ciertos privilegios en el "esquema público" ("public schema"). Las versiones anteriores no tenían dicho requisito. Es probable que algunas bases de código necesiten mantenimiento.

La ventaja de encargar el desarrollo de un driver MQL5 personalizado para Postgres es que, si este se comparte, puede resultar útil para muchos usuarios de MQL5. La desventaja es obvia: los costes de tiempo y dinero.

Por dónde empezar si elegimos este camino:

Una búsqueda general de MySQL en la sección "Artículos" de este sitio arrojará algunos resultados útiles.

La biblioteca de cliente C++ de código abierto libpqxx

Biblioteca de cliente C oficial para Postgres libpq


2. Una .dll desde la interfaz de cliente de C++

Es una biblioteca C++ oficial con soporte externo, libpqxx, construida sobre la biblioteca C oficial con soporte interno, libpq, provista con distribución de Postgres.

Personalmente, nunca la he utilizado. Todo lo que puedo decir es que ha existido durante mucho tiempo y parece hallarse en buenas condiciones. La desventaja de este método es que MQL5 Market no admite archivos DLL: si esto no es un problema para su proyecto y se siente cómodo trabajando con las .dll de MetaTrader, esta solución podría ser adecuada para usted.

Por dónde empezar si elegimos este camino:

Biblioteca de cliente C++ de código abierto libpqxx


3. A través del driver .NET Npgsql

Desde 2018, MetaTrader 5 tiene soporte nativo para bibliotecas .NET con importación de funciones "inteligentes". Con el lanzamiento del build 1930, las bibliotecas .NET se pueden usar sin escribir envoltorios especiales: MetaEditor hace todo por sí sola. 

Todo lo que necesitamos para usar el driver .NET Npgsql es importar la propia .dll. Existen algunas limitaciones sobre las que podrá obtener información en el mensaje oficial (https://www.mql5.com/es/forum/285795).

Por dónde empezar si elegimos este camino:

Driver Postgres con código fuente abierto para .NET


4. REST API

Si prefiere trabajar sin .dll, este enfoque será el más rápido y económico. La API se puede escribir en cualquier lenguaje y se puede entregar un prototipo funcional en un día o incluso en unas pocas horas.

Además, algunos proveedores de la nube ofrecen una API REST integrada para Postgres de forma gratuita. Todo lo que necesitamos para comenzar es un buen entorno de desarrollo para nuestro código MQL5.

Con este enfoque, nuestro código MQL5 podrá usar nuestras respuestas JSON de Postgres.

Por dónde empezar si elegimos este camino:

Por este artículo: siga leyendo, siga las instrucciones a continuación, descargue el código de muestra y comience a almacenar y consultar sus transacciones en una base de datos de Postgres.


Ajustando el entorno de desarrollo

Cualquiera que sea el método que elijamos, necesitaremos un entorno de desarrollo en una máquina Windows con un servidor Postgres en ejecución. Hay varias formas de comenzar. Enumeraré tres de ellas, desde la más difícil y lenta hasta la más simple:

  1. compilación desde la fuente
  2. contenedor Docker
  3. instalador msi de terceros

Todas estas son buenas formas de instalar Postgres en Windows, pero confíe en mí, compilar desde el código fuente en Windows debería ser la última opción, a menos que deseemos ponernos a prueba contra el estrés del desarrollo de software.

El contenedor Docker es una muy buena opción, con una instalación fiable y flexible. Nuestro servidor de base de datos se ejecutará en la máquina "remota", no en el "host local" (ver más abajo). Todo es bastante sencillo: solo necesitaremos instalar Docker, ingresar dos o tres líneas de comando y estará listo para comenzar.

Si no le tenemos miedo al software de "terceros", el instalador msi será una buena alternativa para evitar la compilación impredecible desde el origen o la instalación y administración de contenedores de Docker.

Sin embargo, no recomendaría un entorno de desarrollo para un servidor de base de datos o cualquier otro servidor como "localhost" si es posible desarrollar en un servidor ubicado en una máquina remota. Por lo general, se recomienda desarrollar, probar y depurar el servidor en un entorno remoto en lugar de un "host local" para solucionar los problemas de conectividad y autenticación lo más rápido posible.

¿Qué es WSL?

WSL significa Windows Subsystem For Linux (Subsistema de Windows para Linux).

A partir de 2016, es posible ejecutar una distribución de Linux en una máquina con Windows como subsistema. ¡No se preocupe!: no tendrá que hackear nada. El WSL ha sido desarrollado por Microsoft e integrado en Windows, solo necesitará habilitarlo. A continuación, veremos cómo hacer esto.

¿Por qué WSL?

¿Por qué no simplemente instalar Postgres en otra máquina con Windows, tal vez una máquina virtual?

Porque Postgres es un sistema Unix nativo creado y desarrollado en sistemas *nix. Después de instalarlo en Linux, obtendremos una instalación simple, así como actualizaciones y un mantenimiento sencillos. Toda la documentación oficial se centra en el sistema Unix, y la mayoría de los ejemplos de código, fragmentos y ayuda general que podemos encontrar en línea reflejan este hecho.

Por lo tanto, nos resultará fácil desarrollar en un sistema Linux. Es con este propósito que Microsoft diseñó WSL.

Instalación de WSL

Condición de la documentación de Microsoft:

"Deberá ejecutar Windows 10 de versión 2004 o posterior (compilación 19041 o posterior) o Windows 11 para usar los comandos a continuación. Si está utilizando versiones anteriores, consulte la página sobre la instalación manual".

Si su sistema cumple con esta condición, abra Power Shell como administrador e introduzca el siguiente comando para instalar/habilitar WSL:

wsl –install

comando wsl install en PowerShell

Este comando instalará Ubuntu en WSL, ya que es la distribución predeterminada. 

Reiniciamos Windows.

Aquí, todo es simple. Si fuera necesario, podemos encontrar una sección con los problemas de instalación más comunes en la documentación oficial de MS.

Después de reiniciar, deberíamos ver algo como esto. Creamos un nuevo nombre de usuario y la contraseña de UNIX.

instalando wsl después del primer reinicio


Después de instalar WSL/Ubuntu, es el momento de instalar Postgres.

Instalación de Postgres en WSL

Introducimos el comando a continuación.

sudo apt install postgresql postgresql-contrib

Este comando instalará el último paquete estable de PostgreSQL disponible en los repositorios de Ubuntu. Incluye un servidor, un cliente pgsql, cómodos archivos binarios y algunas utilidades. Eso es todo lo que necesitamos para empezar.

Si desea instalar la última versión estable de Postgres (generalmente diferente de la última versión estable en los repositorios de Ubuntu), podrá incluir el repositorio oficial de Postgres en la lista de fuentes de su administrador de paquetes. Podrá encontrar instrucciones detalladas en la documentación oficial de Postgres.

Si todo está en orden, introducimos el comando

psql --version

este deberá devolver la versión instalada de su base de datos de Postgres.


Inicio del servidor

Introducimos el siguiente comando para iniciar el servidor.

sudo service postgresql start

Por defecto, una nueva instalación de Postgres solo acepta conexiones desde localhost. Vamos a cambiar esto.

Localizamos el archivo de configuración de Postgres.

sudo -u postgres psql -c "SHOW config_file"

archivo de configuración de postgres


Editamos el archivo de configuración para aceptar conexiones fuera del host local. Cambiamos la línea listen_addresses.

línea listen_addresses en postgres


Buscamos el archivo de configuración pg_hba.

sudo -u postgres psql -c "SHOW hba_file"

archivo de configuración pg_hba en postgres


Editamos el archivo pg_hba.conf para permitir la autenticación por contraseña tanto para IPv4 como para IPv6.

autenticación por contraseña


Ahora iniciamos sesión en la utilidad psql como el usuario predeterminado de Postgres creado durante la instalación. Se llama 'postgres'.

sudo -u postgres psql


Creamos un usuario de base de datos normal con el privilegio CREATEDB. Hasta ahora solo tenemos el usuario 'postgres' creado por la instalación.

Otorgamos todos los privilegios en el esquema público al usuario mt5_user. Esto no será necesario si nuestra versión de Postgres es inferior a 15.

CREATE USER mt5_user PASSWORD '123' CREATEDB;

GRANT ALL ON SCHEMA public TO mt5_user;

otorgamos todos los privilegios a un usuario en postgres


Creamos la base de datos my_remote_db y otorgamos todos los derechos al usuario mt5_user.

GRANT ALL PRIVILEGES ON DATABASE my_remote_db TO mt5_user;

creamos la base de datos y otorgamos todos los privilegios


Conexión a Postgres

A estas alturas, deberíamos tener un servidor de base de datos ejecutándose en una máquina remota con una IP diferente a la de nuestro host local de Windows y listo para aceptar conexiones de red. Podemos conectarnos usando un socket o HTTP. Como interactuaremos con una API REST, en este ejemplo usaremos HTTP.

Veamos si podemos conectarnos a my_remote_db como mt5_user con la contraseña 123 en el host WSL.

Introducimos el siguiente comando para obtener el nombre de host WSL (IP).

hostname -I

nombre de host del comando del terminal de ubuntu


Verificamos el estado del servidor Postgres, y lo ejecutamos si es necesario. Podemos usar el siguiente comando para iniciar, reiniciar o detener el servidor.

sudo service postgresql {status, start, stop}


En el terminal MetaTrader 5, vamos a la pestaña "Herramientas" > "Configuración" > "Expertos" y añadimos la dirección IP del host WSL a la lista permitida.

Menú de configuración del terminal MetaTrader 5

El terminal MetaTrader 5 solo acepta conexiones HTTP y HTTPS en los puertos 80 y 443, respectivamente. Solo los puertos 80 y 443. Debemos tener en cuenta esta precaución al diseñar nuestra API. Normalmente, el servidor de desarrollo escucha en un puerto sin privilegios, como 3000 o 5000, antes de ir al servidor de producción real. Por lo tanto, para poder enviar solicitudes a la dirección IP que especificamos en la configuración del terminal anterior, debemos redirigir el tráfico que llega al puerto del servidor de desarrollo al puerto 80 para las solicitudes HTTP y/o 443 para solicitudes HTTPS.

Para simplificar, consultamos el archivo README de la aplicación Python adjunta para obtener instrucciones sobre cómo ejecutar esta redirección en WSL.


Ejecutando la aplicación de demostración

Como este artículo trata sobre MQL5, no discutiremos los detalles de implementación de la API. En cambio, hemos hecho una aplicación de demostración que se puede descargar e instalar como un paquete de Python para probar cómo interactúa su código MQL5 con la API.

Para ejecutar la aplicación de demostración, solo necesitamos instalar Python en WSL. Él ya debería encontrarse allí.

Le recomiendo encarecidamente crear un entorno virtual de Python ('venv') para instalar la aplicación. Esto garantizará que la instalación del sistema de Python funcione sin problemas. Una vez que hayamos trabajado con la aplicación, simplemente podremos eliminar el entorno virtual.

Podemos crear un entorno virtual con el siguiente comando.

python3 -m venv 'venv'


Por lo tanto, después de haber instalado la aplicación de demostración, deberemos:

  1. iniciar el WSL
  2. iniciar el servidor postgres
  3. iniciar la aplicación de demostración
  4. obtener la dirección IP del host de la aplicación
  5. añadir la dirección IP del host a las direcciones permitidas en el terminal

Tanto WSL como el servidor de Postgres se pueden configurar para iniciarse cuando se inicie Windows.


Insertamos los datos desde MQL5

Vamos a tratar de insertar datos. Primero, la información sobre nuestra cuenta. En nuestro terminal MetaTrader 5, creamos un nuevo script y añadimos el siguiente código.

//+------------------------------------------------------------------+
//|                                                post_acc_info.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh> //--- include the JSON library
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- gathering the data - Account Info
   CJAVal data;
   CJAVal acc_info;
//--- doubles
   data["balance"] =          AccountInfoDouble(ACCOUNT_BALANCE);
   data["credit"] =           AccountInfoDouble(ACCOUNT_CREDIT);
   data["profit"] =           AccountInfoDouble(ACCOUNT_PROFIT);
   data["equity"] =           AccountInfoDouble(ACCOUNT_EQUITY);
   data["margin"] =           AccountInfoDouble(ACCOUNT_MARGIN);
   data["margin_free"] =      AccountInfoDouble(ACCOUNT_MARGIN_FREE);
   data["margin_level"] =     AccountInfoDouble(ACCOUNT_MARGIN_LEVEL);
   data["margin_so_call"] =   AccountInfoDouble(ACCOUNT_MARGIN_SO_CALL);
   data["margin_so_so"] =     AccountInfoDouble(ACCOUNT_MARGIN_SO_SO);
//--- integers
   data["login"] =            AccountInfoInteger(ACCOUNT_LOGIN);
   data["leverage"] =         AccountInfoInteger(ACCOUNT_LEVERAGE);
   data["trade_allowed"] =    AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);
   data["ea_allowed"] =       AccountInfoInteger(ACCOUNT_TRADE_EXPERT);
   data["trade_mode"] =       AccountInfoInteger(ACCOUNT_TRADE_MODE);
   data["margin_so_mode"] =   AccountInfoInteger(ACCOUNT_MARGIN_SO_MODE);
//-- strings
   data["company"] =          AccountInfoString(ACCOUNT_COMPANY);
   data["currency"] =         AccountInfoString(ACCOUNT_CURRENCY);
   data["name"] =             AccountInfoString(ACCOUNT_NAME);
   data["server"] =           AccountInfoString(ACCOUNT_SERVER);
   
//--- fill in the acc_info array with Account Info data
   acc_info["acc_info"].Add(data);
   
//--- WebRequest arguments
   string method = "POST";
   string url = "http://172.22.18.235/accs";
   string headers = "Content-Type: application/json";
   int timeout = 500;
   char post[], result[];
   string result_headers;
   
//--- prepare JSON data to send
   string json = acc_info.Serialize();
   ArrayResize(post, json.Length(), 0);
   StringToCharArray(json, post, 0, StringLen(json), CP_UTF8);
   ResetLastError();
   
//--- send the request
   int res = WebRequest(method, url, headers, timeout, post, result, result_headers);
   if(res == -1)
     {
      Print("Error in WebRequest  =", GetLastError());
      MessageBox("Add " + url + " to allowed URLs on MT5 terminal", "Unknown URL", MB_ICONINFORMATION);     }
   else
     {
      Print("Starting post...");
      
      if(res == 201)// HTTP result code 201 (created)
        {
         Print("posted accs");
        }
     }
  }

Como podemos ver desde el inicio del archivo, estamos usando una biblioteca auxiliar para serializar/deserializar nuestros datos JSON. Fue desarrollada por un miembro de la comunidad MQL5. Podrá encontrar la biblioteca en en su repositorio de GitHub.

Ahora añadimos nuestras operaciones de la historia de MetaTrader 5. Creamos un nuevo script y añadimos el siguiente código.

//+------------------------------------------------------------------+
//|                                                   post_deals.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh> //--- include the JSON library
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- gathering the data - Deals
   CJAVal data;
   CJAVal deals;
//--- request trade history
   HistorySelect(0, TimeCurrent());
   int deals_total = HistoryDealsTotal();
//--- iterate over all deals to get data
//--- of each deal from its ticket number
   for(int i = 0; i < deals_total; i++)
     {
      //-- integers
      ulong deal_ticket =   HistoryDealGetTicket(i);
      data["ticket"] =     (int) deal_ticket;
      data["order"] =      (int) HistoryDealGetInteger(deal_ticket, DEAL_ORDER);
      data["position"] =   (int) HistoryDealGetInteger(deal_ticket, DEAL_POSITION_ID);
      data["time"] =       (int) HistoryDealGetInteger(deal_ticket, DEAL_TIME);
      data["type"] =       (int) HistoryDealGetInteger(deal_ticket, DEAL_TYPE);
      data["entry"] =      (int) HistoryDealGetInteger(deal_ticket, DEAL_ENTRY);
      data["magic"] =      (int) HistoryDealGetInteger(deal_ticket, DEAL_MAGIC);
      data["reason"] =     (int) HistoryDealGetInteger(deal_ticket, DEAL_REASON);
      //--- strings
      data["symbol"] =     (string) HistoryDealGetString(deal_ticket, DEAL_SYMBOL);
      //--- doubles
      data["volume"] =     (double) HistoryDealGetDouble(deal_ticket, DEAL_VOLUME);
      data["price"] =      (double) HistoryDealGetDouble(deal_ticket, DEAL_PRICE);
      data["profit"] =     (double) HistoryDealGetDouble(deal_ticket, DEAL_PROFIT);
      data["swap"] =       (double) HistoryDealGetDouble(deal_ticket, DEAL_SWAP);
      data["comission"] =  (double) HistoryDealGetDouble(deal_ticket, DEAL_COMMISSION);
 //--- fill in the deals array with each deal data
      deals["deals"].Add(data);
     }
 //--- WebRequest arguments
   string method = "POST";
   string url = "http://172.22.18.235/deals";
   string headers = "Content-Type: application/json";
   int timeout = 500;
   char post[], result[];
   string result_headers;
   
 //--- prepare JSON data to send
   string json = deals.Serialize();
   ArrayResize(post, json.Length(), 0);
   StringToCharArray(json, post, 0, StringLen(json), CP_UTF8);
   ResetLastError();
//--- send the request
   int res = WebRequest(method, url, headers, timeout, post, result, result_headers);
   
   if(res == -1)
     {
      Print("Error in WebRequest  =", GetLastError());
      MessageBox("Add " + url + " to allowed URLs on MT5 terminal", "Unknown URL", MB_ICONINFORMATION);     }
   else
     {
      Print("Starting post...");
      
      if(res == 201)// HTTP result code 201 (created)
        {
         Print("posted deals");
        }
     }
  }


Solicitud de datos de MQL5

Ahora vamos a consultar nuestros datos recién añadidos. En el terminal MetaTrader 5, creamos un nuevo script y añadimos el siguiente código.

//+------------------------------------------------------------------+
//|                                                 get_endpoint.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh> //--- include the JSON library
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- choose the testing endpoint
   string endpoint = "accs"; // or "deals"
//--- WebRequest arguments
   string method = "GET";
   string url = "http://172.22.18.235/" + endpoint;
   string cookie = NULL, headers;
   int timeout = 500;
   char post[], result[];
   ResetLastError();
//--- send the request
   int res = WebRequest(method, url, cookie, NULL, timeout, post, 0, result, headers);
   if(res == -1)
     {
      Print("Error in WebRequest  =", GetLastError());
      MessageBox("Add " + url + " to allowed URLs on MT5 terminal", "Unknown URL", MB_ICONINFORMATION);
     }
   else
     {
      Print("Starting get...");
      if(res == 200)// HTTP result code 200 (OK)
        {
         PrintFormat("Server headers: %s", headers);
         ResetLastError();
         // save the returned JSON in a file
         string terminal_data_path = TerminalInfoString(TERMINAL_DATA_PATH);
         string subfolder = "TutoPostgres";
         string filename = endpoint + "_fromserver.json";
         int filehandle = FileOpen(subfolder + "\\" + filename, FILE_WRITE | FILE_BIN);
         if(filehandle != INVALID_HANDLE)
           {
            FileWriteArray(filehandle, result, 0, ArraySize(result));
            FileClose(filehandle);
            Print(filename + " created at " + terminal_data_path + "\\" + subfolder);
           }
         else
            Print("File open failed with error ", GetLastError());
        }
      else
         PrintFormat("Request to '%s' failed with error code %d", url, res);
     }
  }

Al cambiar el punto final de "accs" a "deals", podremos consultar las transacciones recién añadidas. Comprobamos <Ruta del terminal MT5>\Files\TutoPostgres. Si todo ha salido bien, debería haber al menos dos archivos: accs_fromserver.json y Deals_fromserver.json. 


Uso de datos JSON en asesores expertos

Para usar el JSON devuelto por el servidor, debemos deserializarlo. La biblioteca mencionada anteriormente puede hacer esto.

Si miramos los archivos JSON guardados después de la consulta de la base de datos con el código de ejemplo anterior, es posible que veamos una cadena JSON como esta:

[
  {
    "a_balance": "10005.93",
    "a_company": "MetaQuotes Software Corp.",
    "a_credit": "0.0",
    "a_currency": "USD",
    "a_ea_allowed": true,
    "a_equity": "10005.93",
    "a_id": 3,
    "a_leverage": 100,
    "a_login": 66744794,
    "a_margin": "0.0",
    "a_margin_free": "10005.93",
    "a_margin_level": "0.0",
    "a_margin_so_call": "50.0",
    "a_margin_so_mode": "0",
    "a_margin_so_so": "30.0",
    "a_name": "MetaTrader 5 Desktop Demo",
    "a_profit": "0.0",
    "a_server": "MetaQuotes-Demo",
    "a_trade_allowed": true,
    "a_trade_mode": "0"
  },
  {
(...)

Usaremos estas claves para acceder a los datos deserializados. El '0' (cero) en el índice del array supone el acceso a la primera cuenta devuelta. Si tenemos más de una cuenta, este punto final ("accs") retornará todas las cuentas, y podremos acceder a cada una recorriendo el array en ese índice.

//+------------------------------------------------------------------+
//|                                                 consume_json.mq5 |
//|                                  Copyright 2023, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2023, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#include <JAson.mqh> //--- include the JSON library
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {
//--- choose the testing endpoint
   string endpoint = "accs"; // or "deals"
//--- WebRequest arguments
   string method = "GET";
   string url = "http://172.22.18.235/" + endpoint;
   string cookie = NULL, headers;
   int timeout = 500;
   char post[], result[];
   ResetLastError();
//--- send the request
   int res = WebRequest(method, url, cookie, NULL, timeout, post, 0, result, headers);
   if(res == -1)
     {
      Print("Error in WebRequest  =", GetLastError());
      MessageBox("Add " + url + " to allowed URLs on MT5 terminal", "Unknown URL", MB_ICONINFORMATION);
     }
   else
     {
      Print("Starting get...");
      if(res == 200)// HTTP result code 200 (OK)
        {
         CJAVal data;
         data.Deserialize(result);
         //--- doubles
         double a_balance =         data[0]["a_balance"].ToDbl();
         double a_credit =          data[0]["a_credit"].ToDbl();
         double a_profit =          data[0]["a_profit"].ToDbl();
         double a_equity =          data[0]["a_equity"].ToDbl();
         double a_margin =          data[0]["a_margin"].ToDbl();
         double a_margin_free =     data[0]["a_margin_free"].ToDbl();
         double a_margin_level =    data[0]["a_margin_level"].ToDbl();
         double a_margin_so_call =  data[0]["a_margin_so_call"].ToDbl();
         double a_margin_so_so =    data[0]["a_margin_so_so"].ToDbl();
         //--- longs
         long a_login =             data[0]["a_login"].ToInt();
         long a_leverage =          data[0]["a_leverage"].ToInt();
         long a_trade_mode =        data[0]["a_trade_mode"].ToInt();
         long a_margin_so_mode =    data[0]["a_margin_so_mode"].ToInt();
         long a_id =                data[0]["a_id"].ToInt(); //--- database generated ID
         //--- strings
         string a_company =         data[0]["a_company"].ToStr();
         string a_currency =        data[0]["a_currency"].ToStr();
         string a_name =            data[0]["a_name"].ToStr();
         string a_server =          data[0]["a_server"].ToStr();
         //--- booleans
         bool a_ea_allowed =        data[0]["a_ea_allowed"].ToBool();
         bool a_trade_allowed =     data[0]["a_trade_allowed"].ToBool();
         //printf("Server headers: %s", headers);
         //--- doubles
         printf("Balance: %d", a_balance);
         printf("Credit: %d", a_credit);
         printf("Profit: %d", a_profit);
         printf("Equity: %d", a_equity);
         printf("Margin: %d", a_margin);
         printf("Margin Free: %d", a_margin_free);
         printf("Margin Level: %d", a_margin_level);
         printf("Margin Call Level: %d", a_margin_so_call);
         printf("Margin Stop Out Level: %d", a_margin_so_so);
         //--- longs
         printf("Login: %d", a_login);
         printf("Leverage: %d", a_leverage);
         printf("Trade Mode: %d", a_trade_mode);
         printf("Margin Stop Out Mode: %d", a_margin_so_mode);
         printf("Database ID: %d", a_id);
         //--- strings
         printf("Company: %s", a_company);
         printf("Currency: %s", a_currency);
         printf("Platform Name: %s", a_name);
         printf("Server: %s", a_server);
         //--- booleans
         printf("Expert Advisor Allowed: %d", a_ea_allowed);
         printf("Trade Allowed: %d", a_trade_allowed);
         Print("Done!");
        }
      else
         PrintFormat("Request to '%s' failed with error code %d", url, res);
     }
  }


SQLite como espejo de Postgres

También podemos utilizar la infraestructura MQL5 existente usando los datos remotos como una base de datos SQLite local. Para implementar esta característica, necesitamos mantener nuestras bases de datos sincronizadas. Dicha sincronización será casi en tiempo real, con un retraso de tan solo unos segundos, pero mejorará el rendimiento: reducirá la latencia de la red y nos permitirá acceder a los datos a través de la GUI estándar del MetaEditor, así como usar las funciones de la base de datos MQL5 en nuestro código MQL5.

Si cree que esta característica resultaría útil, hágamelo saber. Escribiré un tutorial detallado con un código de ejemplo para sincronizar una base de datos Postgres remota con las bases de datos SQLite locales.


Conclusión

Hemos revisado algunos métodos disponibles actualmente para conectar el código MQL5 a una base de datos de Postgres. Asimismo, hemos elegido la API REST como una alternativa viable y rápida al desarrollo de drivers personalizados más costosos o al uso de .dll. Además, hemos desarrollado una aplicación de demostración básica como ejemplo de configuración de un entorno de desarrollo para MQL5/Postgres en el subsistema de Windows para Linux.

¡Ahora puedes comenzar a desarrollar! Elija un buen proveedor de nube y use la potencia de las extensiones de análisis, automatización, escalabilidad web y aprendizaje automático de Postgres para mejorar su rendimiento comercial.

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

Teoría de Categorías en MQL5 (Parte 6): Productos fibrados monomórficos y coproductos fibrados epimórficos Teoría de Categorías en MQL5 (Parte 6): Productos fibrados monomórficos y coproductos fibrados epimórficos
La teoría de categorías es un apartado diverso y en expansión de las matemáticas, que solo recientemente ha comenzado a ser trabajado por la comunidad MQL5. Esta serie de artículos tiene por objetivo repasar algunos de sus conceptos para crear una biblioteca abierta y seguir usando este maravilloso apartado en la creación de estrategias comerciales.
Aprendizaje automático y Data Science (Parte 14): Aplicación de los mapas de Kohonen a los mercados Aprendizaje automático y Data Science (Parte 14): Aplicación de los mapas de Kohonen a los mercados
¿Quiere encontrar un nuevo enfoque comercial que lo ayude a orientarse en mercados complejos y en cambio constante? Eche un vistazo a los mapas de Kohonen, una forma innovadora de redes neuronales artificiales que puede ayudarle a descubrir patrones y tendencias ocultos en los datos del mercado. En este artículo, veremos cómo funcionan los mapas de Kohonen y cómo usarlos para desarrollar estrategias comerciales efectivas. Creo que este nuevo enfoque resultará de interés tanto a los tráders experimentados como para los principiantes.
Experimentos con redes neuronales (Parte 5): Normalización de parámetros de entrada para su transmisión a una red neuronal Experimentos con redes neuronales (Parte 5): Normalización de parámetros de entrada para su transmisión a una red neuronal
Las redes neuronales lo son todo. Vamos a comprobar en la práctica si esto es así. MetaTrader 5 como herramienta autosuficiente para el uso de redes neuronales en el trading. Una explicación sencilla.
Multibot en MetaTrader: iniciamos múltiples robots desde un gráfico Multibot en MetaTrader: iniciamos múltiples robots desde un gráfico
En este artículo, veremos una plantilla simple para crear un robot MetaTrader universal que se pueda usar en varios gráficos, pero adjunto a uno solo, sin necesidad de configurar cada ejemplar del robot en cada gráfico individual.