English
preview
スイングエントリーモニタリングEAの開発

スイングエントリーモニタリングEAの開発

MetaTrader 5 | 17 4月 2025, 07:36
73 0
Clemence Benjamin
Clemence Benjamin

内容


はじめに

BTCUSDは、長期的な見通しにおいて注目される主要な取引ペアのひとつです。本稿では、開発プロセスの指針とする例としてこのペアを選びました。

ビットコインの価格は、市場のセンチメント、マクロ経済的要因、規制環境の変化などにより、非常に高いボラティリティを示すのが特徴です。こうした激しい価格変動の中で収益性の高いエントリーポイントを見極めることは、特に手動による分析のみに頼っているトレーダーにとって大きな課題となります。たとえば、過去2年間において、BTCの価格は約16,000ドルの安値から、2024年11月の史上最高値である99,645.39ドルまで大きく変動しました。この期間中、EMA100付近では複数の重要なエントリーチャンスが現れ、長期的な取引戦略において貴重な示唆を与えてくれました。

この課題に対する解決策として、MQL5を用いて長期取引のエントリーシグナルを監視するEAを開発します。このEAは以下のような機能を備えます。

  • 指定された通貨ペアの価格変動を継続的に監視する
  • EMA100を動的な支持または抵抗の指標として活用し、潜在的なエントリーポイントを検出する
  • EMA100での反発など、特定の条件が満たされた場合にトレーダーへアラートを通知する

このツールにより分析作業を自動化し、トレーダーは常時チャートを監視する必要がなくなり、意思決定に集中できるようになります。

以下の画像は、BTCの100日EMAと一致する主要な支持レベルを示しています。

BTC価格反発100日移動平均線

BTCUSD、H4:ビットコイン対米ドル:価格反発EMA 100



2021年から2024年までのBTCUSDの簡単な振り返り

2021年から2024年にかけて、ビットコインは大きな価格変動を経験しました。2022年には、金利の上昇や市場の低迷を背景に急落し、年末には20,000ドルを下回って取引を終えました。しかし2023年には反発し、年初の16,530ドルから年末には42,258ドルまで上昇しました。2024年に入ると、ビットコイン現物ETFの承認と米連邦準備制度(FRB)による利下げを受けて価格は急騰し、11月には過去最高値となる99,645.39ドルを記録しました。

執筆時点でのBTCUSDは約97,300ドルで取引されており、1年前の価格と比較して146.5%の大幅な上昇を示しています。 CoinGeckoによると、史上最高値は2024年12月初旬に記録された99,645.39ドルです。

BTC日足チャート

BTCUSD日足チャート価格見通し2021-2024


スイングエントリーモニタリング(EA)の開発

完全に機能するEAを効率的に開発するために、本プロジェクトの開発プロセスを3つのステップに整理しました。最初のステップでは、カスタムインジケーターの基盤となる移動平均の理解から始めます。このインジケーターは単体ツールとしても活用でき、後のステップで構築するモニタリングEAの基礎にもなります。

手順1:EMA100戦略を理解する


指数移動平均(EMA)は、直近のデータにより大きな重みを置く、広く利用されているテクニカル指標です。中でもEMA100は、通貨ペア取引において重要な節目として機能し、支持または抵抗となるケースが多く見られます。特にビットコインにおいては、価格がEMA100で反発した際に有利なエントリーポイントが出現することが多く、ボラティリティの高い局面でその傾向が顕著です。

手順2:モニタリングインジケーター


今回の開発では、MetaTrader 5端末に標準搭載されているEMA機能を活用し、監視用インジケーターを先に構築しました。このアプローチにより、値動きの中で注目すべき価格帯をより簡単に特定できるようになります。私の経験上、このインジケーターは端末通知、プッシュ通知、メール通知を通じてアラートを送信することが可能です。

ただし、一般的なSNSなどの高度なアラート配信サービスとの連携には、Webリクエスト処理機能が必要となります。この制約を解決するため、次のステップ3ではこのインジケーターをEAとして拡張し、より強力な機能を実装します。Telegramを通じた通知など、外部とのシームレスな通信を可能にすることで、より包括的かつ効率的な監視システムを構築することが目的です。

モニタリングインジケーターの開発内容は次のように分けられます。

プロパティとメタデータ

