English Русский Español Deutsch 日本語 Português
preview
构建K线趋势约束模型(第五部分):通知系统(第三部分)

构建K线趋势约束模型(第五部分):通知系统(第三部分)

MetaTrader 5交易系统 | 14 一月 2025, 09:12
311 0
Clemence Benjamin
Clemence Benjamin

内容:


概述

我们现已在模型中扩展了信号的可访问性,使其对所有用户都大有帮助。此外,我们还激励了众多新兴开发者,指导他们如何在我们广受欢迎的MetaTrader 5交易平台中无缝集成社交网络以获取信号。最后,我们来详细探讨一下WhatsApp集成的细节。我们的目标是自动将我们自定义的MetaTrader 5指标生成的信号发送到WhatsApp号码或群组。WhatsApp推出了新的频道功能,可以帮助信号覆盖更广泛的受众。正如波特·盖尔(Porter Gale)在她的著作《你的网络就是你的净值》(Your Network Is Your Net Worth)中所言,融入一个社群对于个人的蓬勃发展至关重要。通过这些进步,我们可以在像Telegram和WhatsApp这样的热门平台上对广大社群产生影响。

我决定通过一个流程图,简述任何社交媒体平台与MetaTrader 5集成的流程。这是基于我的研究得出的简化版分步开发流程。流程图清晰地描绘了关键阶段。让我简要地解释一下其中的部分阶段。流程图叙述了在开发过程中积累的经验。重要的一点是,要做好重复某些流程的准备,因为可能会出现错误。在本文中,我将只关注那些经我验证有效的方法,尽管在写作过程中进行了大量的调试。这些是我们在(第二部分)中将Telegram与趋势约束指标集成用于通知时经历的阶段。

社交媒体平台与MetaTrader 5集成流程图


社交媒体平台与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 API

通过登录您的Twilio账户的控制台部分,您可以找到发送WhatsApp消息的位置。在那里,您可以看到您的sandbox编号,该号码将在API端保存,以便将来自MetaTrader 5的通知推送给已加入的WhatsApp号码。但遗憾的是,它只能发送给已加入的参与者。请参见下面的截图,展示了您需要发送sandbox编号申请加入的消息。首先,将sandbox编号保存到您的联系人列表中,然后找到它并发送“join so-cave”。一旦完成此操作,您就可以在命令提示符中运行集成脚本并发送测试消息,以查看其工作情况。

加入Twilio sandbox

这是一张我加入Twilio sandbox的截图:

为Twilio加入WhatsApp号码

至此,我们已经按照流程图概述的集成过程获得了所需的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:要发送的消息文本。

url:构建用于Twilio API端点的URL。data:一个数据集合,包含要在POST请求中发送的消息数据。

requests.post:使用消息数据和认证凭证向Twilio API发送POST请求。
response:捕获来自Twilio API的响应。
status_code:检查响应的状态码以确定消息是否成功发送(201表示成功)。

脚本最终的这段构成了主代码,是执行发生的位置。当执行此脚本时,将:

  1. 从命令行参数中读取消息文本。
  2. 使用提供的凭证、电话号码和消息调用send_whatsapp_message函数。
  3. 根据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);
  }
//+------------------------------------------------------------------+


测试结果

我们已经在上述两个小节中进行了测试。我们的项目端运行正常。我在下方附上了收到信号的部分截图。

一个可正常工作的WhatsApp集成

指标运行良好,能够按照既定目标绘制图形并向我的社交媒体渠道发送信号。下方附上Boom 500指数图:

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

使用PatchTST机器学习算法预测未来24小时的价格走势 使用PatchTST机器学习算法预测未来24小时的价格走势
在本文中,我们将应用2023年发布的一种相对复杂的神经网络算法——PatchTST,来预测未来24小时的价格走势。我们将使用官方仓库的代码,并对其进行一些微小的修改,训练一个针对EURUSD(欧元兑美元)的模型,然后在Python和MQL5环境中应用该模型进行未来预测。
跨邻域搜索(ANS) 跨邻域搜索(ANS)
本文揭示了跨邻域搜索(ANS)算法的潜力,作为重要的一步,旨在开发灵活且智能的优化方法,使其能够在搜索空间中考虑问题的具体特性和环境的动态变化。
开发回放系统(第 52 部分):事情变得复杂(四) 开发回放系统(第 52 部分):事情变得复杂(四)
在本文中,我们将修改鼠标指针,以实现与控制指标的交互,确保可靠、稳定地运行。
开发多币种 EA 交易(第 12 部分):开发自营交易级别风险管理器 开发多币种 EA 交易(第 12 部分):开发自营交易级别风险管理器
在正在开发的 EA 中,我们已经有了某种控制回撤的机制。但它具有概率性,因为它是以历史价格数据的测试结果为基础的。因此,回撤有时会超过最大预期值(尽管概率很小)。让我们试着增加一种机制,以确保遵守指定的回撤水平。