English Deutsch
preview
プライスアクション分析ツールキットの開発(第7回):Signal Pulse EA

プライスアクション分析ツールキットの開発(第7回):Signal Pulse EA

MetaTrader 5 | 29 5月 2025, 06:56
70 0
Christian Benjamin
Christian Benjamin

内容



はじめに

この記事では、MQL5で「Signal Pulse EA」と呼ばれるエキスパートアドバイザー(EA)を開発する方法を紹介します。このEAは、3つの異なる時間枠にわたって、ボリンジャーバンドとストキャスティクスを組み合わせて売買シグナルを検出します。複数の時間枠にまたがるシグナルを確認することで、取引エントリー前に信頼性を高め、トレーダーがより的確な判断を下せるよう支援することが目的です。Signal Pulse EAでは、ボリンジャーバンド(BB)、ストキャスティクス、複数の時間枠を統合してシグナルを生成しています。私たちの目標は、トレーダーの判断を惑わせる誤ったシグナルを最小限に抑え、これらの要素を組み合わせることで、取シグナルの精度を高めることです。以下に、シグナル生成における各要素の役割と重要性を表形式でまとめました。

  • 複数の時間枠
利点 詳細
リスク管理 複数の時間枠を用いることで、EAは異なる視点から市場を分析でき、誤ったシグナルの影響を軽減し、リスク管理を強化できます。
シグナルへの信頼性 時間枠ごとにシグナルを確認することで、取引方向への確信が高まり、弱いまたは偽のシグナルによるエントリーを回避できます。
多様化 複数の時間枠を分析することで、市場の多角的な見方が可能となり、変化する相場環境に柔軟に対応できます。
  • ストキャスティクス
利点 詳細
買われ過ぎ/売られ過ぎの判断
ストキャスティクスは、買われ過ぎや売られ過ぎの状態を特定し、市場の反転ポイントを示す手がかりになります。
シグナル確認ツール
ボリンジャーバンドのシグナルに対する確認手段として機能し、BB単独での判断による誤ったエントリーを防ぎます。
誤シグナルのフィルタリング
特にボラティリティの高い状況下で、BBから発生する誤シグナルをフィルタリングする役割を果たします。

  • ボリンジャーバンド
利点 詳細
ボラティリティの可視化
市場のボラティリティを視覚的に示すことで、EAは市場の状況や取引機会を把握しやすくなります。
支持/抵抗レベル
ボリンジャーバンドは動的な支持・抵抗として機能し、エントリーおよびイグジットポイントの判断に活用されます。
トレンドの確認 
バンドの幅の変化によってトレンドの有無を判断でき、戦略の方向性を補強します。

複数の時間枠、ストキャスティクス、ボリンジャーバンド(BB)の相互作用は、EAにおける取引シグナルの信頼性向上とリスク管理強化に大きく貢献します。各時間枠での分析により、BBとストキャスティクスという異なる性質を持つインジケーターの合流点が確認されたときのみ、EAは取引を検討します。このような多次元的なアプローチにより、シグナルの信頼性が高まり、誤った取引のリスクが低減されます。その結果、EAによって生成されるシグナルの精度と信頼性が向上し、トレーダーはより自信を持って取引判断をおこなえるようになります。 


戦略の理解

「Signal Pulse」スクリプトは、ボリンジャーバンドとストキャスティクスの整合性に基づき、3つの時間枠(M15、M30、H1)で取引シグナルを生成します。各シグナルの条件は以下のとおりです。

買いシグナル

  1. ボリンジャーバンドの条件:価格がM15、M30、H1のすべての時間枠でボリンジャーバンドの下限に接触していること。
  2. ストキャスティクスの条件:すべての時間枠において、ストキャスティクスが売られすぎの状態(一般的に20未満)を示していること。
  3. 確認要件:上記の2つの条件が、M15、M30、H1のすべての時間枠で同時に満たされていること。

買いの条件

図1:買いの条件

