English Deutsch
preview
MQL5での取引戦略の自動化(第27回):視覚的なフィードバックによるプライスアクションクラブハーモニックパターンの作成

MQL5での取引戦略の自動化(第27回):視覚的なフィードバックによるプライスアクションクラブハーモニックパターンの作成

MetaTrader 5トレーディングシステム |
77 0
Allan Munene Mutiiria
Allan Munene Mutiiria

はじめに

前回の記事(第26回)では、ピンバーを活用して取引を開始し、複数ポジションをナンピン戦略で管理する「ピンバーナンピンシステム」をMetaQuotes Language 5 (MQL5)で開発しました。このシステムには、リアルタイムで監視可能な動的ダッシュボードも搭載されていました。今回の第27回では、ピボットポイントとフィボナッチ比率を用いて弱気、強気両方のクラブ(Crab)パターンを識別し、正確なエントリー、ストップロス、利確レベルを使用した取引を自動化するクラブパターンシステムを作成します。さらに、三角形やトレンドラインなどのチャートオブジェクトを用いて、パターンを視覚的に分かりやすく表示します。本記事では以下のトピックを扱います。

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

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


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

クラブパターンは、5つの主要なスイングポイント(X、A、B、C、D)で構成されるハーモニックトレーディングのフォーメーションで、強気(上昇)パターンと弱気(下降)パターンの2種類があります。強気のクラブの場合、構造は低–高–低–高–低の順に形成されます。ポイントXはスイングロー、Aはスイングハイ、BはXAの0.618戻しのスイングロー、CはABの0.382〜0.886延長のスイングハイ、DはXAの1.618延長でXより下に位置するスイングローです。一方、弱気のクラブは高–低–高–低–高の順で形成され、Xはスイングハイ、Aはスイングロー、Bはスイングハイ、Cはスイングロー、DはXAの1.618延長でXより上に位置するスイングハイとなります。以下に、パターンの種類を図示します。

強気のクラブハーモニックパターン

強気のクラブハーモニックパターン

弱気のクラブハーモニックパターン

弱気のクラブハーモニックパターン

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

  • XAレッグの定義:XからAへの最初の勢いのある動きがパターンの基盤を形成します。この動きはパターンの方向性を決定し(強気のクラブでは下向き、弱気のクラブでは上向き)、フィボナッチ計算の基準としても機能します。
  • ABレッグの確定:BはXAの動きの約0.618戻しとなるよう設定します。これは、初期の動きを過度に反転させずに修正を確認するためです。
  • BCレッグの分析:BCレッグはABの動きの0.382〜0.886の範囲で伸びる必要があります。このレッグは、最終的な延長(CDレッグ)につながる鋭い逆行の動きを作り出します。
  • CDレッグの設定:最後のCDレッグはXAの動きの1.618倍に延長され、Dでの潜在的反転ゾーンを示します。ここでパターンが完成し、売買のシグナルが発生します。

これらの幾何学的およびフィボナッチに基づく条件を用いることで、取引システムは価格データ上で有効なクラブパターンを体系的に検出できます。パターンが確認されたら、三角形やトレンドライン、X、A、B、C、Dのラベル、エントリーおよび利確の目安となる点線などを用いて、チャート上に形成を視覚化します。この設定により、Dでの売買を自動的に実行し、計算済みの損切りや段階的な利確を活用できます。パターンの高確率な反転特性を活かし、効果的な市場エントリーが可能です。それでは、MQL5での実装に進みましょう。


MQL5での実装

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

//+------------------------------------------------------------------+
//|                                             Crab 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 Crab Strategy"
#property strict

#include <Trade\Trade.mqh>                         //--- Include Trade library for order management
CTrade obj_Trade;                                  //--- Instantiate trade object for executing orders

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

//---------------------------------------------------------------------------
//--- Crab pattern definition:
//--- Bullish Crab:
//--- 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.5*(A-X); Legs within specified ranges.
//--- Bearish Crab:
//--- 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.5*(X-A); Legs within specified ranges.
//---------------------------------------------------------------------------

struct Pivot {                                     //--- Define structure for pivot points
   datetime time;                                  //--- Store time of pivot bar
   double   price;                                 //--- Store price (high for swing high, low for swing low)
   bool     isHigh;                                //--- Indicate true for swing high, false for swing low
};

Pivot pivots[];                                    //--- Declare array to store pivot points
int      g_patternFormationBar = -1;               //--- Store bar index of pattern formation (-1 if none)
datetime g_lockedPatternX = 0;                     //--- Store X pivot time for locked pattern

クラブパターンの実装を開始するにあたり、まず<Trade\Trade.mqh>ライブラリをインクルードし、注文管理(買いと売りのリクエスト送信など)をおこなうためにobj_TradeをCTradeオブジェクトとしてインスタンス化します。次に、ユーザーがカスタマイズできる入力パラメータを定義します。PivotLeftとPivotRightはそれぞれ5本のバーでスイングピボットを識別する際の遡及範囲を指定し、Toleranceは0.10でフィボナッチの許容誤差を設定、LotSizeは0.01で取引量を指定、AllowTradingはtrueで自動取引を有効にします。

次にPivot構造体を定義し、time(datetime型)、price(double型)、isHigh(bool型)でスイングポイントを格納します。pivotsをPivotの配列として宣言し、グローバル変数g_patternFormationBarを-1、g_lockedPatternXを0に初期化してパターン形成バーの追跡とXピボットのロックをおこない、パターン識別の基盤を整えます。視覚化のために、ライン、ラベル、三角形を描画する関数を用意します。

//+------------------------------------------------------------------+
//| Draw filled triangle on chart                                    |
//+------------------------------------------------------------------+
void DrawTriangle(string name, datetime t1, double p1, datetime t2, double p2, datetime t3, double p3, color cl, int width, bool fill, bool back) {
   if (ObjectCreate(0, name, OBJ_TRIANGLE, 0, t1, p1, t2, p2, t3, p3)) { //--- Create triangle with three points
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set triangle color
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); //--- Set solid line style
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width); //--- Set line width
      ObjectSetInteger(0, name, OBJPROP_FILL, fill); //--- Enable or disable fill
      ObjectSetInteger(0, name, OBJPROP_BACK, back); //--- Set background or foreground
   }
}

//+------------------------------------------------------------------+
//| Draw trend line on chart                                         |
//+------------------------------------------------------------------+
void DrawTrendLine(string name, datetime t1, double p1, datetime t2, double p2, color cl, int width, int style) {
   if (ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) { //--- Create trend line between two points
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set line color
      ObjectSetInteger(0, name, OBJPROP_STYLE, style); //--- Set line style (solid, dotted, etc.)
      ObjectSetInteger(0, name, OBJPROP_WIDTH, width); //--- Set line width
   }
}

//+------------------------------------------------------------------+
//| Draw dotted horizontal line on chart                             |
//+------------------------------------------------------------------+
void DrawDottedLine(string name, datetime t1, double p, datetime t2, color lineColor) {
   if (ObjectCreate(0, name, OBJ_TREND, 0, t1, p, t2, p)) { //--- Create horizontal dotted line
      ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor); //--- Set line color
      ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT); //--- Set dotted style
      ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); //--- Set line width to 1
   }
}

//+------------------------------------------------------------------+
//| Draw anchored text label for pivots                              |
//+------------------------------------------------------------------+
void DrawTextEx(string name, string text, datetime t, double p, color cl, int fontsize, bool isHigh) {
   if (ObjectCreate(0, name, OBJ_TEXT, 0, t, p)) { //--- Create text label at specified coordinates
      ObjectSetString(0, name, OBJPROP_TEXT, text); //--- Set label text content
      ObjectSetInteger(0, name, OBJPROP_COLOR, cl); //--- Set text color
      ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontsize); //--- Set font size
      ObjectSetString(0, name, OBJPROP_FONT, "Arial Bold"); //--- Set font to Arial Bold
      if (isHigh) {                                //--- Check if pivot is swing high
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_BOTTOM); //--- Anchor label above pivot
      } else {                                     //--- Handle swing low
         ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_TOP); //--- Anchor label below pivot
      }
      ObjectSetInteger(0, name, OBJPROP_ALIGN, ALIGN_CENTER); //--- Center-align text
   }
}

ここでは、クラブハーモニックパターンとエントリーレベルをチャート上に描画する視覚化関数を実装します。まずDrawTriangle関数を作成します。この関数ではObjectCreateを使用してOBJ_TRIANGLEを作成し、3つの点(t1 p1, t2 p2, t3 p3)で塗りつぶされた三角形を描画します。その後、ObjectSetIntegerを使用してOBJPROP_COLORに指定の色を設定し、OBJPROP_STYLEをSTYLE_SOLIDに設定、OBJPROP_WIDTHで線幅を指定、OBJPROP_FILLで塗りつぶしの有効・無効を設定し、OBJPROP_BACKで背景か前景かを指定します。

