Envío de mensajes desde MQL5 a Discord: creación de un bot Discord–MetaTrader 5
Contenido
- Introducción
- Creando un Webhook de Discord
- Envío del primer mensaje desde MetaTrader 5 a Discord
- Creando una clase de Discord en MQL5
- Identidad del bot de Discord
- Trabajar con archivos incrustados y archivos de imagen
- Envío de notificaciones sobre operaciones a Discord
- Conclusión
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" "" );
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 |
|
|
| Personalización |
|
|
| Carga y seguridad |
|
|
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
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.
Utilizando redes neuronales en MetaTrader
De novato a experto: Noticias animadas utilizando MQL5 (II)
Particularidades del trabajo con números del tipo double en MQL4
De novato a experto: Noticias animadas utilizando MQL5 (I)
- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso












