
构建K线趋势约束模型(第五部分):通知系统(第三部分)
内容:
概述
我们现已在模型中扩展了信号的可访问性,使其对所有用户都大有帮助。此外,我们还激励了众多新兴开发者,指导他们如何在我们广受欢迎的MetaTrader 5交易平台中无缝集成社交网络以获取信号。最后,我们来详细探讨一下WhatsApp集成的细节。我们的目标是自动将我们自定义的MetaTrader 5指标生成的信号发送到WhatsApp号码或群组。WhatsApp推出了新的频道功能,可以帮助信号覆盖更广泛的受众。正如波特·盖尔(Porter Gale)在她的著作《你的网络就是你的净值》(Your Network Is Your Net Worth)中所言,融入一个社群对于个人的蓬勃发展至关重要。通过这些进步,我们可以在像Telegram和WhatsApp这样的热门平台上对广大社群产生影响。
我决定通过一个流程图,简述任何社交媒体平台与MetaTrader 5集成的流程。这是基于我的研究得出的简化版分步开发流程。流程图清晰地描绘了关键阶段。让我简要地解释一下其中的部分阶段。流程图叙述了在开发过程中积累的经验。重要的一点是,要做好重复某些流程的准备,因为可能会出现错误。在本文中,我将只关注那些经我验证有效的方法,尽管在写作过程中进行了大量的调试。这些是我们在(第二部分)中将Telegram与趋势约束指标集成用于通知时经历的阶段。
社交媒体平台与MetaTrader 5集成流程图
根据流程图,我们简化了对整个最终阶段(即决策阶段)的理解过程,该阶段有两种可能的结果:是或否。
测试是否成功?
如果成功:如果不成功:
- 部署集成
- 监控与维护(一切正常,我们可以按照原定目标开始使用新系统)
- 结束(成功集成的终点)
- 调试并修复问题(回退到设置开发环境阶段,主要是重新检查所有步骤以查找错误,更正并重新测试)
- 搜索其他选项(如果没有可用的API,则回退到尝试其他社交平台,因此也要回退到早期阶段)
配置消息API
根据提供的流程图,我们决定像上一篇文章中集成Telegram那样,推进WhatsApp的集成工作。这两个平台都提供了符合我们目标的API。选择WhatsApp的主要原因是其庞大的用户基础。就我个人而言,我使用WhatsApp的频率高于任何其他社交平台,通过该平台接收信号将有助于我有效地监控MetaTrader5业务。
根据whatsthebigdata,截至2023年7月,WhatsApp在全球拥有约27.8亿的活跃用户。这款流行的即时通讯应用早在2020年就达到了20亿用户的里程碑,并预计将在2025年将达到31.4亿用户。
在选择平台以增加我们的净值时,应考虑统计数值。网上可以找到许多消息API提供商,它们具有不同程度的易用性和用户友好性。许多提供商提供试用服务,但要访问高级功能则需要订阅。据我了解,互联网上的一些可用选项包括:
我在这个项目中使用了Twilio,因为它以更用户友好的方式提供API访问,并且适合与WhatsApp集成。虽然提供的选项很有效,但配置过程可能相对复杂,而且需要订阅才能访问其他关键功能。然而,为了拓宽领域,这些选项还是值得探索的。
我创建了Twilio账户并完成了所有设置。使用网站所需的关键要素包括account_sid(账户SID)、auth_token(身份验证令牌)、在Twilio sandbox中分配的WhatsApp号码,以及用于发起对话的个人接收号码。脚本所需的所有要素都可以在您的Twilio账户页面上找到。
通过登录您的Twilio账户的控制台部分,您可以找到发送WhatsApp消息的位置。在那里,您可以看到您的sandbox编号,该号码将在API端保存,以便将来自MetaTrader 5的通知推送给已加入的WhatsApp号码。但遗憾的是,它只能发送给已加入的参与者。请参见下面的截图,展示了您需要发送sandbox编号申请加入的消息。首先,将sandbox编号保存到您的联系人列表中,然后找到它并发送“join so-cave”。一旦完成此操作,您就可以在命令提示符中运行集成脚本并发送测试消息,以查看其工作情况。
这是一张我加入Twilio sandbox的截图:
至此,我们已经按照流程图概述的集成过程获得了所需的API凭证。接下来,让我们进入下一阶段,详细探讨如何应用这些凭证。
脚本 (send_whatsapp_message.py)
我们可以使用IDLE进行脚本编写。IDLE可以随Python解释器进行预安装。或者,我更倾向于使用开源选择,如Notepad++进行脚本编写。Notepad++支持多种语言,并提供适当的格式化功能。首先,创建一个新文件,并将其命名为send_whatsapp_message.py。建议将脚本文件保存在Python解释器所在目录的scripts文件夹中。我从该目录测试程序时未遇到任何问题。
接下来,我们将在脚本中导入模块:
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:此函数使用Twilio API发送WhatsApp消息。
在函数定义中,有几个脚本执行所需的参数。其中许多参数是在设置Twilio账户时获得的。
参数:
- account_sid:来自您Twilio的帐户SID。
- auth_token:来自您Twilio帐户的认证令牌。
- from_whatsapp_number:启用了WhatsApp的Twilio号码,格式为带有"whatsapp:"前缀的字符串。
- to_whatsapp_number:接收者的WhatsApp号码,格式为带有"whatsapp:"前缀的字符串。
- message:要发送的消息文本。
requests.post:使用消息数据和认证凭证向Twilio API发送POST请求。
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_whatsapp_message.py文件的路径,并输入以下命令:
python send_whatsapp_message.py "Test Message"
如果一切正常,消息将被发送到您的WhatsApp号码,命令提示符窗口将显示“消息发送成功”。如果遇到错误,您需要重新检查代码和凭据,特别是由于提供的代码已经过调试和修正,应该可以正常工作。 下面我将分享我的工作集成截图。
修改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文件由可信的权威机构签名,这有助于验证其完整性和来源。
- 最小权限原则:以执行所需的最小权限运行应用程序,以降低任何潜在利用的影响。
- 验证输入:始终验证和清理输入参数,特别是在构建命令字符串时。
- 错误处理:实现完善的错误处理机制,全面地管理和记录错误。
- 定期更新:使您的软件和库保持最新,及时应用安全补丁和更新。
- 隔离:使用sandbox或其他隔离技术,在受限环境中运行可能存在风险的操作。
结论
WhatsApp集成已成功完成。我们的模型现在可以从MetaTrader 5向Telegram和WhatsApp发送信号。将集成整合到一个程序中,并同时在两个平台上广播信号是可行的。我注意到信号推送通知的到达时间略有差异,推送通知比社交平台的通知早到达几秒钟,尽管时间差异非常小,可能只有大约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

