MQL5での取引戦略の自動化(第27回):視覚的なフィードバックによるプライスアクションクラブハーモニックパターンの作成
はじめに
前回の記事(第26回)では、ピンバーを活用して取引を開始し、複数ポジションをナンピン戦略で管理する「ピンバーナンピンシステム」をMetaQuotes Language 5 (MQL5)で開発しました。このシステムには、リアルタイムで監視可能な動的ダッシュボードも搭載されていました。今回の第27回では、ピボットポイントとフィボナッチ比率を用いて弱気、強気両方のクラブ(Crab)パターンを識別し、正確なエントリー、ストップロス、利確レベルを使用した取引を自動化するクラブパターンシステムを作成します。さらに、三角形やトレンドラインなどのチャートオブジェクトを用いて、パターンを視覚的に分かりやすく表示します。本記事では以下のトピックを扱います。
この記事を読み終える頃には、ハーモニックパターン取引のための高度な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に設定し、iHighとiLowでバーの高値と安値を取得します。その後、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
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
Parafracオシレーター:パラボリックとフラクタルインジケーターの組み合わせ
プライスアクション分析ツールキットの開発(第36回):MetaTrader 5マーケットストリームへ直接アクセスするPython活用法
MQL5で自己最適化エキスパートアドバイザーを構築する(第12回):行列分解を用いた線形分類器の構築
MQL5での取引戦略の自動化(第26回):複数ポジション取引のためのピンバーナンピンシステムの構築
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索