次にDrawTrendLine関数を実装します。この関数ではObjectCreateを使用してOBJ_TRENDとして2点間にトレンドラインを作成します。作成後、ObjectSetIntegerを用いてOBJPROP_COLORで線の色を設定し、OBJPROP_STYLEで線種(実線、点線など)を設定、OBJPROP_WIDTHで線幅を指定します。これにより、カスタマイズ可能なトレンドラインを描画できます。次にDrawDottedLine関数を作成します。この関数では、指定した価格で水平の点線(OBJ_TREND)をt1からt2まで描画します。最後にDrawTextEx関数を実装します。この関数は、座標(t, p)にテキストラベル(OBJ_TEXT)を作成し、前の関数と同じ形式を使用してクラブパターンとエントリーレベルをチャート上に明確に表示できるようにします。これでOnTickイベントハンドラに進み、後でパターン識別に使用できるピボットポイントを検出する処理をおこなう準備が整いました。そのためのロジックを以下に実装します。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick() {
   static datetime lastBarTime = 0;               //--- Store time of last processed bar
   datetime currentBarTime = iTime(_Symbol, _Period, 1); //--- Get time of current confirmed bar
   if (currentBarTime == lastBarTime) return;     //--- Exit if no new bar
   lastBarTime = currentBarTime;                  //--- Update last processed bar time
   ArrayResize(pivots, 0);                        //--- Clear pivot array for fresh analysis
   int barsCount = Bars(_Symbol, _Period);        //--- Retrieve total number of bars
   int start = PivotLeft;                         //--- Set starting index for pivot detection
   int end = barsCount - PivotRight;              //--- Set ending index for pivot detection
   for (int i = end - 1; i >= start; i--) {       //--- Iterate through bars to identify pivots
      bool isPivotHigh = true;                    //--- Assume bar is a swing high
      bool isPivotLow = true;                     //--- Assume bar is a swing low
      double currentHigh = iHigh(_Symbol, _Period, i); //--- Get current bar high price
      double currentLow = iLow(_Symbol, _Period, i); //--- Get current bar low price
      for (int j = i - PivotLeft; j <= i + PivotRight; j++) { //--- Check surrounding bars
         if (j < 0 || j >= barsCount) continue;   //--- Skip out-of-bounds indices
         if (j == i) continue;                    //--- Skip current bar
         if (iHigh(_Symbol, _Period, j) > currentHigh) isPivotHigh = false; //--- Invalidate swing high
         if (iLow(_Symbol, _Period, j) < currentLow) isPivotLow = false; //--- Invalidate swing low
      }
      if (isPivotHigh || isPivotLow) {            //--- Check if bar is a pivot
         Pivot p;                                 //--- Create new pivot structure
         p.time = iTime(_Symbol, _Period, i);     //--- Set pivot bar time
         p.price = isPivotHigh ? currentHigh : currentLow; //--- Set pivot price
         p.isHigh = isPivotHigh;                  //--- Set pivot type
         int size = ArraySize(pivots);            //--- Get current pivot array size
         ArrayResize(pivots, size + 1);           //--- Resize pivot array
         pivots[size] = p;                        //--- Add pivot to array
      }
   }
}

次にOnTickイベントハンドラの初期ロジックを実装し、スイングピボットを検出してクラブハーモニックパターン識別の基礎を作ります。まず、新しいバーが形成されたかを確認するため、lastBarTime(staticで初期値0)とiTimeで取得したcurrentBarTime(シフト1)を比較し、現在進行中の未確定バーを使用しないようにします。バーが変わっていなければ処理を終了し、新しいバーが確認された場合はlastBarTimeを更新します。次に、ArrayResizeを使用してpivots配列をクリアし、解析をリセットします。その後、Barsで総バー数を取得し、ピボット検出範囲をstart(PivotLeftと同じ)からend(総バー数からPivotRightを引いた値)に設定し、end-1からstartまでバーを逆順で反復処理します。

各バーについて、スイングハイであると仮定してisPivotHighをtrue、スイングローであると仮定してisPivotLowをtrueに設定し、iHighiLowでバーの高値と安値を取得します。その後、PivotLeftとPivotRightの範囲内の周囲バーをチェックし、隣接するバーにより高い高値または低い安値があればピボットを無効化します。最後に、バーが有効なピボット(高値または低値)のままであれば、Pivot構造体を作成し、timeにiTimeで取得した時間、priceにisPivotHighに応じた高値または安値、isHighフラグを設定してpivots配列に追加(ArrayResizeで配列サイズを調整)します。この配列を出力すると、以下のような結果になります。

ピボットデータ

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