売りシグナル

  1. ボリンジャーバンドの条件:価格がM15、M30、H1のすべての時間枠でボリンジャーバンドの上限に接触していること。
  2. ストキャスティクスの条件:すべての時間枠において、ストキャスティクスが買われすぎの状態(一般的に80超)を示していること。
  3. 確認要件:上記の2つの条件が、M15、M30、H1のすべての時間枠で同時に満たされていること。

売りの条件

図2:売りの条件

このプロセスを下の図で可視化してみましょう。 

シグナル生成チャート

図3:シグナル生成プロセス


MQL5コード

//+------------------------------------------------------------------+
//|                                              Signal Pulse EA.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

// Input parameters
input ENUM_TIMEFRAMES Timeframe1 = PERIOD_M15; // M15 timeframe
input ENUM_TIMEFRAMES Timeframe2 = PERIOD_M30; // M30 timeframe
input ENUM_TIMEFRAMES Timeframe3 = PERIOD_H1;  // H1 timeframe
input int BB_Period = 20;                      // Bollinger Bands period
input double BB_Deviation = 2.0;               // Bollinger Bands deviation
input int K_Period = 14;                       // Stochastic %K period
input int D_Period = 3;                        // Stochastic %D period
input int Slowing = 3;                         // Stochastic slowing
input double SignalOffset = 10.0;              // Offset in points for signal arrow
input int TestBars = 10;                       // Number of bars after signal to test win condition
input double MinArrowDistance = 5.0;          // Minimum distance in points between arrows to avoid overlapping

// Signal tracking structure
struct SignalInfo
  {
   datetime          time;
   double            price;
   bool              isBuySignal;
  };

// Arrays to store signal information
datetime signalTimes[];
double signalPrices[];
bool signalBuySignals[];

//+------------------------------------------------------------------+
//| Retrieve Bollinger Band Levels                                   |
//+------------------------------------------------------------------+
bool GetBollingerBands(ENUM_TIMEFRAMES timeframe, double &upper, double &lower, double &middle)
  {
   int handle = iBands(Symbol(), timeframe, BB_Period, 0, BB_Deviation, PRICE_CLOSE);

   if(handle == INVALID_HANDLE)
     {
      Print("Error creating iBands for timeframe: ", timeframe);
      return false;
     }

   double upperBand[], middleBand[], lowerBand[];

   if(!CopyBuffer(handle, 1, 0, 1, upperBand) ||
      !CopyBuffer(handle, 0, 0, 1, middleBand) ||
      !CopyBuffer(handle, 2, 0, 1, lowerBand))
     {
      Print("Error copying iBands buffer for timeframe: ", timeframe);
      IndicatorRelease(handle);
      return false;
     }

   upper = upperBand[0];
   middle = middleBand[0];
   lower = lowerBand[0];

   IndicatorRelease(handle);
   return true;
  }

//+------------------------------------------------------------------+
//| Retrieve Stochastic Levels                                       |
//+------------------------------------------------------------------+
bool GetStochastic(ENUM_TIMEFRAMES timeframe, double &k_value, double &d_value)
  {
   int handle = iStochastic(Symbol(), timeframe, K_Period, D_Period, Slowing, MODE_SMA, STO_CLOSECLOSE);

   if(handle == INVALID_HANDLE)
     {
      Print("Error creating iStochastic for timeframe: ", timeframe);
      return false;
     }

   double kBuffer[], dBuffer[];

   if(!CopyBuffer(handle, 0, 0, 1, kBuffer) ||  // %K line
      !CopyBuffer(handle, 1, 0, 1, dBuffer))   // %D line
     {
      Print("Error copying iStochastic buffer for timeframe: ", timeframe);
      IndicatorRelease(handle);
      return false;
     }

   k_value = kBuffer[0];
   d_value = dBuffer[0];

   IndicatorRelease(handle);
   return true;
  }

//+------------------------------------------------------------------+
//| Check and Generate Signal                                        |
//+------------------------------------------------------------------+
void CheckAndGenerateSignal()
  {
   double upper1, lower1, middle1, close1;
   double upper2, lower2, middle2, close2;
   double upper3, lower3, middle3, close3;
   double k1, d1, k2, d2, k3, d3;

   if(!GetBollingerBands(Timeframe1, upper1, lower1, middle1) ||
      !GetBollingerBands(Timeframe2, upper2, lower2, middle2) ||
      !GetBollingerBands(Timeframe3, upper3, lower3, middle3))
     {
      Print("Error retrieving Bollinger Bands data.");
      return;
     }

   if(!GetStochastic(Timeframe1, k1, d1) ||
      !GetStochastic(Timeframe2, k2, d2) ||
      !GetStochastic(Timeframe3, k3, d3))
     {
      Print("Error retrieving Stochastic data.");
      return;
     }

// Retrieve the close prices
   close1 = iClose(Symbol(), Timeframe1, 0);
   close2 = iClose(Symbol(), Timeframe2, 0);
   close3 = iClose(Symbol(), Timeframe3, 0);

   bool buySignal = (close1 <= lower1 && close2 <= lower2 && close3 <= lower3) &&
                    (k1 < 5 && k2 < 5 && k3 < 5); // Oversold condition
   bool sellSignal = (close1 >= upper1 && close2 >= upper2 && close3 >= upper3) &&
                     (k1 > 95 && k2 > 95 && k3 > 95); // Overbought condition

// Check if an arrow already exists in the same region before placing a new one
   if(buySignal && !ArrowExists(close1))
     {
      Print("Buy signal detected on all timeframes with Stochastic confirmation!");

      string arrowName = "BuySignal" + IntegerToString(TimeCurrent());
      ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), close1);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 241);
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clrGreen);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

      // Store signal data
      ArrayResize(signalTimes, ArraySize(signalTimes) + 1);
      ArrayResize(signalPrices, ArraySize(signalPrices) + 1);
      ArrayResize(signalBuySignals, ArraySize(signalBuySignals) + 1);

      signalTimes[ArraySize(signalTimes) - 1] = TimeCurrent();
      signalPrices[ArraySize(signalPrices) - 1] = close1;
      signalBuySignals[ArraySize(signalBuySignals) - 1] = true;
     }

   if(sellSignal && !ArrowExists(close1))
     {
      Print("Sell signal detected on all timeframes with Stochastic confirmation!");

      string arrowName = "SellSignal" + IntegerToString(TimeCurrent());
      ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), close1);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 242);
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clrRed);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

      // Store signal data
      ArrayResize(signalTimes, ArraySize(signalTimes) + 1);
      ArrayResize(signalPrices, ArraySize(signalPrices) + 1);
      ArrayResize(signalBuySignals, ArraySize(signalBuySignals) + 1);

      signalTimes[ArraySize(signalTimes) - 1] = TimeCurrent();
      signalPrices[ArraySize(signalPrices) - 1] = close1;
      signalBuySignals[ArraySize(signalBuySignals) - 1] = false;
     }
  }

//+------------------------------------------------------------------+
//| Check if an arrow already exists within the MinArrowDistance     |
//+------------------------------------------------------------------+
bool ArrowExists(double price)
  {
   for(int i = 0; i < ArraySize(signalPrices); i++)
     {
      if(MathAbs(signalPrices[i] - price) <= MinArrowDistance)
        {
         return true; // Arrow exists in the same price region
        }
     }
   return false; // No arrow exists in the same region
  }

//+------------------------------------------------------------------+
//| OnTick Event                                                     |
//+------------------------------------------------------------------+
void OnTick()
  {
   CheckAndGenerateSignal();
  }

//+------------------------------------------------------------------+
//| OnDeinit Function                                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// Clean up the objects
   long chart_id = 0;
   for(int i = ObjectsTotal(chart_id) - 1; i >= 0; i--)
     {
      string name = ObjectName(chart_id, i);
      if(StringFind(name, "Signal") != -1)
        {
         ObjectDelete(chart_id, name);
        }
     }

   Print("Multitimeframe Bollinger-Stochastic Analyzer deinitialized.");
  }
//+------------------------------------------------------------------+



コードの分解

このセクションでは、「Signal Pulse EA」の構成要素、動作メカニズム、そしてその機能の背後にあるロジックについて、ステップバイステップで詳しく解説していきます。このセクションでは、「Signal Pulse EA」の構成要素、動作メカニズム、そしてその機能の背後にあるロジックについて、ステップバイステップで詳しく解説していきます。
  • ヘッダー、プロパティ、入力パラメータ

EAの冒頭では、基本的なメタデータを記載したヘッダーが含まれています。ここには、EAの名称、著作権情報、そして開発元に関するリンクなどが記載されています。しかし、トレーダーにとって特に重要なのは入力パラメータです。これらのカスタマイズ可能な設定により、EAの挙動を自分の取引スタイルに合わせて柔軟に調整することができます。

//+------------------------------------------------------------------+
//|                                              Signal Pulse EA.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Christian Benjamin"
#property link      "https://www.mql5.com/ja/users/lynnchris"
#property version   "1.00"

// Input parameters
input ENUM_TIMEFRAMES Timeframe1 = PERIOD_M15; // M15 timeframe
input ENUM_TIMEFRAMES Timeframe2 = PERIOD_M30; // M30 timeframe
input ENUM_TIMEFRAMES Timeframe3 = PERIOD_H1;  // H1 timeframe
input int BB_Period = 20;                      // Bollinger Bands period
input double BB_Deviation = 2.0;               // Bollinger Bands deviation
input int K_Period = 14;                       // Stochastic %K period
input int D_Period = 3;                        // Stochastic %D period
input int Slowing = 3;                         // Stochastic slowing
input double SignalOffset = 10.0;              // Offset in points for signal arrow
input int TestBars = 10;                       // Number of bars after signal to test win condition
input double MinArrowDistance = 5.0;          // Minimum distance in points between arrows to avoid overlapping
この部分では、異なる時間枠(M15、M30、H1)を指定しており、複数の期間にわたる価格の動きを分析できるようになっています。また、ボリンジャーバンドの期間や偏差といったパラメータ、ストキャスティクスの%Kおよび%Dの期間なども設定しています。さらに、チャート上に買い・売りシグナルを表示するための矢印に関する視覚的な設定も含まれており、重なり合う矢印を回避することで視認性を高めるためのパラメータも用意されています。
  •  シグナルトラッキングと構造体

次に、取引シグナルの管理において重要な役割を果たすSignalInfo構造体を定義します。この構造体には、シグナルが発生した時刻、そのときの価格、シグナルが買いか売りかといった情報が格納されます。また、これらの情報を動的に保持するために、配列を用いて記録・追跡できるように設定しています。 

// Signal tracking structure
struct SignalInfo {
   datetime time;
   double price;
   bool isBuySignal;
};

// Arrays to store signal information
datetime signalTimes[];
double signalPrices[];
bool signalBuySignals[];
signalTimes、signalPrices、signalBuySignalsといった配列を使用することで、EAが生成した取引シグナルの記録を明確に保持できます。これにより、複数のシグナルを扱う際にも混乱を防ぎ、効率的に管理することが可能になります。
  •  インジケーター取得関数

次に、インジケーターの値を取得するための関数について詳しく説明します。ここでは、特にボリンジャーバンドとストキャスティクスの値を取得するために使用される関数に注目します。 

//+------------------------------------------------------------------+
//| Retrieve Bollinger Band Levels                                   |
//+------------------------------------------------------------------+
bool GetBollingerBands(ENUM_TIMEFRAMES timeframe, double &upper, double &lower, double &middle) {
   int handle = iBands(Symbol(), timeframe, BB_Period, 0, BB_Deviation, PRICE_CLOSE);

   if(handle == INVALID_HANDLE) {
      Print("Error creating iBands for timeframe: ", timeframe);
      return false;
   }

   double upperBand[], middleBand[], lowerBand[];

   if(!CopyBuffer(handle, 1, 0, 1, upperBand) ||
      !CopyBuffer(handle, 0, 0, 1, middleBand) ||
      !CopyBuffer(handle, 2, 0, 1, lowerBand)) {
      Print("Error copying iBands buffer for timeframe: ", timeframe);
      IndicatorRelease(handle);
      return false;
   }

   upper = upperBand[0];
   middle = middleBand[0];
   lower = lowerBand[0];

   IndicatorRelease(handle);
   return true;
}

//+------------------------------------------------------------------+
//| Retrieve Stochastic Levels                                       |
//+------------------------------------------------------------------+
bool GetStochastic(ENUM_TIMEFRAMES timeframe, double &k_value, double &d_value) {
   int handle = iStochastic(Symbol(), timeframe, K_Period, D_Period, Slowing, MODE_SMA, STO_CLOSECLOSE);

   if(handle == INVALID_HANDLE) {
      Print("Error creating iStochastic for timeframe: ", timeframe);
      return false;
   }

   double kBuffer[], dBuffer[];

   if(!CopyBuffer(handle, 0, 0, 1, kBuffer) ||  // %K line
      !CopyBuffer(handle, 1, 0, 1, dBuffer)) { // %D line
      Print("Error copying iStochastic buffer for timeframe: ", timeframe);
      IndicatorRelease(handle);
      return false;
   }

   k_value = kBuffer[0];
   d_value = dBuffer[0];

   IndicatorRelease(handle);
   return true;
}
最初の関数であるGetBollingerBandsは、指定された時間枠におけるボリンジャーバンドの上限・中央・下限レベルを取得します。この関数では、まずインジケーターのハンドルを作成し、その作成が成功したかどうかを確認します。すべてが正常であれば、バンドの値を配列にコピーし、後で取引ロジックに使用できるようにします。同様に、GetStochastic関数では、ストキャスティクスの%Kおよび%Dの値を取得します。この関数も同様に、エラーチェックとデータコピーの処理をおこない、常に正確なデータに基づいて意思決定ができるようになっています。

  • シグナルのチェックと生成

インジケーターの準備が整ったところで、EAの中核となるロジックを含むCheckAndGenerateSignal関数に移ります。この関数では、前述のインジケーター取得関数を呼び出し、指定されたすべての時間枠におけるボリンジャーバンドとストキャスティクスのデータを取得します。加えて、それぞれの時間枠の最新の終値も取得します。

この関数では、市場の現在の状況に基づいて買いシグナルまたは売りシグナルをチェックします。買いシグナルは、終値がボリンジャーバンドの下限を下回り、かつストキャスティクスの値が5未満(売られすぎ)である場合に発生します。反対に、売りシグナルは、終値が上限バンドを上回り、ストキャスティクスの値が95超(買われすぎ)であるときに発生します。

//+------------------------------------------------------------------+
//| Check and Generate Signal                                        |
//+------------------------------------------------------------------+
void CheckAndGenerateSignal() {
   double upper1, lower1, middle1, close1;
   double upper2, lower2, middle2, close2;
   double upper3, lower3, middle3, close3;
   double k1, d1, k2, d2, k3, d3;

   if(!GetBollingerBands(Timeframe1, upper1, lower1, middle1) ||
      !GetBollingerBands(Timeframe2, upper2, lower2, middle2) ||
      !GetBollingerBands(Timeframe3, upper3, lower3, middle3)) {
      Print("Error retrieving Bollinger Bands data.");
      return;
   }

   if(!GetStochastic(Timeframe1, k1, d1) ||
      !GetStochastic(Timeframe2, k2, d2) ||
      !GetStochastic(Timeframe3, k3, d3)) {
      Print("Error retrieving Stochastic data.");
      return;
   }

   // Retrieve the close prices
   close1 = iClose(Symbol(), Timeframe1, 0);
   close2 = iClose(Symbol(), Timeframe2, 0);
   close3 = iClose(Symbol(), Timeframe3, 0);

   bool buySignal = (close1 <= lower1 && close2 <= lower2 && close3 <= lower3) &&
                    (k1 < 5 && k2 < 5 && k3 < 5); // Oversold condition
   bool sellSignal = (close1 >= upper1 && close2 >= upper2 && close3 >= upper3) &&
                     (k1 > 95 && k2 > 95 && k3 > 95); // Overbought condition

   // Check if an arrow already exists in the same region before placing a new one
   if(buySignal && !ArrowExists(close1)) {
      Print("Buy signal detected on all timeframes with Stochastic confirmation!");
      string arrowName = "BuySignal" + IntegerToString(TimeCurrent());
      ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), close1);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 241);
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clrGreen);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

      // Store signal data
      ArrayResize(signalTimes, ArraySize(signalTimes) + 1);
      ArrayResize(signalPrices, ArraySize(signalPrices) + 1);
      ArrayResize(signalBuySignals, ArraySize(signalBuySignals) + 1);

      signalTimes[ArraySize(signalTimes) - 1] = TimeCurrent();
      signalPrices[ArraySize(signalPrices) - 1] = close1;
      signalBuySignals[ArraySize(signalBuySignals) - 1] = true;
   }

   if(sellSignal && !ArrowExists(close1)) {
      Print("Sell signal detected on all timeframes with Stochastic confirmation!");
      string arrowName = "SellSignal" + IntegerToString(TimeCurrent());
      ObjectCreate(0, arrowName, OBJ_ARROW, 0, TimeCurrent(), close1);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, 242);
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, clrRed);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, 2);

      // Store signal data
      ArrayResize(signalTimes, ArraySize(signalTimes) + 1);
      ArrayResize(signalPrices, ArraySize(signalPrices) + 1);
      ArrayResize(signalBuySignals, ArraySize(signalBuySignals) + 1);

      signalTimes[ArraySize(signalTimes) - 1] = TimeCurrent();
      signalPrices[ArraySize(signalPrices) - 1] = close1;
      signalBuySignals[ArraySize(signalBuySignals) - 1] = false;
   }
}

さらに、チャート上にシグナルを示す矢印を配置する前に、関数はArrowExistsを呼び出して既存の矢印が重複していないかを確認します。問題がなければ、適切な矢印を描画し、シグナルに関する情報を前述の配列に保存します。

  • 矢印存在チェック

チャートを整然と保つために、ArrowExists関数は現在のシグナル価格付近に既存の矢印がないかどうかをチェックする重要な役割を果たします。これにより、複数の矢印が重なって混乱を招くのを防ぎます。具体的には、新しいシグナル価格をsignalPrices配列に格納された価格と比較し、既存の矢印が十分に近い場合は新しい矢印の作成をスキップするかどうかを判断します。

//+------------------------------------------------------------------+
//| Check if an arrow already exists within the MinArrowDistance     |
//+------------------------------------------------------------------+
bool ArrowExists(double price) {
   for(int i = 0; i < ArraySize(signalPrices); i++) {
      if(MathAbs(signalPrices[i] - price) <= MinArrowDistance) {
         return true; // Arrow exists in the same price region
      }
   }
   return false; // No arrow exists in the same region
}

  • OnTick関数とOnDeinit関数

最後に、OnTick関数とOnDeinit関数について説明します。OnTick関数は、新しいマーケットティックが発生するたびに呼び出され、EAが常に最新の状態で反応できるようにします。この関数は、最新データに基づいて取引シグナルを再評価するために、CheckAndGenerateSignal関数を呼び出します。

一方、OnDeinit関数は、EAが削除されたり端末が閉じられたりする際に呼び出されます。この関数の役割は、EAによって作成されたグラフィカルオブジェクト(特に売買シグナルを示す矢印)を削除し、チャートを整理された状態に保つことです。

//+------------------------------------------------------------------+
//| OnTick Event                                                     |
//+------------------------------------------------------------------+
void OnTick() {
   CheckAndGenerateSignal();
}

//+------------------------------------------------------------------+
//| OnDeinit Function                                                |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   // Clean up the objects
   long chart_id = 0;
   for(int i = ObjectsTotal(chart_id) - 1; i >= 0; i--) {
      string name = ObjectName(chart_id, i);
      if(StringFind(name, "Signal") != -1) {
         ObjectDelete(chart_id, name);
      }
   }

   Print("Multitimeframe Bollinger-Stochastic Analyzer deinitialized.");
}

また、Signal Pulse EAでは、ストキャスティクスの閾値レベルを調整し、最も信頼性の高いシグナルのみが生成されるようにしています。これらの調整は、市場が極端な状態(買いシグナルの場合は売られ過ぎ、売りシグナルの場合は買われ過ぎ)にあることを確認してから取引をおこなうことに重点を置いています。

  • 売られ過ぎ状態:買いシグナル
bool buySignal = (close1 <= lower1 && close2 <= lower2 && close3 <= lower3) &&
                 (k1 < 5 && k2 < 5 && k3 < 5); // Oversold condition

買いシグナルの場合、価格は3つの時間枠(M15、M30、H1)すべてでボリンジャーバンドの下限以下である必要があり、これは強い下落圧力を示しています。さらに、ストキャスティクスの%K値は3つの時間枠すべてで5未満でなければなりません。この値は極端な売られ過ぎの状態を示しており、市場が上昇に転じる可能性が非常に高いことを意味します。より厳しい閾値の「5未満」に設定することで、EAは反転の確率が著しく高いシグナルのみを考慮するようにしています。

  • 買われ過ぎ状態:売りシグナル

bool sellSignal = (close1 >= upper1 && close2 >= upper2 && close3 >= upper3) &&
                  (k1 > 95 && k2 > 95 && k3 > 95); // Overbought condition

売りシグナルの場合、価格は3つの時間枠すべてでボリンジャーバンドの上限以上である必要があり、これは強い上昇モメンタムがありながら、まもなく反転する可能性を示しています。さらに、ストキャスティクスの%K値はすべての時間枠で95を超えている必要があり、これは極端な買われ過ぎの状態を示し、市場が弱気に転じる可能性が高いことを意味します。この厳しい条件により、EAは中程度の価格変動やレンジ相場での誤った売りシグナルを回避します。


テストと結果

  • 履歴データを使ったバックテスト
バックテストはEA開発において重要な工程であり、トレーダーは過去の価格データを用いてEAのパフォーマンスを分析することができます。このプロセスにより、EAの収益性、精度、堅牢性を把握し、戦略の改善やパフォーマンスの最適化をおこなうことが可能になります。
1.履歴データを読み込む

バックテストを開始するには、対象となる銘柄および時間枠に対応した高品質なティックレベルの履歴データを読み込むことが必要です。このデータはEAのパフォーマンス評価の基盤となるため、正確で信頼できるものでなければなりません。データに誤りや不整合があると、EAの性能評価に誤った結論をもたらす可能性があります。

2.テストパラメータを設定する

データを読み込んだ後は、MetaTraderのストラテジーテスターにて必要なパラメータを設定します。設定項目は以下の通りです。
  • Symbol:取引対象となる通貨ペアや資産を選択します。
  • Period:EAで使用している時間枠と同じものを設定します(例:M15、M30、H1)。
  • Spread:取引コストをリアルにシミュレートするため、現実的な値または固定スプレッドを設定します。
  • Optimization:ボリンジャーバンドの期間やストキャスティクスの閾値など、入力パラメータを調整して最適なパフォーマンスを探ります。
3.結果を評価する
テストパラメータを設定したら、出力されたメトリックを分析します。
  • 収益性:EAの純利益やプロフィットファクターから全体の収益性を評価します。
  • リスク:最大ドローダウンを確認し、EAのリスク許容度を把握します。
  • 勝率と取引頻度:勝ちトレードの割合や取引回数を分析し、異なる市場環境でのEAの動きを理解します。

以下に示すテスト結果をご覧ください。

テスト結果1

図4:テスト結果1

結果2

図5:テスト結果2

上の図はEAのパフォーマンステストを示しています。GIFでは、検出されたすべてのシグナルのログ記録を確認できます。要件を満たす結果が得られるまで、入力パラメータ、時間枠、ストキャスティクスのレベルを変更してさらにテストをおこなうことが可能です。


結論

この記事では、ボリンジャーバンドとストキャスティクスのインジケーターを組み合わせ、M15、M30、H1の複数時間枠で取引シグナルを生成する「Signal Pulse」EAを開発しました。複数の時間枠で買われ過ぎ・売られ過ぎの状態が一致する場合のみシグナルを検出することで、成功確率を高めています。さまざまな市場環境における徹底的なバックテストが不可欠です。今後の改良点としては、トレンド方向のフィルター追加、高度なリスク管理機能、異なる市場タイプに応じたシグナルロジックの洗練などが考えられます。トレーダーの皆さんには、包括的な取引戦略の一環として、ご自身の戦略やテクニックと組み合わせて「Signal Pulse」を活用されることをおすすめします。

日付 ツール名  詳細 バージョン  アップデート  備考
01/10/24 ChartProjector 前日のプライスアクションをゴースト効果でオーバーレイするスクリプト 1.0 初回リリース Lynnchris Tool Chestの最初のツール
18/11/24 Analytical Comment 前日の情報を表形式で提供し、市場の将来の方向性を予測する 1.0 初回リリース Lynnchris Tool Chestの2番目のツール
27/11/24 Analytics Master 2時間ごとに市場指標を定期的に更新する  1.01 v.2 Lynnchris Tool Chestの3番目のツール
02/12/24 Analytics Forecaster  Telegram統合により、2時間ごとに市場指標を定期的に更新する 1.1 v.3 ツール番号4
09/12/24 Volatility Navigator ボリンジャーバンド、RSI、ATR指標を使用して市場の状況を分析するEA 1.0 初回リリース ツール番号5
19/12/24 Mean Reversion Signal Reaper  平均回帰戦略を用いて市場を分析し、シグナルを提供する  1.0  初回リリース  ツール番号6 
09/01/2025  Signal Pulse  多時間枠分析ツール 1.0  初回リリース  ツール番号7 

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

添付されたファイル |
トレンドフォロー型ボラティリティ予測のための隠れマルコフモデル トレンドフォロー型ボラティリティ予測のための隠れマルコフモデル
隠れマルコフモデル(HMM)は、観測可能な価格変動を分析することで、市場の潜在的な状態を特定する強力な統計手法です。取引においては、市場レジームの変化をモデル化・予測することで、ボラティリティの予測精度を高め、トレンドフォロー戦略の構築に役立ちます。本記事では、HMMをボラティリティのフィルターとして活用し、トレンドフォロー戦略を開発するための一連の手順を紹介します。
MQL5取引ツールキット(第6回):直近で約定された予約注文に関する関数で履歴管理EX5ライブラリを拡張 MQL5取引ツールキット(第6回):直近で約定された予約注文に関する関数で履歴管理EX5ライブラリを拡張
EX5モジュールで、直近で約定された予約注文のデータをシームレスに取得・格納するエクスポート可能な関数を作成する方法を学びます。このステップバイステップの包括的なガイドでは、直近で約定された予約注文の重要なプロパティ(注文タイプ、発注時間、約定時間、約定タイプなど)を取得するための専用かつ機能別の関数群を開発することで、履歴管理EX5ライブラリをさらに強化していきます。これらのプロパティは、予約注文の取引履歴を効果的に管理・分析するうえで重要な情報です。
ログレコードをマスターする(第3回):ログを保存するためのハンドラの調査 ログレコードをマスターする(第3回):ログを保存するためのハンドラの調査
この記事では、ログライブラリのハンドラの概念を説明し、その仕組みを理解するとともに、コンソール、データベース、ファイルの3種類の基本的な実装を作成します。今後の記事に向けて、ハンドラの基本構造から実践的なテストまでを網羅し、完全な機能実装の基盤を整えます。
初級から中級まで:配列(I) 初級から中級まで:配列(I)
この記事は、これまでに議論してきた内容と、新たな研究段階との橋渡しとなるものです。この記事を理解するためには、前回までの記事を読んでおく必要があります。ここで提示されるコンテンツは、教育目的のみに使用されることを意図しています。いかなる状況においても、提示された概念を学習し習得する以外の目的でアプリケーションを利用することは避けてください。