English Deutsch
preview
MQL5での取引戦略の自動化(第29回):プライスアクションに基づくガートレーハーモニックパターンシステムの作成

MQL5での取引戦略の自動化(第29回):プライスアクションに基づくガートレーハーモニックパターンシステムの作成

MetaTrader 5トレーディング |
59 1
Allan Munene Mutiiria
Allan Munene Mutiiria

はじめに

前回の記事(第28回)では、正確なフィボナッチ比率を使用して強気と弱気のバットハーモニックパターンを検出するバットパターンシステムをMetaQuotes Language 5 (MQL5)で開発しました。今回の第29回では、ピボットポイントと特定のフィボナッチリトレースメントを利用して強気と弱気のガートレーパターンを識別し、動的なエントリーと複数レベルのテイクプロフィット(TP)ポイントを備え、視覚的な三角形、トレンドライン、ラベルによってパターンを明確に表示するガートレーパターンプログラムを作成します。本記事では以下のトピックを扱います。

  1. ガートレーハーモニックパターンのフレームワークを理解する
  2. MQL5での実装
  3. バックテスト
  4. 結論

この記事を読み終える頃には、ガートレーハーモニックパターン取引のための強力なMQL5戦略を手に入れ、自由にカスタマイズできるようになります。それでは、さっそく始めましょう。


ガートレーハーモニックパターンのフレームワークを理解する

ガートレーパターンは、X、A、B、C、Dの5つの主要スイングポイントによって構成されるハーモニックパターンで、強気パターンと弱気パターンの2種類が存在します。いずれもフィボナッチ比に基づき、特定のフィボナッチ比率を用いて高い確度の反転ゾーンを特定するよう設計されています。強気のガートレーでは、構造が安値-高値-安値-高値-安値という順序を形成し、Xがスイングロー、Aがスイングハイ、BがXAの約0.618まで戻すスイングロー、CがABの0.382〜0.886まで伸びるスイングハイ、そしてDがXAの0.786まで戻しXを上回るスイングローとなります。逆に、弱気のガートレーでは、高値-安値-高値-安値-高値という順序を形成し、Xがスイングハイ、Aがスイングロー、Bがスイングハイ、Cがスイングロー、そしてDがXAの0.786まで戻しXを下回るスイングハイとなります。以下に、パターンの種類を図示します。

強気のガートレーハーモニックパターン

強気のガートレーハーモニックパターン

弱気のガートレーハーモニックパターン

弱気のガートレーハーモニックパターン

パターンを識別するために、以下のような構造化されたアプローチを採用します。

  • XAレッグの定義:XからAへの最初の動きがパターンの方向(強気では上向き、弱気では下向き)を決定し、その後のフィボナッチ計算の基準となります。
  • ABレッグの確定:BはXAレッグの約0.618まで戻す必要があり、これは初動の大きな上昇(または下落)に対する十分な調整を示します。
  • BCレッグの分析:このレッグはABレッグの0.382〜0.886の範囲まで伸び、最終的な反転ゾーンに向かう前の逆方向の動きを形成します。
  • CDレッグの設定:最終レッグはXAレッグの0.786まで戻し、パターンが完成するDポイントにおける反転の可能性を示します。ここがトレードシグナルの発生地点となります。

これらの幾何学的およびフィボナッチに基づく条件を適用することで、取引システムは価格データの中から有効なガートレーパターンを体系的に検出します。パターンが確認されると、システムはチャート上に三角形、トレンドライン、X、A、B、C、Dのラベルに加えて、エントリーや利益確定の取引レベルを描画し、パターン形成を可視化します。これにより、計算されたストップロスおよび複数のTPゾーンを用いてDでの取引を自動的に実行でき、パターンが持つ反転予測の優位性を活かせるようになります。それでは、MQL5での実装に進みましょう。


MQL5での実装

MQL5でプログラムを作成するには、まずMetaEditorを開き、ナビゲータに移動して、Indicatorsフォルダを見つけ、[新規作成]タブをクリックして、表示される手順に従ってファイルを作成します。ファイルが作成されたら、コーディング環境で、まずプログラム全体で使用するグローバル変数をいくつか宣言する必要があります。

//+------------------------------------------------------------------+
//|                                       Gartley Pattern EA.mq5     |
//|                        Copyright 2025, Forex Algo-Trader, Allan. |
//|                                 "https://t.me/Forex_Algo_Trader" |
//+------------------------------------------------------------------+
#property copyright "Forex Algo-Trader, Allan"
#property link      "https://t.me/Forex_Algo_Trader"
#property version   "1.00"
#property description "This EA trades based on Gartley Strategy"
#property strict

//--- Include the trading library for order functions  
#include <Trade\Trade.mqh>  //--- Include Trade library
CTrade obj_Trade;  //--- Instantiate a obj_Trade object

//--- Input parameters for user configuration  
input int    PivotLeft    = 5;      // Number of bars to the left for pivot check  
input int    PivotRight   = 5;      // Number of bars to the right for pivot check  
input double Tolerance    = 0.10;   // Allowed deviation (10% of XA move)  
input double LotSize      = 0.01;   // Lot size for new orders  
input bool   AllowTrading = true;   // Enable or disable trading

//---------------------------------------------------------------------------  
//--- Gartley pattern definition:  
//  
//--- Bullish Gartley:  
//---   Pivots (X-A-B-C-D): X swing low, A swing high, B swing low, C swing high, D swing low.  
//---   Normally XA > 0; Ideal B = A - 0.618*(A-X); Legs within specified ranges; D at 0.786 retracement.  
//  
//--- Bearish Gartley:  
//---   Pivots (X-A-B-C-D): X swing high, A swing low, B swing high, C swing low, D swing high.  
//---   Normally XA > 0; Ideal B = A + 0.618*(X-A); Legs within specified ranges; D at 0.786 retracement.  
//---------------------------------------------------------------------------

//--- Structure for a pivot point  
struct Pivot {  
   datetime time;   //--- Bar time of the pivot  
   double   price;  //--- Pivot price (High for swing high, low for swing low)  
   bool     isHigh; //--- True if swing high; false if swing low  
};  

//--- Global dynamic array for storing pivots in chronological order  
Pivot pivots[];     //--- Declare a dynamic array to hold identified pivot points

//--- Global variables to lock in a pattern (avoid trading on repaint)  
int      g_patternFormationBar = -1;  //--- Bar index where the pattern was formed (-1 means none)  
datetime g_lockedPatternX      = 0;   //--- The key X pivot time for the locked pattern

ガートレーパターンの基盤を構築するために、まず<Trade\Trade.mqh>ライブラリをインクルードし、売買注文の実行などの取引操作を処理するためにobj_TradeをCTradeオブジェクトとしてインスタンス化します。次に、ユーザーがカスタマイズできる入力パラメータを定義します。PivotLeftとPivotRightはそれぞれ5バーに設定してピボット検出のルックバック範囲を決定し、Toleranceは0.10に設定してフィボナッチ比率の偏差許容度を10%に定め、LotSizeは0.01に設定して取引量を指定し、AllowTradingはtrueに設定して自動取引を有効化します。

続いて、スイングポイントを保存するためにtime(datetime)、price(double)、isHigh(bool)を保持するPivot構造体を定義し、これらの点を格納するためにpivotsを動的配列として宣言します。さらに、パターン形成を追跡するためにg_patternFormationBarを-1、Xピボットの時間をロックするg_lockedPatternXを0に初期化します。また、同じパターンで複数回取引がおこなわれることを防ぐためにg_tradeTakenをfalseに設定し、ガートレーパターン検出と取引に必要なEAの基盤を整えます。 視覚化のために、ライン、ラベル、三角形を描画する関数を用意します。

//+------------------------------------------------------------------+  
//| Helper: Draw a filled triangle                                   |  
//+------------------------------------------------------------------+  
void DrawTriangle(string name, datetime t1, double p1, datetime t2, double p2, datetime t3, double p3, color cl, int width, bool fill, bool back) {  
   //--- Attempt to create a triangle object with three coordinate points  
   if(ObjectCreate(0, name, OBJ_TRIANGLE, 0, t1, p1, t2, p2, t3, p3)) {  
      //--- Set the triangle's color  
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);  
      //--- Set the triangle's line style to solid  
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID);  
      //--- Set the line width of the triangle  
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width);  
      //--- Determine if the triangle should be filled  
      ObjectSetInteger(0, name, OBJPROP_FILL, fill);  
      //--- Set whether the object is drawn in the background  
      ObjectSetInteger(0, name, OBJPROP_BACK, back);  
   }  
}  

//+------------------------------------------------------------------+  
//| Helper: Draw a trend line                                        |  
//+------------------------------------------------------------------+  
void DrawTrendLine(string name, datetime t1, double p1, datetime t2, double p2, color cl, int width, int style) {  
   //--- Create a trend line object connecting two points  
   if(ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) {  
      //--- Set the trend line's color  
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);  
      //--- Set the trend line's style (solid, dotted, etc.)  
      ObjectSetInteger(0, name, OBJPROP_STYLE, style);  
      //--- Set the width of the trend line  
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width);  
   }  
}  

//+------------------------------------------------------------------+  
//| Helper: Draw a dotted trend line                                 |  
//+------------------------------------------------------------------+  
void DrawDottedLine(string name, datetime t1, double p, datetime t2, color lineColor) {  
   //--- Create a horizontal trend line at a fixed price level with dotted style  
   if(ObjectCreate(0, name, OBJ_TREND, 0, t1, p, t2, p)) {  
      //--- Set the dotted line's color  
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);  
      //--- Set the line style to dotted  
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT);  
      //--- Set the line width to 1  
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1);  
   }  
}  

//+------------------------------------------------------------------+  
//| Helper: Draw anchored text label (for pivots)                    |  
//| If isHigh is true, anchor at the bottom (label appears above);   |  
//| if false, anchor at the top (label appears below).               |  
//+------------------------------------------------------------------+  
void DrawTextEx(string name, string text, datetime t, double p, color cl, int fontsize, bool isHigh) {  
   //--- Create a text label object at the specified time and price  
   if(ObjectCreate(0, name, OBJ_TEXT, 0, t, p)) {  
      //--- Set the text of the label  
      ObjectSetString(0, name, OBJPROP_TEXT, text);  
      //--- Set the color of the text  
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl);  
      //--- Set the font size for the text  
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontsize);  
      //--- Set the font type and style  
      ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold");  
      //--- Anchor the text depending on whether it's a swing high or low  
      if(isHigh)  
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_BOTTOM);  
      else  
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_TOP);  
      //--- Center-align the text  
      ObjectSetInteger(0, name, OBJPROP_ALIGN, ALIGN_CENTER);  
   }  
}

パターンおよびエントリーレベルを明確にチャート上へ表示するため、システムの視覚化関数を実装していきます。まずDrawTriangle関数を作成します。この関数ではObjectCreateを使用してOBJ_TRIANGLEを作成し、3つの時刻(t1、t2、t3)と価格(p1、p2、p3)でで塗りつぶされた三角形を描画します。その後、ObjectSetIntegerを使用してOBJPROP_COLORに指定の色を設定し、OBJPROP_STYLEをSTYLE_SOLIDに設定、OBJPROP_WIDTHで線幅を指定、OBJPROP_FILLで塗りつぶしの有効・無効を設定し、ObjectSetInteger関数を用いてOBJPROP_BACKで背景か前景かを指定します。

次に、2点間にトレンドライン(OBJ_TREND)を描画するDrawTrendLine関数の作成に進みます。次にDrawDottedLine関数を実装します。この関数では、指定した価格で水平の点線(OBJ_TREND)をt1からt2まで描画します。最後に、座標(t、p)にテキストラベル(OBJ_TEXT)を作成し、ObjectSetStringとObjectSetIntegerを使用してOBJPROP_TEXTを指定テキストに設定し、OBJPROP_COLOR、OBJPROP_FONTSIZE、OBJPROP_FONTをArial Boldに設定し、OBJPROP_ANCHORを使用してisHighに基づきスイングハイの場合は上に、スイングローの場合は下にアンカーし、OBJPROP_ALIGNプロパティを用いて中央に配置するDrawTextEx関数を開発します。これにより、ガートレーパターンおよび関連するトレードレベルを視覚的にわかりやすく表示できるようになります。 これでOnTickイベントハンドラに進み、後でパターン識別に使用できるピボットポイントを検出する処理をおこなう準備が整いました。そのためのロジックを以下に実装します。

//+------------------------------------------------------------------+  
//| Expert tick function                                             |  
//+------------------------------------------------------------------+  
void OnTick() {  
   //--- Declare a static variable to store the time of the last processed bar  
   static datetime lastBarTime = 0;  
   //--- Get the time of the current confirmed bar  
   datetime currentBarTime = iTime(_Symbol, _Period, 1);  
   //--- If the current bar time is the same as the last processed, exit  
   if(currentBarTime == lastBarTime)  
      return;  
   //--- Update the last processed bar time  
   lastBarTime = currentBarTime;  
   
   //--- Clear the pivot array for fresh analysis  
   ArrayResize(pivots, 0);  
   //--- Get the total number of bars available on the chart  
   int barsCount = Bars(_Symbol, _Period);  
   //--- Define the starting index for pivot detection (ensuring enough left bars)  
   int start = PivotLeft;  
   //--- Define the ending index for pivot detection (ensuring enough right bars)  
   int end = barsCount - PivotRight;  
   
   //--- Loop through bars from 'end-1' down to 'start' to find pivot points  
   for(int i = end - 1; i >= start; i--) {  
      //--- Assume current bar is both a potential swing high and swing low  
      bool isPivotHigh = true;  
      bool isPivotLow = true;  
      //--- Get the high and low of the current bar  
      double currentHigh = iHigh(_Symbol, _Period, i);  
      double currentLow = iLow(_Symbol, _Period, i);  
      //--- Loop through the window of bars around the current bar  
      for(int j = i - PivotLeft; j <= i + PivotRight; j++) {  
         //--- Skip if the index is out of bounds  
         if(j < 0 || j >= barsCount)  
            continue;  
         //--- Skip comparing the bar with itself  
         if(j == i)  
            continue;  
         //--- If any bar in the window has a higher high, it's not a swing high  
         if(iHigh(_Symbol, _Period, j) > currentHigh)  
            isPivotHigh = false;  
         //--- If any bar in the window has a lower low, it's not a swing low  
         if(iLow(_Symbol, _Period, j) < currentLow)  
            isPivotLow = false;  
      }  
      //--- If the current bar qualifies as either a swing high or swing low  
      if(isPivotHigh || isPivotLow) {  
         //--- Create a new pivot structure  
         Pivot p;  
         //--- Set the pivot's time  
         p.time = iTime(_Symbol, _Period, i);  
         //--- Set the pivot's price depending on whether it is a high or low  
         p.price = isPivotHigh ? currentHigh : currentLow;  
         //--- Set the pivot type (true for swing high, false for swing low)  
         p.isHigh = isPivotHigh;  
         //--- Get the current size of the pivots array  
         int size = ArraySize(pivots);  
         //--- Increase the size of the pivots array by one  
         ArrayResize(pivots, size + 1);  
         //--- Add the new pivot to the array  
         pivots[size] = p;  
      }  
   }
}

ここでは、OnTick関数の初期ロジックを実装し、スイングピボットを検出してガートレーハーモニックパターンを識別する基盤を形成します。まず、最後に処理したバーを追跡するために0で初期化した静的変数lastBarTimeを宣言し、現在の銘柄と時間足におけるシフト1のiTimeから取得したcurrentBarTimeと比較し、変化がない場合は冗長な処理を防ぐため処理を中断し、新しいバーが検出された時点でlastBarTimeを更新します。次に、ArrayResizeを使用してpivots配列をクリアし、解析をリセットします。

その後、Barsでバーの総数を取得し、ピボット検出の範囲としてstartをPivotLeft、endを総バー数からPivotRightを引いた値に設定し、「end − 1」からstartまでのバーを順に処理します。各バーについて、スイングハイ(isPivotHighがtrue)およびスイングロー(isPivotLowがtrue)であると仮定し、iHighiLowを使ってそのバーの高値と安値を取得し、PivotLeftおよびPivotRightの範囲内にある周囲のバーをiHighとiLowでチェックし、隣接するバーにより高い高値またはより低い安値が存在する場合は、そのピボットを無効とします。最後に、バーがピボットとして有効であればPivot構造体を生成し、timeをiTimeで設定し、priceをisPivotHighに応じて高値または安値に設定し、isHighフラグを割り当てて、ArrayResizeを用いてpivots配列に追加します。ピボット構造体を印刷すると、次のデータ配列が取得されます。

ピボットデータ構造体