このセクションでは、著作権情報、開発者プロフィールのリンク、バージョン番号、およびインジケーターの概要といった基本的なメタデータを定義します。これらの情報はドキュメントとしての役割を果たし、ユーザーが開発者やインジケーターの目的を一目で把握できるようにするために重要です。

#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/ja/users/billionaire2024/seller"
#property version   "1.0"
#property description "EMA 100 Monitoring Indicator"

インジケーター設定

このセクションでは、取引チャート上に表示される視覚的な要素を設定します。#propertyindicator_chart_windowディレクティブは、このEAがメインチャートウィンドウ内で動作することを示しています。「indicator_buffers 2」より、2種類の異なるシグナルを表示するためのインジケーターバッファを準備します。プロパティindicator_type1とindicator_type2では、両方のシグナルが矢印として表示されること、そしてそれぞれ異なる色(オレンジと青)で視覚的に区別されることを指定しています。また、「Look for EMA 100 bounce」というラベルを付けることで、トレーダーに向けて明確なシグナルを提供します。この設定により、価格が指数移動平均(EMA)とどのように相互作用しているかを直感的に把握でき、潜在的なエントリーポイントの視認性が大きく向上します。

///Properties and Settings

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2

#property indicator_type1 DRAW_ARROW
#property indicator_width1 5
#property indicator_color1 0xFFAA00
#property indicator_label1 "Look for EMA 100 bounce "

#property indicator_type2 DRAW_ARROW
#property indicator_width2 5
#property indicator_color2 0x0000FF
#property indicator_label2 "Look for EMA 100 bounce "

バッファ定義

このセクションでは、EAが生成するシグナルの値を保持するインジケーターバッファを定義します。Buffer1とBuffer2はdouble型の配列として宣言されており、価格変動およびEMAに関連する2種類のシグナルデータをそれぞれ格納します。定数PLOT_MAXIMUM_BARS_BACKおよびOMIT_OLDEST_BARSは、処理対象とする過去のバー数を制御するために使用されます。これにより、不要に古いデータを扱ってシステムに負荷をかけることなく、効率的な動作が実現されます。この設計によって、EAはパフォーマンスを維持しつつ、ユーザーに対して常に関連性の高い最新情報を提供できるようになります。

#define PLOT_MAXIMUM_BARS_BACK 5000
#define OMIT_OLDEST_BARS 50

//--- indicator buffers
double Buffer1[];
double Buffer2[];

入力パラメータ

このセクションでは、EAの柔軟性を高めるために、ユーザーが設定可能な入力パラメータを定義します。「input int EMA_Period = 100」により、ユーザーは指数移動平均の計算に使用する期間を自由に設定でき、EAをさまざまな取引戦略に対応させることができます。さらに、Audible_AlertsやPush_Notificationsなどのフラグはデフォルトでtrueに設定されており、重要な市場イベントが発生した際にリアルタイムでアラートや通知を受け取ることができます。そのほか、Low、High、MA_handleなどの変数も宣言されており、これらは価格データの取得および移動平均の計算を処理するために使用され、EAの主要な機能において重要な役割を果たします。

input int EMA_Period = 100;
datetime time_alert; //used when sending alert
bool Audible_Alerts = true;
bool Push_Notifications = true;
double myPoint; //initialized in OnInit
double Low[];
int MA_handle;
double MA[];
double High[];

アラート関数

myAlert関数は、EA内でアラートの管理を一元化するために設計されています。この関数は2つのパラメータを受け取ります。アラートの種類を指定するtype(例:「print」、「error」、「indicator」)と、アラートテキストを含むmessageです。typeに応じて、関数はデバッグ用にメッセージを出力するか、市場の変動に関するアラートを送信します。この設計により、コードの整理性と可読性が向上し、保守性の高い構造が実現されます。また、音声アラートとプッシュ通知の両方に対応しているため、ユーザーは重要な市場の動きをリアルタイムで把握でき、迅速な取引判断を下すのに役立ちます。

void myAlert(string type, string message)
  {
   if(type == "print")
      Print(message);
   else if(type == "error")
     {
      Print(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
     }
   else if(type == "order")
     {
     }
   else if(type == "modify")
     {
     }
   else if(type == "indicator")
     {
      Print(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
      if(Audible_Alerts) Alert(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
      if(Push_Notifications) SendNotification(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
     }
  }

初期化関数

OnInitEAがチャートに読み込まれた際に最初に実行される関数であり、インジケーターバッファの初期化や指数移動平均の計算設定などをおこないます。​SetIndexBuffer関数を使用して、定義されたバッファをインジケータープロットに関連付けることで、チャート上に値を表示できるようにします。​また、移動平均ハンドル(MA_handle)の作成が成功したかを確認し、エラー処理をおこなうことで、EAの堅牢性を高めています。​初期化中に問題が発生した場合には、明確なエラーメッセージを出力することで、ユーザーが効果的にトラブルシューティングをおこなえるようにします。​このような徹底した初期化設定は、EAが正しく機能するために不可欠です。

//+------------------------------------------------------------------+
//| 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, 233);
   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, 234);
   //initialize myPoint
   myPoint = Point();
   if(Digits() == 5 || Digits() == 3)
     {
      myPoint *= 10;
     }
   MA_handle = iMA(NULL, PERIOD_CURRENT, EMA_Period, 0, MODE_SMA, PRICE_CLOSE);
   if(MA_handle < 0)
     {
      Print("The creation of iMA has failed: MA_handle=", INVALID_HANDLE);
      Print("Runtime error = ", GetLastError());
      return(INIT_FAILED);
     }
   
   return(INIT_SUCCEEDED);
  }

計算関数

OnCalculate関数はEAの中核を成す部分であり、インジケーターの値を算出するロジックが詰まっています。この関数は、受信した市場データの処理、価格情報の取得、そして移動平均の計算をおこないます。まず、全体のレート数と既に計算済みのレート数に基づいて、処理すべきレート数を決定します。次に、バッファの初期化や最低価格、最高価格といった必要なデータの取得をおこない、その後メインループに入ります。メインループでは、価格データを順次処理し、価格とEMAとの関係に基づいた取引シグナルの生成条件を検証します。この関数の効率性は、EAが市場の変動にリアルタイムで柔軟に対応するために非常に重要です。

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);
   //--- initial zero
   if(prev_calculated < 1)
     {
      ArrayInitialize(Buffer1, EMPTY_VALUE);
      ArrayInitialize(Buffer2, EMPTY_VALUE);
     }
   else
      limit++;
   datetime Time[];
   
   if(CopyLow(Symbol(), PERIOD_CURRENT, 0, rates_total, Low) <= 0) return(rates_total);
   ArraySetAsSeries(Low, 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(CopyHigh(Symbol(), PERIOD_CURRENT, 0, rates_total, High) <= 0) return(rates_total);
   ArraySetAsSeries(High, true);
   if(CopyTime(Symbol(), Period(), 0, rates_total, Time) <= 0) return(rates_total);
   ArraySetAsSeries(Time, true);

メインロジック

OnCalculate関数内では、メインロジックのループがEMAに対する価格変動を詳細に解析します。たとえば、安値がEMAを下回る場合や高値がEMAを上回る場合といった特定条件を検出します。これらの条件が成立すると、対応するバッファ(Buffer1はEMA下回りでの反発用、Buffer2はEMA上回りでの反発用)に現在の安値または高値が格納され、ユーザーへアラートが発信されます。この仕組みは、実用的な取引シグナルを提供するための基盤となり、トレーダーがテクニカル分析に基づいて迅速かつ確実な意思決定をおこなうのに役立ちます。また、インジケーター値の設定が明確かつ体系的に行われることで、潜在的な取引機会に関するタイムリーで有用な情報がユーザーに提供されます。

//--- 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   
      
      //Indicator Buffer 1
      if(Low[i] < MA[i]
      && Low[i+1] > MA[i+1] //Candlestick Low crosses below Moving Average
      )
        {
         Buffer1[i] = Low[i]; //Set indicator value at Candlestick Low
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Look for EMA 100 bounce "); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer1[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 2
      if(High[i] > MA[i]
      && High[i+1] < MA[i+1] //Candlestick High crosses above Moving Average
      )
        {
         Buffer2[i] = High[i]; //Set indicator value at Candlestick High
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Look for EMA 100 bounce "); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer2[i] = EMPTY_VALUE;
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

以下はインジケーターの完全なコードです。エラーは発生しません。

#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/ja/users/billionaire2024/seller"
#property version   "1.0"
#property description "EMA 100 Monitoring Indicator"

//--- indicator settings
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots 2

#property indicator_type1 DRAW_ARROW
#property indicator_width1 5
#property indicator_color1 0xFFAA00
#property indicator_label1 "Look for EMA 100 bounce "

#property indicator_type2 DRAW_ARROW
#property indicator_width2 5
#property indicator_color2 0x0000FF
#property indicator_label2 "Look for EMA 100 bounce "

#define PLOT_MAXIMUM_BARS_BACK 5000
#define OMIT_OLDEST_BARS 50

//--- indicator buffers
double Buffer1[];
double Buffer2[];

input int EMA_Period = 100;
datetime time_alert; //used when sending alert
bool Audible_Alerts = true;
bool Push_Notifications = true;
double myPoint; //initialized in OnInit
double Low[];
int MA_handle;
double MA[];
double High[];

void myAlert(string type, string message)
  {
   if(type == "print")
      Print(message);
   else if(type == "error")
     {
      Print(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
     }
   else if(type == "order")
     {
     }
   else if(type == "modify")
     {
     }
   else if(type == "indicator")
     {
      Print(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
      if(Audible_Alerts) Alert(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
      if(Push_Notifications) SendNotification(type+" | EMA 100 Monitor @ "+Symbol()+","+IntegerToString(Period())+" | "+message);
     }
  }

//+------------------------------------------------------------------+
//| 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, 233);
   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, 234);
   //initialize myPoint
   myPoint = Point();
   if(Digits() == 5 || Digits() == 3)
     {
      myPoint *= 10;
     }
   MA_handle = iMA(NULL, PERIOD_CURRENT, EMA_Period, 0, MODE_SMA, PRICE_CLOSE);
   if(MA_handle < 0)
     {
      Print("The creation of iMA has failed: MA_handle=", 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);
   //--- initial zero
   if(prev_calculated < 1)
     {
      ArrayInitialize(Buffer1, EMPTY_VALUE);
      ArrayInitialize(Buffer2, EMPTY_VALUE);
     }
   else
      limit++;
   datetime Time[];
   
   if(CopyLow(Symbol(), PERIOD_CURRENT, 0, rates_total, Low) <= 0) return(rates_total);
   ArraySetAsSeries(Low, 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(CopyHigh(Symbol(), PERIOD_CURRENT, 0, rates_total, High) <= 0) return(rates_total);
   ArraySetAsSeries(High, 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   
      
      //Indicator Buffer 1
      if(Low[i] < MA[i]
      && Low[i+1] > MA[i+1] //Candlestick Low crosses below Moving Average
      )
        {
         Buffer1[i] = Low[i]; //Set indicator value at Candlestick Low
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Look for EMA 100 bounce "); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer1[i] = EMPTY_VALUE;
        }
      //Indicator Buffer 2
      if(High[i] > MA[i]
      && High[i+1] < MA[i+1] //Candlestick High crosses above Moving Average
      )
        {
         Buffer2[i] = High[i]; //Set indicator value at Candlestick High
         if(i == 1 && Time[1] != time_alert) myAlert("indicator", "Look for EMA 100 bounce "); //Alert on next bar open
         time_alert = Time[1];
        }
      else
        {
         Buffer2[i] = EMPTY_VALUE;
        }
     }
   return(rates_total);
  }
//+------------------------------------------------------------------+

手順3:EA開発

このセクションでは、MQL5を用いたシンプルかつ効果的なスイングエントリーモニタリングEAの開発プロセスを解説します。このEAは、例としてビットコインに焦点を当て、市場価格の監視と事前に定義された条件に基づくアラートの送信を目的としています。

なお、プロセスの効率化を図るため、インジケーター開発段階で既に十分に説明されているメタデータの詳細はここでは省略します。以降、コードを段階的に分解し、各部分が理解しやすく実装可能な形で説明していきます。

1.入力パラメータ

input string IndicatorName = "ema100_monitoring_indicator"; // Name of the custom indicator
input bool EnableTerminalAlerts = true;
input bool EnablePushNotifications = true;
input bool EnableTelegramAlerts = false;
input string TelegramBotToken = "YOUR_BOT_TOKEN"; // Replace with your bot token
input string TelegramChatID = "YOUR_CHAT_ID";     // Replace with your chat ID

この部分では、EAの柔軟性と使いやすさを高めるための各種カスタマイズ可能な入力パラメータを定義します。IndicatorNameパラメータでは、監視対象のカスタムインジケーターの名称を指定でき、デフォルト値は「Bitcoin Monitor」となっています。また、ブールフラグ(EnableTerminalAlertsEnablePushNotificationsEnableTelegramAlerts)を利用することで、取引端末、モバイルデバイス、またはTelegram経由で通知を受け取るなど、アラートの受信方法をカスタマイズできます。さらに、Telegramアラートを有効にするには、TelegramボットのトークンとチャットIDを入力する必要があります。このカスタマイズ機能により、ユーザーは自身の好みやニーズに合わせて取引環境を最適化できるようになります。

2.インジケーターハンドル

int indicatorHandle = INVALID_HANDLE;
int emaHandle = INVALID_HANDLE;

このセクションでは、EAで使用するインジケーターを管理するために必要な変数を宣言します。indicatorHandle変数とemaHandle変数には、それぞれカスタムインジケーターと指数移動平均(EMA)への参照が格納されます。両方のハンドルは初期値としてINVALID_HANDLEに設定され、まだ割り当てられていないことを示しています。この初期化は、プログラムが指定されたインジケーターに正しくアクセスし、分析に必要な市場データを取得するために不可欠なステップです。

3.アラート関数

void AlertMessage(string message) {
   if (EnableTerminalAlerts) Alert(message);
   if (EnablePushNotifications) SendNotification(message);
   if (EnableTelegramAlerts) SendTelegramMessage(message);
}

AlertMessage関数は、EA内でアラート通知を管理する上で欠かせない役割を果たします。この関数は、送信するアラート内容を含む文字列パラメータ「message」を受け取り、ユーザーが設定した各アラート種別(端末アラート、プッシュ通知、Telegramメッセージ))に基づいて、適切な方法でメッセージを送信します。アラート管理をこの関数に集約することで、コード全体が整理され保守しやすくなるとともに、市場動向に応じた迅速な通知が求められるトレーダーにとって、重要な情報が的確に伝達される仕組みが実現されます。

4.Telegramアラート 

void SendTelegramMessage(string message) {
   if (EnableTelegramAlerts) {
      string url = "https://api.telegram.org/bot" + TelegramBotToken + "/sendMessage?chat_id=" + TelegramChatID + "&text=" + message;
      int timeout = 5000;
      ResetLastError();

      char postData[];
      uchar result[];
      string response;

      int res = WebRequest("GET", url, NULL, timeout, postData, result, response);

      if (res != 200) {
         Print("Telegram WebRequest failed. Error: ", GetLastError(), ", HTTP Code: ", res);
      } else {
         Print("Telegram message sent successfully: ", response);
      }
   }
}

ユーザーとのコミュニケーションを円滑にするために、SendTelegramMessage関数が実装されています。この関数は、Telegram API を利用して指定チャットにメッセージを送信するためのURLを作成します。まず、Telegramアラートが有効かどうかを確認し、もし有効であれば、ボットトークンとチャットIDを含むURLを用いてGETリクエストを準備・送信します。さらに、この関数はリクエスト送信時に発生する可能性のあるエラーを処理し、メッセージ送信に失敗した場合はユーザーにフィードバックを提供します。この機能により、ユーザーはTelegram上で直接アラートを受信でき、利便性とアクセシビリティが向上します。

5.初期化関数

int OnInit() {
   Print("Bitcoin Monitoring EA started.");

   // Attach the custom indicator to the chart
   indicatorHandle = iCustom(_Symbol, _Period, IndicatorName);
   if (indicatorHandle == INVALID_HANDLE) {
      Print("Failed to attach indicator: ", IndicatorName, ". Error: ", GetLastError());
      return(INIT_FAILED);
   }

   // Attach built-in EMA 100 to the chart
   emaHandle = iMA(_Symbol, _Period, 100, 0, MODE_EMA, PRICE_CLOSE);
   if (emaHandle == INVALID_HANDLE) {
      Print("Failed to create EMA 100. Error: ", GetLastError());
      return(INIT_FAILED);
   }

   // Add EMA 100 to the terminal chart
   if (!ChartIndicatorAdd(0, 0, emaHandle)) {
      Print("Failed to add EMA 100 to the chart. Error: ", GetLastError());
   }

   return(INIT_SUCCEEDED);
}

OnInit関数は、EAが初めてロードされた際に実行され、必要なインジケーターの設定や初期化をおこない、EAが正常に動作する状態であることを確認します。この関数内では、まずカスタムインジケーターがその名称を用いてチャートにアタッチされ、ハンドルの値をチェックしてアタッチの成否が判断されます。アタッチに失敗した場合は、診断に役立つエラーメッセージが出力されます。さらに、期間100のEMAが生成され、その生成が正しくおこなわれたかどうかを確認した上でチャートに追加されます。適切な初期化により、すべてのコンポーネントが正確にセットアップされ、市場データを処理する準備が整うため、EAの機能性が確保されます。

6.初期化関数

void OnDeinit(const int reason) {
   Print(" EA stopped.");
   if (indicatorHandle != INVALID_HANDLE) {
      IndicatorRelease(indicatorHandle);
   }
   if (emaHandle != INVALID_HANDLE) {
      IndicatorRelease(emaHandle);
   }
}

OnDeinit関数は、EAがチャートから削除された場合や端末が閉じられた際に呼び出されます。この関数の主な役割は、リソースの解放とメモリリークの防止です。まず、各インジケーターハンドルが有効かどうかを確認し、もし有効であればそれらを解放してシステムリソースを回収します。また、EAが停止したことを示すメッセージを出力することで、ユーザーにEAの状態を明確にフィードバックします。適切な後処理は、最適なパフォーマンスの維持と取引環境の整然とした状態の確保に不可欠です。

7.メインロジック

void OnTick() {
   static datetime lastAlertTime = 0; // Prevent repeated alerts for the same signal

   if (indicatorHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE) return;

   double buffer1[], buffer2[];
   ArraySetAsSeries(buffer1, true);
   ArraySetAsSeries(buffer2, true);

   // Read data from the custom indicator
   if (CopyBuffer(indicatorHandle, 0, 0, 1, buffer1) < 0) {
      Print("Failed to copy Buffer1. Error: ", GetLastError());
      return;
   }
   if (CopyBuffer(indicatorHandle, 1, 0, 1, buffer2) < 0) {
      Print("Failed to copy Buffer2. Error: ", GetLastError());
      return;
   }

   // Check for signals in Buffer1
   if (buffer1[0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) {
      string message = "Signal detected: Look for EMA 100 bounce (Low). Symbol: " + _Symbol;
      AlertMessage(message);
      lastAlertTime = TimeCurrent();
   }

   // Check for signals in Buffer2
   if (buffer2[0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) {
      string message = "Signal detected: Look for EMA 100 bounce (High). Symbol: " + _Symbol;
      AlertMessage(message);
      lastAlertTime = TimeCurrent();
   }

   // Debugging EMA 100 value
   double emaValueArray[];
   ArraySetAsSeries(emaValueArray, true); // Ensure it's set as series
   if (CopyBuffer(emaHandle, 0, 0, 1, emaValueArray) > 0) {
      Print("EMA 100 Current Value: ", emaValueArray[0]);
   } else {
      Print("Failed to read EMA 100 buffer. Error: ", GetLastError());
   }
}

OnTick関数は、EAの中核となるロジックを担い、市場に新たなティックデータが到着するたびに実行されます。この関数は、計算を開始する前に各インジケーターハンドルの有効性を確認します。続いて、カスタムインジケーターからのデータを格納する配列を初期化し、インジケーターバッファから最新の値を取得します。いずれかのバッファでシグナルが検出された場合は、AlertMessage関数を介してアラートが発信され、ユーザーに潜在的な取引チャンスが通知されます。さらに、デバッグ目的でEMAの現在値も取得し、EAの動作状況を明確に把握できるようにしています。このようなリアルタイム分析により、EAは市場の変化に迅速に対応できるため、トレーダーにとって非常に有用なツールとなります。

以下は完全なEAコードです。

//+------------------------------------------------------------------+
//| Bitcoin Monitoring Expert Advisor                                |
//| Copyright 2024, Clemence Benjamin                                |
//| https://www.mql5.com/ja/users/billionaire2024/seller            |
//+------------------------------------------------------------------+
#property copyright "Clemence Benjamin"
#property link      "https://www.mql5.com/ja/users/billionaire2024/seller"
#property version   "1.0"
#property description "BTCUSD Monitoring Expert Advisor"

//--- Input parameters
input string IndicatorName = "ema100_monitoring_indicator"; // Name of the custom indicator
input bool EnableTerminalAlerts = true;
input bool EnablePushNotifications = true;
input bool EnableTelegramAlerts = false;
input string TelegramBotToken = "YOUR_BOT_TOKEN";         // Replace with your bot token
input string TelegramChatID = "YOUR_CHAT_ID";             // Replace with your chat ID

//--- Indicator handles
int indicatorHandle = INVALID_HANDLE;
int emaHandle = INVALID_HANDLE;

//--- Alert function
void AlertMessage(string message) {
   if (EnableTerminalAlerts) Alert(message);
   if (EnablePushNotifications) SendNotification(message);
   if (EnableTelegramAlerts) SendTelegramMessage(message);
}

//--- Telegram Alerting
void SendTelegramMessage(string message) {
   if (EnableTelegramAlerts) {
      string url = "https://api.telegram.org/bot" + TelegramBotToken + "/sendMessage?chat_id=" + TelegramChatID + "&text=" + message;
      int timeout = 5000;
      ResetLastError();

      char postData[];
      uchar result[];
      string response;

      int res = WebRequest("GET", url, NULL, timeout, postData, result, response);

      if (res != 200) {
         Print("Telegram WebRequest failed. Error: ", GetLastError(), ", HTTP Code: ", res);
      } else {
         Print("Telegram message sent successfully: ", response);
      }
   }
}

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit() {
   Print("Bitcoin Monitoring EA started.");

   // Attach the custom indicator to the chart
   indicatorHandle = iCustom(_Symbol, _Period, IndicatorName);
   if (indicatorHandle == INVALID_HANDLE) {
      Print("Failed to attach indicator: ", IndicatorName, ". Error: ", GetLastError());
      return(INIT_FAILED);
   }

   // Attach built-in EMA 100 to the chart
   emaHandle = iMA(_Symbol, _Period, 100, 0, MODE_EMA, PRICE_CLOSE);
   if (emaHandle == INVALID_HANDLE) {
      Print("Failed to create EMA 100. Error: ", GetLastError());
      return(INIT_FAILED);
   }

   // Add EMA 100 to the terminal chart
   if (!ChartIndicatorAdd(0, 0, emaHandle)) {
      Print("Failed to add EMA 100 to the chart. Error: ", GetLastError());
   }

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   Print("Bitcoin Monitoring EA stopped.");
   if (indicatorHandle != INVALID_HANDLE) {
      IndicatorRelease(indicatorHandle);
   }
   if (emaHandle != INVALID_HANDLE) {
      IndicatorRelease(emaHandle);
   }
}

void OnTick() {
   static datetime lastAlertTime = 0; // Prevent repeated alerts for the same signal

   if (indicatorHandle == INVALID_HANDLE || emaHandle == INVALID_HANDLE) return;

   double buffer1[], buffer2[];
   ArraySetAsSeries(buffer1, true);
   ArraySetAsSeries(buffer2, true);

   // Read data from the custom indicator
   if (CopyBuffer(indicatorHandle, 0, 0, 1, buffer1) < 0) {
      Print("Failed to copy Buffer1. Error: ", GetLastError());
      return;
   }
   if (CopyBuffer(indicatorHandle, 1, 0, 1, buffer2) < 0) {
      Print("Failed to copy Buffer2. Error: ", GetLastError());
      return;
   }

   // Check for signals in Buffer1
   if (buffer1[0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) {
      string message = "Signal detected: Look for EMA 100 bounce (Low). Symbol: " + _Symbol;
      AlertMessage(message);
      lastAlertTime = TimeCurrent();
   }

   // Check for signals in Buffer2
   if (buffer2[0] != EMPTY_VALUE && TimeCurrent() != lastAlertTime) {
      string message = "Signal detected: Look for EMA 100 bounce (High). Symbol: " + _Symbol;
      AlertMessage(message);
      lastAlertTime = TimeCurrent();
   }

   // Debugging EMA 100 value
   double emaValueArray[];
   ArraySetAsSeries(emaValueArray, true); // Ensure it's set as series
   if (CopyBuffer(emaHandle, 0, 0, 1, emaValueArray) > 0) {
      Print("EMA 100 Current Value: ", emaValueArray[0]);
   } else {
      Print("Failed to read EMA 100 buffer. Error: ", GetLastError());
   }
}


テストと最適化

コードを正常にコンパイルした後、MetaTrader5端末のストラテジーテスターを使用してテストを進めました。以下に、テストのプロセスと結果を示す画像をいくつか示します。

ビットコインモニタリングEA

ビットコインモニタリングEA:ストラテジーテスターでの実行

ストラテジーテスターの実行中に、EAのリアルタイム価格監視機能を正常に実証しました。以下は、実際のプロセスとEAのパフォーマンスを示す図です。

テスターでビットコインモニタリングEAによる価格モニタリング

オンティックBTC価格変動の監視:2022年


結果と分析

特にH4やD1といった長期時間足におけるEMA100との相互作用を観察することで、インジケーターのパフォーマンスを視覚的に確認することができました。このシステムは、Telegram通知を含む3種類の方法でアラートを送信できる機能を備えており、柔軟な通知手段を提供します。私たちが共有した図からも分かる通り、価格は選択されたEMAを一貫して意識しています。下の画像は、MetaTrader 5端末上でEAとインジケーターの両方を起動した様子を示しており、それぞれの連携と機能性が視覚的に表現されています。

EAとインジケーターをチャートに追加する

EAとインジケーターをチャートに追加する



結論

本記事で開発したモニタリングEAは、すべてのトレーダーにとって有益なツールとなり得ます。価格の監視を自動化し、EMA100といった戦略を組み込むことで、取引チャンスを見つけるために必要な手作業の負担を大きく軽減します。今回の例ではBTCUSDを対象としましたが、このEAは他の金融商品にも拡張可能であり、さらに他のインジケーターに対応させることも容易です。このプロジェクトは、初心者が取引自動化の第一歩を踏み出すための、動機づけとなるシンプルなベースとしても最適です。アイデア次第で可能性は無限大ですので、ぜひさまざまなアプローチを試してみてください。

添付されているEAおよびインジケーターをダウンロードし、ご自身の設定でバックテストをおこない、取引戦略に合わせてカスタマイズしてみてください。テクニカル分析と自動化の融合により、変化の激しいトレーディングの世界でも一歩先を行くことができます。なお、このシステムは監視および通知を目的として設計されており、現時点では自動売買機能は搭載されていないことにご留意ください。Telegramの認証情報については、次の記事を参照してください:  リンク1リンク2

以下は添付ファイルの表です。

ファイル 詳細
ema100_monitoring_indicator.mq5 EMA100バウンス戦略に基づくカスタムインジケーター
bitcoin_monitoring_expert.mq5 ExpertAdvisorは、WebRequest経由でTelegramアラート機能を有効にし、継続的に監視します。


MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16563

プライスアクション分析ツールキットの開発(第6回):Mean Reversion Signal Reaper プライスアクション分析ツールキットの開発(第6回):Mean Reversion Signal Reaper
いくつかの概念は一見するとシンプルに思えるかもしれませんが、実際にそれを形にするのは想像以上に難しいことがあります。この記事では、平均回帰(Mean Reversion)戦略を用いて市場を巧みに分析するエキスパートアドバイザー(EA)の自動化に取り組んだ、革新的なアプローチをご紹介します。この魅力的な自動化プロセスの奥深さを、一緒に紐解いていきましょう。
出来高ベースの取引システムを構築し最適化する方法(チャイキンマネーフロー:CMF) 出来高ベースの取引システムを構築し最適化する方法(チャイキンマネーフロー:CMF)
この記事では、出来高ベースの指標であるチャイキンマネーフロー(CMF)の構築方法、計算方法、使用方法を説明した上で、その概要を説明します。カスタムインジケーターの構築方法を理解します。使用できるいくつかの簡単な戦略を共有し、それらをテストしてどれが優れているかを理解します。
MQL5での取引戦略の自動化(第3回):ダイナミック取引管理のためのZone Recovery RSIシステム MQL5での取引戦略の自動化(第3回):ダイナミック取引管理のためのZone Recovery RSIシステム
この記事では、MQL5を使ってZone Recovery RSI EAシステムを構築し、RSIシグナルによって取引を開始し、損失を管理するためのリカバリーストラテジーを実装します。取引エントリー、リカバリーロジック、ポジション管理を自動化するために、ZoneRecoveryクラスを作成します。この記事の最後では、EAのパフォーマンスを最適化し、その有効性を高めるためのバックテストの洞察を紹介します。
MQL5でのファイル操作の習得:基本的なI/OからカスタムCSVリーダーの構築まで MQL5でのファイル操作の習得:基本的なI/OからカスタムCSVリーダーの構築まで
この記事では、取引ログ、CSVの処理、外部データの統合など、MQL5における基本的なファイル操作テクニックに焦点を当て、概念的な理解と実践的なコーディングガイドの両面から解説します。読者は、カスタムCSVインポート用のクラスを段階的に構築する方法を学び、実践的なスキルを身につけることができます。