English Deutsch
preview
プライスアクション分析ツールキットの開発(第14回):Parabolic Stop and Reverseツール

プライスアクション分析ツールキットの開発(第14回):Parabolic Stop and Reverseツール

MetaTrader 5インディケータ | 19 6月 2025, 09:15
95 0
Christian Benjamin
Christian Benjamin

内容


はじめに

テクニカルインジケーターとは、価格、出来高、建玉などの過去データのパターンを分析することによって生成されるシグナルです。これらのヒューリスティックなツールは、トレーダーが市場の挙動を評価し、統計モデルや実績のあるトレンドに基づいて将来の動きを予測することを可能にします。

本記事では、MQL5を用いて開発したエキスパートアドバイザー(EA)に焦点を当てます。このEAは、市場の反転ポイントを特定することを目的として設計されており、パラボリックSARを用いたシグナル検出機能を備えています。インジケーターを監視しながらリアルタイムでその有効性を評価し、あらかじめ定めたレベルに達した際に、最適なエグジットポイントを特定し、取引シグナルを生成します。

まず、採用した戦略の概要と、取引においてテクニカルインジケーターを活用する意義について説明します。その後、MQL5での実装手順を詳述し、テスト結果の詳細な分析を行い、最後にこれらの技術をシステムに取り入れたいと考えているトレーダーに向けた考察と提言をまとめます。


戦略の理解

Parabolic SAR(パラボリックSAR)

パラボリックSARインジケーターにおける「パラボリック」とは、プロットされるカーブの形状を指します。トレンドが進行するにつれて、インジケーター上のドットは放物線状に加速して動き、価格が直近の高値または安値からさらに乖離するにつれてモメンタムが強まっていることを示します。

一方、「SAR」は「Stop and Reverse」の略で、これはトレンドの反転の可能性を示すシグナルです。ドットが価格の片側から反対側へ移動した場合、それは現在のトレンドが終了する可能性があり、ポジションの決済や新たなトレンドへの転換に備えるべきタイミングであることを示唆します。

本稿のコンセプトをさらに深く掘り下げる前に、パラボリックSARをチャートに表示しておくことが重要です。追加方法はいくつかありますが、ここではそのうちの1つを説明します。MetaTrader 5アプリケーションで、[挿入]メニューをクリックします。次に、[インディケータ]->[トレンド系]と進み、[Parabolic SAR]を選択してチャートに挿入します。挿入後は、EAで使用するパラメータと一致するように設定値を調整します。

インジケーターの追加方法の詳細については、以下の図1を参照してください。

インジケーターの追加

図1:インジケーターの追加

シグナル生成ロジック

パラボリックSARインジケーターを活用する際には、SARの放物線と価格の動きとの関係を綿密に監視することが重要です。本戦略では、以下のロジックに基づいてシグナルを生成します。

買いシグナル

買いシグナルは次の場合にトリガーされます:

  • 現在のバーが陽線である(終値が始値より高い)かつ、PSARの値が終値より下にある。
  • 直前の2本のバーが、いずれも終値の上にPSARドットが位置しており、下降トレンドが継続していたことを示している。
  • 直前のバー間におけるPSAR値の差が許容範囲内であり、一貫した動きがあることを確認できる。

買い条件

図2:買いの条件

売りシグナル

売りシグナルは次の場合にトリガーされます。

  • 現在のバーが陰線である(終値が始値より低い)かつ、PSARの値が終値より上にある。
  • 直前の2本のバーが、いずれも終値の下にPSARドットが位置しており、上昇トレンドが継続していたことを示している。
  • また、直前のバー間におけるPSAR値の差があらかじめ設定された許容範囲内である。

売りシグナル

図3:売りの条件


MQL5への実装

ファイルヘッダーとプロパティ

コードの最上部では、ファイル名、著作権情報、そして作者のWebページへのリンクなど、重要なファイル情報を含むヘッダーから始めます。ここでは私自身の情報を記載しています。続いて、バージョン番号やstrictモードなど、重要なコンパイル設定をおこなう複数の#propertyディレクティブが続きます。 

//+--------------------------------------------------------------------+
//|                                              Parabolic SAR EA.mql5 |
//|                                 Copyright 2025, Christian Benjamin |
//|                                               https://www.mql5.com |
//+--------------------------------------------------------------------+
#property copyright "2025, Christian Benjamin"
#property link      "https://www.mql5.com/ja/users/lynnchris"
#property version   "1.0"
#property strict

#property strictの使用は非常に重要で、EA内で厳格な型チェックとパラメータ検証を強制します。この設定により、実行時エラーに発展する前に潜在的なコーディングミスを早期に発見できるため、安全性が高まります。EAを公開する場合でも、実際の取引環境で使用する場合でも、この設定はスムーズな動作と将来的なMetaTrader 5のアップデートへの適合性を確保するための重要な品質管理措置といえます。

入力パラメータ

次に、MetaTrader 5のインターフェイスから直接カスタマイズ可能な一連の入力パラメータが定義されます。これらの入力には、パラボリックSARインジケーターの加速係数を制御するSARStepや最大加速を設定するSARMaximumなどが含まれます。また、MinConsecutiveDotsMaxDotGapPercentageといったシグナル検出の精度を高める設定もあり、強いトレンドのみがシグナルを発生させるように調整できます。  さらに、アラートの有効化、サウンド通知やチャート上への矢印描画なども設定できます。

// Input parameters for the Parabolic SAR indicator
input double SARStep    = 0.02;  // Acceleration factor for PSAR
input double SARMaximum = 0.2;   // Maximum acceleration for PSAR

// Input parameters for refining the signal based on PSAR dots
input int    MinConsecutiveDots = 2;          // Require at least 2 consecutive bars in one trend before reversal
input double MaxDotGapPercentage  = 1.0;      // Maximum allowed gap between consecutive PSAR dots (% of current close)

// Input parameters for alerts and arrow drawing
input bool   EnableAlerts  = true;            // Enable popup alerts
input bool   EnableSound   = true;            // Enable sound alerts
input bool   EnableArrows  = true;            // Draw arrows on chart
input string BuyArrowSymbol  = "233";         // Wingdings up arrow (as string)
input string SellArrowSymbol = "234";         // Wingdings down arrow (as string)
input int    ArrowWidth    = 2;               // Arrow thickness
input double ArrowOffsetMultiplier = 5;       // Multiplier for arrow placement offset

これらの入力パラメーターにより、EAをさまざまな市場状況やご自身のトレードスタイルに合わせて柔軟にカスタマイズできます。 ここで注目すべき点の一つは、MetaTrader 5で使用できる矢印記号の多様性です。これらのシンボルはWingdingsフォントに由来しており、簡単にカスタマイズ可能です。たとえば、買いシグナルにはチェックマークを、売りシグナルには“X”マークを使用するなど、ご自身の取引スタイルに応じて視覚的に分かりやすい表示が可能です。また、矢印の幅などのパラメータを調整することで、シグナルの視認性をさらに高め、チャート上で直感的に認識できるようにすることができます。

グローバル変数と列挙型

EAの内部状態を管理するために、いくつかのグローバル変数と列挙型が宣言されます。たとえば、パラボリックSARインジケーターハンドルはsarHandleという変数に保存されており、コード全体でこのインジケーターを参照できるようになっています。また、lastBarTimeを使用して最後に処理されたバーの時間を記録し、EAが同じバーを複数回処理しないように制御します。列挙体SignalTypeでは、シグナルの状態を明確に定義しています。これには、シグナルなし・買いシグナル・売りシグナルの3つの状態が含まれ、EAのロジックの中で分岐処理に利用されます。

// Global indicator handle for PSAR
int sarHandle = INVALID_HANDLE;

// Global variable to track last processed bar time
datetime lastBarTime = 0;

// Enumeration for signal types
enum SignalType
  {
   NO_SIGNAL,
   BUY_SIGNAL,
   SELL_SIGNAL
  };

// Global variables for pending signal mechanism
SignalType pendingSignal = NO_SIGNAL;
int        waitCount     = 0;          // Counts new closed bars since signal detection
double     pendingReversalLevel = 0.0; // Stores the PSAR value at signal detection

さらに、pendingSignalwaitCountpendingReversalLevelといった変数は、最終的なアクションを実行する前に数本のバーにわたって追加の確認を待っている保留中のシグナルを管理するために使用されます。  特に重要なのがpendingReversalLevelであり、これはシグナルが発生した時点のPSARの値を記録する変数です。この値は基準点として機能し、その後の価格の動きを監視することで、反転が本物か、それとも騙しのシグナルであるかをEAが判断するために使用されます。このようなチェックポイントの仕組みは、不必要な取引を減らし、EA全体の精度を高めるうえで非常に重要です。

チャートへの矢印の描画