このデータを使ってピボットポイントを抽出でき、十分な数のピボットがあればパターンの解析と検出をおこなうことができます。これを実現するために、以下のロジックを実装しています。

//--- Determine the total number of pivots found  
int pivotCount = ArraySize(pivots);  
//--- If fewer than five pivots are found, the pattern cannot be formed  
if(pivotCount < 5) {  
   //--- Reset pattern lock variables  
   g_patternFormationBar = -1;  
   g_lockedPatternX = 0;  
   //--- Exit the OnTick function  
   return;  
}  

//--- Extract the last five pivots as X, A, B, C, and D  
Pivot X = pivots[pivotCount - 5];  
Pivot A = pivots[pivotCount - 4];  
Pivot B = pivots[pivotCount - 3];  
Pivot C = pivots[pivotCount - 2];  
Pivot D = pivots[pivotCount - 1];  

//--- Initialize a flag to indicate if a valid Gartley pattern is found  
bool patternFound = false;  
//--- Initialize pattern type  
string patternType = "";  
//--- Check for the high-low-high-low-high (Bearish reversal) structure  
if(X.isHigh && (!A.isHigh) && B.isHigh && (!C.isHigh) && D.isHigh) {  
   //--- Calculate the difference between pivot X and A  
   double diff = X.price - A.price;  
   //--- Ensure the difference is positive  
   if(diff > 0) {  
      //--- Calculate the ideal position for pivot B based on Fibonacci ratio  
      double idealB = A.price + 0.618 * diff;  
      //--- Check if actual B is within tolerance of the ideal position  
      if(MathAbs(B.price - idealB) <= Tolerance * diff) {  
         //--- Calculate the AB leg length  
         double AB = B.price - A.price;  
         //--- Calculate the BC leg length  
         double BC = B.price - C.price;  
         //--- Verify that BC is within the acceptable Fibonacci range  
         if((BC >= 0.382 * AB) && (BC <= 0.886 * AB)) {  
            //--- Calculate the retracement  
            double retrace = D.price - A.price;  
            //--- Verify that the retracement is within tolerance of 0.786 and that D is below X  
            if(MathAbs(retrace - 0.786 * diff) <= Tolerance * diff && (D.price < X.price)) {  
               patternFound = true;  
               patternType = "Bearish";  
            }  
         }  
      }  
   }  
}  
//--- Check for the low-high-low-high-low (Bullish reversal) structure  
if((!X.isHigh) && A.isHigh && (!B.isHigh) && C.isHigh && (!D.isHigh)) {  
   //--- Calculate the difference between pivot A and X  
   double diff = A.price - X.price;  
   //--- Ensure the difference is positive  
   if(diff > 0) {  
      //--- Calculate the ideal position for pivot B based on Fibonacci ratio  
      double idealB = A.price - 0.618 * diff;  
      //--- Check if actual B is within tolerance of the ideal position  
      if(MathAbs(B.price - idealB) <= Tolerance * diff) {  
         //--- Calculate the AB leg length  
         double AB = A.price - B.price;  
         //--- Calculate the BC leg length  
         double BC = C.price - B.price;  
         //--- Verify that BC is within the acceptable Fibonacci range  
         if((BC >= 0.382 * AB) && (BC <= 0.886 * AB)) {  
            //--- Calculate the retracement  
            double retrace = A.price - D.price;  
            //--- Verify that the retracement is within tolerance of 0.786 and that D is above X  
            if(MathAbs(retrace - 0.786 * diff) <= Tolerance * diff && (D.price > X.price)) {  
               patternFound = true;  
               patternType = "Bullish";  
            }  
         }  
      }  
   }  
}

まず、ArraySize(pivots)で得られるピボットの総数をpivotCountに格納し、ピボット数が5未満の場合は処理を終了し、g_patternFormationBarとg_lockedPatternXをそれぞれ-1と0にリセットします。これは、ガートレーパターンではX、A、B、C、Dの5点が必須であるためです。次に、pivots配列から最後の5つのピボットを抽出し、最も古いものから順にX、A、B、C、Dを割り当ててパターン構造を形成します。

続いて、弱気のガートレーパターン(Xが高、Aが安、Bが高、Cが安、Dが高)を確認するため、まずXAレッグの差をX.price−A.priceとして計算し、それが正であることを確認します。次に、理想的なB点を「A.price + 0.618 * diff」として算出し、MathAbsを用いてBが「Tolerance * diff」の範囲内にあるかどうかを検証します。さらに、BCレッグがABの0.382〜0.886の範囲にあるかを確認し、ADが戻ること(XAの0.786でDがXより下)を確認し、有効であればpatternFoundをtrue、patternTypeをBearishに設定します。最後に、強気のガートレーパターンを確認します。順序がX(低)、A(高)、B(低)、C(高)、D(低)であることを検証し、XAを「A.price - X.price」として計算して正であることを確認、Bが0.618戻し、BCがABの0.382~0.886、ADがXAの0.786でDがXより下であることを検証し、有効であればpatternFoundをtrue、patternTypeをBearishに設定します。 パターンが検出された場合、チャート上にそのパターンを可視化する処理を進めることができます。

//--- If a valid Gartley pattern is detected  
if(patternFound) {  
   //--- Print a message indicating the pattern type and detection time  
   Print(patternType, " Gartley pattern detected at ", TimeToString(D.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS));  
   
   //--- Create a unique prefix for all graphical objects related to this pattern  
   string signalPrefix = "GA_" + IntegerToString(X.time);  
   
   //--- Choose triangle color based on the pattern type  
   color triangleColor = (patternType=="Bullish") ? clrBlue : clrRed;  
   
   //--- Draw the first triangle connecting pivots X, A, and B  
   DrawTriangle(signalPrefix+"_Triangle1", X.time, X.price, A.time, A.price, B.time, B.price,  
                triangleColor, 2, true, true);  
   //--- Draw the second triangle connecting pivots B, C, and D  
   DrawTriangle(signalPrefix+"_Triangle2", B.time, B.price, C.time, C.price, D.time, D.price,  
                triangleColor, 2, true, true);  
}

有効なパターンが検出された場合(patternFoundがtrue)、Printを用いて検出をログに記録し、patternType(BullishまたはBearish)と、TimeToStringによって日付、分、秒形式で整形されたDピボットの時間を出力します。続いて、「GA_」とX.timeをIntegerToStringで文字列に変換して連結し、一意の識別子であるsignalPrefixを作成します。次に、triangleColorを、強気パターンの場合は青、弱気パターンの場合は赤に設定します。最後に、パターンを可視化するためにDrawTriangleを2回呼び出します。最初はピボットX、A、Bを結ぶ三角形XABを、次にピボットB、C、Dを結ぶ三角形BCDを描画し、signalPrefixに「_Triangle1」と「_Triangle2」を付加したID、各ピボットの時間と価格、triangleColor、幅2、塗りつぶしと背景表示のtrueフラグを指定します。次のような結果が得られます。

三角形セット

画像から、検出されたパターンを正しくマッピングおよび可視化できていることが確認できます。次に、トレンドラインを引き続きマッピングしてパターンを境界内で完全に可視化し、レベルをよりわかりやすく識別できるようにラベルを追加する必要があります。

//--- Draw boundary trend lines connecting the pivots for clarity  
DrawTrendLine(signalPrefix+"_TL_XA", X.time, X.price, A.time, A.price, clrBlack, 2, STYLE_SOLID);  
DrawTrendLine(signalPrefix+"_TL_AB", A.time, A.price, B.time, B.price, clrBlack, 2, STYLE_SOLID);  
DrawTrendLine(signalPrefix+"_TL_BC", B.time, B.price, C.time, C.price, clrBlack, 2, STYLE_SOLID);  
DrawTrendLine(signalPrefix+"_TL_CD", C.time, C.price, D.time, D.price, clrBlack, 2, STYLE_SOLID);  
DrawTrendLine(signalPrefix+"_TL_XB", X.time, X.price, B.time, B.price, clrBlack, 2, STYLE_SOLID);  
DrawTrendLine(signalPrefix+"_TL_BD", B.time, B.price, D.time, D.price, clrBlack, 2, STYLE_SOLID);  

//--- Retrieve the symbol's point size to calculate offsets for text positioning  
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);  
//--- Calculate an offset (15 points) for positioning text above or below pivots  
double offset = 15 * point;  

//--- Determine the Y coordinate for each pivot label based on its type  
double textY_X = (X.isHigh ? X.price + offset : X.price - offset);  
double textY_A = (A.isHigh ? A.price + offset : A.price - offset);  
double textY_B = (B.isHigh ? B.price + offset : B.price - offset);  
double textY_C = (C.isHigh ? C.price + offset : C.price - offset);  
double textY_D = (D.isHigh ? D.price + offset : D.price - offset);  

//--- Draw text labels for each pivot with appropriate anchoring  
DrawTextEx(signalPrefix+"_Text_X", "X", X.time, textY_X, clrBlack, 11, X.isHigh);  
DrawTextEx(signalPrefix+"_Text_A", "A", A.time, textY_A, clrBlack, 11, A.isHigh);  
DrawTextEx(signalPrefix+"_Text_B", "B", B.time, textY_B, clrBlack, 11, B.isHigh);  
DrawTextEx(signalPrefix+"_Text_C", "C", C.time, textY_C, clrBlack, 11, C.isHigh);  
DrawTextEx(signalPrefix+"_Text_D", "D", D.time, textY_D, clrBlack, 11, D.isHigh);  

//--- Calculate the central label's time as the midpoint between pivots X and B  
datetime centralTime = (X.time + B.time) / 2;  
//--- Set the central label's price at pivot D's price  
double centralPrice = D.price;  
//--- Create the central text label indicating the pattern type  
if(ObjectCreate(0, signalPrefix+"_Text_Center", OBJ_TEXT, 0, centralTime, centralPrice)) {  
   ObjectSetString(0, signalPrefix+"_Text_Center", OBJPROP_TEXT,  
      (patternType=="Bullish") ? "Bullish Gartley" : "Bearish Gartley");  
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_COLOR, clrBlack);  
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_FONTSIZE, 11);  
   ObjectSetString(0, signalPrefix+"_Text_Center", OBJPROP_FONT, "Arial Bold");  
   ObjectSetInteger(0, signalPrefix+"_Text_Center", OBJPROP_ALIGN, ALIGN_CENTER);  
}

検出されたパターンの視覚化をさらに強化するため、パターン構造を示す詳細なチャートオブジェクトを追加します。まず、主要なピボット点を結ぶために、固有のsignalPrefixを付けたDrawTrendLineを使用して6本の実線トレンドラインを描画します。XA、AB、BC、CD、XB、BDは、それぞれのピボット時間と価格(例:X.time、X.price)を使用し、OBJPROP_COLORを黒、OBJPROP_WIDTHを2、OBJPROP_STYLEをSTYLE_SOLIDに設定し、ObjectSetIntegerでパターンの各レッグを明確にします。次に、SymbolInfoDouble(_Symbol, SYMBOL_POINT)でシンボルの点サイズを取得し、ラベル配置のために15点のオフセットを計算し、各ピボットがスイングハイ(isHighがtrue)かスイングローかに応じてオフセットを加減してtextY_X、textY_A、textY_B、textY_C、textY_Dを決定し、高値の上、安値の下にラベルを配置します。

続いて、DrawTextExを使ってピボットX、A、B、C、Dのテキストラベルを作成します。signalPrefixに「_Text_X」などの接尾辞を付け、それぞれの文字を表示し、ピボット時間と調整済みのY座標に配置し、clrBlack、フォントサイズ11、ピボットのisHigh値をアンカーとして設定します。最後に、中央ラベルの位置をX.timeとB.timeの中点としてcentralTimeに、価格位置をD.priceとしてcentralPriceに設定し、ObjectCreateでsignalPrefix に「_Text_Center」を付加した名前のテキストオブジェクトを生成し、OBJPROP_TEXTをpatternTypeに応じて「Bullish Gartley」または「Bearish Gartley」に設定し、ObjectSetStringとObjectSetIntegerでOBJPROP_COLORをclrBlack、OBJPROP_FONTSIZEを11、OBJPROP_FONTをArial Bold、OBJPROP_ALIGNをALIGN_CENTERに設定します。このロジックにより、パターンの構造と種類がチャート上に包括的に表示されます。プログラムを実行すると、次のような表示が得られます。

ラベルとエッジのあるパターン

画像から、パターンにエッジ(トレンドライン)とラベルを追加したことで、より明確かつ視覚的に把握しやすくなったことが分かります。次におこなうべきことは、このパターンに基づいてトレードレベルを決定することです。

//--- Define start and end times for drawing horizontal dotted lines for trade levels  
datetime lineStart = D.time;  
datetime lineEnd = D.time + PeriodSeconds(_Period)*2;  

//--- Declare variables for entry price and take profit levels  
double entryPriceLevel, TP1Level, TP2Level, TP3Level, tradeDiff;  
//--- Calculate trade levels based on whether the pattern is Bullish or Bearish  
if(patternType=="Bullish") { //--- Bullish → BUY signal  
   //--- Use the current ASK price as the entry  
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_ASK);  
   //--- Set TP3 at pivot C's price  
   TP3Level = C.price;  
   //--- Calculate the total distance to be covered by the trade  
   tradeDiff = TP3Level - entryPriceLevel;  
   //--- Set TP1 at one-third of the total move  
   TP1Level = entryPriceLevel + tradeDiff/3;  
   //--- Set TP2 at two-thirds of the total move  
   TP2Level = entryPriceLevel + 2*tradeDiff/3;  
} else { //--- Bearish → SELL signal  
   //--- Use the current BID price as the entry  
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_BID);  
   //--- Set TP3 at pivot C's price  
   TP3Level = C.price;  
   //--- Calculate the total distance to be covered by the trade  
   tradeDiff = entryPriceLevel - TP3Level;  
   //--- Set TP1 at one-third of the total move  
   TP1Level = entryPriceLevel - tradeDiff/3;  
   //--- Set TP2 at two-thirds of the total move  
   TP2Level = entryPriceLevel - 2*tradeDiff/3;  
}  

//--- Draw dotted horizontal lines to represent the entry and TP levels  
DrawDottedLine(signalPrefix+"_EntryLine", lineStart, entryPriceLevel, lineEnd, clrMagenta);  
DrawDottedLine(signalPrefix+"_TP1Line", lineStart, TP1Level, lineEnd, clrForestGreen);  
DrawDottedLine(signalPrefix+"_TP2Line", lineStart, TP2Level, lineEnd, clrGreen);  
DrawDottedLine(signalPrefix+"_TP3Line", lineStart, TP3Level, lineEnd, clrDarkGreen);  

//--- Define a label time coordinate positioned just to the right of the dotted lines  
datetime labelTime = lineEnd + PeriodSeconds(_Period)/2;  

//--- Construct the entry label text with the price  
string entryLabel = (patternType=="Bullish") ? "BUY (" : "SELL (";  
entryLabel += DoubleToString(entryPriceLevel, _Digits) + ")";  
//--- Draw the entry label on the chart  
DrawTextEx(signalPrefix+"_EntryLabel", entryLabel, labelTime, entryPriceLevel, clrMagenta, 11, true);  

//--- Construct and draw the TP1 label  
string tp1Label = "TP1 (" + DoubleToString(TP1Level, _Digits) + ")";  
DrawTextEx(signalPrefix+"_TP1Label", tp1Label, labelTime, TP1Level, clrForestGreen, 11, true);  

//--- Construct and draw the TP2 label  
string tp2Label = "TP2 (" + DoubleToString(TP2Level, _Digits) + ")";  
DrawTextEx(signalPrefix+"_TP2Label", tp2Label, labelTime, TP2Level, clrGreen, 11, true);  

//--- Construct and draw the TP3 label  
string tp3Label = "TP3 (" + DoubleToString(TP3Level, _Digits) + ")";  
DrawTextEx(signalPrefix+"_TP3Label", tp3Label, labelTime, TP3Level, clrDarkGreen, 11, true);

検出されたパターンの取引レベルを定義して可視化するために、lineStartをピボットDの時間(D.time)に設定し、「PeriodSeconds(_Period) * 2」を用いてlineEndを2期間先に設定します。取引計算用の変数としてentryPriceLevel、TP1Level、TP2Level、TP3Level、tradeDiffを宣言します。次に、強気パターン(patternType == Bullish)の場合、SymbolInfoDoubleを使ってentryPriceLevelを現在の売値に設定し、TP3LevelをCピボットの価格に設定し、tradeDiffをTP3Level−entryPriceLevelとして計算し、TP1LevelとTP2LevelをtradeDiffの3分の1および3分の2をentryPriceLevelに加算して算出します。弱気パターンの場合は、買値を使用し、TP3LevelをCの価格に設定、tradeDiffをentryPriceLevel−TP3Levelとして計算し、取引差の3分の1および3分の2を減算してTP1LevelとTP2Levelを求めます。

次に、DrawDottedLineを使用して4本の点線水平線を描画します。マゼンタ色でentryPriceLevelのエントリーレベル線、TP1Levelをフォレストグリーン、TP2Levelをグリーン、TP3LevelをダークグリーンでTPラインとして、lineStartからlineEndまで描画します。最後に、labelTimeをlineEndに半期間を加えた値に設定し、DoubleToStringで価格をフォーマットしてラベルテキストを作成します(例:エントリーでは「BUY(価格)」または「SELL(価格)」、TP1は「TP1(価格)」)。DrawTextExを使用してこれらのラベルをlabelTimeに描画し、対応する色、フォントサイズ11、価格レベルの上にアンカー設定します。これにより、ガートレーパターンのエントリーおよびTPレベルをチャート上で明確に可視化できます。コンパイルすると、次の結果が得られます。

弱気パターン

弱気パターン

強気パターン

強気パターン

画像から、エントリーレベルが正しく表示されていることが確認できます。次に必要なのは実際にエントリーすることだけです。

//--- Retrieve the index of the current bar  
int currentBarIndex = Bars(_Symbol, _Period) - 1;  
//--- If no pattern has been previously locked, lock the current pattern formation  
if(g_patternFormationBar == -1) {  
   g_patternFormationBar = currentBarIndex;  
   g_lockedPatternX = X.time;  
   //--- Print a message that the pattern is detected and waiting for confirmation  
   Print("Pattern detected on bar ", currentBarIndex, ". Waiting for confirmation on next bar.");  
   return;  
}  
//--- If still on the same formation bar, the pattern is considered to be repainting  
if(currentBarIndex == g_patternFormationBar) {  
   Print("Pattern is repainting; still on locked formation bar ", currentBarIndex, ". No trade yet.");  
   return;  
}  
//--- If we are on a new bar compared to the locked formation  
if(currentBarIndex > g_patternFormationBar) {  
   //--- Check if the locked pattern still corresponds to the same X pivot  
   if(g_lockedPatternX == X.time) {  
      Print("Confirmed pattern (locked on bar ", g_patternFormationBar, "). Opening trade on bar ", currentBarIndex, ".");  
      //--- Update the pattern formation bar to the current bar  
      g_patternFormationBar = currentBarIndex;  
      //--- Only proceed with trading if allowed and if there is no existing position  
      if(AllowTrading && !PositionSelect(_Symbol)) {  
         double entryPriceTrade = 0, stopLoss = 0, takeProfit = 0;  
         point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);  
         bool tradeResult = false;  
         //--- For a Bullish pattern, execute a BUY trade  
         if(patternType=="Bullish") {  //--- BUY signal  
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_ASK);  
            double diffTrade = TP2Level - entryPriceTrade;  
            stopLoss = entryPriceTrade - diffTrade * 3;  
            takeProfit = TP2Level;  
            tradeResult = obj_Trade.Buy(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Gartley Signal");  
            if(tradeResult)  
               Print("Buy order opened successfully.");  
            else  
               Print("Buy order failed: ", obj_Trade.ResultRetcodeDescription());  
         }  
         //--- For a Bearish pattern, execute a SELL trade  
         else if(patternType=="Bearish") {  //--- SELL signal  
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_BID);  
            double diffTrade = entryPriceTrade - TP2Level;  
            stopLoss = entryPriceTrade + diffTrade * 3;  
            takeProfit = TP2Level;  
            tradeResult = obj_Trade.Sell(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Gartley Signal");  
            if(tradeResult)  
               Print("Sell order opened successfully.");  
            else  
               Print("Sell order failed: ", obj_Trade.ResultRetcodeDescription());  
         }  
      }  
      else {  
         //--- If a position is already open, do not execute a new trade  
         Print("A position is already open for ", _Symbol, ". No new trade executed.");  
      }  
   }  
   else {  
      //--- If the pattern has changed, update the lock with the new formation bar and X pivot  
      g_patternFormationBar = currentBarIndex;  
      g_lockedPatternX = X.time;  
      Print("Pattern has changed; updating lock on bar ", currentBarIndex, ". Waiting for confirmation.");  
      return;  
   }  
}  
}  
else {  
   //--- If no valid Gartley pattern is detected, reset the pattern lock variables  
   g_patternFormationBar = -1;  
   g_lockedPatternX = 0;  
}

ここでは、検出されたパターンの取引実行とパターン確認を管理することで、ティック実装を完了します。まず、「Bars(_Symbol, _Period) - 1」で現在のバーのインデックスを取得し、currentBarIndexに格納します。次に、パターンがロックされていない場合(g_patternFormationBar == -1)、g_patternFormationBarをcurrentBarIndexに設定し、g_lockedPatternXにX.timeを格納してXピボットの時間をロックします。パターン検出をログに記録し、確認待ちであることを示して処理を終了します。次に、まだフォーメーションバー上にある場合(currentBarIndex == g_patternFormationBar)、再描画をログに記録して終了し、早すぎる取引を防ぎます。

最後に、新しいバーが形成され(currentBarIndex > g_patternFormationBar)かつXピボットがg_lockedPatternXと一致する場合、パターンを確認してログに記録し、g_patternFormationBarを更新します。その上で、AllowTradingで取引が許可されているか、PositionSelectでポジションが存在しないかを確認します。強気パターンの場合、entryPriceTradeをAsk価格に設定し、diffTradeを「TP2Level - entryPriceTrade」として計算し、stopLossをこの距離の3倍下に設定、takeProfitをTP2Levelに設定して、LotSizeとGartley Signalを用いてobj_Trade.Buyで買いを実行し、成功または失敗をログに記録します。弱気パターンの場合はBid価格を使用し、stopLossをこの距離の3倍上に設定してobj_Trade.Sellで売りを実行します。取引が許可されていない場合やポジションが存在する場合は、取引はおこなわれずログに記録されます。パターンが変更された場合はg_lockedPatternXを更新して待機し、パターンが見つからない場合はグローバル変数をリセットします。コンパイルすると、次の結果が得られます。