int pivotCount = ArraySize(pivots);            //--- Get total number of pivots
if (pivotCount < 5) {                          //--- Check if insufficient pivots
   g_patternFormationBar = -1;                 //--- Reset pattern formation bar
   g_lockedPatternX = 0;                       //--- Reset locked X pivot
   return;                                     //--- Exit function
}
Pivot X = pivots[pivotCount - 5];              //--- Extract X pivot (earliest)
Pivot A = pivots[pivotCount - 4];              //--- Extract A pivot
Pivot B = pivots[pivotCount - 3];              //--- Extract B pivot
Pivot C = pivots[pivotCount - 2];              //--- Extract C pivot
Pivot D = pivots[pivotCount - 1];              //--- Extract D pivot (latest)
bool patternFound = false;                     //--- Initialize pattern detection flag
if (X.isHigh && !A.isHigh && B.isHigh && !C.isHigh && D.isHigh) { //--- Check bearish Crab pattern
   double diff = X.price - A.price;            //--- Calculate XA leg difference
   if (diff > 0) {                             //--- Ensure positive XA move
      double idealB = A.price + 0.618 * diff;  //--- Compute ideal B (0.618 retracement)
      if (MathAbs(B.price - idealB) <= Tolerance * diff) { //--- Verify B within tolerance
         double AB = B.price - A.price;        //--- Calculate AB leg length
         double BC = B.price - C.price;        //--- Calculate BC leg length
         if (BC >= 0.382 * AB && BC <= 0.886 * AB) { //--- Check BC within Fibonacci range
            double extension = D.price - A.price; //--- Calculate AD extension
            if (MathAbs(extension - 1.618 * diff) <= Tolerance * diff && D.price > X.price) { //--- Verify 1.618 extension and D > X
               patternFound = true;            //--- Confirm bearish pattern
            }
         }
      }
   }
}
if (!X.isHigh && A.isHigh && !B.isHigh && C.isHigh && !D.isHigh) { //--- Check bullish Crab pattern
   double diff = A.price - X.price;            //--- Calculate XA leg difference
   if (diff > 0) {                             //--- Ensure positive XA move
      double idealB = A.price - 0.618 * diff;  //--- Compute ideal B (0.618 retracement)
      if (MathAbs(B.price - idealB) <= Tolerance * diff) { //--- Verify B within tolerance
         double AB = A.price - B.price;        //--- Calculate AB leg length
         double BC = C.price - B.price;        //--- Calculate BC leg length
         if (BC >= 0.382 * AB && BC <= 0.886 * AB) { //--- Check BC within Fibonacci range
            double extension = A.price - D.price; //--- Calculate AD extension
            if (MathAbs(extension - 1.618 * diff) <= Tolerance * diff && D.price < X.price) { //--- Verify 1.618 extension and D < X
               patternFound = true;            //--- Confirm bullish pattern
            }
         }
      }
   }
}

パターンを識別するために、フィボナッチに基づく基準を使用します。まず、ArraySize(pivots)でピボットの総数を取得し、pivotCountに格納します。クラブパターンにはX、A、B、C、Dの5点が必要なため、ピボットが5つ未満の場合はg_patternFormationBarを-1、g_lockedPatternXを0にリセットして処理を終了します。その後、pivots配列から最後の5つのピボットを抽出し、X(最も古い)、A、B、C、D(最新)に割り当ててパターン構造を表します。

次に、弱気クラブパターンを確認します。順序がX高値、A低値、B高値、C低値、D高値であることを検証し、XAレッグの差(X.price - A.price)を計算して正であることを確認し、理想のBを「A.price + 0.618 * diff」として計算します。そしてBが「Tolerance * diff」の範囲内であることをMathAbsで確認します。その後、BCレッグがABの0.382~0.886の範囲内であること、AD延長がXAの1.618でDがXより上にあることを検証し、すべての条件を満た下場合はpatternFoundをtrueに設定します。最後に、強気のクラブパターンを確認します。順序がX低値、A高値、B低値、C高値、D低値であることを検証し、XAを「A.price - X.price」として計算して正であることを確認、Bが0.618戻し、BCがABの0.382~0.886、ADがXAの1.618でDがXより下であることを検証し、有効であればpatternFoundをtrueに設定します。パターンが検出された場合、チャート上にそのパターンを可視化する処理を進めることができます。

string patternType = "";                                                //--- Initialize pattern type
if (patternFound) {                                                     //--- Check if pattern detected
   if (D.price > X.price) patternType = "Bearish";                      //--- Set bearish pattern (sell signal)
   else if (D.price < X.price) patternType = "Bullish";                 //--- Set bullish pattern (buy signal)
}
if (patternFound) {                                                     //--- Process valid Crab pattern
   Print(patternType, " Crab pattern detected at ", TimeToString(D.time, TIME_DATE|TIME_MINUTES|TIME_SECONDS)); //--- Log pattern detection
   string signalPrefix = "CR_" + IntegerToString(X.time);               //--- Generate unique prefix for objects
   color triangleColor = (patternType == "Bullish") ? clrBlue : clrRed; //--- Set triangle color based on pattern
   DrawTriangle(signalPrefix + "_Triangle1", X.time, X.price, A.time, A.price, B.time, B.price, triangleColor, 2, true, true); //--- Draw XAB triangle
   DrawTriangle(signalPrefix + "_Triangle2", B.time, B.price, C.time, C.price, D.time, D.price, triangleColor, 2, true, true); //--- Draw BCD triangle
}

検出されたパターンをチャート上で分類および可視化するために、まずpatternTypeを空文字列で初期化し、パターンが強気か弱気かを格納できるようにします。次に、patternFoundがtrueの場合、D.priceとX.priceを比較してパターンの種類を決定します。DがXより上であればpatternTypeをBearishに設定(売りシグナル)、DがXより下であればBullishに設定(買いシグナル)します。その後、有効なクラブパターンが確認された場合、Printで検出をログ出力し、patternTypeとDピボットの時間をTimeToStringで日付、分、秒形式で表示します。

最後に、X.timeを文字列に変換して「CR_」と連結した一意の識別子signalPrefixを作成し、triangleColorを強気の場合は青、弱気の場合は赤に設定します。そしてDrawTriangleを2回呼び出してパターンを可視化します。1回目はXAB三角形(X、A、Bを結ぶ)、2回目はBCD三角形(B、C、Dを結ぶ)で、それぞれsignalPrefixに_Triangle1、_Triangle2を付与し、各ピボットの時間と価格、triangleColor、幅2、塗りつぶしと背景表示を有効にして、検出されたクラブパターンを明確に識別、視覚化できるようにします。これが私たちが達成したマイルストーンです。

三角形のパターン

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

DrawTrendLine(signalPrefix + "_TL_XA", X.time, X.price, A.time, A.price, clrBlack, 2, STYLE_SOLID); //--- Draw XA trend line
DrawTrendLine(signalPrefix + "_TL_AB", A.time, A.price, B.time, B.price, clrBlack, 2, STYLE_SOLID); //--- Draw AB trend line
DrawTrendLine(signalPrefix + "_TL_BC", B.time, B.price, C.time, C.price, clrBlack, 2, STYLE_SOLID); //--- Draw BC trend line
DrawTrendLine(signalPrefix + "_TL_CD", C.time, C.price, D.time, D.price, clrBlack, 2, STYLE_SOLID); //--- Draw CD trend line
DrawTrendLine(signalPrefix + "_TL_XB", X.time, X.price, B.time, B.price, clrBlack, 2, STYLE_SOLID); //--- Draw XB trend line
DrawTrendLine(signalPrefix + "_TL_BD", B.time, B.price, D.time, D.price, clrBlack, 2, STYLE_SOLID); //--- Draw BD trend line
double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Retrieve symbol point size
double offset = 15 * point;                    //--- Calculate label offset (15 points)
double textY_X = X.isHigh ? X.price + offset : X.price - offset; //--- Set X label Y coordinate
double textY_A = A.isHigh ? A.price + offset : A.price - offset; //--- Set A label Y coordinate
double textY_B = B.isHigh ? B.price + offset : B.price - offset; //--- Set B label Y coordinate
double textY_C = C.isHigh ? C.price + offset : C.price - offset; //--- Set C label Y coordinate
double textY_D = D.isHigh ? D.price + offset : D.price - offset; //--- Set D label Y coordinate
DrawTextEx(signalPrefix + "_Text_X", "X", X.time, textY_X, clrBlack, 11, X.isHigh); //--- Draw X pivot label
DrawTextEx(signalPrefix + "_Text_A", "A", A.time, textY_A, clrBlack, 11, A.isHigh); //--- Draw A pivot label
DrawTextEx(signalPrefix + "_Text_B", "B", B.time, textY_B, clrBlack, 11, B.isHigh); //--- Draw B pivot label
DrawTextEx(signalPrefix + "_Text_C", "C", C.time, textY_C, clrBlack, 11, C.isHigh); //--- Draw C pivot label
DrawTextEx(signalPrefix + "_Text_D", "D", D.time, textY_D, clrBlack, 11, D.isHigh); //--- Draw D pivot label
datetime centralTime = (X.time + B.time) / 2;                                       //--- Calculate central label time
double centralPrice = D.price;                                                      //--- Set central label price
if (ObjectCreate(0, signalPrefix + "_Text_Center", OBJ_TEXT, 0, centralTime, centralPrice)) { //--- Create central pattern label
   ObjectSetString(0, signalPrefix + "_Text_Center", OBJPROP_TEXT, patternType == "Bullish" ? "Bullish Crab" : "Bearish Crab"); //--- Set pattern name
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_COLOR, clrBlack);     //--- Set text color
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_FONTSIZE, 11);        //--- Set font size
   ObjectSetString(0, signalPrefix + "_Text_Center", OBJPROP_FONT, "Arial Bold");   //--- Set font type
   ObjectSetInteger(0, signalPrefix + "_Text_Center", OBJPROP_ALIGN, ALIGN_CENTER); //--- Center-align text
}

パターン構造を表現するために、さらに線とラベルを追加していきます。まず、DrawTrendLineを使用して6本のトレンドラインを描画します。一意のsignalPrefixを用いて主要なピボットポイント(XA、AB、BC、CD、XB、BD)を接続します。それぞれの線は対応するピボットの時間と価格(例:X.time、X.price)を端点として設定し、色はclrBlack、幅は2、STYLE_SOLIDで実線として描画し、XABCD構造と補助レッグをアウトライン化します。次に、SymbolInfoDouble(_Symbol, SYMBOL_POINT)で銘柄のポイントサイズを取得し、15倍してラベルのオフセットを計算します。各ピボットのスイングハイ(isHighがtrue)またはスイングローに応じてオフセットを加算または減算し、ピボットラベル(textY_X、textY_A、textY_B、textY_C、textY_D)のY座標を決定し、高値の上、安値の下にラベルが表示されるようにします。

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

縁とラベルが付いたクラブパターン

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

datetime lineStart = D.time;                                     //--- Set start time for trade level lines
datetime lineEnd = D.time + PeriodSeconds(_Period) * 2;          //--- Set end time for trade level lines
double entryPriceLevel, TP1Level, TP2Level, TP3Level, tradeDiff; //--- Declare trade level variables
if (patternType == "Bullish") {                                  //--- Handle bullish trade levels
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_ASK);      //--- Set entry at ask price
   TP3Level = C.price;                                           //--- Set TP3 at C pivot price
   tradeDiff = TP3Level - entryPriceLevel;                       //--- Calculate total trade distance
   TP1Level = entryPriceLevel + tradeDiff / 3;                   //--- Set TP1 at 1/3 of distance
   TP2Level = entryPriceLevel + 2 * tradeDiff / 3;               //--- Set TP2 at 2/3 of distance
} else {                                                         //--- Handle bearish trade levels
   entryPriceLevel = SymbolInfoDouble(_Symbol, SYMBOL_BID);      //--- Set entry at bid price
   TP3Level = C.price;                                           //--- Set TP3 at C pivot price
   tradeDiff = entryPriceLevel - TP3Level;                       //--- Calculate total trade distance
   TP1Level = entryPriceLevel - tradeDiff / 3;                   //--- Set TP1 at 1/3 of distance
   TP2Level = entryPriceLevel - 2 * tradeDiff / 3;               //--- Set TP2 at 2/3 of distance
}
DrawDottedLine(signalPrefix + "_EntryLine", lineStart, entryPriceLevel, lineEnd, clrMagenta);  //--- Draw entry level line
DrawDottedLine(signalPrefix + "_TP1Line", lineStart, TP1Level, lineEnd, clrForestGreen);       //--- Draw TP1 level line
DrawDottedLine(signalPrefix + "_TP2Line", lineStart, TP2Level, lineEnd, clrGreen);             //--- Draw TP2 level line
DrawDottedLine(signalPrefix + "_TP3Line", lineStart, TP3Level, lineEnd, clrDarkGreen);         //--- Draw TP3 level line
datetime labelTime = lineEnd + PeriodSeconds(_Period) / 2;                                     //--- Set time for trade level labels
string entryLabel = patternType == "Bullish" ? "BUY (" : "SELL (";                             //--- Start entry label text
entryLabel += DoubleToString(entryPriceLevel, _Digits) + ")";                                  //--- Append entry price
DrawTextEx(signalPrefix + "_EntryLabel", entryLabel, labelTime, entryPriceLevel, clrMagenta, 11, true); //--- Draw entry label
string tp1Label = "TP1 (" + DoubleToString(TP1Level, _Digits) + ")";                           //--- Create TP1 label text
DrawTextEx(signalPrefix + "_TP1Label", tp1Label, labelTime, TP1Level, clrForestGreen, 11, true); //--- Draw TP1 label
string tp2Label = "TP2 (" + DoubleToString(TP2Level, _Digits) + ")";                           //--- Create TP2 label text
DrawTextEx(signalPrefix + "_TP2Label", tp2Label, labelTime, TP2Level, clrGreen, 11, true);     //--- Draw TP2 label
string tp3Label = "TP3 (" + DoubleToString(TP3Level, _Digits) + ")";                           //--- Create TP3 label text
DrawTextEx(signalPrefix + "_TP3Label", tp3Label, labelTime, TP3Level, clrDarkGreen, 11, true); //--- Draw TP3 label