より直感的な視覚表現を実現するために、EAにはDrawSignalArrowという関数が含まれています。この関数は、シグナルが検出された際にチャート上に矢印を描画する役割を担います。具体的には、まずバーの時間情報をもとにユニークなオブジェクト名を生成し、その名前のオブジェクトがすでに存在していないかを確認します。もし存在していれば、重複を避けるためにそれを削除してから新しい矢印を描画します。

void DrawSignalArrow(string prefix, datetime barTime, double price, color arrowColor, long arrowCode)
  {
   string arrowName = prefix + "_" + TimeToString(barTime, TIME_SECONDS);
   // Remove existing object with the same name to prevent duplicates
   if(ObjectFind(0, arrowName) != -1)
      ObjectDelete(0, arrowName);

   if(ObjectCreate(0, arrowName, OBJ_ARROW, 0, barTime, price))
     {
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, ArrowWidth);
     }
   else
     {
      Print("Failed to create arrow object: ", arrowName);
     }
  }

EAは、チャート上にシグナルを視覚的に表示するために、OBJ_ARROWオブジェクトを使用します。重複したマーカーによってチャートが煩雑になるのを防ぐために、コードはまず既存の矢印が存在するかを確認し、存在していればそれを削除したうえで新しい矢印を描画します。この手法により、チャートは常に見やすく整った状態に保たれ、リアルタイムの取引判断において視認性が確保されます。また、矢印の記号、色、位置のオフセットなどをカスタマイズすることで、ご自身の視覚的な好みに合わせて表示を調整することも可能です。

シグナル検出機能

EAの中核をなすのが、CheckForSignal関数です。この関数では、直近3本のローソク足とPSARデータを解析して、売買シグナルの判定をおこないます。買いシグナルが発生するためには、現在のバーが陽線であり(つまり終値が始値より高い)、PSARのドットがローソク足の下に位置している必要があります。また、その直前の2本のバーは陰線で、それぞれのPSARドットがローソク足の上にあることが条件です。売りシグナルの場合はこの逆です。

SignalType CheckForSignal(const double &sarArray[], const double &openArray[], const double &closeArray[])
  {
   // Mapping indices:
   // Index 0: Last closed bar (current candidate)
   // Index 1: Previous bar
   // Index 2: Bar before previous
   
   double sar0 = sarArray[0], sar1 = sarArray[1], sar2 = sarArray[2];
   double open0 = openArray[0], close0 = closeArray[0];
   double open1 = openArray[1], close1 = closeArray[1];
   double open2 = openArray[2], close2 = closeArray[2];
   
   // Check for BUY signal:
   if((close0 > open0) && (sar0 < close0) &&
      (sar1 > close1) && (sar2 > close2))
     {
      int countBearish = 0;
      if(sar1 > close1) countBearish++;
      if(sar2 > close2) countBearish++;
      double dotGap = MathAbs(sar1 - sar2);
      double gapThreshold = (MaxDotGapPercentage / 100.0) * close0;
      if(countBearish >= MinConsecutiveDots && dotGap <= gapThreshold)
         return BUY_SIGNAL;
     }
   
   // Check for SELL signal:
   if((close0 < open0) && (sar0 > close0) &&
      (sar1 < close1) && (sar2 < close2))
     {
      int countBullish = 0;
      if(sar1 < close1) countBullish++;
      if(sar2 < close2) countBullish++;
      double dotGap = MathAbs(sar1 - sar2);
      double gapThreshold = (MaxDotGapPercentage / 100.0) * close0;
      if(countBullish >= MinConsecutiveDots && dotGap <= gapThreshold)
         return SELL_SIGNAL;
     }
   
   return NO_SIGNAL;
  }

この関数では、連続するPSARドット間の間隔が許容範囲内に収まっているかを確認するためのギャップ閾値も使用しています。すべての条件が満たされた場合、関数は適切なシグナルタイプ(買いまたは売り)を返し、そうでない場合は「シグナルなし」を返します。 このシグナル検出機能は、PSARの位置関係とローソク足のパターンを活用してトレンドの反転を効果的に識別します。ただし、設計上さらに拡張できるようになっており、移動平均、RSI、あるいはボラティリティインデックスなどの追加フィルターを組み込むことで精度を高めることが可能です。これにより、レンジ相場や重要なニュース発表によるノイズの多い局面でも、騙しのシグナルを減らす手助けとなります。本EAは本質的に柔軟性を備えており、トレーダーが自身の戦略や市場状況に合わせてロジックを自由に調整できる設計になっています。

新しいバーの検出

データを処理する前に、EAは新しいバーが確定したかどうかを判断する必要があります。これをおこなうのがIsNewBar関数です。この関数では、直近で確定したバーの時間を取得し、それを保存されているlastBarTimeと比較します。時間が異なる場合は、新しいバーが形成されたことを意味するため、関数はlastBarTimeを更新し、trueを返します。そうでなければfalseが返され、同じバー内での重複処理を防ぎます。

bool IsNewBar()
  {
   datetime times[];
   if(CopyTime(_Symbol, _Period, 1, 1, times) <= 0)
     {
      Print("Failed to retrieve bar time in IsNewBar().");
      return false;
     }
   if(times[0] != lastBarTime)
     {
      lastBarTime = times[0];
      return true;
     }
   return false;
  }

このチェックは非常に重要であり、EAが新しいバーごとに一度だけデータを処理することを保証します。これにより、同じバー内での繰り返しや誤ったシグナル処理を防ぐことができます。 IsNewBar関数は、EAの効率を高めるためのシンプルながら賢い仕組みです。シグナルが新しいバーごとに1回だけ処理されるようにす新しいバーが形成されたタイミングでのみシグナル処理をおこなうことで、無駄な計算や重複アラートの発生を抑えます。  この簡単なチェックにより、EAは軽快かつ正確に動作し、1本のバー内での価格変動を誤解釈するリスクも軽減されます。結果として、この仕組みはMetaTrader上でのEAの安定性と一貫性のあるパフォーマンスを維持するうえで大きな役割を果たします。

初期化と初期化解除

EAが読み込まれると、まずOnInit関数が実行されます。この関数の主な役割は、ユーザーが指定したパラメータを使って組み込みのパラボリックSARインジケーターのハンドルを作成することです。インジケーターが正常に初期化されると確認用のメッセージが表示され、初期化に失敗した場合はエラーメッセージが出力されて処理は失敗となります。一方、OnDeinit関数は、EAがチャートから削除されたときやターミナルが終了するときに呼び出されます。

int OnInit()
  {
   // Create the built-in Parabolic SAR indicator handle
   sarHandle = iSAR(_Symbol, _Period, SARStep, SARMaximum);
   if(sarHandle == INVALID_HANDLE)
     {
      Print("Error creating PSAR handle");
      return INIT_FAILED;
     }
   Print("SAR EA initialized successfully.");
   return INIT_SUCCEEDED;
  }

この関数は、インジケーターのハンドルを解放し、EAの稼働中に作成された矢印などのグラフィカルなオブジェクトをすべて削除して、不要な残留物を残さないように処理します。 適切に設計されたEAは、終了時のクリーンアップにもしっかり配慮しています。動作中に生成された矢印やその他の視覚要素を削除することで、チャート上に古いマーカーが溜まって見づらくなるのを防ぎます。このクリーンアップはまさに「良い家事」と同じで、EAを起動するたびに、すっきりとした状態のチャートと最適なシステムパフォーマンスを維持できるようにしています。

メインロジック(OnTick関数)

最後に、OnTick関数は毎ティック実行されるEAのメインエンジンです。まず、IsNewBar関数を使って新しいバーが確定したかどうかを確認し、新しいバーがなければ無駄な処理を避けるために早期に終了します。新しいバーが確認されると、EAは最新のPSAR値と、それに対応する直近バーの始値および終値を取得します。この時点で、保留中のシグナルがあるかどうかを評価し、シグナルが保留されている場合は、経過したバー数を管理するカウンタ(waitCount)を増加させます。

void OnTick()
  {
   // Process only once per new closed bar
   if(!IsNewBar())
      return;
   
   // Retrieve PSAR and price data
   double sarArray[4];
   if(CopyBuffer(sarHandle, 0, 1, 4, sarArray) < 3) { /* error handling */ }
   double openArray[4], closeArray[4];
   if(CopyOpen(_Symbol, _Period, 1, 4, openArray) < 3 ||
      CopyClose(_Symbol, _Period, 1, 4, closeArray) < 3) { /* error handling */ }
   
   // Pending Signal Logic...
   if(pendingSignal != NO_SIGNAL)
     {
      waitCount++; // Increment waiting counter
      
      if(pendingSignal == BUY_SIGNAL)
        {
         if(closeArray[0] <= pendingReversalLevel)
           {
            // Confirm reversal: Close alert for BUY signal
           }
         else if(waitCount >= 3)
           {
            // Warn about a possible fake BUY signal
           }
        }
      else if(pendingSignal == SELL_SIGNAL)
        {
         if(closeArray[0] >= pendingReversalLevel)
           {
            // Confirm reversal: Close alert for SELL signal
           }
         else if(waitCount >= 3)
           {
            // Warn about a possible fake SELL signal
           }
        }
      return; // Wait until pending signal is resolved
     }
   
   // Check for a new reversal signal if no pending signal exists
   SignalType newSignal = CheckForSignal(sarArray, openArray, closeArray);
   if(newSignal != NO_SIGNAL)
     {
      pendingSignal = newSignal;
      waitCount = 0; // Reset counter
      pendingReversalLevel = sarArray[0]; // Store current PSAR value
      
      // Alert and optionally draw an arrow based on the new signal
      if(newSignal == BUY_SIGNAL) { /* process BUY signal */ }
      else if(newSignal == SELL_SIGNAL) { /* process SELL signal */ }
     }
  }

EAは次に、現在の価格が保存された反転レベルに達しているかを確認します。達していれば「ここでクローズ」というアラートを発し、もし3本のバーが経過しても確認が取れなければ、潜在的なダマシシグナルの警告を出します。保留中のシグナルがない場合は、CheckForSignal関数を呼び出して新たなシグナルの発生を検出します。新しいシグナルがあれば、そのシグナルを保存し、待機カウンタをリセットし、現在のPSAR値を基に反転レベルを設定します。そして、対応するアラートや視覚的マーカーを表示します。OnTick関数内の革新的な機能の一つが、騙しのシグナルを除去するフィルタリングの手法です。

これは、シグナル発生後に経過したバー数をカウンタ(waitCount)で監視し、実際の行動を起こす前に確認の時間を設けるというものです。この遅延処理は、急激な価格変動が早すぎるシグナル発生を招くボラティリティの高い市場において特に有効です。確認バー数を調整することで、反応速度と慎重さのバランスを取り、短期のスキャルピングから長期のトレンド戦略まで幅広く対応できる柔軟性をEAに持たせています。

完全なMQL5 EAコード

//+--------------------------------------------------------------------+
//|                                              Parabolic SAR EA.mql5 |
//|                                 Copyright 2025, Christian Benjamin |
//|                                               https://www.mql5.com |
//+--------------------------------------------------------------------+

#property copyright "2025, Christian Benjamin"
#property link      "https://www.mql5.com/ja/users/lynnchris"
#property version   "1.2"
#property strict

// Input parameters for the Parabolic SAR indicator
input double SARStep    = 0.1;  // Acceleration factor for PSAR
input double SARMaximum = 1;   // Maximum acceleration for PSAR

// Input parameters for refining the signal based on PSAR dots
input int    MinConsecutiveDots = 2;         // Require at least 2 consecutive bars in one trend before reversal
input double MaxDotGapPercentage  = 1.0;     // Maximum allowed gap between consecutive PSAR dots (% of current close)

// Input parameters for alerts and arrow drawing
input bool   EnableAlerts  = true;            // Enable popup alerts
input bool   EnableSound   = true;            // Enable sound alerts
input bool   EnableArrows  = true;            // Draw arrows on chart
input string BuyArrowSymbol  = "233";         // Wingdings up arrow (as string)
input string SellArrowSymbol = "234";         // Wingdings down arrow (as string)
input int    ArrowWidth    = 2;               // Arrow thickness
input double ArrowOffsetMultiplier = 5;       // Multiplier for arrow placement offset

// Global indicator handle for PSAR
int sarHandle = INVALID_HANDLE;

// Global variable to track last processed bar time
datetime lastBarTime = 0;

// Enumeration for signal types
enum SignalType
  {
   NO_SIGNAL,
   BUY_SIGNAL,
   SELL_SIGNAL
  };

// Global variables for pending signal mechanism
SignalType pendingSignal = NO_SIGNAL;
int        waitCount     = 0;        // Counts new closed bars since signal detection
double     pendingReversalLevel = 0.0; // Stores the PSAR value at signal detection

//+------------------------------------------------------------------+
//| DrawSignalArrow - Draws an arrow object on the chart             |
//+------------------------------------------------------------------+
void DrawSignalArrow(string prefix, datetime barTime, double price, color arrowColor, long arrowCode)
  {
   string arrowName = prefix + "_" + TimeToString(barTime, TIME_SECONDS);
// Remove existing object with the same name to prevent duplicates
   if(ObjectFind(0, arrowName) != -1)
      ObjectDelete(0, arrowName);

   if(ObjectCreate(0, arrowName, OBJ_ARROW, 0, barTime, price))
     {
      ObjectSetInteger(0, arrowName, OBJPROP_COLOR, arrowColor);
      ObjectSetInteger(0, arrowName, OBJPROP_ARROWCODE, arrowCode);
      ObjectSetInteger(0, arrowName, OBJPROP_WIDTH, ArrowWidth);
     }
   else
     {
      Print("Failed to create arrow object: ", arrowName);
     }
  }

//+-------------------------------------------------------------------+
//| CheckForSignal - Evaluates PSAR and price data to determine signal|
//+-------------------------------------------------------------------+
SignalType CheckForSignal(const double &sarArray[], const double &openArray[], const double &closeArray[])
  {
// Mapping indices:
// Index 0: Last closed bar (current candidate)
// Index 1: Previous bar
// Index 2: Bar before previous

   double sar0 = sarArray[0], sar1 = sarArray[1], sar2 = sarArray[2];
   double open0 = openArray[0], close0 = closeArray[0];
   double open1 = openArray[1], close1 = closeArray[1];
   double open2 = openArray[2], close2 = closeArray[2];

// Check for BUY signal:
   if((close0 > open0) && (sar0 < close0) &&
      (sar1 > close1) && (sar2 > close2))
     {
      int countBearish = 0;
      if(sar1 > close1)
         countBearish++;
      if(sar2 > close2)
         countBearish++;
      double dotGap = MathAbs(sar1 - sar2);
      double gapThreshold = (MaxDotGapPercentage / 100.0) * close0;
      if(countBearish >= MinConsecutiveDots && dotGap <= gapThreshold)
         return BUY_SIGNAL;
     }

// Check for SELL signal:
   if((close0 < open0) && (sar0 > close0) &&
      (sar1 < close1) && (sar2 < close2))
     {
      int countBullish = 0;
      if(sar1 < close1)
         countBullish++;
      if(sar2 < close2)
         countBullish++;
      double dotGap = MathAbs(sar1 - sar2);
      double gapThreshold = (MaxDotGapPercentage / 100.0) * close0;
      if(countBullish >= MinConsecutiveDots && dotGap <= gapThreshold)
         return SELL_SIGNAL;
     }

   return NO_SIGNAL;
  }

//+------------------------------------------------------------------+
//| IsNewBar - Determines if a new closed bar is available           |
//+------------------------------------------------------------------+
bool IsNewBar()
  {
   datetime times[];
   if(CopyTime(_Symbol, _Period, 1, 1, times) <= 0)
     {
      Print("Failed to retrieve bar time in IsNewBar().");
      return false;
     }
   if(times[0] != lastBarTime)
     {
      lastBarTime = times[0];
      return true;
     }
   return false;
  }

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
// Create the built-in Parabolic SAR indicator handle
   sarHandle = iSAR(_Symbol, _Period, SARStep, SARMaximum);
   if(sarHandle == INVALID_HANDLE)
     {
      Print("Error creating PSAR handle");
      return INIT_FAILED;
     }
   Print("SAR EA initialized successfully.");
   return INIT_SUCCEEDED;
  }

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
   if(sarHandle != INVALID_HANDLE)
      IndicatorRelease(sarHandle);

// Remove all arrow objects from the current chart window
   ObjectsDeleteAll(0, (int)OBJ_ARROW, 0);
  }

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
// Process only once per new closed bar
   if(!IsNewBar())
      return;

// Retrieve the last 4 PSAR values (we require at least 3 values)
   double sarArray[4];
   if(CopyBuffer(sarHandle, 0, 1, 4, sarArray) < 3)
     {
      Print("Failed to retrieve PSAR data.");
      return;
     }

// Retrieve the last 4 bars' price data (Open and Close)
   double openArray[4], closeArray[4];
   if(CopyOpen(_Symbol, _Period, 1, 4, openArray) < 3 ||
      CopyClose(_Symbol, _Period, 1, 4, closeArray) < 3)
     {
      Print("Failed to retrieve price data.");
      return;
     }

// Process pending signal logic if a signal is waiting confirmation
   if(pendingSignal != NO_SIGNAL)
     {
      waitCount++; // Increment the waiting counter

      if(pendingSignal == BUY_SIGNAL)
        {
         if(closeArray[0] <= pendingReversalLevel)
           {
            Print("Reversal level reached for BUY signal. Close here.");
            if(EnableAlerts)
               Alert("Close here for BUY signal on ", _Symbol, " at price ", DoubleToString(closeArray[0], _Digits));
            if(EnableSound)
               PlaySound("alert.wav");
            pendingSignal = NO_SIGNAL;
            waitCount = 0;
           }
         else
            if(waitCount >= 3)
              {
               Print("Warning: Possible fake BUY signal - reversal not confirmed in 3 candles.");
               if(EnableAlerts)
                  Alert("Warning: Possible fake BUY signal on ", _Symbol);
               pendingSignal = NO_SIGNAL;
               waitCount = 0;
              }
        }
      else
         if(pendingSignal == SELL_SIGNAL)
           {
            if(closeArray[0] >= pendingReversalLevel)
              {
               Print("Reversal level reached for SELL signal. Close here.");
               if(EnableAlerts)
                  Alert("Close here for SELL signal on ", _Symbol, " at price ", DoubleToString(closeArray[0], _Digits));
               if(EnableSound)
                  PlaySound("alert.wav");
               pendingSignal = NO_SIGNAL;
               waitCount = 0;
              }
            else
               if(waitCount >= 3)
                 {
                  Print("Warning: Possible fake SELL signal - reversal not confirmed in 3 candles.");
                  if(EnableAlerts)
                     Alert("Warning: Possible fake SELL signal on ", _Symbol);
                  pendingSignal = NO_SIGNAL;
                  waitCount = 0;
                 }
           }
      return;
     }

// Check for a new reversal signal
   SignalType newSignal = CheckForSignal(sarArray, openArray, closeArray);
   if(newSignal != NO_SIGNAL)
     {
      pendingSignal = newSignal;
      waitCount = 0; // Reset waiting counter
      pendingReversalLevel = sarArray[0]; // Set reversal level from current PSAR value

      if(newSignal == BUY_SIGNAL)
        {
         Print("Buy signal detected on ", TimeToString(lastBarTime, TIME_DATE|TIME_SECONDS),
               ". Waiting for reversal confirmation (up to 3 candles).");
         if(EnableAlerts)
            Alert("Buy signal detected on ", _Symbol, " at price ", DoubleToString(closeArray[0], _Digits));
         if(EnableArrows)
           {
            double lowVal[];
            if(CopyLow(_Symbol, _Period, 1, 1, lowVal) > 0)
              {
               double offset = _Point * ArrowOffsetMultiplier;
               DrawSignalArrow("BuyArrow", lastBarTime, lowVal[0] - offset, clrGreen, StringToInteger(BuyArrowSymbol));
              }
            else
               Print("Failed to retrieve low price for arrow placement.");
           }
        }
      else
         if(newSignal == SELL_SIGNAL)
           {
            Print("Sell signal detected on ", TimeToString(lastBarTime, TIME_DATE|TIME_SECONDS),
                  ". Waiting for reversal confirmation (up to 3 candles).");
            if(EnableAlerts)
               Alert("Sell signal detected on ", _Symbol, " at price ", DoubleToString(closeArray[0], _Digits));
            if(EnableArrows)
              {
               double highVal[];
               if(CopyHigh(_Symbol, _Period, 1, 1, highVal) > 0)
                 {
                  double offset = _Point * ArrowOffsetMultiplier;
                  DrawSignalArrow("SellArrow", lastBarTime, highVal[0] + offset, clrRed, StringToInteger(SellArrowSymbol));
                 }
               else
                  Print("Failed to retrieve high price for arrow placement.");
              }
           }
     }
  }
//+------------------------------------------------------------------+


テストと結果

実際の取引に移る前に、デモ口座でのバックテストをお勧めします。以下の手順でテスト結果をもとにEAの調整をおこないましょう。
  • まず、MetaEditorでEAを正常にコンパイルしたら、MetaTraderを開きます。
  • MetaTraderで、ストラテジーテスターに移動します。テストするEAを選択し、希望する時間枠、テスト期間、その他の関連パラメータを選択します。
  • 設定が完了したら、[開始]をクリックしてバックテストを開始します。

テスターの初期化

図4:テスターの初期化

以下に、バックテストセッション中にキャプチャした画像と複数のGIFを示します。最初の画像はVolatility 25 Indexのチャート図で、テスト中に売りシグナルと買いシグナルがそれぞれ確認されたポイントを示しています。その後に続くGIFでは、さらなる視覚的な補足を提供しており、EAの動作やパフォーマンスをより深く理解する手助けとなるはずです。

買いと売り

図5:Volatility 25 Indexのバックテスト

Volatility 25 Indexのバックテスト GIF

Volatility 25 Indexのテスト

図6:Volatility 25 Indexのバックテスト 

ステップインデックスでのバックテスト。

ステップインデックスのバックテスト

図7:ステップインデックスのバックテスト

ステップインデックスの実取引

ステップインデックス

図8:ライブ取引



結論

EAを開発し、テストを通じて良好な結果を得ることができました。ただし、一部のシグナルについては追加のフィルタリングが必要な場合があります。このEAは、あくまでご自身の取引戦略を補完するためのツールであり、それに完全に依存することを目的としたものではありません。EAのシグナルに基づいて取引を行う際は、常に関連するすべての条件を確認してください。また、騙しのシグナルを最小限に抑えるためには、より大きな時間枠で運用することが重要になる場合もあります。

日付 ツール名  詳細 バージョン  アップデート  備考
01/10/24 ChartProjector 前日のプライスアクションをゴースト効果でオーバーレイするスクリプト 1.0 初回リリース ツール番号1
18/11/24 Analytical Comment 前日の情報を表形式で提供し、市場の将来の方向性を予測する 1.0 初回リリース ツール番号2
27/11/24 Analytics Master 2時間ごとに市場指標を定期的に更新  1.01 v.2 ツール番号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 
9/01/25  Signal Pulse  多時間枠分析ツール 1.0  初回リリース  ツール番号7 
17/01/25  Metrics Board  分析用のボタン付きパネル  1.0  初回リリース ツール番号8 
21/01/25 External Flow 外部ライブラリによる分析 1.0  初回リリース ツール番号9 
27/01/25 VWAP 出来高加重平均価格   1.3  初回リリース  ツール番号10 
02/02/25  Heikin Ashi  トレンドの平滑化と反転シグナルの識別  1.0  初回リリース  ツール番号11
04/02/25  FibVWAP  Python分析によるシグナル生成  1.0  初回リリース  ツール番号12
14/02/25  RSI DIVERGENCE  プライスアクションとRSIのダイバージェンス  1.0  初回リリース  ツール番号13 
17/02/2025  Parabolic Stop and Reverse (PSAR)  PSAR戦略の自動化 1.0 初回リリース  ツール番号14

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

添付されたファイル |
Parabolic_SAR.mq5 (12.72 KB)
MQL5で自己最適化エキスパートアドバイザーを構築する(第6回):ストップアウト防止 MQL5で自己最適化エキスパートアドバイザーを構築する(第6回):ストップアウト防止
本日は、勝ちトレードでストップアウトされる回数を最小限に抑えるためのアルゴリズム的手法を探るディスカッションにご参加ください。この問題は非常に難易度が高く、取引コミュニティで見られる多くの提案は、明確で一貫したルールに欠けているのが実情です。私たちはこの課題に対してアルゴリズム的なアプローチを用いることで、トレードの収益性を高め、1回あたりの平均損失を減らすことに成功しました。とはいえ、ストップアウトを完全に排除するには、まださらなる改良が必要です。私たちの解決策は、それには至らないものの、誰にとっても試す価値のある良い第一歩です。
MQL5での取引戦略の自動化(第7回):動的ロットスケーリングを備えたグリッド取引EAの構築 MQL5での取引戦略の自動化(第7回):動的ロットスケーリングを備えたグリッド取引EAの構築
この記事では、動的なロットスケーリングを採用したMQL5のグリッドトレーディングエキスパートアドバイザー(EA)を構築します。戦略の設計、コードの実装、バックテストのプロセスについて詳しく解説します。最後に、自動売買システムを最適化するための重要な知見とベストプラクティスを共有します。
MQL5での取引戦略の自動化(第8回):バタフライハーモニックパターンを用いたエキスパートアドバイザーの構築 MQL5での取引戦略の自動化(第8回):バタフライハーモニックパターンを用いたエキスパートアドバイザーの構築
この記事では、バタフライハーモニックパターンを検出するためのMQL5エキスパートアドバイザー(EA)を構築します。ピボットポイントを特定し、フィボナッチレベルを検証してパターンを確認します。次に、チャート上にパターンを可視化し、確認された際には自動的に取引を実行します。
プライスアクション分析ツールキットの開発(第13回):RSIセンチネルツール プライスアクション分析ツールキットの開発(第13回):RSIセンチネルツール
プライスアクションは、ダイバージェンスを特定することで効果的に分析することができます。RSI(相対力指数)などのテクニカル指標は、その確認シグナルとして重要な役割を果たします。本記事では、自動化されたRSIダイバージェンス分析によって、トレンドの継続や反転をどのように識別できるかを解説し、市場心理を読み解く上で理解を深める手助けをします。