
Построение модели для ограничения диапазона сигналов по тренду (Часть 5): Система уведомлений (Часть II)
Содержание
- Введение
- Интеграция Telegram в Trend Constraint
- Telegram BOT API
- Установка модулей Python в Windows
- Python-скрипт send_telegram_messages
- Настройка Trend Constraint для Telegram
- Окончательный код
- Обработка ошибок
- Результаты тестирования
- Заключение
Введение
В предыдущей статье мы кратко представили процесс интеграции. Разделив его на подразделы, мы стремились упростить понимание процесса шаг за шагом. Надеюсь, что заложенная основа окажется прочной по мере того, как мы будем глубже вникать в обеспечение бесперебойной работы этих интеграций, особенно в рамках нашей модели Trend Constraint. Наша конечная цель — легко получать уведомления как в Telegram, так и в WhatsApp. Такая настройка позволит нам быть в курсе показаний индикаторов, не пропуская ни одного сигнала, и при этом общаться с друзьями и семьей в социальных сетях. Обмен сигналами непосредственно в социальной платформе будет упрощен и избавит от необходимости переключаться между приложениями.
Цель этой статьи — подробно рассмотреть каждый шаг, пока мы не достигнем желаемых результатов. Всё должно быть понятно благодаря базовым знаниям, полученным в предыдущей статье. Я поясню каждую строку кода интегрированной программы. В этом проекте, связанном с интеграцией Telegram, есть четыре ключевых компонента, которые необходимо постоянно учитывать.
- Telegram bot API.
- Python-скрипт.
- Выделенный сервер для размещения скрипта при работе с веб-запросами.
- Наша программа обработки индикаторов для Telegram.
Это базовый обзор компонентов, участвующих в основном процессе интеграции. Хотя я упомянул лишь Telegram и WhatsApp, другие платформы социальных сетей также могут быть интегрированы, если имеется язык программирования, облегчающий этот процесс. Осознавая важность совместимости языков в программировании, мы объединили Python и MQL5 в один проект. Это подчеркивает преимущество знания разных языков, таких как Python, C++, ONNX и C#. Это может существенно помочь MQL5-программистам в разработке функциональных возможностей платформы и интеграции с другими социальными API.
В следующей статье серии мы более подробно рассмотрим интеграцию WhatsApp, следуя структуре, подобной той, что применяется для Telegram, но используя API обмена сообщениями вместо API бота. Уже полученные знания об общих принципах работы упростят задачу.
Интеграция Telegram в Trend Constraint
Продолжим нашу работу с того места, на котором остановились. Я решил сосредоточиться на методе, который включает использование скрипта Python и функции ShellExecuteW для облегчения взаимодействия между индикатором MetaTrader 5 и ботом для Telegram. Я более подробно остановлюсь на этих аспектах. Такой подход имеет преимущество, поскольку он относительно прост для тех, кто знаком с Python и MQL5. Он не требует существенных изменений в индикаторе MQL5. Единственным недостатком является то, что требуются внешние зависимости, такие как скрипты и библиотеки Python.
Наша цель — улучшить доступность сигналов MetaTrader 5 в Telegram для более широкой аудитории и упростить обмен и пересылку данных посредством внедрения ботов.
В предыдущей статье (Часть I), я рассматривал метод интеграции, который использует функцию WebRequest. Однако при изучении документации MQL5 было обнаружено, что этот метод не идеален для интеграции индикаторов, но хорошо работает с роботами.
При использовании DLL-файлов оболочки следует соблюдать осторожность, поскольку они могут представлять серьезную опасность при использовании с ненадежными приложениями. Крайне важно полностью понимать и доверять функционалу этих систем на вашем компьютере, чтобы предотвратить атаки и взломы.
Telegram BOT API
Я предполагаю, что вы уже являетесь активным пользователем Telegram. Проект требует большей персонализации и конфиденциальности. Следуйте инструкциям, чтобы создать своего бота Telegram с уникальным именем, как это сделал я, используя @Botfather. Я назвал бота Trend Constraint с именем @trend_constraint_bot. Аналогичным образом вы можете создать своего собственного бота с уникальным именем. Вот краткий обзор того, как начать работу с Botfather. Следуйте инструкциям Botfather, чтобы завершить процесс. После этого вы получите токен бота для доступа к API бота. После этого начните разговор с ботом, добавьте его в группу или канал, чтобы начать общение. Каждый чат имеет уникальный идентификатор, который бот будет использовать для взаимодействия с определенным пользователем. Этот чат также используется для передачи сигналов от индикатора MetaTrader 5 пользователю Telegram.
После того, как я все настроил, я зашел в API бота с помощью браузера Chrome. Запомните токен бота, который вы получили от BotFather, используйте ссылку на API https://api.telegram.org/bot<your bot-token>/getUpdates, замените выделенный текст на свой токен бота.
Типичный пример: https://api.telegram.org/bot9004946256:shTUYuq52f8CHLt8BLdYGHYJi2QM6H3donA/getUpdates
Примечание: Выделенный токен API Token был сгенерирован случайным образом в образовательных целях. Используйте для своего робота токен, предоставленный @BotFather. Обязательно создайте чат с ботом, чтобы API отобразил то, что нам нужно. Также обновите вкладку API браузера, чтобы могли появиться новые обновления чата. Установите флажок pretty-print на вкладке API браузера, чтобы иметь удобный для восприятия вид.
API отобразит JSON-код для текущего взаимодействия с ботом. Для начала давайте определим JSON (JavaScript Object Notation) как облегченный формат обмена данными, который легко читать и записывать людям, а также легко парсить и генерировать машинам. Он обычно используется для передачи данных в веб-приложениях (например, между сервером и клиентом), а также для настройки приложений и хранения данных. Он структурирован объектом (Object) и массивами (Arrays).
Ниже представлен API JSON, который был отображен после отправки боту сообщения "hey" (привет):
{ "ok": true, "result": [ { "update_id": 464310132, "message": { "message_id": 12, "from": { "id": 7049213628, "is_bot": false, "first_name": "Clemence", "last_name": "Benjamin", "username": "benjc_trade_advisor", "language_code": "en" }, "chat": { "id": 7049213628, "first_name": "Clemence", "last_name": "Benjamin", "username": "benjc_trade_advisor", "type": "private" }, "date": 1719044625, "text": "hey" }
Вот подробное объяснение кода JSON:
Структура верхнего уровня:
- ok - логическое значение, указывающее на успешность запроса API. В данном случае это true, что означает, что запрос был успешным.
- result - массив, содержащий один или несколько объектов обновления. В этом примере имеется один объект обновления.
Каждый элемент в результирующем массиве представляет собой обновление. Здесь у нас есть одно обновление:
- update_id - уникальный идентификатор. Это помогает отслеживать обновления и гарантировать, что ни одно из них не будет пропущено или обработано несколько раз. В этом случае update_id равен 464310132.
Объект message
Этот объект содержит информацию о сообщении, вызвавшем обновление:
- message_id - уникальный идентификатор сообщения в чате. Здесь это 12.
- from - объект содержит информацию об отправителе сообщения:
- id - уникальный идентификатор пользователя, отправившего сообщение. Здесь это 7049213628.
- is_bot - логическое значение, которое указывает, является ли отправитель ботом. Здесь это false, то есть отправитель - человек.
- first_name - имя отправителя (Clemence).
- last_name - фамилия отправителя Benjamin).
- language_code - код языка, представляющий языковые настройки отправителя. Здесь это en для английского языка.
- chat - объект содержит информацию о чате, в который было отправлено сообщение.
- id - уникальный идентификатор чата. Поскольку это приватный чат, он соответствует идентификатору пользователя (7049213628).
- first_name - имя участника чата (Clemence).
- last_name - фамилия участника чата (Benjamin).
- type - тип чата. В данном случае private, что означает общение один на один между пользователем и ботом.
- date - дата и время отправки сообщения виде временной метки Unix (количество секунд с 1 января 1970 года). В данном случае временная метка — 1719044625.
- text - фактическое текстовое содержание сообщения ("hey").
Я решил отложить раздел чата в JSON, чтобы мы могли сосредоточиться на самой важной части — идентификаторе чата, который нам понадобится в нашей программе-индикаторе. Рассмотрим фрагмент JSON:
"chat": { "id": 7049213628, "first_name": "Clemence", "last_name": "Benjamin", "username": "benjc_trade_advisor", "type": "private" }
Объект chat предоставляет подробную информацию о чате, в который было отправлено сообщение, включая уникальный идентификатор чата, имя и фамилию участника, а также тип чата. В данном случае речь идет о приватном чате с участием пользователя по имени Clemence Benjamin. Рассмотрим объект chat подробнее:
id:
- Описание: Уникальный идентификатор чата.
- Значение: 7049213628
- Значимость: В контексте приватных чатов идентификатор обычно совпадает с идентификатором пользователя, участвующего в чате.
first_name:
- Описание: Имя участника чата.
- Значение: Clemence
- Значимость: Помогает идентифицировать пользователя по его имени в чате.
last_name:
- Описание: Фамилия участника чата.
- Значение: Benjamin
- Значимость: Дополняет имя и полностью идентифицирует пользователя в чате.
"username"
- Отписание: Ключ.
- Значение: "benjc_trade_advisor"
- Значимость: строка указывает, что имя пользователя, связанное с объектом (например, пользователем, ботом или чатом), - benjc_trade_advisor. Это имя пользователя обычно используется для идентификации сущности в узнаваемом формате в приложениях или системах, использующих данные JSON.
type:
- Описание: Тип чата.
- Значение: private
- Значимость: Указывает на то, что чат представляет собой индивидуальную беседу между пользователем и ботом (в отличие от группового чата или канала).
Итоги:
Целью раздела было создание работающего бота Telegram и получение токена бота и идентификатора чата — важнейших элементов основного проекта. Мы углубились в API, чтобы глубже понять каждый компонент. Наличие токена бота и идентификатора чата позволит нам продолжить интеграцию, в ходе которой мы также столкнемся с различными языками программирования.
Установка модулей Python в Windows
Прежде всего необходимо установить Python. Убедитесь, что ваш компьютер подключен к Интернету. Загрузить можно с сайта Python.org. Я создал это руководство на компьютере с ОС Windows. На Mac, Linux и других платформах подход может быть другим. Следующий шаг - установка модулей Python Telegram API. Это позволит скриптам Python для Telegram работать должным образом. Откройте cmd.exe (окно командной строки) и запустите фрагмент кода, указанный ниже. Скопируйте код и вставьте в командную строку Windows. Нажмите Enter, чтобы начать загрузку кода, и подождите немного, пока модуль завершит установку.
pip install pyTelegramBotAPI
Ниже показан фрагмент скриншота командной строки.
После завершения вы можете закрыть окно и перезагрузить компьютер.
После завершения этого шага ваш компьютер будет полностью подготовлен к выполнению скриптов Python для взаимодействия с API Telegram Bot. На следующем этапе мы внимательно изучим код, чтобы настроить нашу систему для решения этой задачи.
Python-скрипт send_telegram_messages
Рассмотрим устройство скрипта. А затем я приведу окончательный код одним фрагментом. Файл должен называться send_telegram_message.py.
Скрипт начинается с импорта необходимых модулей следующим образом:
- import telebot - импортирует модуль telebot, который предоставляет функции, необходимые для взаимодействия с Telegram Bot API.
- import sys - импортирует модуль sys, который позволяет скрипту использовать аргументы командной строки.
import telebot import sys
Переходим к объявлению API_TOKEN и идентификатора чата:
- API_TOKEN - переменная хранит API-токен бота, который используется для аутентификации бота на серверах Telegram.
- CHAT_ID - уникальное значение идентификатора каждого чата между пользователем и модулем telebot или каналом и группами.
API_TOKEN = '9004946256:shTUYuq52f8CHLt8BLdYGHYJi2QM6H3donA' #Замените API TOKEN на токен вашего бота из @BotFather CHAT_ID = '7049213628' #Замените ID на ваш фактический идентификатор чата из telebot API
Нам нужно инициализировать объект TeleBot с предоставленным токеном API, чтобы разрешить взаимодействие с Telegram Bot API.
bot = telebot.TeleBot(API_TOKEN)
Следующий фрагмент кода Python определяет функцию отправки сообщения через Telegram как обработку исключений ошибок, связанных с API и системой.
def send_telegram_message(message): try: bot.send_message(CHAT_ID, message) print("Message sent successfully!") except telebot.apihelper.ApiTelegramException as e: print(f"Failed to send message: {e}") except Exception as e: print(f"An error occurred: {e}")
Последняя часть кода представляет собой условие, которое гарантирует, что этот блок кода будет запущен только в том случае, если скрипт выполняется напрямую, а не импортирован как модуль. Она извлекает сообщение из аргументов командной строки или устанавливает сообщение по умолчанию, если аргументы не указаны.
if __name__ == "__main__": message = sys.argv[1] if len(sys.argv) > 1 else "Test message" send_telegram_message(message)
Итак наш код приобрел окончательный вид. Сохраните файл как send_telegram_message.py в папке скриптов Python. Доступ к папке скриптов работает нормально.
import telebot import sys API_TOKEN = '9004946256:shTUYuq52f8CHLt8BLdYGHYJi2QM6H3donA'#Замените вашим API_TOKEN, полученным от BotFather CHAT_ID = '7049213628' #Замените вашим CHAT_ID bot = telebot.TeleBot(API_TOKEN) def send_telegram_message(message): try: bot.send_message(CHAT_ID, message) print("Message sent successfully!") except telebot.apihelper.ApiTelegramException as e: print(f"Failed to send message: {e}") except Exception as e: print(f"An error occurred: {e}") if __name__ == "__main__": message = sys.argv[1] if len(sys.argv) > 1 else "Test message" send_telegram_message(message)
Следующий важный шаг - настройка индикатора MQL5 для вызова скрипта Python.
Настройка Trend Constraint для Telegram
Здесь нам нужно изменить функцию myAlert в MQL5-индикаторе для вызова скрипта Python с помощью функции ShellExecuteW. Функция выполнит скрипт Python и передаст предупреждающее сообщение в качестве аргумента.
Вот как мы изменяем функцию в Trend Constraint. Я включил два фрагмента кода - до и после изменения:
До изменений:
void myAlert(string type, string message) { if(type == "print") Print(message); else if(type == "error") { Print(type+" | Trend Constraint V1.05 @ "+Symbol()+","+IntegerToString(Period())+" | "+message); } else if(type == "order") { } else if(type == "modify") { } else if(type == "indicator") { if(Audible_Alerts) Alert(type+" | Trend Constraint V1.05 @ "+Symbol()+","+IntegerToString(Period())+" | "+message); if(Push_Notifications) SendNotification(type+" | Trend Constraint V1.05 @ "+Symbol()+","+IntegerToString(Period())+" | "+message); } }
После изменений:
//--- ShellExecuteW declaration ---------------------------------------------- #import "shell32.dll" int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); #import datetime last_alert_time; input int alert_cooldown_seconds = 60; // Cooldown period in seconds, this helps to avoid instant continuous alerting depending on indicator conditions //Modify the myAlert Function for telegram notification void myAlert(string type, string message) { datetime current_time = TimeCurrent(); if (current_time - last_alert_time < alert_cooldown_seconds) { // Skip alert if within cooldown period return; } last_alert_time = current_time; string full_message = type + " | Trend Constraint V1.04 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message; if (type == "print") { Print(message); } else if (type == "error") { Print(type + " | Trend Constraint V1.04 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message); } else if (type == "order") { // Add order alert handling if needed } else if (type == "modify") { // Add modify alert handling if needed } else if (type == "indicator") { if (Audible_Alerts) { Alert(full_message); } if (Push_Notifications) { SendNotification(full_message); } // Send to Telegram string python_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; string script_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_message.py"; string command = python_path + " \"" + script_path + "\" \"" + full_message + "\""; Print("Executing command to send Telegram message: ", command); // Use cmd.exe to execute the command and then wait for 5 seconds string final_command = "/c " + command + " && timeout 5"; int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 1); if (result <= 32) { int error_code = GetLastError(); Print("Failed to execute Python script. Error code: ", error_code); } else { Print("Successfully executed Python script. Result code: ", result); } } } //--- End of Telegram Integration functions ---------------------------------------
Кратко объясню, для чего нужно это изменение.
Сначала нам нужно импортировать функцию ShellExecuteW из библиотеки Windows shell32.dll, что позволяет программе MQL5 выполнять внешние команды. В данном случае она запускает скрипт send_telegram_message.py. Программа не будет работать без объявления функции ShellExecuteW. Мы также использовали кулдаун, чтобы ограничить непрерывное мгновенное выполнение cmd.exe из-за некоторых неправильно настроенных условий индикатора. В моем случае, как я уже упоминал в предыдущей статье, в буферах 5 и 6 Trend Constraint V1.04 возникло состояние оповещения, которое вызвало несколько оповещений о сигналах за короткий промежуток времени. Результат стал еще хуже, когда я интегрировал функцию Telegram: cmd.exe запускался несколько раз подряд, и компьютер зависал. Чтобы исправить ситуацию, мне пришлось позволить индикатору отрисовываться без myAlert(). Другими словами, я закомментировал функцию.
//--- ShellExecuteW declaration ---------------------------------------------- #import "shell32.dll" int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); #import
Другая важная часть кода:
// Send to Telegram string python_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; string script_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_message.py"; string command = python_path + " \"" + script_path + "\" \"" + full_message + "\"";
Приведенный выше код создает команду для запуска скрипта Python из программы-индикатора MetaTrader 5, указывая пути к интерпретатору Python и скрипту, а также отправляемое сообщение. Не забудьте заменить путь, выделенный цветом, на ваш собственный.
Окончательный код
Мы всё успешно рассмотрели и интегрировали. Обновляемся до Trend Constraint V1.05
//+------------------------------------------------------------------+ //| Trend Constraint V1.05.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com" #property version "1.05" #property description "A model that seeks to produce sell signals when D1 candle is Bearish only and buy signals when it is Bullish" //--- indicator settings #property indicator_chart_window #property indicator_buffers 6 #property indicator_plots 6 #property indicator_type1 DRAW_ARROW #property indicator_width1 5 #property indicator_color1 0xFF3C00 #property indicator_label1 "Buy" #property indicator_type2 DRAW_ARROW #property indicator_width2 5 #property indicator_color2 0x0000FF #property indicator_label2 "Sell" #property indicator_type3 DRAW_ARROW #property indicator_width3 2 #property indicator_color3 0xE8351A #property indicator_label3 "Buy Reversal" #property indicator_type4 DRAW_ARROW #property indicator_width4 2 #property indicator_color4 0x1A1AE8 #property indicator_label4 "Sell Reversal" #property indicator_type5 DRAW_LINE #property indicator_style5 STYLE_SOLID #property indicator_width5 2 #property indicator_color5 0xFFAA00 #property indicator_label5 "Buy" #property indicator_type6 DRAW_LINE #property indicator_style6 STYLE_SOLID #property indicator_width6 2 #property indicator_color6 0x0000FF #property indicator_label6 "Sell" #define PLOT_MAXIMUM_BARS_BACK 5000 #define OMIT_OLDEST_BARS 50 //--- indicator buffers double Buffer1[]; double Buffer2[]; double Buffer3[]; double Buffer4[]; double Buffer5[]; double Buffer6[]; input double Oversold = 30; input double Overbought = 70; input int Slow_MA_period = 200; input int Fast_MA_period = 100; datetime time_alert; //used when sending alert input bool Audible_Alerts = true; input bool Push_Notifications = true; double myPoint; //initialized in OnInit int RSI_handle; double RSI[]; double Open[]; double Close[]; int MA_handle; double MA[]; int MA_handle2; double MA2[]; int MA_handle3; double MA3[]; int MA_handle4; double MA4[]; double Low[]; double High[]; int MA_handle5; double MA5[]; int MA_handle6; double MA6[]; int MA_handle7; double MA7[]; //--- ShellExecuteW declaration ---------------------------------------------- #import "shell32.dll" int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); #import //--- functions for telegram integration ----------------------------------------------- datetime last_alert_time; input int alert_cooldown_seconds = 60; // Cooldown period in seconds void myAlert(string type, string message) { datetime current_time = TimeCurrent(); if (current_time - last_alert_time < alert_cooldown_seconds) { // Skip alert if within cooldown period return; } last_alert_time = current_time; string full_message = type + " | Trend Constraint V1.05 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message; if (type == "print") { Print(message); } else if (type == "error") { Print(type + " | Trend Constraint V1.05 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message); } else if (type == "order") { // Add order alert handling if needed } else if (type == "modify") { // Add modify alert handling if needed } else if (type == "indicator") { if (Audible_Alerts) { Alert(full_message); } if (Push_Notifications) { SendNotification(full_message); } // Send to Telegram //Remember to replace the storages path with your actual path. string python_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; string script_path = "C:\\Users\\Pro_tech\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_telegram_message.py"; string command = python_path + " \"" + script_path + "\" \"" + full_message + "\""; // Debugging: Print the command being executed Print("Executing command to send Telegram message: ", command); // Use cmd.exe to execute the command and then wait for 5 seconds string final_command = "/c " + command + " && timeout 5"; int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 1); if (result <= 32) { int error_code = GetLastError(); Print("Failed to execute Python script. Error code: ", error_code); } else { Print("Successfully executed Python script. Result code: ", result); } } } //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, Buffer1); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(0, PLOT_ARROW, 241); SetIndexBuffer(1, Buffer2); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(1, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(1, PLOT_ARROW, 242); SetIndexBuffer(2, Buffer3); PlotIndexSetDouble(2, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(2, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(2, PLOT_ARROW, 236); SetIndexBuffer(3, Buffer4); PlotIndexSetDouble(3, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(3, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); PlotIndexSetInteger(3, PLOT_ARROW, 238); SetIndexBuffer(4, Buffer5); PlotIndexSetDouble(4, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(4, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); SetIndexBuffer(5, Buffer6); PlotIndexSetDouble(5, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetInteger(5, PLOT_DRAW_BEGIN, MathMax(Bars(Symbol(), PERIOD_CURRENT)-PLOT_MAXIMUM_BARS_BACK+1, OMIT_OLDEST_BARS+1)); //initialize myPoint myPoint = Point(); if(Digits() == 5 || Digits() == 3) { myPoint *= 10; } RSI_handle = iRSI(NULL, PERIOD_CURRENT, 14, PRICE_CLOSE); if(RSI_handle < 0) { Print("The creation of iRSI has failed: RSI_handle=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle = iMA(NULL, PERIOD_CURRENT, 7, 0, MODE_SMMA, PRICE_CLOSE); if(MA_handle < 0) { Print("The creation of iMA has failed: MA_handle=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle2 = iMA(NULL, PERIOD_CURRENT, 400, 0, MODE_SMA, PRICE_CLOSE); if(MA_handle2 < 0) { Print("The creation of iMA has failed: MA_handle2=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle3 = iMA(NULL, PERIOD_CURRENT, 100, 0, MODE_EMA, PRICE_CLOSE); if(MA_handle3 < 0) { Print("The creation of iMA has failed: MA_handle3=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle4 = iMA(NULL, PERIOD_CURRENT, 200, 0, MODE_SMA, PRICE_CLOSE); if(MA_handle4 < 0) { Print("The creation of iMA has failed: MA_handle4=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle5 = iMA(NULL, PERIOD_CURRENT, Fast_MA_period, 0, MODE_SMA, PRICE_CLOSE); if(MA_handle5 < 0) { Print("The creation of iMA has failed: MA_handle5=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle6 = iMA(NULL, PERIOD_CURRENT, Slow_MA_period, 0, MODE_SMA, PRICE_CLOSE); if(MA_handle6 < 0) { Print("The creation of iMA has failed: MA_handle6=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } MA_handle7 = iMA(NULL, PERIOD_CURRENT, 200, 0, MODE_EMA, PRICE_CLOSE); if(MA_handle7 < 0) { Print("The creation of iMA has failed: MA_handle7=", INVALID_HANDLE); Print("Runtime error = ", GetLastError()); return(INIT_FAILED); } return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ int OnCalculate(const int rates_total, const int prev_calculated, const datetime& time[], const double& open[], const double& high[], const double& low[], const double& close[], const long& tick_volume[], const long& volume[], const int& spread[]) { int limit = rates_total - prev_calculated; //--- counting from 0 to rates_total ArraySetAsSeries(Buffer1, true); ArraySetAsSeries(Buffer2, true); ArraySetAsSeries(Buffer3, true); ArraySetAsSeries(Buffer4, true); ArraySetAsSeries(Buffer5, true); ArraySetAsSeries(Buffer6, true); //--- initial zero if(prev_calculated < 1) { ArrayInitialize(Buffer1, EMPTY_VALUE); ArrayInitialize(Buffer2, EMPTY_VALUE); ArrayInitialize(Buffer3, EMPTY_VALUE); ArrayInitialize(Buffer4, EMPTY_VALUE); ArrayInitialize(Buffer5, EMPTY_VALUE); ArrayInitialize(Buffer6, EMPTY_VALUE); } else limit++; datetime Time[]; datetime TimeShift[]; if(CopyTime(Symbol(), PERIOD_CURRENT, 0, rates_total, TimeShift) <= 0) return(rates_total); ArraySetAsSeries(TimeShift, true); int barshift_M1[]; ArrayResize(barshift_M1, rates_total); int barshift_D1[]; ArrayResize(barshift_D1, rates_total); for(int i = 0; i < rates_total; i++) { barshift_M1[i] = iBarShift(Symbol(), PERIOD_M1, TimeShift[i]); barshift_D1[i] = iBarShift(Symbol(), PERIOD_D1, TimeShift[i]); } if(BarsCalculated(RSI_handle) <= 0) return(0); if(CopyBuffer(RSI_handle, 0, 0, rates_total, RSI) <= 0) return(rates_total); ArraySetAsSeries(RSI, true); if(CopyOpen(Symbol(), PERIOD_M1, 0, rates_total, Open) <= 0) return(rates_total); ArraySetAsSeries(Open, true); if(CopyClose(Symbol(), PERIOD_D1, 0, rates_total, Close) <= 0) return(rates_total); ArraySetAsSeries(Close, true); if(BarsCalculated(MA_handle) <= 0) return(0); if(CopyBuffer(MA_handle, 0, 0, rates_total, MA) <= 0) return(rates_total); ArraySetAsSeries(MA, true); if(BarsCalculated(MA_handle2) <= 0) return(0); if(CopyBuffer(MA_handle2, 0, 0, rates_total, MA2) <= 0) return(rates_total); ArraySetAsSeries(MA2, true); if(BarsCalculated(MA_handle3) <= 0) return(0); if(CopyBuffer(MA_handle3, 0, 0, rates_total, MA3) <= 0) return(rates_total); ArraySetAsSeries(MA3, true); if(BarsCalculated(MA_handle4) <= 0) return(0); if(CopyBuffer(MA_handle4, 0, 0, rates_total, MA4) <= 0) return(rates_total); ArraySetAsSeries(MA4, true); if(CopyLow(Symbol(), PERIOD_CURRENT, 0, rates_total, Low) <= 0) return(rates_total); ArraySetAsSeries(Low, true); if(CopyHigh(Symbol(), PERIOD_CURRENT, 0, rates_total, High) <= 0) return(rates_total); ArraySetAsSeries(High, true); if(BarsCalculated(MA_handle5) <= 0) return(0); if(CopyBuffer(MA_handle5, 0, 0, rates_total, MA5) <= 0) return(rates_total); ArraySetAsSeries(MA5, true); if(BarsCalculated(MA_handle6) <= 0) return(0); if(CopyBuffer(MA_handle6, 0, 0, rates_total, MA6) <= 0) return(rates_total); ArraySetAsSeries(MA6, true); if(BarsCalculated(MA_handle7) <= 0) return(0); if(CopyBuffer(MA_handle7, 0, 0, rates_total, MA7) <= 0) return(rates_total); ArraySetAsSeries(MA7, true); if(CopyTime(Symbol(), Period(), 0, rates_total, Time) <= 0) return(rates_total); ArraySetAsSeries(Time, true); //--- main loop for(int i = limit-1; i >= 0; i--) { if (i >= MathMin(PLOT_MAXIMUM_BARS_BACK-1, rates_total-1-OMIT_OLDEST_BARS)) continue; //omit some old rates to prevent "Array out of range" or slow calculation if(barshift_M1[i] < 0 || barshift_M1[i] >= rates_total) continue; if(barshift_D1[i] < 0 || barshift_D1[i] >= rates_total) continue; //Indicator Buffer 1 if(RSI[i] < Oversold && RSI[i+1] > Oversold //Relative Strength Index crosses below fixed value && Open[barshift_M1[i]] >= Close[1+barshift_D1[i]] //Candlestick Open >= Candlestick Close && MA[i] > MA2[i] //Moving Average > Moving Average && MA3[i] > MA4[i] //Moving Average > Moving Average ) { Buffer1[i] = Low[1+i]; //Set indicator value at Candlestick Low if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open time_alert = Time[1]; } else { Buffer1[i] = EMPTY_VALUE; } //Indicator Buffer 2 if(RSI[i] > Overbought && RSI[i+1] < Overbought //Relative Strength Index crosses above fixed value && Open[barshift_M1[i]] <= Close[1+barshift_D1[i]] //Candlestick Open <= Candlestick Close && MA[i] < MA2[i] //Moving Average < Moving Average && MA3[i] < MA4[i] //Moving Average < Moving Average ) { Buffer2[i] = High[1+i]; //Set indicator value at Candlestick High if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open time_alert = Time[1]; } else { Buffer2[i] = EMPTY_VALUE; } //Indicator Buffer 3 if(MA5[i] > MA6[i] && MA5[i+1] < MA6[i+1] //Moving Average crosses above Moving Average ) { Buffer3[i] = Low[i]; //Set indicator value at Candlestick Low if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy Reversal"); //Alert on next bar open time_alert = Time[1]; } else { Buffer3[i] = EMPTY_VALUE; } //Indicator Buffer 4 if(MA5[i] < MA6[i] && MA5[i+1] > MA6[i+1] //Moving Average crosses below Moving Average ) { Buffer4[i] = High[i]; //Set indicator value at Candlestick High if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell Reversal"); //Alert on next bar open time_alert = Time[1]; } else { Buffer4[i] = EMPTY_VALUE; } //Indicator Buffer 5, Alert muted by turning it into a comment if(MA3[i] > MA7[i] //Moving Average > Moving Average ) { Buffer5[i] = MA3[i]; //Set indicator value at Moving Average //if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Buy"); //Alert on next bar open //time_alert = Time[1]; } else { Buffer5[i] = EMPTY_VALUE; } //Indicator Buffer 6, Alert muted by turning it into a comment if(MA3[i] < MA7[i] //Moving Average < Moving Average ) { Buffer6[i] = MA3[i]; //Set indicator value at Moving Average //if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Sell"); //Alert on next bar open //time_alert = Time[1]; } else { Buffer6[i] = EMPTY_VALUE; } } return(rates_total); } //+------------------------------------------------------------------+
Обработка ошибок
Failed to send message: A request to the Telegram API was unsuccessful. Error code: 401. Description: Unauthorized
Согласно моим тестам, код ошибки выше был вызван неисправным API_TOKEN, таким как тот, который мы использовали в качестве примера ранее. Вам необходимо использовать рабочее значение API_TOKEN. Я удалил большую часть ошибок, чтобы получить чистый рабочий код для этого руководства. Однако при редактировании или изменении кода могут возникать ошибки. Поэтому тщательно проверяйте каждый свой шаг.
Результаты тестирования
После добавления индикатора на график я включил опцию разрешения DLL, чтобы разрешить нашему индикатору выполнять скрипты через командную строку. Анимированное изображение демонстрирует процесс добавления индикатора и его внешний вид на графике.
Вы можете проверить, работает ли скрипт, запустив файл по указанному пути через командную строку (смотрите изображение ниже). В командной строке, открытой в папке со скриптом, введите python send_telegram_message.py. Сообщение отправлено успешно, ответ показывает, что скрипт работает, и тестовое сообщение также пересылается в чат.
На изображении ниже показано начало разговора с ботом, что позволило нам получить идентификатор чата в API бота. Также показан входящий сигнал, отправленный ботом из Trend Constraint V1.05. Сигналы поступили сразу же после их генерации на платформе MetaTrader 5.
Заключение
Мы успешно интегрировали Telegram в нашу модель. Trend Constraint V1.05 значительно усовершенствован и теперь может передавать сигналы в том числе и во внешнюю систему, предоставляя преимущества трейдерам по всему миру с доступом к Telegram. Передача сигнала происходит быстро, без задержек благодаря эффективному выполнению алгоритма. Система привязана к конкретному индикатору в пределах платформы, что гарантирует отсутствие помех для других функций. Сигналы безопасно передаются напрямую на указанный идентификатор. Эти системы могут быть размещены на виртуальном частном сервере для непрерывной работы. В ходе разработки подобных проектов могут возникать ошибки, но я рад, что мне удалось их успешно решить.
Надеюсь, этот проект вас вдохновил. Если вы столкнулись с трудностями при таком типе интеграции, пожалуйста, поделитесь своими комментариями. Исходные файлы приложены к статье. Вы можете вносить в них изменения при работе над собственными проектами и исследовать те или иные идеи с помощью комментариев, предоставленных в образовательных целях. Далее я планирую интегрировать еще одну популярную социальную платформу — WhatsApp.
Вложение | Описание |
---|---|
send_telegram_message.py | Скрипт, позволяющий индикатору передавать уведомления в Telegram, содержит API_Token и Chat ID |
Trend Constraint V1.05.mq5 | Исходный код основной программы-индикатора MQL5 |
Telebot_API.txt | Структура Telegram Bot API |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/14968
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.





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