ここでは、検出されたパターンのエントリーレベルの定義と可視化を続けます。まず、lineStartをDピボットの時間(D.time)に設定し、lineEndを「PeriodSeconds(_Period) * 2」で2期間先に設定します。また、取引計算用にentryPriceLevel、TP1Level、TP2Level、TP3Level、tradeDiffの変数を宣言します。次に、強気パターン(patternType == 'Bullish')の場合、entryPriceLevelをSymbolInfoDoubleで現在のAsk価格に設定し、TP3LevelをCピボットの価格に設定します。tradeDiffを「TP3Level - entryPriceLevel」として計算し、TP1LevelとTP2LevelをtradeDiffの1/3および2/3をentryPriceLevelに加えて算出します。弱気パターンの場合は、entryPriceLevelにBid価格を使用し、TP3LevelをCピボット価格に設定、tradeDiffを「entryPriceLevel - TP3Level」として計算し、TP1LevelとTP2Levelを取引差の1/3および2/3を引いて算出します。

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

弱気パターン

弱気

強気パターン

強気

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

int currentBarIndex = Bars(_Symbol, _Period) - 1;       //--- Retrieve current bar index
if (g_patternFormationBar == -1) {                      //--- Check if no pattern is locked
   g_patternFormationBar = currentBarIndex;             //--- Lock current bar as formation bar
   g_lockedPatternX = X.time;                           //--- Lock X pivot time
   Print("Pattern detected on bar ", currentBarIndex, ". Waiting for confirmation on next bar."); //--- Log detection
   return;                                              //--- Exit function
}
if (currentBarIndex == g_patternFormationBar) {         //--- Check if still on formation bar
   Print("Pattern is repainting; still on locked formation bar ", currentBarIndex, ". No trade yet."); //--- Log repainting
   return;                                              //--- Exit function
}
if (currentBarIndex > g_patternFormationBar) {          //--- Check if new bar after formation
   if (g_lockedPatternX == X.time) {                    //--- Verify same X pivot for confirmation
      Print("Confirmed pattern (locked on bar ", g_patternFormationBar, "). Opening trade on bar ", currentBarIndex, "."); //--- Log confirmed pattern
      g_patternFormationBar = currentBarIndex;          //--- Update formation bar to current
      if (AllowTrading && !PositionSelect(_Symbol)) {   //--- Check trading allowed and no open position
         double entryPriceTrade = 0, stopLoss = 0, takeProfit = 0; //--- Declare trade parameters
         point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); //--- Update point value
         bool tradeResult = false;                      //--- Initialize trade result flag
         if (patternType == "Bullish") {                //--- Process bullish trade
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_ASK); //--- Set entry at ask price
            double diffTrade = TP2Level - entryPriceTrade; //--- Calculate trade distance
            stopLoss = entryPriceTrade - diffTrade * 3; //--- Set stop loss (3x distance)
            takeProfit = TP2Level;                      //--- Set take profit at TP2
            tradeResult = obj_Trade.Buy(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Crab Signal"); //--- Execute buy trade
            if (tradeResult) {                          //--- Check trade success
               Print("Buy order opened successfully."); //--- Log successful buy
            } else {                                    //--- Handle trade failure
               Print("Buy order failed: ", obj_Trade.ResultRetcodeDescription()); //--- Log failure reason
            }
         } else if (patternType == "Bearish") {         //--- Process bearish trade
            entryPriceTrade = SymbolInfoDouble(_Symbol, SYMBOL_BID); //--- Set entry at bid price
            double diffTrade = entryPriceTrade - TP2Level; //--- Calculate trade distance
            stopLoss = entryPriceTrade + diffTrade * 3; //--- Set stop loss (3x distance)
            takeProfit = TP2Level;                      //--- Set take profit at TP2
            tradeResult = obj_Trade.Sell(LotSize, _Symbol, entryPriceTrade, stopLoss, takeProfit, "Crab Signal"); //--- Execute sell trade
            if (tradeResult) {                          //--- Check trade success
               Print("Sell order opened successfully."); //--- Log successful sell
            } else {                                    //--- Handle trade failure
               Print("Sell order failed: ", obj_Trade.ResultRetcodeDescription()); //--- Log failure reason
            }
         }
      } else {                                          //--- Trading not allowed or position exists
         Print("A position is already open for ", _Symbol, ". No new trade executed."); //--- Log no trade
      }
   } else {                                            //--- Pattern has changed
      g_patternFormationBar = currentBarIndex;         //--- Update formation bar
      g_lockedPatternX = X.time;                       //--- Update locked X pivot
      Print("Pattern has changed; updating lock on bar ", currentBarIndex, ". Waiting for confirmation."); //--- Log pattern change
      return;                                          //--- Exit function
   }
}
} else {                                               //--- No valid pattern detected
   g_patternFormationBar = -1;                         //--- Reset formation bar
   g_lockedPatternX = 0;                               //--- Reset locked X pivot
}

まず、「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とCrab Signalコメントでobj_Trade.Buyを実行し、成功または失敗をログに記録します。弱気パターンの場合はBid価格を使用し、stopLossを3倍上に設定してobj_Trade.Sellを実行します。エントリーが許可されていない場合や既存ポジションがある場合は、エントリーがおこなわれなかったことをログに記録します。パターンが変化した場合はロックを更新して確認を待ちます。パターンが検出されない場合は、g_patternFormationBarとg_lockedPatternXをリセットします。これにより、確認済みのクラブパターンに基づいて、適切なリスク管理の下でエントリーできることが保証されます。 最後にプログラムを削除する際には、チャート上のパターンも削除する必要があります。

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason) {
   ObjectsDeleteAll(0, "CR_");                    //--- Remove all chart objects with "CR_" prefix
   ArrayResize(pivots, 0);                        //--- Clear pivots array
   g_patternFormationBar = -1;                    //--- Reset pattern formation bar index
   g_lockedPatternX = 0;                          //--- Reset locked pattern X pivot time
   ChartRedraw(0);                                //--- Redraw chart to reflect changes
}

ここでは、EAをチャートから削除した際に適切にクリーンアップをおこなうために、OnDeinitイベントハンドラを実装します。まず、ObjectsDeleteAllを使用して「CR_」接尾辞の付いたすべてのチャートオブジェクトを削除し、クラブパターンに関連する三角形、トレンドライン、ラベルなどの視覚要素をクリアします。次に、ArrayResizeでpivots配列のサイズを0に設定し、保存されているピボットデータを消去します。さらに、g_patternFormationBarを-1に、g_lockedPatternXを0にリセットしてパターン追跡変数をクリアします。最後にChartRedrawを呼び出してチャートを更新し、すべてのオブジェクトとデータの削除が反映されるようにします。これにより、リソースを解放し、残留要素を残さずにクリーンな終了が保証されます。コンパイルすると、次の結果が得られます。

弱気シグナル

弱気シグナル

強気シグナル

強気シグナル

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


バックテスト

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

バックテストグラフ

グラフ

バックテストレポート

レポート


結論

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

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

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

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

添付されたファイル |
Crab_Pattern_EA.mq5 (47.93 KB)
Parafracオシレーター:パラボリックとフラクタルインジケーターの組み合わせ Parafracオシレーター:パラボリックとフラクタルインジケーターの組み合わせ
パラボリックSARとフラクタルインジケーターを組み合わせて、新しいオシレーターベースのインジケーターを作成する方法について説明します。両ツールの独自の強みを統合することにより、トレーダーはより洗練された効果的な取引戦略の開発を目指すことができます。
プライスアクション分析ツールキットの開発(第36回):MetaTrader 5マーケットストリームへ直接アクセスするPython活用法 プライスアクション分析ツールキットの開発(第36回):MetaTrader 5マーケットストリームへ直接アクセスするPython活用法
MetaTrader 5ターミナルの潜在能力を最大限に引き出すために、Pythonのデータサイエンスエコシステムと公式のMetaTrader 5クライアントライブラリを活用する方法を紹介します。本記事では、認証をおこない、ライブティックおよび分足データを直接Parquetストレージにストリーミングする手法を解説し、taやProphetを用いた高度な特徴量エンジニアリングをおこない、時間依存型の勾配ブースティングモデルを学習させる方法を示します。その後、軽量なFlaskサービスを展開して、リアルタイムで取引シグナルを提供します。ハイブリッドクオンツフレームワークを構築する場合でも、エキスパートアドバイザー(EA)に機械学習を組み込む場合でも、データ駆動型アルゴリズム取引のための堅牢なエンドツーエンドパイプラインを習得できます。
MQL5で自己最適化エキスパートアドバイザーを構築する(第12回):行列分解を用いた線形分類器の構築 MQL5で自己最適化エキスパートアドバイザーを構築する(第12回):行列分解を用いた線形分類器の構築
本記事では、アルゴリズム取引における行列分解の強力な役割、特にMQL5アプリケーション内での活用について探ります。回帰モデルからマルチターゲット分類器まで、実際の例を通して、これらの手法が組み込みのMQL5関数を使ってどれほど容易に統合できるかを示します。価格の方向性を予測する場合でも、インジケーターの挙動をモデル化する場合でも、このガイドは行列手法を用いたインテリジェントな取引システム構築の強固な基盤を提供します。
MQL5での取引戦略の自動化(第26回):複数ポジション取引のためのピンバーナンピンシステムの構築 MQL5での取引戦略の自動化(第26回):複数ポジション取引のためのピンバーナンピンシステムの構築
本記事では、ピンバーを検出して取引を開始し、複数ポジションを管理するためのナンピン(難平、Averaging)戦略を用いたピンバーシステムをMQL5で開発します。さらに、トレーリングストップやブレークイーブン調整で強化し、リアルタイムでポジションと利益を監視できるダッシュボードも組み込みます。