Построение модели для ограничения диапазона сигналов по тренду (Часть 5): Система уведомлений (Часть III)
Содержание:
- Введение
- Настройка API для обмена сообщениями
- Скрипт send_whatsapp_message.py
- Адаптация индикатора Trend Constraint для уведомлений WhatsApp
- Тестирование
- Безопасность
- Заключение
Введение
Мы расширили доступность сигнала нашей модели, сделав ее выгодной для всех пользователей. Кроме того, мы показали многочисленным будущим разработчикам, как можно легко интегрировать социальные сети для получения сигналов от торговой платформы MetaTrader 5. Давайте в заключение рассмотрим детали интеграции WhatsApp. Наша цель — автоматически отправлять сигналы, генерируемые нашими пользовательскими индикаторами MetaTrader 5, на номер или в группу WhatsApp. Компания Meta представила новую функцию каналов в WhatsApp, которая может помочь охватить более широкую аудиторию. Как отмечает Портер Гейл (Porter Gale) в своей книге "Ваши связи - ваш капитал" (Your Network Is Your Net Worth), чтобы достичь успеха, крайне важно быть частью сообщества. Благодаря нововведениям мы можем оказать влияние на большое сообщество, особенно на таких популярных платформах, как Telegram и WhatsApp.
Ниже представлена блок-схема, описывающая интеграцию любой платформы социальных сетей с MetaTrader 5. Это краткое пошаговое описание, основанное на моих исследованиях. Критически важные этапы четко обозначены на блок-схеме. Рассмотрим кратко некоторые из этапов. Блок-схема описывает опыт, полученный в ходе разработки. Важно быть готовым к повторному выполнению некоторых процессов, поскольку могут возникнуть ошибки. В этой статье я сосредоточусь только на том, что доказало свою эффективность, хотя в процессе написания была проделана значительная работа по отладке. Это этапы, которые мы рассматривали в Части II при интеграции Telegram с индикатором Trend Constraint для уведомлений.
Интеграция социальных платформ с Meta Trader 5
Блок-схема упрощает наше понимание всего процесса интеграции на заключительном этапе — этапе принятия решения, который имеет два возможных результата: "Да" или "Нет".
Тестирование прошло успешно?
Если да:Если нет:
- Начать интеграцию
- Мониторинг и обслуживание (все работает хорошо, и мы можем начать использовать нашу новую систему в соответствии с первоначальной целью)
- Конец (Конец успешной интеграции)
- Отладка и исправление проблем (возврат к настройке среды разработки, основная идея — пройти все предыдущие шаги, чтобы проверить ошибки, исправить их и снова протестировать)
- Изучите другие варианты (вернитесь назад, чтобы попробовать другие социальные платформы, то есть более ранние этапы, если API недоступен)
Настройка API для обмена сообщениями
На основе представленной блок-схемы мы решили перейти к интеграции с WhatsApp, как мы это сделали для Telegram в предыдущей статье. Обе платформы предлагают API, соответствующие нашим целям. Выбор WhatsApp обусловлен прежде всего его обширной базой пользователей. Лично я использую WhatsApp чаще, чем любую другую социальную платформу, и получение сигналов через эту платформу поможет мне эффективно контролировать свою работу в MetaTrader 5.
В соответствии с whatsthebigdata, по состоянию на июль 2023 года у WhatsApp было около 2,78 млрд уникальных активных пользователей по всему миру. Это популярное приложение для обмена сообщениями достигло отметки в 2 миллиарда еще в 2020 году и, как ожидается, достигнет 3,14 миллиарда к 2025 году.
При выборе платформ следует учитывать статистику, чтобы увеличить нашу чистую стоимость с помощью этих сетей. В сети можно найти множество поставщиков API для обмена сообщениями, различающихся по уровню простоты использования и удобства для пользователя. Многие предлагают пробный период, но для доступа к расширенным функциям требуется подписка. В Интернете я нашел следующие варианты:
Для этого проекта я использовал Twilio, поскольку он предлагает более удобный доступ к API и подходит для интеграции с WhatsApp. Предоставленные возможности эффективны, хотя процесс настройки может оказаться довольно сложным, а для доступа к другим ключевым функциям требуются подписки. Тем не менее, эти функции стоит изучить, чтобы расширить понимание.
Я создал учетную запись Twilio и все настроил. Ключевые требуемые элементы: account_sid, auth_token, номер Whatsapp, указанный в песочнице Twilio, а также ваш номер получателя для начала обмена сообщениями. Все необходимые учетные данные для скрипта можно найти на странице вашего аккаунта Twilio.
Перейдя в раздел консоли вашего аккаунта Twilio, вы можете определить, куда отправляются сообщения WhatsApp. Там вы увидите свой номер песочницы, который будет сохранен на стороне API для отправки уведомлений, входящих из MetaTrader 5, на ваши подключенные номера WhatsApp. К сожалению, сообщение отправляется только присоединившимся участникам. На скриншоте ниже показано сообщение о присоединении, которое необходимо отправить на номер песочницы, чтобы присоединиться. Сохраните номер песочницы в вашем списке контактов, затем найдите его и отправьте сообщение "join so-cave". После этого вы автоматически сможете запустить скрипт интеграции с тестовыми сообщениями в командной строке и убедиться, что он работает.
Скриншот моего подключения:
На этом этапе мы получили учетные данные API, необходимые для интеграции, как показано на блок-схеме. Давайте теперь перейдем к следующему этапу, где мы подробно рассмотрим применение этих данных.
Скрипт send_whatsapp_message.py
Мы можем использовать IDLE для написания скриптов. Он поставляется с предустановленным интерпретатором Python. В качестве альтернативы я склоняюсь к вариантам с открытым исходным кодом, таким как Notepad++, для написания скриптов. Notepad++ поддерживает различные языки с соответствующим форматированием. Начнем с создания нового файла, назовем его send_whatsapp_message.py. Рекомендуется сохранить файл скрипта в папке скриптов вместе с интерпретатором Python. При тестировании программы из этого каталога у меня не возникло никаких проблем.
Начнем с импорта модуля в наш скрипт:
import sys import requests
- import sys - модуль обеспечивает доступ к некоторым переменным, используемым или поддерживаемым интерпретатором Python. Позволяет взаимодействовать с интерпретатором.
- import requests - модуль используется для отправки HTTP-запросов. Это упрощает процесс выполнения API-запросов.
На следующем этапе определяются функции:
def send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message): url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json" data = { "From": f"whatsapp:{from_whatsapp_number}", "To": f"whatsapp:{to_whatsapp_number}", "Body": message, } response = requests.post(url, data=data, auth=(account_sid, auth_token)) if response.status_code == 201: print("Message sent successfully") else: print(f"Failed to send message: {response.status_code} - {response.text}")
send_whatsapp_message - функция отправляет сообщение WhatsApp с помощью Twilio API.
В определении функции у нас есть несколько параметров, необходимых для выполнения скрипта. Многие из них были получены при создании учетной записи Twilio.
Параметры:
- account_sid - SID из вашей учетной записи Twilio.
- auth_token - токен аутентификации из вашей учетной записи Twilio.
- from_whatsapp_number - номер Twilio с поддержкой WhatsApp, отформатированный с префиксом "whatsapp:".
- to_whatsapp_number - номер получателя в WhatsApp, отформатированный с префиксом "whatsapp:".
- message - текст сообщения для отправки.
requests.post - отправляет запрос POST в Twilio API с данными сообщения и учетными данными аутентификации.
response - сохраняет ответ от Twilio API.
status_code - проверяет код состояния ответа, чтобы определить, было ли сообщение отправлено успешно (201 указывает на успешную отправку).
Последний фрагмент скрипта формирует основной код, и именно здесь будет происходить выполнение. При выполнении произойдет следующее:
- Скрипт прочитает текст сообщения из аргументов командной строки.
- Вызовет функцию send_whatsapp_message с указанными учетными данными, номерами телефонов и сообщением.
- Выведет сообщение об успешном или неудачном выполнении на основе ответа Twilio API
# Replace with your actual Twilio credentials and phone numbers account_sid = "**********************" # Your SID auth_token = "***************************" # Your Auth Token from_whatsapp_number = "+14**********" # Your WhatsApp-enabled Twilio number to_whatsapp_number = "+************" # Recipient's WhatsApp number message = sys.argv[1] send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message)
Наш готовый рабочий файл send_whatsapp_message.py будет выглядеть так:
import sys import requests def send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message): url = f"https://api.twilio.com/2010-04-01/Accounts/{account_sid}/Messages.json" data = { "From": f"whatsapp:{from_whatsapp_number}", "To": f"whatsapp:{to_whatsapp_number}", "Body": message, } response = requests.post(url, data=data, auth=(account_sid, auth_token)) if response.status_code == 201: print("Message sent successfully") else: print(f"Failed to send message: {response.status_code} - {response.text}") # Replace with your actual Twilio credentials and phone numbers account_sid = "****************" auth_token = "**********************" from_whatsapp_number = "+14***********" to_whatsapp_number = "+***************" message = sys.argv[1] send_whatsapp_message(account_sid, auth_token, from_whatsapp_number, to_whatsapp_number, message)
Мы можем перейти к тестированию в командной строке, чтобы проверить, работает ли скрипт. Откройте путь к файлу send_whatapp_message.py и введите следующую команду:
python send_whatsapp_message.py "Test Message"
Если всё в порядке, сообщение будет отправлено на ваш номер WhatsApp, а в окне командной строки отобразится сообщение "Message sent successfully" (сообщение успешно отправлено). Если вы столкнулись с ошибками, вам необходимо перепроверить свой код и учетные данные. Предоставленный код был отлажен и исправлен для корректной работы. Ниже я поделюсь скриншотом моей работающей интеграции.
Адаптация индикатора Trend Constraint для уведомлений WhatsApp
Чтобы завершить интеграцию без существенного изменения кода индикатора, я решил сосредоточиться на настройке блока myAlert для поддержки сообщений WhatsApp. Крайне важно уделить особое внимание правильному вводу пути к интерпретатору Python и скрипту. Это ключевые компоненты интеграции при переходе с этого фрагмента кода. Большую часть кода из нашей предыдущей интеграции с Telegram я оставил без изменений. Кроме того, я включил функцию инициализации тестирования, которая позволяет отправлять приветственное сообщение при запуске для проверки функциональности системы перед получением реальных сигналов.
int OnInit() { // Send test message on launch myAlert("info", "Thank you for subscribing. You shall be receiving Trend Constraint signal alerts via Whatsapp."); return(INIT_SUCCEEDED); }
Добавление в OnInit() гарантирует, что сообщение будет отправлено на ваши номера, когда вы добавите индикатор в любой чат.
Ниже представлена наиболее важная часть нашей логики интеграции для отправки сообщений в WhatsApp. Я выделил разделы кода, которые я изменил, по сравнению с кодом, использованным для интеграции Telegram.
//--- ShellExecuteW declaration ---------------------------------------------- #import "shell32.dll" int ShellExecuteW(int hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd); #import //--- global variables ------------------------------------------------------ datetime last_alert_time; input int alert_cooldown_seconds = 60; // Cooldown period in seconds //--- myAlert function ------------------------------------------------------ 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.06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message; if (type == "print") { Print(message); } else if (type == "error") { Print(type + " | Trend Constraint V1.06 @ " + 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" || type == "info") { if (Audible_Alerts) { Alert(full_message); } if (Push_Notifications) { SendNotification(full_message); } // Send to WhatsApp //Edit to match your actual path, these I gave as an example for my computer string python_path = "C:\\Users\\******\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; string script_path = "C:\\Users\\******\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_whatsapp_message.py"; string command = python_path + " \"" + script_path + "\" \"" + full_message + "\""; // Debugging: Print the command being executed Print("Executing command to send WhatsApp 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); } } }
Ниже представлен код Trend Constraint V1.06 после внесения изменений:
//+------------------------------------------------------------------+ //| Trend Constraint V1.06.mq5 | //| Copyright 2024, Clemence Benjamin | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Clemence Benjamin" #property link "https://www.mql5.com" #property version "1.06" #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 //--- global variables ------------------------------------------------------ datetime last_alert_time; input int alert_cooldown_seconds = 60; // Cooldown period in seconds //--- myAlert function ------------------------------------------------------ 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.06 @ " + Symbol() + "," + IntegerToString(Period()) + " | " + message; if (type == "print") { Print(message); } else if (type == "error") { Print(type + " | Trend Constraint V1.06 @ " + 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" || type == "info") { if (Audible_Alerts) { Alert(full_message); } if (Push_Notifications) { SendNotification(full_message); } // Send to WhatsApp string python_path = "C:\\Users\\****\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; string script_path = "C:\\Users\\****\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_whatsapp_message.py"; string command = python_path + " \"" + script_path + "\" \"" + full_message + "\""; // Debugging: Print the command being executed Print("Executing command to send WhatsApp 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)); // Send test message on launch myAlert("info", "Thank you for subscribing. You shall be receiving Trend Constraint signal alerts via Whatsapp."); //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); } //+------------------------------------------------------------------+
Тестирование и результаты
Мы провели тестирование по двум из вышеуказанных подразделов. С моей стороны проект функционирует нормально. Ниже я приложил несколько скриншотов полученных сигналов.
Индикатор работает хорошо, отображает данные в соответствии с поставленной целью и посылает сигналы в мои социальные каналы, как и было задумано. Ниже прилагается изображение графика индекса Boom 500:
Наша программа при запуске открывает окно командной строки для отображения текущих процессов и отправляет вступительное сообщение. Та же процедура применяется при передаче сигналов с течением времени. Однако всплывающее окно может мешать работе над задачами, поэтому программу можно изменить, чтобы она работала без отображения окна командной строки. В этом проекте мы решили не скрывать окно для наблюдения за процессом. Чтобы предотвратить появление окна командной строки, вы можете включить следующую строку кода:
// execute the command without showing the window string final_command = "/c " + command + " && timeout 5"; int result = ShellExecuteW(0, "open", "cmd.exe", final_command, NULL, 0); // Change the last parameter to 0 (SW_HIDE) 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); }
Последний параметр называется nShowCmd. Он управляет отображением окна приложения, меняя параметр на 0 (SW_HIDE) с 1 (SW_SHOWNORMAL).
Безопасность
Используемая техника использует файлы DLL, которые считаются потенциальной угрозой. В ходе своего исследования я определил следующие сопутствующие риски. Обязательно позаботьтесь о сохранности своих учетных данных для API. Я также попытался скрыть свои данные в этой статье. Раскрывайте их только доверенным лицам.
Некоторые опасности, связанные с файлами DLL:
- Вредоносное ПО: Загрузка вредоносной DLL-библиотеки может привести к выполнению вредоносного кода в вашей системе. Вероятность повышается, если DLL получена из ненадежного или неизвестного источника.
- Взлом DLL: Если злоумышленник сможет разместить вредоносную DLL в месте, откуда загружается приложение, он сможет выполнить произвольный код. Это особенно опасно, если приложение работает с повышенными привилегиями.
- Сбои и нестабильная работа: Неправильное использование функций DLL или вызов функций с недопустимыми параметрами может привести к сбоям приложения или нестабильности системы.
- Утечки ресурсов: Неправильная обработка ресурсов (например, памяти, хэндлов файлов) в функциях DLL может привести к утечкам ресурсов, что со временем скажется на производительности системы.
- Конфликты версий: Различные версии DLL могут иметь разные сигнатуры или поведение функций, что приводит к проблемам совместимости.
- Ад зависимостей: Управление зависимостями от нескольких DLL и их версий усложняет дело и может приводить к ошибкам выполнения.
Для борьбы с такими угрозами крайне важно включить в нашу программу функции безопасности. Следующая строка кода может решить проблемы безопасности, если будет реализована в нашей модели:
// You can secure the program by adding SecureExecuteCommand(command); below these lines. string python_path = "C:\\Users\\*****\\AppData\\Local\\Programs\\Python\\Python312\\python.exe"; string script_path = "C:\\Users\\*****\\AppData\\Local\\Programs\\Python\\Python312\\Scripts\\send_whatsapp_message.py"; //***** is your computer user name, the rest of the path txt is correct based on the python version being used. string message = "Hello, this is a test message"; string command = python_path + " \"" + script_path + "\" \"" + message + "\""; SecureExecuteCommand(command);
Функция SecureExecuteCommand() проверяет наличие недопустимых символов в командной строке, что может помочь предотвратить атаки путем внедрения команд.
Вот некоторые оптимальные методы борьбы с рисками:
- Проверенные источники: Используйте DLL-библиотеки только из надежных и проверенных источников.
- Подписывание кода: Убедитесь, что DLL подписываются доверенным органом, что помогает проверить их происхождение и целостность.
- Меньшие привилегии: Запускайте приложения с минимальными необходимыми привилегиями, чтобы снизить влияние любой потенциальной уязвимости.
- Validate Inputs: Всегда проверяйте и очищайте входные параметры, особенно при построении командных строк.
- Обработка ошибок: Реализуйте надежную обработку ошибок для корректного управления и регистрации ошибок.
- Регулярные обновления: Поддерживайте свое программное обеспечение и библиотеки в актуальном состоянии с помощью исправлений и обновлений безопасности.
- Изоляция: Используйте песочницу или другие методы изоляции для выполнения потенциально рискованных операций в ограниченной среде.
Заключение
Интеграция WhatsApp успешно завершена. Теперь наша модель может передавать сигналы из MetaTrader 5 в Telegram и WhatsApp. Интеграцию можно объединить в одну программу и транслировать сигналы на обеих платформах одновременно. Я заметил небольшие расхождения в доставке сигналов: push-уведомления приходят немного раньше, чем на социальных платформах, хотя и с очень небольшой разницей во времени - около 7 секунд. Сигналы Telegram опередили сигналы WhatsApp на несколько секунд. Тем не менее, нашей главной целью было обеспечить доставку наших сигналов, и эта цель была достигнута.
Надеюсь, статья была для вас полезна. Как разработчики, мы должны поддерживать работоспособность блок-схемы до тех пор, пока не достигнем своих целей. Мы должны использовать возможности нашего сообщества для совершенствования наших навыков в программировании алгоритмической торговли. Обмен идеями всегда приветствуется. В нашем следующем проекте мы будем опираться на уже проделанную работу. Ниже вы найдете прикрепленные файлы и их описание.
Файл | Описание |
---|---|
send_whatsapp_message.py | Отправка сообщений из MetaTrader 5 в Twilio, а затем - в WhatsApp. |
Trend Constraint V1.06.mq5 | WhatsApp интегрирован, но для работы на вашем компьютере вам необходимо обновить учетные данные и пути к каталогам. |
Secure Command.mq5 | Скрывает окно командной строки и защищает программу от внешних атак. |
Перевод с английского произведен MetaQuotes Ltd.
Оригинальная статья: https://www.mql5.com/en/articles/14969
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования