
Отправка сообщений из MQL5 в Discord, создание бота Discord-MetaTrader 5
Содержание
- Введение
- Создание вебхуков на Discord
- Отправление первого сообщения из MetaTrader 5 в Discord
- Создание класс Discord на MQL5
- Идентичность бота Discord
- Работа с эмбедами (embed) и файлами изображений
- Отправка торговых уведомлений в Discord
- Заключение
Введение
Мы уже не живем на ранних стадиях развития Интернета и в первобытную цифровую эпоху; сегодня практически все можно связать с чем угодно в Интернете; все зависит только от готовности человека выполнить необходимую работу.
Наличие API (интерфейсов прикладного программирования), представляющих собой набор правил и протоколов, позволяющих различным программным приложениям взаимодействовать друг с другом, упростило подключение нескольких программ или приложений друг к другу, и, следовательно, сделало Интернет наиболее всеобъемлющим, таким, каким мы его видим сегодня.
Чтобы использовать любой предоставленный API, вы должны соблюдать правила и протоколы, не говоря уже о том, что поставщик API должен предоставить вам безопасный способ (если таковой имеется) доступа к нему.
Организация связи между MetaTrader 5 и внешними приложениями не является чем-то новым; она уже была реализована в нескольких приложениях, предлагающих надежные API, которые разработчики MQL5 используют для отправки и получения информации с помощью веб-запросов. Наиболее распространенным приложением, которое MQL5-трейдеры используют для таких сообщений, является Telegram.
В данной статье мы обсудим, как отправлять сообщения и торговую информацию (сигналы) из MetaTrader 5 в Discord с помощью языка программирования MQL5.
Создание вебхук на Discord
Для тех, кто еще не знаком с Discord:
Discord — это бесплатная коммуникационная платформа, позволяющая пользователям общаться с помощью текстовых сообщений, голоса и видео, а также делиться скриншотами. Он популярен для общения с друзьями и сообществами, особенно в онлайн-играх, но также используется и для различных других групп, например, книжных клубов или кружков шитья.
Подобно Telegram, это коммуникационная платформа, позволяющая пользователям общаться друг с другом. Обе эти платформы предоставляют API, позволяющие общаться даже за пределами своих платформ, но Discord значительно опережает Telegram в вопросах интеграции и автоматизации.
Эта платформа позволяет пользователям создавать гибкие сообщества и управлять приложениями (также известными как боты) разных типов.
Существуют различные способы создания ботов, автоматизации коммуникации и процесса обмена информацией в Discord, но самый простой способ — использование вебхука в одном из каналов вашего сообщества. Так создается один из них.
![]()
Рисунок 0.
Из вашего сообщества перейдите в раздел Настройки сервера (server settings).
![]()
Рисунок 02.
Из настроек сервера перейдите в Integrations > Webhooks.
![]()
Рисунок 03.
Для создания вебхука нажмите кнопку Новый вебхук (New Webhook). Будет создан вебхук с именем по умолчанию «Капитан Крюк» (Captain Hook). Можно свободно изменить это имя на любое другое, какое захотите.
![]()
Рисунок 04.
После изменения имени можно выбрать канал, к которому хотите применить этот вебхук. На этом сервере у меня есть канал под названием торговые сигналы, к которому я и хочу применить этот вебхук. Смотрите на рисунок 01.
Наконец, нам нужно скопировать URL-адрес вебхука. Это наш API-шлюз, который мы можем использовать с веб-запросами на MQL5.
![]()
Рисунок 05.
Отправление первого сообщения из MetaTrader 5 в Discord
Первое, что нам нужно сделать, это добавить https://discord.com в список разрешенных URL-адресов в MetaTrader 5, иначе все, что мы обсудим дальше, работать не будет.
В MetaTrader 5 перейдите в Сервис (Tools)> Настройки Options > Советники (Expert Advisors)> Убедитесь, что флажок Разрешить WebRequest для перечисленных URL установлен (Ensure Allow WebRequest for listed URL is checked), затем перейдите к добавить URL-адрес в форму ниже (add a URL on the form below).
С помощью URL-адреса вебхука мы можем отправить запрос POST к этой конкретной конечной точке API, получив данные в формате JSON.
Имя файла: 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); }
Вся информация и сообщения, которые необходимо отправить в Discord, всегда должны быть в формате JSON.
Все, что находится под кнопкой content в объекте JSON, представляет собой основное текстовое тело сообщения, отправляемого в Discord.
Запуск кода выше отправляет простое сообщение в Discord.
Отлично! Однако это был утомительный процесс обработки JSON-данных и всего остального только для того, чтобы отправить простое сообщение в Discord. Давайте объединим все в класс, чтобы упростить отправку сообщений и не беспокоиться каждый раз о технических деталях.
Создание класса Discord на MQL5
Чтобы обеспечить наличие стандартного класса функциональности, добавим в него ведение лога для отслеживания ошибок, создаваемых конечной точкой API и всем процессом отправки запросов.
Имя файла: 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__); }
Мы оборачиваем повторяющиеся процессы отправки POST-запросов и преобразования текстов в формат 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; }
Ниже представлена простая функция отправки сообщений в 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); }
Применение показано ниже.
Имя файла: 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!"); }
Эта простая и мощная функция, она может использоваться для отправки сообщений разного типа, поскольку Discord использует текст в формате Markdown.
Пример сообщения в формате 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.
И это еще не самое крутое, что можно сделать с помощью текстового редактора Markdown, предоставляемого Discord. Давайте поделимся несколькими строками кода с 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" "```");
Результат.
Это сделано для того, чтобы показать вам, насколько мощными являются текстовые редакторы Markdown.
Добавление эмодзи в ваши сообщения
Эмодзи весьма полезны в текстовых сообщениях: они добавляют элегантности, улучшают читаемость, не говоря уже о том, что привносят толику юмора.
В отличие от других коммуникационных платформ, где эмодзи создаются из определенных числовых кодов, Discord использует уникальный синтаксис для идентификации и отображения эмодзи непосредственно в текстовых сообщениях.
Все, что вам нужно сделать, это поместить название эмодзи между двумя двоеточиями — одно двоеточие в начале, а другое в конце названия эмодзи.
При вставке кодов этих эмодзи в текстовое сообщение они будут отображены как эмодзи в Discord.
discord.SendMessage(":rotating_light: Trade Alert!");
Сообщение.
Существуют тысячи эмодзи и уследить за всеми ними довольно сложно. Вот шпаргалка — https://github.com/ikatyang/emoji-cheat-sheet/blob/master/README.md. Я создал простой файл библиотеки с некоторыми кодами эмодзи и синтаксисом. Не стесняйтесь добавлять больше кодов эмодзи по вашему желанию.
Имя файла: 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:"
Отправка сообщения.
discord.SendMessage(DISCORD_EMOJI_ROTATING_LIGHT " Trade Alert! " DISCORD_EMOJI_JOY);
Результат сообщения.
Упоминание пользователей и ролей
Это простое сообщение может содержать упоминания и роли, которые имеют решающее значение для эффективной доставки сообщения.
- @everyone — Это оповещает всех пользователей на канале, даже если они находятся офлайн.
- @here — Это оповестит всех активных пользователей в чате. Все пользователи, находящиеся в сети.
- <@user_id> — Уведомление отправляется определенному пользователю или пользователям.
- <@&role_id> — Это уведомляет всех пользователей, которым назначена определенная роль. Например, отправка торговых сигналов всем пользователям, которым назначена роль ручных трейдеров.
Пример использования.
discord.SendMessage("@everyone Это информация о торговом счете"); discord.SendMessage("@here Это быстрые торговые сигналы для всех вас, кто активен.);
Результат сообщения.
Идентификация бота Discord
Преимущество вебхука состоит в том, что он не ограничивается внешним видом по умолчанию (имя и аватар); вы можете изменить эти значения в любой момент, когда отправляете запрос на публикацию.
Эта возможность весьма удобна, поскольку вы можете иметь несколько торговых роботов, которые отправляют какую-либо информацию одному или двум сообществам, при этом все они будут использовать один вебхук. Возможность использовать разные идентификации помогает различать отправителей и полученные сообщения.
Мы можем сделать эту идентификацию обязательной при каждом вызове (инициализации) класса Discord, чтобы сделать идентификацию глобальной.
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); }
Мы объявляем эти переменные публично, поскольку пользователь может захотеть изменить их или получить к ним доступ в процессе (после инициализации класса).
Это еще больше упрощает отслеживание логов для каждого имени пользователя, назначенного этому мессенджеру 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__); }
Теперь нам необходимо добавить эту идентификационную информацию (имя и аватар) везде, где встречается строка 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); }
Внутри функции SendJSON() нам нужно сделать эту идентификацию необязательной, поскольку у пользователя может быть изначально другое мнение, поэтому он решил использовать функцию, которая принимает необработанную строку JSON, дающую ему контроль над тем, какую информацию он хочет отправить.
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); }
Давайте установим другую идентификацию для нашего бота.
#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); }
Результат.
Работа с эмбедами (embed) и файлами изображений
Несмотря на то, что при создании класса Discord в предыдущем разделе к сообщению было прикреплено изображение, существует надлежащий способ прикреплять файлы и встраивать элементы в сообщение.
Мы можем сделать это с помощью независимого JSON-запроса.
discord.SendJSON( "{" "\"content\": \"Here's the latest chart update:\"," "\"embeds\": [" "{" "\"title\": \"Chart Update\"," "\"image\": {" "\"url\": \"https://imgur.com/SBsomI7.png\"" "}" "}" "]" "}" );
Результаты.
К сожалению, вы не можете отправлять изображение напрямую из MQL5 с помощью Web Request, несмотря на то, что Discord способен принимать файлы. В настоящее время лучшее, что вы можете сделать, — это поделиться изображением по какой-либо ссылке, где файл размещен в сети.
В предыдущем примере мне пришлось использовать imgur.com .
Отправка торговых уведомлений из MetaTrader 5 в Discord
Трейдеры часто используют возможность отправки информации из MetaTrader 5 на внешние платформы для отправки обновлений о своей торговой деятельности. Давайте используем этого бота (советника) для такой задачи.
Начнем с уведомлений об открытии сделки.
01: Отправка уведомлений об открытии сделок
Для этой задачи удобна функция OnTradeTransaction.
Ниже приведены средства проверки состояния для проверки состояния недавней позиции и сделки.
#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 //... }
Примечание:
При форматировании рыночных значений, таких как цена входа, стоп-лосс, тейк-профит и т. д., я заключаю каждое из них в одну обратную кавычку (`). Это создает встроенный блок кода в Discord, который помогает визуально разделять цифры и упрощает их копирование пользователями.
Виды использования Discord.
- Одинарная открывающая кавычка (`) для форматирования встроенного кода — отлично подходит для коротких значений, таких как цены.
- Тройные обратные кавычки (```) для форматирования многострочных блоков кода — используются для более крупных блоков кода или сообщений.
Я открыл две противоположные сделки вручную, используя «Торговлю в один клик» (One-Click Trading) в MetaTrader 5, ниже представлен результат.
02: Отправка уведомлений об изменении сделок
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); }
В этом сообщении все то же самое, что и при отправке торговых уведомлений, изменены только названия полей стоп-лосса и тейк-профита.
03: Отправка уведомлений о закрытии сделок
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); }
В сообщении о закрытии сделки мы добавляем эмодзи с поднятым вверх большим пальцем, если сделка закрыта с прибылью, и эмодзи с опущенным большим пальцем, если сделка закрыта с убытком. В отличие от открытия и изменения позиции, где у нас был номер тикета позиции. Закрытая позиция больше не является позицией — это сделка, поэтому для нее мы используем номер тикета сделки.
Эта сделка закрыта вручную, поэтому ее причиной становится Client (Desktop Terminal), это соответствует 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"; } }
Заключение
Вебхуки программы Discord являются хорошей начальной точкой для интеграции MetaTrader 5 и Discord, обеспечивая сообщение между этими двумя мощными платформами. Однако, как я уже говорил ранее, у всех API есть правила и ограничения — API вебхука Discord ничем не отличается, поскольку запросы вебхука ограничены по частоте.
- Используя один вебхук можно отправлять только 5 сообщений для 2 каждые 2 секунды.
- Для одного вебхук допускается не более 30 сообщений в минуту на канал.
При превышении этого ограничения Discord вернет HTTP 429 (Слишком много запросов). Чтобы решить эту проблему, вы можете добавить задержки или очереди в свой код MQL5.
Кроме того, на протяжении всей статьи я говорю о боте (ботах) Discord, но Webhook сильно отличается от бота Discord. Весь рабочий процесс между MQL5 и Discord я называю ботом.
По сути, боты — это программы, работающие в Discord и предлагающие широкий спектр функций: от базовых команд до сложной автоматизации. С другой стороны, вебхуки — это простые URL-адреса, которые позволяют внешним приложениям отправлять автоматические сообщения на определенный канал Discord.
Ниже приведена таблица различий между вебхуком программы Discord и Discord-ботом.
Вебхуки | Боты | |
---|---|---|
Функция |
|
|
Возможность пользовательской настройки |
|
|
Нагрузка и безопасность |
|
|
Бот Discord довольно сложен и не очень практичен для программистов MQL5, поскольку нам часто нужна функция, позволяющая оповещать наших коллег-трейдеров только о ходе торговли. Не говоря уже о том, что для создания бота Discord вам понадобится пара модулей на Python.
Однако при желании вы можете разработать полноценный Discord-бот, работающий с MetaTrader 5.
Поскольку у нас есть пакет MetaTrader 5-Python, из среды Python вы можете добиться полной интеграции между этими двумя платформами.
Всем удачи!
Таблица вложений
Имя файла | Описание и назначение |
---|---|
Include\discord emojis.mqh | Содержит коды эмодзи и синтаксис, совместимый с Discord. |
Include\discord.mqh | Имеет класс CDiscord для отправки сообщений в формате JSON на платформу Discord. |
Include\errordescription.mqh | Содержит описания всех кодов ошибок, выдаваемых MetaTrader 5, на языке MQL5. |
Include\jason.mqh | Библиотека для сериализации и десериализации протокола JSON. |
Include\logging.mqh | Библиотека для регистрации всей информации и ошибок, создаваемых классом CDiscord (отправитель вебхук). |
Experts\Discord EA.mq5 | Советник (EA) для тестирования вебхука Discord и отправки торговых оповещений на назначенный URL-адрес вебхука Discord. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/18550




- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования