Candlestick Trend Constraintモデルの構築(第5回):通知システム(パート3)
内容
はじめに
モデルでシグナルのアクセシビリティを拡大し、すべてのユーザーにとって有益なものにしました。さらに、有名なMetaTrader 5取引プラットフォーム上でシグナル用にソーシャルネットワークをシームレスに統合する方法について、多くの今後の開発者の意欲をかきたてました。最後に、WhatsAppとの統合について詳しく学びましょう。ここでの目標は、カスタムMetaTrader 5指標によって生成されたシグナルを自動的にWhatsApp番号またはグループに送信することです。MetaはWhatsAppに新しいチャンネル機能を導入しました。これにより、シグナルがより広い視聴者に届くようになります。ポーター・ゲイルが著書「Your Network Is Your Net Worth」で述べたように、成功するためにはコミュニティの一員であることが重要です。このような進歩を通じて、特にTelegramやWhatsAppのような人気のあるプラットフォームでは、大きなコミュニティに影響を与えることができます。
あらゆるソーシャルメディアプラットフォームをMetaTrader 5に統合するプロセスの概要を示すフローチャートを掲載することにしました。これは、私の研究に基づいた段階的な展開の要約です。重要な段階はフローチャートに明確に示されています。いくつかの段階を簡単に説明します。フローチャートは、開発中に遭遇した経験を物語っています。エラーが発生する可能性があるため、特定のプロセスをやり直す準備をしておくことが重要です。本稿では、執筆中にかなりの量のデバッグをおこないましたが、私が効果的だと確認したものだけを取り上げます。これらは、パート2で通知のためにTelegramをTrend Constraint指標を統合する際におこなった段階です。
ソーシャルプラットフォームとMetaTrader 5の統合フローチャート
このフローチャートは、最終段階である決定段階(「はい」または「いいえ」の 2 つの結果が考えられる)を統合するプロセス全体を簡単に理解できるようにします。
テストは成功したか?
「はい」の場合「いいえ」の場合
- 統合の展開
- 監視と維持(すべてが順調に機能し、当初の目標通りに新システムを使い始めることができる)
- 終了(正常な統合の終了)
- デバッグと問題の修正(「開発環境の設定」に戻る。主な考え方は、すべてのステップを戻ってエラーをチェックし、修正して再度テストすること)
- 他の選択肢を探る(APIが利用できない場合は、ループバックして他のソーシャルプラットフォームを試す)
メッセージングAPIの設定
提供されたフローチャートに基づき、前回のTelegramの場合と同様に、WhatsAppの統合を進めることにしました。どちらのプラットフォームも、ここでの目的に沿ったAPIを提供しています。WhatsAppを選択したのは、その幅広いユーザーベースが主な理由です。個人的には、他のソーシャルプラットフォームよりもWhatsAppを使用する頻度が高く、このプラットフォームを通じてシグナルを受信することで、MetaTrader5のビジネスを効果的にモニターすることができます。
whatsthebigdataによると、2023年7月現在、WhatsAppのユニークアクティブユーザーは世界で約27億8000万人です。この人気メッセージングアプリは2020年に20億の大台に乗り、2025年には31億4000万に達すると予想されています。
これらのネットワークを通じて純資産を増やすためのプラットフォームを選択する際には、統計的な数値を考慮に入れるべきです。数多くのメッセージングAPIプロバイダーがオンラインで見つかりますが、使いやすさやユーザーフレンドリーなレベルは様々です。多くは体験版を提供していますが、高度な機能を利用するにはサブスクリプションが必要です。私の調査によると、インターネットで入手可能な選択肢には以下のようなものがあります。
このプロジェクトでTwilioを使用したのは、よりユーザーフレンドリーな方法でAPIアクセスを提供し、WhatsAppとの統合に適しているからです。提供されるオプションは効果的ですが、設定プロセスが複雑な場合があり、他の主要機能にアクセスするにはサブスクリプションが必要です。とはいえ、理解を広めるためにも、探索する価値はあります。
Twilioのアカウントを作成し、すべての設定をおこないました。サイトに必要な主な要素は、account_sid、auth_token、Twilioサンドボックスで割り当てられた Whatsapp番号、会話を開始するための自分の受信番号です。スクリプトに必要なすべての認証情報は、Twilioアカウントページにあります。
Twilioアカウントのコンソールセクションに移動すると、WhatsAppメッセージの送信先が表示されます。そこにサンドボックス番号が表示されます。この番号はAPI側に保存され、MetaTrader 5から受信した通知を、参加しているWhatsApp番号にプッシュします。残念なのは、参加者にしか送信されないことです。下のスクリーンショットは、参加するためにサンドボックス番号に送信する必要がある参加メッセージを示しています。まず、サンドボックスの番号を連絡先リストに保存し、そのサンドボックスを見つけて「join so-cave」と送信します。それが完了すると、自動的に、コマンドプロンプトでテストメッセージを含む統合スクリプトを実行し、それが機能していることを確認できます。
これが私が参加したときのスクリーンショットです。
この時点で、フローチャートで説明したように、統合プロセスに必要なAPI認証情報を取得しています。それでは次の段階に進み、これらの認証情報の応用について詳しく掘り下げていきましょう。
send_whatsapp_message.pyスクリプト
スクリプティングにはIDLEを利用できます。Pythonインタプリタがプリインストールされています。あるいは、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アカウントのAuthトークン
- 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_whatapp_message.pyファイルが保存されているパスを開き、以下のコマンドを入力します。
python send_whatsapp_message.py "Test Message"
問題がなければWhatsApp番号にメッセージが送信され、コマンドプロンプトウィンドウに「Message sent successfully」と表示されます。エラーが発生した場合は、コードと認証情報を再確認する必要があります。特に、提供されたコードはデバッグされ、問題なく動作するように修正されているからです。以下、いくつかのセクションに分けて、私の統合作業のスクリーンショットを紹介します。
Whatsapp通知用のTrend Constraintの修正
指標のコードを大幅に変更することなく統合を完了させるために、WhatsappメッセージをサポートするためにmyAlertブロックを調整することに集中することにしました。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); } //+------------------------------------------------------------------+
テストと結果
上記のサブセクションのうち、2つのサブセクションでテストを実施しました。プロジェクトは私の方では正常に機能しています。以下に受信したシグナルのスクリーンショットを掲載します。
この指標はうまく機能しており、目標に従って描画し、設計通りにソーシャルチャンネルにシグナルを送っています。以下はブーム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と呼ばれ、アプリケーションウィンドウの表示方法を制御します。パラメータを1 (SW_SHOWNORMAL)ではなく0 (SW_HIDE)に変更します。
セキュリティ
使用される手法は、潜在的な脅威とみなされる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が信頼できる機関によって署名されていることを確認し、その完全性と出所を検証します。
- 最小特権:潜在的な悪用の影響を減らすために、必要最小限の特権でアプリケーションを実行します。
- 入力の検証:特にコマンド文字列を作成するときは、常に入力パラメータを検証し、サニタイズします。
- エラー処理:堅牢なエラー処理を実装して、エラーを優雅に管理し、ログに記録します。
- 定期更新:ソフトウェアやライブラリのセキュリティパッチやアップデートを常に最新の状態に保ちます。
- 絶縁:サンドボックスやその他の分離テクニックを使用して、潜在的にリスクの高い操作を制約された環境で実行します。
結論
WhatsAppの統合が完了しました。私たちのモデルはMetaTrader 5からTelegramとWhatsAppにシグナルを送信できるようになりました。統合を1つのプログラムに統合し、両方のプラットフォームで同時にシグナルをブロードキャストすることが可能です。シグナルの配信にはわずかな差異が見られ、プッシュ通知はソーシャルプラットフォームの通知よりもわずかに早く届きますが、その時間差はおそらく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千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索