MQL5 и Telegram API: Отправка скриншотов при активации отложенных ордеров

MQL5 и Telegram API: Отправка скриншотов при активации отложенных ордеров MQL4 / MQL5 (MetaTrader)
Подробная инструкция по интеграции MQL5 с Telegram API для автоматической отправки скриншотов графика при срабатывании отложенных ордеров. Включает MQL5 код и пошаговое руководство.
Суть: Мы разработаем MQL5 советник, который использует Telegram API для отправки скриншота текущего графика в указанный чат Telegram каждый раз, когда активируется отложенный ордер (Buy Limit, Sell Limit, Buy Stop, Sell Stop и т.д.), размещенный с определенным магическим числом.

В современном алгоритмическом трейдинге своевременные уведомления играют ключевую роль. Интеграция торгового терминала MetaTrader 5 с популярными мессенджерами, такими как Telegram, позволяет трейдерам мгновенно получать информацию о важных событиях. В этой статье мы, как Senior Quant Developer, покажем, как настроить MQL5 советник для отправки скриншота графика в Telegram при срабатывании отложенного ордера. Это позволит вам всегда быть в курсе рыночных движений, даже когда вы не у терминала.

Для более сложных сценариев взаимодействия между терминалами или внешними приложениями, вы можете рассмотреть использование связи между терминалами MT5 через Named Pipes на MQL5, но для отправки уведомлений Telegram API является отличным решением.

Исходный код

Представленный ниже MQL5 код реализует функционал советника. Он включает в себя функцию для отправки фотографий в Telegram через WebRequest и обработчик торговых транзакций OnTradeTransaction для обнаружения активации отложенных ордеров.


//+------------------------------------------------------------------+
//|                                              TelegramNotifier.mq5|
//|                                                  FinFluct.com    |
//+------------------------------------------------------------------+
#property copyright "FinFluct.com"
#property link      "https://finfluct.com"
#property version   "1.00"
#property description "Интеграция MQL5 с Telegram API для отправки скриншотов при активации отложенных ордеров."
#property strict

//--- Входные параметры
input string TelegramBotToken = "YOUR_BOT_TOKEN"; // Токен вашего Telegram бота
input long   TelegramChatID   = 0;                // ID чата или пользователя Telegram
input int    ScreenshotWidth  = 800;              // Ширина скриншота
input int    ScreenshotHeight = 600;              // Высота скриншота
input ulong  MagicNumber      = 12345;            // Магическое число для фильтрации ордеров (0 - для всех ордеров)

//--- Глобальные переменные
string       g_screenshot_filename = "chart_screenshot.png";
bool         g_is_initialized = false;

//+------------------------------------------------------------------+
//| Отправка скриншота в Telegram                                    |
//+------------------------------------------------------------------+
bool SendTelegramPhoto(string token, long chat_id, string file_path, string caption)
  {
   string url = "https://api.telegram.org/bot" + token + "/sendPhoto";
   string boundary = "---------------------------" + LongToString(GetTickCount64()); // Уникальный разделитель
   string headers = "Content-Type: multipart/form-data; boundary=" + boundary + "\r\n";

   //--- Формирование частей тела запроса
   string part_chat_id = "--" + boundary + "\r\n" +
                         "Content-Disposition: form-data; name=\"chat_id\"\r\n\r\n" +
                         LongToString(chat_id) + "\r\n";

   string part_caption = "";
   if (!StringIsEmpty(caption))
     {
      part_caption = "--" + boundary + "\r\n" +
                     "Content-Disposition: form-data; name=\"caption\"\r\n\r\n" +
                     caption + "\r\n";
     }

   string part_photo_header = "--" + boundary + "\r\n" +
                              "Content-Disposition: form-data; name=\"photo\"; filename=\"" + g_screenshot_filename + "\"\r\n" +
                              "Content-Type: image/png\r\n\r\n";

   string part_end = "\r\n--" + boundary + "--\r\n"; // Завершающий разделитель

   //--- Чтение файла скриншота
   int file_handle = FileOpen(file_path, FILE_READ | FILE_BIN | FILE_ANSI);
   if (file_handle == INVALID_HANDLE)
     {
      Print("Ошибка: Не удалось открыть файл скриншота ", file_path, ". Ошибка: ", GetLastError());
      return false;
     }

   uchar file_data[];
   ulong file_size = FileSize(file_handle);
   if (file_size == 0 || FileReadArray(file_handle, file_data, 0, file_size) != file_size)
     {
      Print("Ошибка: Не удалось прочитать данные из файла скриншота ", file_path, ". Ошибка: ", GetLastError());
      FileClose(file_handle);
      return false;
     }
   FileClose(file_handle);

   //--- Преобразование строковых частей тела в массивы байтов
   uchar part_chat_id_data[];
   StringToCharArray(part_chat_id, part_chat_id_data, 0, WHOLE_ARRAY, CP_UTF8);

   uchar part_caption_data[];
   if (!StringIsEmpty(part_caption))
     {
      StringToCharArray(part_caption, part_caption_data, 0, WHOLE_ARRAY, CP_UTF8);
     }

   uchar part_photo_header_data[];
   StringToCharArray(part_photo_header, part_photo_header_data, 0, WHOLE_ARRAY, CP_UTF8);

   uchar part_end_data[];
   StringToCharArray(part_end, part_end_data, 0, WHOLE_ARRAY, CP_UTF8);

   //--- Объединение всех частей в один массив байтов для отправки
   int total_size = ArraySize(part_chat_id_data) +
                    ArraySize(part_caption_data) +
                    ArraySize(part_photo_header_data) +
                    ArraySize(file_data) +
                    ArraySize(part_end_data);

   uchar request_data[];
   ArrayResize(request_data, total_size);

   int offset = 0;
   CopyArray(request_data, part_chat_id_data, offset, 0, ArraySize(part_chat_id_data));
   offset += ArraySize(part_chat_id_data);

   if (!StringIsEmpty(part_caption))
     {
      CopyArray(request_data, part_caption_data, offset, 0, ArraySize(part_caption_data));
      offset += ArraySize(part_caption_data);
     }

   CopyArray(request_data, part_photo_header_data, offset, 0, ArraySize(part_photo_header_data));
   offset += ArraySize(part_photo_header_data);

   CopyArray(request_data, file_data, offset, 0, ArraySize(file_data));
   offset += ArraySize(file_data);

   CopyArray(request_data, part_end_data, offset, 0, ArraySize(part_end_data));

   //--- Отправка запроса
   uchar result_data[];
   string result_headers;
   int res = WebRequest("POST", url, headers, 0, "", request_data, result_data, result_headers);

   if (res == -1)
     {
      Print("Ошибка WebRequest. Код ошибки: ", GetLastError());
      return false;
     }

   string response_str = CharArrayToString(result_data, 0, WHOLE_ARRAY, CP_UTF8);
   Print("Telegram API Response: ", response_str);

   // Простая проверка на успешность (можно улучшить парсингом JSON)
   if (StringFind(response_str, "\"ok\":true") != -1)
     {
      Print("Скриншот успешно отправлен в Telegram.");
      return true;
     }
   else
     {
      Print("Ошибка отправки скриншота в Telegram. Ответ: ", response_str);
      return false;
     }
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
   //--- Проверка входных параметров
   if (TelegramBotToken == "YOUR_BOT_TOKEN" || TelegramChatID == 0)
     {
      Print("Ошибка: Пожалуйста, укажите корректные TelegramBotToken и TelegramChatID.");
      return INIT_PARAMETERS_INCORRECT;
     }

   //--- Разрешение WebRequest для api.telegram.org
   // Убедитесь, что "https://api.telegram.org" добавлен в список разрешенных URL в настройках терминала.
   // Tools -> Options -> Expert Advisors -> Allow WebRequest for listed URL:
   // Добавьте: https://api.telegram.org

   g_is_initialized = true;
   Print("Telegram Notifier EA инициализирован. Ожидание активации отложенных ордеров.");
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   Print("Telegram Notifier EA деинициализирован.");
  }

//+------------------------------------------------------------------+
//| Trade transaction event handler                                  |
//+------------------------------------------------------------------+
void OnTradeTransaction(const MqlTradeTransaction& transaction,
                        const MqlTradeRequest&     request,
                        const MqlTradeResult&      result)
  {
   if (!g_is_initialized) return;

   //--- Отслеживаем добавление сделки (активация ордера)
   if (transaction.type == TRADE_TRANSACTION_DEAL_ADD)
     {
      //--- Загружаем историю сделок и ордеров
      if (!HistorySelect(0, TimeCurrent()))
        {
         Print("Ошибка HistorySelect: ", GetLastError());
         return;
        }

      MqlDeal deal;
      if (!HistoryDealGetTicket(transaction.deal, deal))
        {
         Print("Ошибка HistoryDealGetTicket для сделки ", transaction.deal, ": ", GetLastError());
         return;
        }

      //--- Проверяем, что это сделка на открытие позиции
      if (deal.Entry() == DEAL_ENTRY_IN)
        {
         MqlOrder order;
         if (!HistoryOrderGetTicket(deal.Order(), order))
           {
            Print("Ошибка HistoryOrderGetTicket для ордера ", deal.Order(), ": ", GetLastError());
            return;
           }

         //--- Проверяем, что ордер был отложенным и соответствует нашему MagicNumber (если MagicNumber > 0)
         bool is_pending_order = (order.Type() == ORDER_TYPE_BUY_LIMIT ||
                                  order.Type() == ORDER_TYPE_SELL_LIMIT ||
                                  order.Type() == ORDER_TYPE_BUY_STOP ||
                                  order.Type() == ORDER_TYPE_SELL_STOP ||
                                  order.Type() == ORDER_TYPE_BUY_STOP_LIMIT ||
                                  order.Type() == ORDER_TYPE_SELL_STOP_LIMIT);

         bool magic_matches = (MagicNumber == 0 || order.Magic() == MagicNumber); // Если MagicNumber = 0, то не фильтруем

         if (is_pending_order && magic_matches)
           {
            //--- Отложенный ордер активирован!
            string symbol_name = Symbol();
            long chart_id = ChartID();
            string screenshot_full_path = TerminalInfoString(TERMINAL_DATA_PATH) + "\\MQL5\\Files\\" + g_screenshot_filename;

            //--- Делаем скриншот
            if (ChartScreenShot(chart_id, g_screenshot_filename, ScreenshotWidth, ScreenshotHeight, ALIGN_RIGHT))
              {
               string caption = StringFormat("✅ Активирован отложенный ордер #%I (%s %s) на %s. Цена: %f",
                                             order.Ticket(),
                                             EnumToString(order.Type()),
                                             (order.Type() == ORDER_TYPE_BUY_LIMIT || order.Type() == ORDER_TYPE_BUY_STOP || order.Type() == ORDER_TYPE_BUY_STOP_LIMIT) ? "BUY" : "SELL",
                                             symbol_name,
                                             deal.Price());
               SendTelegramPhoto(TelegramBotToken, TelegramChatID, screenshot_full_path, caption);
              }
            else
              {
               Print("Ошибка при создании скриншота: ", GetLastError());
              }
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //--- OnTick не используется для логики активации ордеров в этом примере.
  }

Разбор параметров

Советник имеет следующие входные параметры, которые необходимо настроить:

  • TelegramBotToken: Ваш уникальный токен Telegram бота. Получить его можно у @BotFather в Telegram.
  • TelegramChatID: ID чата или пользователя, куда будут отправляться уведомления. Чтобы узнать свой ID, можно написать боту @userinfobot.
  • ScreenshotWidth: Ширина генерируемого скриншота графика в пикселях.
  • ScreenshotHeight: Высота генерируемого скриншота графика в пикселях.
  • MagicNumber: Магическое число, используемое для фильтрации ордеров. Если вы хотите получать уведомления только по ордерам, размещенным вашим конкретным советником, укажите его MagicNumber. Если установить 0, уведомления будут отправляться по активации любых отложенных ордеров на графике, независимо от их магического числа.

Как запустить

Для успешной работы советника выполните следующие шаги:

  1. Создайте Telegram бота:

    • Откройте Telegram и найдите @BotFather.
    • Отправьте ему команду /newbot и следуйте инструкциям, чтобы создать нового бота.
    • После создания вы получите HTTP API Token. Сохраните его.
  2. Получите ID чата:

    • Напишите что-нибудь вашему новому боту.
    • Найдите в Telegram бота @userinfobot и отправьте ему любое сообщение. Он покажет ваш Chat ID. Сохраните его.
  3. Разрешите WebRequest в MetaTrader 5:

    • В MetaTrader 5 перейдите в меню Сервис -> Настройки (Tools -> Options).
    • Выберите вкладку Советники (Expert Advisors).
    • Установите флажок Разрешить WebRequest для перечисленных URL (Allow WebRequest for listed URL).
    • В список добавьте https://api.telegram.org.
  4. Установите советник:

    • Скопируйте предоставленный MQL5 код в новый файл в MetaEditor (Файл -> Создать -> Советник (шаблон) -> Далее -> Имя: TelegramNotifier -> Готово).
    • Сохраните файл как TelegramNotifier.mq5 в папке MQL5/Experts.
    • Скомпилируйте код (нажмите F7).
  5. Прикрепите советник к графику:

    • Перетащите TelegramNotifier из окна Навигатор на любой график в MetaTrader 5.
    • Во вкладке Общие (Common) установите флажок Разрешить алгоритмическую торговлю (Allow Algo Trading).
    • Во вкладке Входные параметры (Inputs) введите ваш TelegramBotToken и TelegramChatID. Настройте ScreenshotWidth, ScreenshotHeight и MagicNumber по вашему усмотрению.
    • Нажмите ОК.
  6. Проверьте работу:

    • Разместите отложенный ордер (например, Buy Limit) на том же символе, где установлен советник, с указанным MagicNumber (или без него, если MagicNumber = 0).
    • Дождитесь его активации. Как только ордер сработает, вы должны получить скриншот графика в ваш Telegram чат.

Этот советник является отличным дополнением к любой торговой стратегии, позволяя вам получать мгновенные визуальные уведомления. Вы можете комбинировать его с другими инструментами, например, с MQL5: Панель управления советником с кнопками Buy/Sell и вводом лота для ручного размещения ордеров или с MQL5: Трейлинг-стоп по Parabolic SAR с динамическим шагом для автоматического управления позициями, получая при этом уведомления о каждом важном событии.

Оцените статью
FinFluct