Envio de mensagens de MQL5 para o Discord, criação de um bot Discord-MetaTrader 5
Sumário
- Introdução
- Criação de webhooks no Discord
- Envio da primeira mensagem do MetaTrader 5 para o Discord
- Criação da classe Discord em MQL5
- Identidade do bot Discord
- Uso de embeds (embed) e arquivos de imagem
- Envio de notificações de trading para o Discord
- Conclusão
Introdução
Já não vivemos nos estágios iniciais do desenvolvimento da Internet nem na era digital primitiva; hoje, praticamente qualquer serviço pode ser integrado a outro pela Internet; tudo depende apenas da disposição de alguém para fazer o esforço necessário.
A existência de APIs (...), conjuntos de regras e protocolos que permitem a interação entre diferentes aplicações de software, simplificou a integração entre programas e aplicações e ajudou a tornar a Internet tão abrangente quanto é hoje.
Para usar qualquer API disponibilizada, é preciso cumprir suas regras e protocolos, sem falar que o provedor da API deve oferecer um meio seguro (se houver) de acessá-la.
A comunicação entre o MetaTrader 5 e aplicações externas não é algo novo; ela já foi implementada com várias aplicações que oferecem APIs confiáveis, que os desenvolvedores MQL5 usam para enviar e receber informações por meio de requisições web. O aplicativo mais comum usado por traders que trabalham com MQL5 para esse tipo de comunicação é o Telegram.
Neste artigo, discutiremos como enviar mensagens e informações de trading (sinais) do MetaTrader 5 para o Discord usando a linguagem de programação MQL5.
Criação de um webhook no Discord
Para quem ainda não conhece o Discord:
O Discord é uma plataforma gratuita de comunicação que permite que os usuários troquem mensagens de texto, conversem por voz e vídeo, além de compartilhar capturas de tela. Ele é popular como ferramenta de comunicação entre amigos e comunidades, especialmente em jogos online, mas também é usado por diversos outros grupos, como clubes de leitura ou grupos de costura.
Assim como o Telegram, o Discord permite que os usuários interajam entre si. Ambas as plataformas fornecem APIs que possibilitam a comunicação até mesmo fora de suas próprias plataformas, mas o Discord está bem à frente do Telegram em termos de integração e automação.
Essa plataforma permite que os usuários criem comunidades flexíveis e gerenciem aplicativos (também conhecidos como bots) de vários tipos.
Existem várias maneiras de criar bots e automatizar a comunicação e a troca de informações no Discord, mas a forma mais simples é usar um webhook em um dos canais da sua comunidade. Veja como criar um deles.
![]()
Figura 0.
A partir da sua comunidade, acesse a seção Configurações do servidor (server settings).
![]()
Figura 02.
Nas configurações do servidor, acesse Integrations > Webhooks.
![]()
Figura 03.
Para criar um webhook, clique no botão Novo webhook (New Webhook). Será criado um webhook com o nome padrão "Capitão Gancho" (Captain Hook). Você pode alterar livremente esse nome para qualquer outro que desejar.
![]()
Figura 04.
Depois de alterar o nome, é possível escolher o canal ao qual deseja associar esse webhook. Neste servidor, tenho um canal chamado sinais de trading, ao qual quero associar esse webhook. Veja a figura 01.
Por fim, precisamos copiar a URL do webhook. Esse é o nosso gateway da API, que podemos usar com requisições web em MQL5.
![]()
Figura 05.
Envio da primeira mensagem do MetaTrader 5 para o Discord
A primeira coisa que precisamos fazer é adicionar https://discord.com à lista de URLs permitidas no MetaTrader 5; caso contrário, tudo o que discutiremos a seguir não funcionará.
No MetaTrader 5, acesse Ferramentas (Tools) > Opções (Options) > EAs (Expert Advisors) > verifique se a caixa Permitir WebRequest para as URLs listadas está marcada (Ensure Allow WebRequest for listed URL is checked) e, em seguida, adicione a URL ao formulário abaixo (add a URL on the form below).
Com a URL do webhook, podemos enviar uma requisição POST para esse endpoint específico da API, passando os dados em formato JSON.
Nome do arquivo: Discord EA.mq5
#include <jason.mqh> //https://www.mql5.com/ru/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); }
Todas as informações e mensagens que precisam ser enviadas ao Discord devem estar sempre em formato JSON.
Tudo o que fica sob a chave content no objeto JSON representa o corpo principal de texto da mensagem enviada ao Discord.
A execução do código acima envia uma mensagem simples ao Discord.
Ótimo! No entanto, esse foi um fluxo trabalhoso de processamento de dados JSON e de todo o restante apenas para enviar uma mensagem simples ao Discord. Vamos reunir tudo em uma classe para simplificar o envio de mensagens e não precisar nos preocupar sempre com os detalhes técnicos.
Criação da classe Discord em MQL5
Para termos uma classe com funcionalidade padrão, adicionaremos a ela o registro em log para rastrear os erros gerados pelo endpoint da API e pelo fluxo de envio de requisições.
Nome do arquivo: 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__); }
Encapsulamos as rotinas repetitivas de envio de requisições POST e de conversão de textos para o 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 seguir, temos uma função simples para enviar mensagens ao 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); }
Seu uso é mostrado a seguir.
Nome do arquivo: 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!"); }
Essa é uma função simples e poderosa, que pode ser usada para enviar mensagens de diferentes tipos, pois o Discord aceita mensagens com formatação Markdown.
Exemplo de mensagem em formato Markdown
discord.SendMessage( "# h1 header\n" "## h2 header\n" "### h3 header\n\n" "**bold text** normal text\n" "[MQL5](https://www.mql5.com)\n" "" );
Outputs.
E isso ainda não é o mais interessante que se pode fazer com o recursos de formatação Markdown oferecidos pelo Discord. Vamos compartilhar algumas linhas de código no 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.
A ideia é mostrar o quanto os recursos de formatação Markdown são poderosos.
Adição de emojis às suas mensagens
Os emojis são muito úteis em mensagens de texto: acrescentam elegância, melhoram a legibilidade e, sem falar que, trazem uma pitada de humor.
Diferentemente de outras plataformas de comunicação, nas quais os emojis são criados a partir de determinados códigos numéricos, o Discord usa uma sintaxe própria para identificar e exibir emojis diretamente em mensagens de texto.
Tudo o que você precisa fazer é colocar o nome do emoji entre dois sinais de dois-pontos: um no início e outro no final do nome.
Ao inserir os códigos desses emojis em uma mensagem de texto, eles serão exibidos como emojis no Discord.
discord.SendMessage(":rotating_light: Trade Alert!"); Mensagem.
Existem milhares de emojis, e acompanhar todos eles é bastante difícil. Aqui está um guia de referência rápida: https://github.com/ikatyang/emoji-cheat-sheet/blob/master/README.md. Criei um arquivo simples de biblioteca com alguns códigos de emojis e a sintaxe correspondente. Fique à vontade para adicionar mais códigos de emojis conforme desejar.
Nome do arquivo: 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:"
Envio da mensagem.
discord.SendMessage(DISCORD_EMOJI_ROTATING_LIGHT " Trade Alert! " DISCORD_EMOJI_JOY); Resultado da mensagem.
Menção a usuários e cargos
Essa mensagem simples pode conter menções e cargos, que são essenciais para a entrega eficiente da mensagem.
- @everyone: notifica todos os usuários no canal, mesmo que estejam offline.
- @here: notifica todos os usuários ativos no chat. ou seja, todos os usuários que estão online.
- <@user_id>: a notificação é enviada a um usuário específico ou a usuários específicos.
- <@&role_id> — notifica todos os usuários aos quais foi atribuído um determinado cargo. Por exemplo, o envio de sinais de trading a todos os usuários a todos os usuários com o cargo de traders manuais.
Exemplo de uso.
discord.SendMessage("@everyone Это информация о торговом счете"); discord.SendMessage("@here Это быстрые торговые сигналы для всех вас, кто активен.);
Resultado da mensagem.
Identidade do bot Discord
A vantagem do webhook é que ele não fica limitado à aparência padrão (nome e avatar); você pode alterar esses valores a qualquer momento ao enviar uma requisição POST.
Esse recurso é bastante conveniente, pois você pode ter vários robôs de trading enviando informações para uma ou duas comunidades, enquanto todos eles usam um único webhook. A possibilidade de usar identidades diferentes ajuda a distinguir os remetentes e as mensagens recebidas.
Podemos tornar essa identidade obrigatória a cada chamada (inicialização) da classe Discord, para que a identidade seja 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 essas variáveis como públicas, pois o usuário pode querer alterá-las ou acessá-las durante a execução (após a inicialização da classe).
Isso facilita ainda mais o rastreamento dos logs para cada nome de usuário atribuído a esse mensageiro do 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__); }
Agora precisamos adicionar essas informações de identidade (nome e avatar) em todos os pontos em que a string JSON aparece.
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 da função SendJSON(), precisamos tornar essa identificação opcional, pois o usuário pode ter uma preferência diferente desde o início e decidir usar a função que aceita uma string JSON bruta, dando-lhe controle sobre as informações que deseja 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); }
Vamos definir uma identidade diferente para o nosso 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.
Uso de embeds (embed) e arquivos de imagem
Embora uma imagem tenha sido anexada à mensagem durante a criação da classe Discord na seção anterior, existe uma forma apropriada de anexar arquivos e incorporar elementos à mensagem.
Podemos fazer isso usando uma requisição JSON independente.
discord.SendJSON( "{" "\"content\": \"Here's the latest chart update:\"," "\"embeds\": [" "{" "\"title\": \"Chart Update\"," "\"image\": {" "\"url\": \"https://imgur.com/SBsomI7.png\"" "}" "}" "]" "}" );
Resultados.
Infelizmente, você não pode enviar uma imagem diretamente pelo MQL5 usando WebRequest, apesar de o Discord ser capaz de receber arquivos. No momento, o melhor que você pode fazer é compartilhar a imagem por meio de um link para o local em que o arquivo esteja hospedado online.
No exemplo anterior, precisei usar imgur.com.
Envio de notificações de trading do MetaTrader 5 para o Discord
Os traders costumam enviar informações do MetaTrader 5 para plataformas externas a fim de compartilhar atualizações sobre sua atividade de trading. Vamos usar esse bot (EA) para essa tarefa.
Começaremos com as notificações de abertura de operação.
01: Envio de notificações de abertura de operações
Para essa tarefa, a função OnTradeTransaction é bastante útil.
Abaixo estão as funções de verificação usadas para conferir o estado da posição e da operação mais recentes.
#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 //... }
Observação:
Ao formatar valores de mercado, como preço de entrada, stop loss, take profit etc., coloco cada um deles entre acentos graves (). Isso cria um bloco de código inline no Discord, o que ajuda a separar visualmente os números e facilita a cópia pelos usuários.
Exemplos de uso no Discord.
- Acento grave simples () para formatar código inline: ótima para valores curtos, como preços.
- Três crases (```) para formatar blocos de código multilinha: usadas para blocos maiores de código ou mensagens.
Abri duas operações opostas manualmente usando a Negociação com Um Clique (One-Click Trading) no MetaTrader 5; veja o resultado abaixo.
02: Envio de notificações de alteração de operações
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); }
Nesta mensagem, tudo é igual ao exemplo de envio de notificações de trading; apenas os nomes dos campos de stop loss e take profit foram alterados.
03: Envio de notificações de fechamento de operações
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); }
Na mensagem de fechamento da operação, adicionamos um emoji de polegar para cima se a operação foi fechada com lucro e um emoji de polegar para baixo se a operação foi fechada com prejuízo. Diferentemente da abertura e da alteração da posição, em que tínhamos o número do ticket da posição. uma posição fechada já não é uma posição, mas uma operação. Portanto, usamos o número do ticket da operação.
Essa operação foi fechada manualmente, portanto seu motivo passa a ser Client (Desktop Terminal), o que corresponde a 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"; } }
Conclusão
Os webhooks do Discord são um bom ponto de partida para integrar o MetaTrader 5 ao Discord, viabilizando a comunicação entre essas duas plataformas robustas. No entanto, como eu já disse antes, todas as APIs têm regras e limitações. A API de webhook do Discord não é diferente, pois suas requisições estão sujeitas a limite de taxa.
- Usando um único webhook, é possível enviar apenas 5 mensagens a cada 2 segundos.
- Para um único webhook, são permitidas no máximo 30 mensagens por minuto por canal.
Ao exceder esse limite, o Discord retornará HTTP 429 (Too Many Requests, "requisições demais"). Para resolver esse problema, você pode adicionar atrasos ou filas ao seu código MQL5.
Além disso, ao longo de todo o artigo eu falo sobre bot(s) do Discord, mas um webhook é bem diferente de um bot do Discord. Eu chamo de bot todo o fluxo operacional entre MQL5 e Discord.
Em essência, bots são programas que rodam no Discord e oferecem uma ampla gama de recursos: desde comandos básicos até automação complexa. Por outro lado, webhooks são URLs simples que permitem que aplicações externas enviem mensagens automáticas para um canal específico do Discord.
Abaixo está uma tabela com as diferenças entre um webhook do Discord e um bot do Discord.
| Webhooks | Bots | |
|---|---|---|
| Função |
|
|
| Possibilidade de personalização |
|
|
| Carga operacional e segurança |
|
|
Um bot do Discord é bastante complexo e pouco prático para desenvolvedores MQL5, pois geralmente precisamos apenas de uma função que permita notificar nossos colegas traders sobre o andamento das atividades de trading. Sem falar que, para criar um bot do Discord, você precisará de alguns módulos em Python.
No entanto, se desejar, você pode desenvolver um bot do Discord completo que funcione com o MetaTrader 5.
Como temos o pacote MetaTrader 5-Python, a partir do ambiente Python, você pode implementar uma integração completa entre essas duas plataformas.
Boa sorte a todos!
Tabela de anexos
| Nome do arquivo | Descrição e finalidade |
|---|---|
| Include\discord emojis.mqh | Contém códigos de emojis e sintaxe compatível com o Discord. |
| Include\discord.mqh | Contém a classe CDiscord para enviar mensagens em formato JSON para a plataforma Discord. |
| Include\errordescription.mqh | Contém descrições de todos os códigos de erro gerados pelo MetaTrader 5 na linguagem MQL5. |
| Include\jason.mqh | Biblioteca para serialização e desserialização do formato JSON. |
| Include\logging.mqh | Biblioteca para o registro em log de todas as informações e erros gerados pela classe CDiscord (envio via webhook). |
| Experts\Discord EA.mq5 | EA para testar o webhook do Discord e enviar alertas de trading para a URL definida do webhook do Discord. |
Traduzido do Inglês pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/en/articles/18550
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Automatização de estratégias de negociação em MQL5 (Parte 20): estratégia multissímbolo usando CCI e AO
Criação de classes Python para trading no MetaTrader 5, análogas às apresentadas em MQL5
Está chegando o novo MetaTrader 5 e MQL5
Do iniciante ao especialista: Sistema de análise autogeométrica
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso












