English Русский 中文 Deutsch 日本語
preview
Envío de mensajes desde MQL5 a Discord: creación de un bot Discord–MetaTrader 5

Envío de mensajes desde MQL5 a Discord: creación de un bot Discord–MetaTrader 5

MetaTrader 5Integración |
37 0
Omega J Msigwa
Omega J Msigwa

Contenido


Introducción

Ya no vivimos en las primeras etapas de Internet y la era digital primigenia; hoy en día, casi todo se puede conectar con cualquier cosa en Internet; sólo depende de la voluntad de uno para hacer el trabajo requerido.

La existencia de las API (Application Programming Interface), que son un conjunto de reglas y protocolos que permiten que diferentes aplicaciones de software se comuniquen entre sí, ha facilitado la conexión entre más de un software o aplicación y, por lo tanto, ha dado lugar a la Internet más conectada que conocemos hoy en día.

Para utilizar cualquier API proporcionada, debe cumplir las normas y protocolos, sin olvidar que el proveedor de la API debe proporcionarle una forma segura (si existe) de acceder a ella.

Establecer una comunicación entre MetaTrader 5 y aplicaciones externas no es algo nuevo; ya se ha hecho antes en varias aplicaciones que ofrecen API fiables que los desarrolladores de MQL5 utilizan para enviar y recibir información mediante solicitudes web. La aplicación más común que utilizan los operadores de MQL5 para este tipo de comunicaciones es Telegram.

En este artículo, vamos a explicar cómo enviar mensajes e información bursátil (señales) desde MetaTrader 5 a Discord utilizando el lenguaje de programación MQL5.


Creando un Webhook de Discord

Para aquellos que aún no estén familiarizados con Discord:

Discord es una plataforma de comunicación gratuita que permite a los usuarios chatear por texto, voz y vídeo, y compartir pantallas. Es popular para conectar con amigos y comunidades, especialmente en los juegos en línea, pero también se utiliza para otros grupos, como clubes de lectura o círculos de costura.

Al igual que Telegram, se trata de una plataforma de comunicación que permite a los usuarios comunicarse entre sí. Ambas plataformas proporcionan API que permiten las comunicaciones incluso fuera de sus plataformas, pero Discord está muy por delante de Telegram en lo que respecta a la integración y la automatización.

Esta plataforma permite a los usuarios crear comunidades flexibles y gestionar aplicaciones (también conocidas como bots) de diferentes tipos.

Hay varias formas de crear bots y automatizar las comunicaciones y el proceso de compartir información en Discord, pero la más sencilla es utilizar un Webhook en uno de los canales de tu comunidad. A continuación te explicamos cómo crear uno.

 

Figura 1.

Desde tu comunidad, ve a Configuración del servidor.

 

Figura 2.

Desde Configuración del servidor, vaya a Integraciones > Webhooks.

 

Figura 3.

Haga clic en el botón Nuevo webhook para crear un webhook. Se creará un webhook con el nombre predeterminado Captain Hook. Siéntete libre de cambiar este nombre por el que quieras.

 

Figura 4.

Después de modificar su nombre, puede seleccionar el canal al que desea aplicar este webhook. En este servidor, tengo un canal llamado «señales de trading», que es donde quiero aplicar este Webhook. Véase la figura 1.

Por último, tenemos que copiar la URL del webhook. Esta es nuestra puerta de enlace API que podemos utilizar con solicitudes web en MQL5.

 

Figura 5.



Envío del primer mensaje desde MetaTrader 5 a Discord

Lo primero que debemos hacer es añadir https://discord.com a la lista de URL permitidas en MetaTrader 5; de lo contrario, todo lo que vamos a comentar a continuación no funcionará.

En MetaTrader 5, vaya a Herramientas > Opciones > Asesores Expertos y asegúrese de que la opción "Permitir WebRequest para las siguientes URL" esté marcada, y luego proceda a agregar una URL en el formulario a continuación.

Utilizando la URL de Webhook, podemos enviar una solicitud POST a este punto final de la API con datos en formato JSON.

Nombre del archivo: Discord EA.mq5

#include <jason.mqh> //https://www.mql5.com/en/code/13663

string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
     
   CJAVal js(NULL, jtUNDEF); //Creating a json object
   
   string raw_json = "{\"content\": \"Hello world from MetaTrader5!\"}";
   
   bool b = js.Deserialize(raw_json); //Parse JSON string into a structured object
   
   string json;
   js.Serialize(json); //Convert the object to a valid JSON string
   
//--- Sending a Post webrequest

   char data[]; 
   ArrayResize(data, StringToCharArray(json, data, 0, StringLen(json))); //--- serialize to string
   
//--- send data

   char res_data[]; //For storing the body of the response
   string res_headers=NULL; //For storing response headers (if needed)
   
   string headers="Content-Type: application/json; charset=utf-8";
   uint timeout=10000; //10 seconds timeout for the HTTP request
   
   int ret = WebRequest("POST", 
                         discord_webhook_url, 
                         headers, 
                         timeout, 
                         data, 
                         res_data, 
                         res_headers); //Send a post request
   
   if (ret==-1)
     {
       printf("func=%s line=%d, Failed to send a webrequest. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError()));
       return false;
     }
   
//--- Check if the post request was successful or not

   if (ret==204)
     {
       if (MQLInfoInteger(MQL_DEBUG))
         Print("Message sent to discord successfully");
     }
   else
     {
       printf("Failed to send message to discord. Json response Error = %s",CharArrayToString(res_data));
     }
  
//---
   return(INIT_SUCCEEDED);
  }

Toda la información y los mensajes que desees enviar a Discord deben estar siempre en formato JSON.

Cualquier elemento bajo la clave content en un objeto JSON representa el cuerpo principal del mensaje enviado a Discord.

Al ejecutar el código anterior, se envía un mensaje sencillo a Discord.

¡Genial! Sin embargo, era un proceso tedioso manejar los datos JSON y todo lo demás solo para enviar un simple mensaje a Discord, así que vamos a ponerlo todo en una clase para que sea más fácil enviar mensajes sin tener que preocuparnos cada vez por los aspectos técnicos.


Creación de una clase Discord en MQL5

Para garantizar que contamos con una clase de funcionalidad estándar, añadamos el registro para realizar un seguimiento de los errores producidos por el punto final de la API y todo el proceso de envío de solicitudes.

Nombre del archivo: Discord.mqh.

#include <errordescription.mqh>
#include <logging.mqh>
#include <jason.mqh>

class CDiscord
  {
protected:
   string m_webhook_url;
   string m_headers;
   uint m_timeout;
   CLogging logging;
   
   bool PostRequest(const string json);
   string GetFormattedJson(const string raw_json);
   
public:
                     CDiscord(const string webhook_url, const string headers="Content-Type: application/json; charset=utf-8", const uint timeout=10000);
                    ~CDiscord(void);
                     
                     bool SendMessage(const string message);
                     bool SendEmbeds(const string title, const string description_, color clr, const string footer_text, const datetime time);
  };
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
CDiscord::CDiscord(const string webhook_url, string headers="Content-Type: application/json; charset=utf-8", uint timeout=10000):
 m_webhook_url(webhook_url),
 m_headers(headers),
 m_timeout(timeout)
 {
//--- Initialize the logger

   logging.Config("Discord server");
   if (!logging.init())
      return;
      
   logging.info("Initialized",MQLInfoString(MQL_PROGRAM_NAME),__LINE__);
 }

Incluimos los procesos repetitivos para enviar solicitudes POST y convertir textos al formato JSON.

string CDiscord::GetFormattedJson(const string raw_json)
 {
   CJAVal js(NULL, jtUNDEF);
   bool b = js.Deserialize(raw_json);
   
   string json;
   js.Serialize(json); 
   
   return json;
 }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool CDiscord::PostRequest(const string json)
 {
   char res[];
    
//--- serialize to string

   char data[]; 
   ArrayResize(data, StringToCharArray(json, data, 0, StringLen(json)));
   
//--- send data

   char res_data[];
   string res_headers=NULL;

   int ret = WebRequest("POST", m_webhook_url, m_headers, m_timeout, data, res_data, res_headers); //Send a post request
   
   if (ret==-1)
     {
       printf("func=%s line=%d, Failed to send a webrequest. Error = %s",__FUNCTION__,__LINE__,ErrorDescription(GetLastError()));
       return false;
     }
   
//--- Check if the post request was successful or not

   if (ret==204)
     {
       if (MQLInfoInteger(MQL_DEBUG))
         Print("Message sent to discord successfully");
         logging.info("Message sent to discord successfully",MQLInfoString(MQL_PROGRAM_NAME), __LINE__);
     }
   else
     {
       printf("Failed to send message to discord. Json response Error = %s",CharArrayToString(res_data));
       logging.error(CharArrayToString(res_data), MQLInfoString(MQL_PROGRAM_NAME), __LINE__);
     }

  return true;
 }

A continuación se muestra una función sencilla para enviar mensajes a Discord.

bool CDiscord::SendMessage(const string message)
 {
   string raw_json = StringFormat("{\"content\": \"%s\"}",message);   
   string json = GetFormattedJson(raw_json); //Deserialize & Serialize the message in JSON format
   
   return PostRequest(json);
 }

A continuación se explica cómo utilizarlo.

Nombre del archivo: Discord EA.mq5

#include <Discord.mqh>
CDiscord *discord;

string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
     
    discord = new CDiscord(discord_webhook_url);  
    discord.SendMessage("Hello from MetaTrader5!");
  }

Esta función, sencilla y potente, se puede utilizar para enviar mensajes de diferentes tipos, ya que Discord utiliza texto con formato Markdown.

Ejemplo de mensaje Markdown

    discord.SendMessage(
                         "# h1 header\n"
                         "## h2 header\n"
                         "### h3 header\n\n"
                         "**bold text** normal text\n"
                         "[MQL5](https://www.mql5.com)\n"
                         "![image](https://i.imgur.com/4M34hi2.png)"
                       );

Resultado:

Y eso ni siquiera es lo más genial que puedes hacer con el editor de texto Markdown que ofrece Discord. Compartamos algunas líneas de código con Discord.

    discord.SendMessage("```MQL5\n" //we put the type of language for markdown highlighters after three consecutive backticks
                        "if (CheckPointer(discord)!=POINTER_INVALID)\n"
                        "  delete discord;\n"
                        "```");
                        
    discord.SendMessage("```Python\n"
                        "while(True):\n"
                        "  break\n"
                        "```");

Resultado:

Esto era para mostrarte lo potentes que son los editores de texto Markdown.

Añadir emojis a tus mensajes

Los emojis son muy útiles en los mensajes de texto; añaden elegancia, mejoran la legibilidad y, por supuesto, aportan un toque de humor.

A diferencia de otras plataformas de comunicación con emojis creados a partir de códigos numéricos específicos, Discord utiliza una sintaxis única para identificar y representar emojis directamente en mensajes de texto sin formato.

Todo lo que tienes que hacer es poner el nombre del emoji entre dos puntos: uno al principio y el otro al final del nombre del emoji.

Cuando los códigos de estos emojis se insertan en un mensaje de texto, se mostrarán como emojis en Discord.

discord.SendMessage(":rotating_light: Trade Alert!");

Mensaje:

Hay miles de emojis y es un desafío recordarlos todos. Aquí tienes la hoja de referencia: https://github.com/ikatyang/emoji-cheat-sheet/blob/master/README.md. He creado un sencillo archivo de biblioteca con algunos de los códigos y la sintaxis de los emojis. No dudes en añadir más códigos de emojis a tu gusto.

Nombre del archivo: discord emojis.mqh

#define DISCORD_EMOJI_ROCKET                   ":rocket:"               
#define DISCORD_EMOJI_CHART_UP                 ":chart_with_upwards_trend:"   
#define DISCORD_EMOJI_CHART_DOWN               ":chart_with_downwards_trend:" 
#define DISCORD_EMOJI_BAR_CHART                ":bar_chart:"            
#define DISCORD_EMOJI_BOMB                     ":bomb:"                 
#define DISCORD_EMOJI_THUMBS_UP                ":thumbsup:"             
#define DISCORD_EMOJI_THUMBS_DOWN              ":thumbsdown:"           
#define DISCORD_EMOJI_WARNING                  ":warning:"              
#define DISCORD_EMOJI_JOY                      ":joy:"                  
#define DISCORD_EMOJI_SOB                      ":sob:"                  
#define DISCORD_EMOJI_SMILE                    ":smile:"                
#define DISCORD_EMOJI_FIRE                     ":fire:"                 
#define DISCORD_EMOJI_STAR                     ":star:"                 
#define DISCORD_EMOJI_BLUSH                    ":blush:"                
#define DISCORD_EMOJI_THINKING                 ":thinking:"             
#define DISCORD_EMOJI_ROTATING_LIGHT           ":rotating_light:"
#define DISCORD_EMOJI_X                        ":x:"
#define DISCORD_EMOJI_WHITE_CHECK_MARK         ":white_check_mark:"
#define DISCORD_EMOJI_BALLOT_BOX_WITH_CHECK    ":ballot_box_with_check:"

#define DISCORD_EMOJI_HASH                     ":hash:"               
#define DISCORD_EMOJI_ASTERISK                 ":asterisk:"           

#define DISCORD_EMOJI_ZERO                     ":zero:"               
#define DISCORD_EMOJI_ONE                      ":one:"                
#define DISCORD_EMOJI_TWO                      ":two:"                
#define DISCORD_EMOJI_THREE                    ":three:"              
#define DISCORD_EMOJI_FOUR                     ":four:"               
#define DISCORD_EMOJI_FIVE                     ":five:"               
#define DISCORD_EMOJI_SIX                      ":six:"                
#define DISCORD_EMOJI_SEVEN                    ":seven:"              
#define DISCORD_EMOJI_EIGHT                    ":eight:"              
#define DISCORD_EMOJI_NINE                     ":nine:"               
#define DISCORD_EMOJI_TEN                      ":keycap_ten:"       
  
#define DISCORD_EMOJI_RED_CIRCLE               ":red_circle:"               
#define DISCORD_EMOJI_GREEN_CIRCLE             ":green_circle:"       

Enviando un mensaje:

discord.SendMessage(DISCORD_EMOJI_ROTATING_LIGHT " Trade Alert! " DISCORD_EMOJI_JOY);

Resultado del mensaje:

Mencionar usuarios y roles

Este sencillo mensaje puede gestionar menciones y roles, que son fundamentales para una entrega eficaz de los mensajes.

  • @everyone — Esto notifica a todos los usuarios del canal, incluso si están desconectados.
  • @here — Esto notifica a todos los usuarios que no están inactivos en el chat. Todos los usuarios que están conectados.
  • <@user_id> — Esto notifica a uno o varios usuarios específicos.
  • <@&role_id> Esto notifica a todos los usuarios asignados a una función específica. Por ejemplo, enviar señales de trading a todos los usuarios a los que se les ha asignado el rol de traders manuales.

Ejemplo de uso.

discord.SendMessage("@everyone This is an information about a trading account");
discord.SendMessage("@here This is a quick trading signals for all of you that are active");

El resultado del mensaje:


Identidad del bot de Discord

Lo bueno de un webhook es que no está restringido a la apariencia predeterminada (nombre y avatar); puedes cambiar estos valores en cualquier momento que envíes una solicitud de publicación.

Esta habilidad es bastante útil ya que puedes tener múltiples robots comerciales que envían algún tipo de información a una o dos comunidades, todos compartiendo un webhook, tener la capacidad de usar una identidad diferente ayuda a distinguir a los remitentes y los mensajes recibidos.

Podemos hacer que esta identidad sea una necesidad cada vez que se llame (inicie) la clase discord para hacer que la identidad sea global.

class CDiscord
  {
protected:

//...
//...
   
public:
                     string m_name;
                     string m_avatar_url;
                     
                     CDiscord(const string webhook_url, 
                              const string name, 
                              const string avatar_url, 
                              const string headers="Content-Type: application/json; charset=utf-8", 
                              const uint timeout=10000);
 }

Declaramos estas variables públicamente ya que un usuario podría querer modificarlas o acceder a ellas en el camino (después de la inicialización de la clase).

Esto hace que sea aún más fácil rastrear los registros de cada nombre de usuario que se asigna a este mensajero de Discord.

CDiscord::CDiscord(const string webhook_url, 
                   const string name, 
                   const string avatar_url, 
                   const string headers="Content-Type: application/json; charset=utf-8", 
                   const uint timeout=10000):
                   
 m_webhook_url(webhook_url),
 m_headers(headers),
 m_timeout(timeout),
 m_name(name),
 m_avatar_url(avatar_url)
 {
//--- Initialize the logger

   logging.Config(m_name);
   if (!logging.init())
      return;
      
   logging.info("Initialized",MQLInfoString(MQL_PROGRAM_NAME),__LINE__);
 }

Ahora, tenemos que agregar esta información de identidad (nombre y avatar) en todos los lugares donde se encuentre una cadena JSON.

bool CDiscord::SendMessage(const string message)
 {
   string raw_json = StringFormat("{"
                                    "\"username\": \"%s\","
                                    "\"avatar_url\": \"%s\","
                                    "\"content\": \"%s\""
                                  "}",
                                  m_name, m_avatar_url, message);   
                                  
   string json = GetFormattedJson(raw_json); //Deserialize & Serialize the message in JSON format
   
   return PostRequest(json);
 }

Dentro de la función SendJSON(), necesitamos hacer que esta identidad sea opcional porque un usuario podría tener una idea diferente en primer lugar, es por eso que eligió utilizar una función que toma una cadena JSON sin procesar que le da control sobre qué información quiere enviar.

bool CDiscord::SendJSON(const string raw_json, bool use_id=true)
 {
   CJAVal js;                                 
   
   js.Deserialize(raw_json);
   
   if (use_id) //if a decides to use the ids assigned to the class constructor, we append that information to the json object
     {
       js["username"] = m_name;
       js["avatar_url"] = m_avatar_url;
     }
     
   string json;
   js.Serialize(json);
   
   return PostRequest(json);
 }

Establezcamos una identidad diferente para nuestro bot.

#include <Discord.mqh>
CDiscord *discord;

input string discord_webhook_url = "https://discord.com/api/webhooks/1384809399767269527/105Kp27yKnQDpKD01VdEb01GS5P-KH5o5rYKuJb_xD_D8O23GPkGLXGn9pHBB1aOt4wR";
input string avatar_url_ = "https://imgur.com/m7sVf51.jpeg";
input string bots_name = "Signals Bot";
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
     
    discord = new CDiscord(discord_webhook_url,
                           bots_name,
                           avatar_url_);  
    
    discord.SendMessage("Same webhook but, different Identity :smile: cheers!");
            
//---
   return(INIT_SUCCEEDED);
  }

Resultado:


Trabajar con archivos incrustados y de imagen

A pesar de haber adjuntado la imagen al mensaje discutido en la sección anterior al crear una clase de Discord, existe una forma adecuada de adjuntar archivos e incrustar elementos a un mensaje.

Podemos lograr esto con una solicitud JSON independiente.

discord.SendJSON(
    "{"
        "\"content\": \"Here's the latest chart update:\","
        "\"embeds\": ["
            "{"
                "\"title\": \"Chart Update\","
                "\"image\": {"
                    "\"url\": \"https://imgur.com/SBsomI7.png\""
                "}"
            "}"
        "]"
    "}"
);

Resultado:

Lamentablemente, no es posible enviar una imagen directamente desde MQL5 mediante una solicitud web., a pesar de que Discord es capaz de recibir los archivos. Actualmente lo mejor que puedes hacer es compartir una imagen desde algún enlace donde esté alojado el archivo en línea.

Tuve que usar imgur.com en el ejemplo anterior.


Envío de notificaciones de operaciones desde MetaTrader 5 a Discord

Los operadores suelen utilizar la función de enviar información desde MetaTrader 5 a plataformas externas para enviar actualizaciones sobre sus actividades de trading. Utilicemos este bot (asesor experto) para dicha tarea.

Empezando por las notificaciones para abrir una operación.

01: Envío de notificaciones de apertura de operaciones

La función OnTradeTransaction resulta muy útil para esta tarea.

A continuación se muestran los verificadores de estado para comprobar el estado de una posición y una operación recientes.

#define IS_TRANSACTION_POSITION_OPENED         (trans.type == TRADE_TRANSACTION_DEAL_ADD && HistoryDealSelect(trans.deal) && (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_IN)
#define IS_TRANSACTION_POSITION_CLOSED         (trans.type == TRADE_TRANSACTION_DEAL_ADD && HistoryDealSelect(trans.deal) && (ENUM_DEAL_ENTRY)HistoryDealGetInteger(trans.deal, DEAL_ENTRY) == DEAL_ENTRY_OUT && ((ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON) != DEAL_REASON_SL && (ENUM_DEAL_REASON)HistoryDealGetInteger(trans.deal, DEAL_REASON) != DEAL_REASON_TP))
#define IS_TRANSACTION_POSITION_MODIFIED       (trans.type == TRADE_TRANSACTION_POSITION)
void OnTradeTransaction(const MqlTradeTransaction& trans,
                        const MqlTradeRequest& request,
                        const MqlTradeResult& result)
  {
     ulong deal_ticket = trans.deal;
     ulong position_ticket = trans.position;

//---
     
     m_deal.Ticket(deal_ticket); //select a deal by it's ticket
     
     if (IS_TRANSACTION_POSITION_OPENED)
       {
         if (deal_ticket==0)
           return;
          
         if (!m_position.SelectByTicket(position_ticket)) //select a position by ticket
            return;
              
         if (!m_symbol.Name(m_deal.Symbol())) //select a symbol from a position
           {
             printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError()));
             return;
           }
         
         string message = 
            DISCORD_EMOJI_ROTATING_LIGHT " **TRADE OPENED ALERT** \n\n"
            "- **Symbol:**"+m_position.Symbol()+"\n"
            "- **Trade Type:** "+ (m_position.PositionType()==POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE: DISCORD_EMOJI_RED_CIRCLE) +" "+m_position.TypeDescription()+"\n"
            "- **Entry Price:** `"+DoubleToString(m_deal.Price(), m_symbol.Digits())+"`\n" 
            "- **Stop Loss:** `"+DoubleToString(m_position.StopLoss(), m_symbol.Digits())+"`\n"
            "- **Take Profit:** `"+DoubleToString(m_position.TakeProfit(), m_symbol.Digits())+"`\n"
            "- **Time UTC:** `"+TimeToString(TimeGMT())+"`\n"
            "- **Position Ticket:** `"+(string)position_ticket+"`\n"
            "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*";
               
         discord.SendMessage(message);
       }

  //...
  //... Other lines of code
  //...     
 }

Nota:

Al formatear valores de mercado como precio de entrada, stop loss y take profit, etc., encierro cada uno de ellos con una simple comilla invertida (`). Esto crea un bloque de código en línea en Discord, que ayuda a separar visualmente los números y hace que sea más fácil copiarlos para los usuarios.

Usos de Discord.

  • Una sola comilla invertida (`) para dar formato al código en línea: ideal para valores cortos como precios.
  • Comillas triples (```) para dar formato a bloques de código de varias líneas (se utilizan para bloques de código o mensajes más grandes).

Abrí dos operaciones opuestas manualmente utilizando operaciones con un solo clic en MetaTrader 5. A continuación se muestra el resultado.

02: Envío de notificaciones de modificación de operaciones

if (IS_TRANSACTION_POSITION_MODIFIED)
  {
    if (!m_position.SelectByTicket(position_ticket))
      {
        printf("Failed to modify a position. Erorr = %s",ErrorDescription(GetLastError()));
        return;
      }
              
    if (!m_symbol.Name(m_deal.Symbol()))
      {
        printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError()));
        return;
      }
         
    string message = 
       DISCORD_EMOJI_BAR_CHART " **TRADE MODIFIED ALERT** \n\n"
       "- **Symbol:** `"+m_position.Symbol()+"`\n"
       "- **Trade Type:** "+ (m_position.PositionType()==POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE: DISCORD_EMOJI_RED_CIRCLE) +" "+m_position.TypeDescription()+"\n"
       "- **Entry Price:** `"+DoubleToString(m_position.PriceOpen(), m_symbol.Digits())+"`\n" 
       "- **New Stop Loss:** `"+DoubleToString(m_position.StopLoss(), m_symbol.Digits())+"`\n"
       "- **New Take Profit:** `"+DoubleToString(m_position.TakeProfit(), m_symbol.Digits())+"`\n"
       "- **Time UTC:** `"+TimeToString(TimeGMT())+"`\n"
       "- **Position Ticket:** `"+(string)position_ticket+"`\n"
       "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*";
            
    discord.SendMessage(message);      
  }

Todo es igual en este mensaje que en el envío de notificaciones comerciales, solo se han cambiado los nombres de los campos «stop loss» y «take profit».

03: Envío de notificaciones de operaciones de cierre

if (IS_TRANSACTION_POSITION_CLOSED)
   {         
    if (!m_symbol.Name(m_deal.Symbol()))
      {
        printf("line=%d Failed to select symbol %s. Error = %s",__LINE__,m_deal.Symbol(),ErrorDescription(GetLastError()));
        return;
      }
         
    m_symbol.RefreshRates(); //Get recent ask and bid prices
         
    long reason_integer;
    m_deal.InfoInteger(DEAL_REASON, reason_integer);
         
    string reason_text = GetDealReasonText(reason_integer);
            
    string message = 
          DISCORD_EMOJI_X " **TRADE CLOSED ALERT**\n\n"
          "- **Symbol:** `" + m_deal.Symbol() + "`\n"
          "- **Trade Type:** " + (m_position.PositionType() == POSITION_TYPE_BUY ? DISCORD_EMOJI_GREEN_CIRCLE : DISCORD_EMOJI_RED_CIRCLE) + " " + m_position.TypeDescription() + "\n"
          "- **Entry Price:** `" + DoubleToString(m_deal.Price(), m_symbol.Digits()) + "`\n"
          "- **Exit Price:** `" + (m_position.PositionType() == POSITION_TYPE_BUY ? (DoubleToString(m_symbol.Bid(), m_symbol.Digits())) : (DoubleToString(m_symbol.Ask(), m_symbol.Digits()))) + "`\n"
          "- **Profit:** " + (m_deal.Profit() >= 0 ? DISCORD_EMOJI_THUMBS_UP : DISCORD_EMOJI_THUMBS_DOWN) + " `" + DoubleToString(m_deal.Profit(), 2) + "`\n"
          "- **Close Reason:** `" + reason_text+ "`\n"
          "- **Commission:** `" + DoubleToString(m_position.Commission(), 2) + "`\n"
          "- **Swap:** `" + DoubleToString(m_position.Swap(), 2)+ "`\n"
          "- **Time (UTC):** `" + TimeToString(TimeGMT()) + "`\n"
          "- **Deal Ticket:** `" + string(deal_ticket) + "`\n"
          "> "DISCORD_EMOJI_WARNING" *Risk what you can afford to lose.*";
               
    discord.SendMessage(message);
  }

En un mensaje de cierre de operación, ponemos un emoji con el pulgar hacia arriba cuando una operación se cierra con ganancias, y un emoji con el pulgar hacia abajo cuando se cierra con pérdidas. A diferencia de la apertura y modificación de la posición, donde teníamos un número de ticket de posición. Una posición cerrada ya no es una posición, sino una operación, por lo que utilizamos el número de ticket de la operación para ella.

Esta operación se cerró manualmente, por lo que su motivo pasa a ser Cliente (terminal de escritorio), según ENUM_DEAL_REASON.

string GetDealReasonText(long reason)
{
   switch((int)reason)
   {
      case DEAL_REASON_CLIENT:            return "Client (Desktop Terminal)";
      case DEAL_REASON_MOBILE:            return "Mobile App";
      case DEAL_REASON_WEB:               return "Web Platform";
      case DEAL_REASON_EXPERT:            return "Expert Advisor";
      case DEAL_REASON_SL:                return "Stop Loss Hit";
      case DEAL_REASON_TP:                return "Take Profit Hit";
      case DEAL_REASON_SO:                return "Stop Out (Margin)";
      case DEAL_REASON_ROLLOVER:          return "Rollover Execution";
      case DEAL_REASON_VMARGIN:           return "Variation Margin Charged";
      case DEAL_REASON_SPLIT:             return "Stock Split Adjustment";
      case DEAL_REASON_CORPORATE_ACTION:  return "Corporate Action";
      default:                            return "Unknown Reason";
   }
}


El resultado final

Los webhooks de Discord son un buen punto de partida para la integración de MetaTrader 5 y Discord, ya que permiten la comunicación entre estas dos potentes plataformas. Sin embargo, como dije anteriormente, todas las API tienen reglas y limitaciones, y la API Webhook de Discord no es una excepción, ya que las solicitudes Webhook tienen un límite de frecuencia.

  • Solo puedes enviar 5 mensajes cada 2 segundos utilizando un único Webhook.
  • Se permite un máximo de 30 mensajes por minuto por canal para un único Webhook.

Si se supera este límite, Discord devolverá el código HTTP 429 (Demasiadas solicitudes). Para solucionar esto, puede añadir retrasos o colas en su código MQL5.

Además, a lo largo del artículo hablo de los bots de Discord, pero un Webhook es muy diferente de un bot de Discord. Todo el flujo de trabajo entre MQL5 y Discord es lo que yo denomino un bot.

Los bots son esencialmente programas que se ejecutan dentro de Discord y ofrecen una amplia gama de funcionalidades, desde comandos básicos hasta automatizaciones complejas. Los webhooks, por otro lado, son URL simples que permiten a aplicaciones externas enviar mensajes automatizados a un canal específico de Discord.

A continuación se muestra una tabla con las diferencias entre un webhook y un bot de Discord.


Webhooks Bots
Función
  • Solo se pueden enviar mensajes a un canal determinado.
  • Solo pueden enviar mensajes, no verlos.
  • Se pueden enviar hasta 10 incrustaciones por mensaje.

  • Mucho más flexibles, ya que pueden realizar acciones más complejas similares a las que puede realizar un usuario normal.
  • Los bots pueden ver y enviar mensajes.
  • Solo se permite una incrustación por mensaje.
Personalización
  • Se pueden crear 10 webhooks por servidor con la posibilidad de personalizar cada avatar y nombre.
  • Capaz de crear hipervínculos a cualquier texto fuera de un elemento incrustado.

  • Los bots públicos suelen tener un avatar y un nombre preestablecidos, que no pueden ser modificados por los usuarios finales.
  • No se puede insertar hipervínculos en ningún texto de un mensaje normal, se debe utilizar una incrustación.
Carga y seguridad
  • Solo un punto final al que enviar datos, no se requiere alojamiento real.
  • No se autentica que los datos enviados al webhook proceden de una fuente fiable.
  • No se autentica que los datos enviados al webhook proceden de una fuente fiable. Si se filtra la URL del webhook, solo pueden producirse problemas no permanentes (por ejemplo, spam).
  • Fácil de cambiar la URL del webhook si es necesario.

  • Los bots deben alojarse en un entorno seguro que deberá mantenerse conectado en todo momento, lo que requiere más recursos.
  • Los bots se autentican mediante un token; un token comprometido puede causar graves daños debido a sus capacidades si el propietario del servidor les ha concedido permisos.
  • Sin embargo, puedes restablecer el token del bot si es necesario.

Un bot de Discord es bastante complejo y poco práctico para los programadores de MQL5, ya que a menudo solo queremos una funcionalidad que alerte a nuestros compañeros traders sobre el progreso de las operaciones. Por no mencionar que, para crear un bot de Discord, necesitas un par de módulos de Python.

Sin embargo, si lo desea, puede desarrollar este completo bot de Discord que funciona con MetaTrader 5.

Dado que contamos con un paquete MetaTrader 5-Python, desde el entorno Python, puede trabajar para lograr una integración completa entre estas dos plataformas.

Saludos cordiales.


Tabla de archivos adjuntos

Nombre del archivo Descripción y uso
Include\discord emojis.mqh Contiene códigos emoji y sintaxis compatibles con Discord.
Include\discord.mqh Cuenta con la clase CDiscord para enviar mensajes en formato JSON a la plataforma Discord.
Include\errordescription.mqh Contiene descripciones de todos los códigos de error generados por MetaTrader 5 en lenguaje MQL5.
Include\jason.mqh Una biblioteca para la serialización y deserialización del protocolo JSON.
Include\logging.mqh  Una biblioteca para registrar toda la información y los errores generados por la clase CDiscord (remitente de Webhook). 
Experts\Discord EA.mq5  Un asesor experto (EA) para probar el webhook de Discord y enviar alertas comerciales a la URL del webhook de Discord asignada. 

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

Archivos adjuntos |
Attachments.zip (19.26 KB)
Utilizando redes neuronales en MetaTrader Utilizando redes neuronales en MetaTrader
En el artículo se muestra la aplicación de las redes neuronales en los programas de MQL, usando la biblioteca de libre difusión FANN. Usando como ejemplo una estrategia que utiliza el indicador MACD se ha construido un experto que usa el filtrado con red neuronal de las operaciones. Dicho filtrado ha mejorado las características del sistema comercial.
De novato a experto: Noticias animadas utilizando MQL5 (II) De novato a experto: Noticias animadas utilizando MQL5 (II)
Hoy damos un paso más adelante al integrar una API de noticias externa como fuente de titulares para nuestro EA News Headline. En esta fase, exploraremos diversas fuentes de noticias, tanto consolidadas como emergentes, y aprenderemos a acceder a sus API de forma eficaz. También abordaremos métodos para analizar los datos recuperados en un formato optimizado para su visualización en nuestro Asesor Experto. Únase al debate mientras exploramos las ventajas de acceder a los titulares de noticias y al calendario económico directamente en el gráfico, todo ello dentro de una interfaz compacta y no intrusiva.
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 (I) De novato a experto: Noticias animadas utilizando MQL5 (I)
La accesibilidad a las noticias es un factor crítico a la hora de operar en el terminal MetaTrader 5. Aunque existen numerosas API de noticias, muchos operadores tienen dificultades para acceder a ellas e integrarlas de forma eficaz en su entorno de negociación. En este debate, nuestro objetivo es desarrollar una solución optimizada que lleve las noticias directamente al gráfico, donde más se necesitan. Lograremos esto mediante la creación de un asesor experto en titulares de noticias que monitorea y muestra actualizaciones de noticias en tiempo real desde fuentes API.