弱気シグナル

弱気シグナル

強気シグナル

強気シグナル

画像から、ハーモニックパターンを正しくプロットできており、パターンが確定した後にそれに応じてエントリーできていることが確認できます。これにより、パターンの識別、描画、取引という目的を達成できています。残っている作業は、このプログラムのバックテストをおこなうことです。バックテストについては次のセクションで扱います。


バックテスト

徹底的なバックテストの結果、次の結果が得られました。

バックテストグラフ

グラフ

バックテストレポート

レポート


結論

結論として、私たちはMQL5でガートレーパターンシステムを開発しました。価格の動きを活用して強気と弱気のガートレーハーモニックパターンを、正確な フィボナッチ比率で検出し、計算されたエントリー、SL、複数レベルのTPポイントを使用したエントリーを自動化し、三角形やトレンドラインといったチャートオブジェクトでパターンを可視化しました。

免責条項:本記事は教育目的のみを意図したものです。取引には重大な財務リスクが伴い、市場の変動によって損失が生じる可能性があります。本プログラムを実際の市場で運用する前に、十分なバックテストと慎重なリスク管理が不可欠です。

提示された概念と実装を活用することで、このガートレーパターンシステムを自分の取引スタイルに適応させ、アルゴリズム戦略を強化できます。取引をお楽しみください。 

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

添付されたファイル |
最後のコメント | ディスカッションに移動 (1)
Miguel Angel Vico Alba
Miguel Angel Vico Alba | 30 8月 2025 において 09:57
この記事は、MQL5でEAを構成しハーモニックパターンを視覚化する方法を学ぶための非常に良い教育的な例です。

読者のために1つだけ明確にしておきたいことがある。序文では「カスタマイズの準備が整った強力なストラテジーを手に入れることができる」と書かれているが、これは取引可能なシステムとして捉えるべきではない。

主な理由は以下の通り:
  • 不完全なガートレーの定義:コードでは点DがXAの0.786付近にあることだけをチェックしている。ハーモニック・トレードでは、重要なアイデアはポテンシャル・リバーサル・ゾーン(PRZ)であり、これは複数の比率(例えば0.786 XAとBCの1.27-1.618)の合流を必要とする。これがないと、多くの偽パターンが現れます。
  • ハーモニック取引では、ストップは通常Xの先に置かれ、ターゲットはADのフィボナッチリトレースメントに 基づく。これが守られないと、リスクとリターンが恣意的になる。
つまり、コーディングと視覚化の練習としては優れている。しかし、実際の取引においては、初心者はこのようなプログラムに頼る前に、より多くのルールと検証が必要であることを理解すべきである。
プライスアクション分析ツールキットの開発(第38回):ティックバッファVWAPと短期不均衡エンジン プライスアクション分析ツールキットの開発(第38回):ティックバッファVWAPと短期不均衡エンジン
第38回では、生のティックを実用的なシグナルに変換する、実稼働グレードのMT5監視パネルを構築します。EAはティックデータをバッファリングし、ティックレベルのVWAP、短期ウィンドウの不均衡(フロー)指標、ATRに基づくポジションサイズを計算します。その後、スプレッド、ATR、フローを低フリッカーのバーで可視化します。システムは推奨ロットサイズと1Rストップを計算し、狭いスプレッド、強いフロー、エッジ条件に対して設定可能なアラートを発行します。自動取引は意図的に無効化しており、堅牢なシグナル生成とクリーンなユーザー体験に重点を置いています。
MetaTraderとGoogleシートがPythonAnywhereで融合:安全なデータフローのガイド MetaTraderとGoogleシートがPythonAnywhereで融合:安全なデータフローのガイド
本記事では、MetaTraderのデータをGoogleスプレッドシートに安全にエクスポートする方法を紹介します。Googleスプレッドシートはクラウドベースで、保存されたデータにいつでもどこからでもアクセスできるため、非常に有用なソリューションです。トレーダーはGoogleスプレッドシートにエクスポートされた取引データや関連情報にいつでもアクセスでき、将来の取引に向けた分析を自由におこなうことができます。
機械学習の限界を克服する(第3回):既約誤差に関する新たな視点 機械学習の限界を克服する(第3回):既約誤差に関する新たな視点
本記事では、モデルがおこなうすべての予測に密かに影響を与える、隠れた幾何学的誤差の源に新たな視点を提供します。取引における機械学習予測の評価方法と活用法を再考することで、従来見過ごされてきたこの視点が、より鋭い意思決定、より高いリターン、そして、すでに理解していると思っていたモデルをより賢く活用する道を開くことを示します。
初心者からエキスパートへ:Reporting EAで詳細な取引レポートをマスターする 初心者からエキスパートへ:Reporting EAで詳細な取引レポートをマスターする
本記事では、取引レポートの内容をより充実させ、最終レポートをPDF形式としてメール配信する方法について解説します。これは前回の記事からさらに一歩踏み込んだ内容であり、MQL5とPythonを組み合わせて、より便利でプロフェッショナルな形式の取引レポートを生成し、スケジュールする方法を継続して探求するものです。本記事を通じて、MQL5エコシステム内で取引レポート生成を最適化するための知見を得ていただければ幸いです。