English Русский Deutsch
preview
プライスアクション分析ツールキットの開発(第59回):幾何学的非対称性を用いたフラクタル保ち合いからの高精度ブレイクアウトの識別

プライスアクション分析ツールキットの開発(第59回):幾何学的非対称性を用いたフラクタル保ち合いからの高精度ブレイクアウトの識別

MetaTrader 5 |
39 0
Christian Benjamin
Christian Benjamin

内容



はじめに

真のブレイクアウト機会は、プライスアクション分析において最も魅力的である一方、最も見極めが難しいセットアップの一つです。市場は保ち合いを抜ける際に力強い勢いを示しても、その直後に失速したり反転したりすることが少なくありません。その多くは、ブレイクアウトを支える十分な内部構造や継続的な方向性圧力が存在しないことが原因です。したがって、本質的な課題はレンジを見つけることではなく、どの保ち合いが本当に持続的なトレンド形成へ向けて準備を進めているかを見極めることにあります。

第58回では、拡張局面に先立ってプライスアクション内部に形成される構造的不均衡と方向性バイアスを取り上げ、ブレイクアウトを単発的あるいは偶発的な現象ではなく、徐々に進行する構造変化の結果として位置付けました。本記事では、その考え方をさらに発展させ、Geometric Asymmetry (GA) Breakoutフレームワークを紹介します。これは、保ち合いが実際に持ち合い離れしようとしているかを評価するための、体系的かつルールベースの手法です。GAでは、フラクタルから導出されたスイング構造と幾何学的な関係性を利用して、レンジ境界が破られる前に方向性バイアスの形成を評価します。

構造の健全性、比率的な不均衡、そして多層的な確認プロセスを重視することで、本フレームワークはダマシのブレイクアウトを排除し、さまざまな市場環境においてより信頼性の高いレンジ相場からトレンド相場への移行シグナルを提供することを目的としています。


戦略概要と売買ロジック

本戦略では、ブレイクアウト検出をボラティリティイベントではなく、構造的かつ幾何学的な問題として捉えます。価格は、制約された構造を形成する一連のスイングとしてモデル化され、拡張局面が起こる前に構造的不均衡を客観的に測定します。 GA Breakout戦略では、保ち合いをレンジ境界だけで評価するのではなく、その内部を構成する各価格スイングの比率的な関係性も評価対象とします。距離・傾き・時間という測定可能な非対称性から方向性バイアスを導き出すことで、反応的なシグナルではなく、構造に基づく確認によってブレイクアウトを事前に捉えます。

本戦略の最大の特徴は、値動きの内部構造そのものに着目している点です。単純にボラティリティ拡大やレンジブレイクへ依存するのではなく、保ち合い内部で価格がどのように推移しているかを評価し、どちらの勢力が主導権を握りつつあるかを判断します。

市場の根本的なメカニズム

基本概念

特定の幾何学的特徴を伴う長期間の保ち合いは、その後の明確な拡張局面に先行することが多くあります。価格が矩形レンジや収束型フォーメーションなどの明確な構造へ圧縮され、その中で直近のスイングが他よりも著しく優勢になると、市場の一方向が主導権を強めつつあることを示唆します。このような不均衡は、通常、方向性を伴うブレイクアウトによって解消されます。


上図は、価格が明確なレンジ内で上下動を繰り返す保ち合い局面を示しています。この構造の内部で不均衡と方向性バイアスが形成され、その後ブレイクアウトへ発展する可能性があります。 

本戦略が捉える市場挙動

  • モメンタムの蓄積:制約された価格レンジ内部で徐々に圧力が蓄積する現象
  • ボラティリティ収縮:拡張局面の前にスイング幅が縮小する現象
  • 方向性の決着:構造的不均衡によって最も可能性の高いブレイクアウト方向が示される現象

多層的な判定ロジック

GAとは、保ち合いレンジ内部における価格運動の比率的な偏りを表します。 単純なレンジブレイクではなく、連続するスイングの長さ・傾き・所要時間を比較することで、価格がレンジ内部をどのように推移しているかを評価します。

一つのスイングが他よりも著しく支配的になる場合、それは保ち合い内部で一方向への圧力が形成されつつあることを示します。そのためGAはトレンド確認ではなく、ブレイクアウト前の方向性バイアスを表現する概念です。この評価は、有効なレンジと客観的なスイング構造が確立された後にのみ実施されます。以下の図は、有効な保ち合いレンジ内部でEAが構造と幾何学的不均衡を評価し、その後ブレイクアウト確認へ移行する流れを示しています。

スイングポイント(HH、HL、LL)はBill Williams Fractalによって検出され、幾何学解析の入力として利用されます。 フラクタル理論およびその実装については、本連載の前回記事で詳しく解説しているため、このセクションでは繰り返しません。

  • フェーズ1:構造基盤(フラクタルフレームワーク)

    3つの交互に並ぶフラクタルが連続して検出された場合、その構造は有効と判断されます。この条件を満たさない場合、システムはそのシーケンスを無視し、新たなフラクタル形成を監視し続けます。

    このフェーズでは戦略全体の構造的基盤を確立します。高値-安値-高値、または安値-高値-安値という交互のスイングを要求することで、小さな価格変動を除外し、意味のある転換点だけを抽出します。これにより、以降の解析は短期的ノイズではなく、重要な価格構造に基づいておこなわれます。

    • フェーズ2:レンジ認定(保ち合いの検証)

    構造が十分な期間維持され、ATRで調整された高さ制限内に収まり、さらに直近でレンジ境界との接触が確認できる場合、その構造は有効な保ち合いとして認定されます。それ以外の場合は破棄されます。

    この段階では、検出された構造が単なる価格の滞留ではなく、本物の保ち合いであることを確認します。ATRによる正規化により、市場ボラティリティの変化へ適応しながら、一貫した判定基準を維持できます。レンジ幅が広すぎるもの、継続時間が短すぎるもの、あるいは価格から既に無視されている構造は除外されます。

    • フェーズ3:幾何学解析(方向性バイアスの検出)

    このフェーズは、本戦略の最大の優位性となる部分です。ブレイクアウトが発生する前の段階で方向性の優勢を識別します。

    距離評価

    最新のスイングレッグの長さが、その一つ前のスイングレッグを設定された比率以上に上回った場合、方向性バイアスに対して1票が加算されます。

    傾き評価

    最新のスイングレッグの傾きが、設定された傾き比率以上で前のスイングレッグを上回った場合、方向性バイアスに対して1票が加算されます。

    時間圧縮評価

    最新のスイングレッグが、設定された時間圧縮閾値以内で前のスイングレッグより短時間で形成された場合、方向性バイアスに対して1票が加算されます。

    確認ルール

    幾何学的評価による累積票数が必要最小票数以上になった時点で、方向性バイアスが確定します。この条件を満たさない場合、その構造は対称的と判断され、方向性バイアスは付与されません。

    複数の比例指標を組み合わせることで、単一要素への依存を避けています。複数の独立した特徴が一致した場合にのみ方向性バイアスが確定します。

    • フェーズ4:パターンロック(セットアップの確認)

    幾何学的条件が満たされると、その構造はロック状態となり、ブレイクアウト監視フェーズへ移行します。

    ロック条件

    • 方向性バイアスが確認されていること
    • 最終スイングが該当するレンジ境界付近で終了していること
    • スイングの大きさがレンジに対して適切な比率を維持していること
    • 競合するアクティブなセットアップが存在しないこと

    ロックされたパターンの属性

    • 方向:上方向優勢なら強気、下方向優勢なら弱気
    • 基準レベル:固定されたレンジ高値・レンジ安値
    • 監視期間:InpMaxLockBarsの範囲内
    • 状態:ブレイクアウト確認を継続的に監視中

    構造をロックすることで、リペイントを防ぎ、分析の整合性を維持します。

    • フェーズ5:ブレイクアウト検出(シグナル生成)


    EAは、有効なGAレンジがロックされ、内部の幾何学的非対称性によって強気バイアスが確認され、さらに価格がGAレンジ高値を上回って確定した場合にのみ買いシグナルを発行します。 売りシグナルも同様の考え方で生成されます。弱気バイアスが確認された後、価格がGAレンジ安値を下回って確定した場合に発行されます。

    実行ロジック

    • 強気セットアップ

    価格が、ブレイクアウト・バッファを加味したレンジ高値を明確に上抜けて確定した場合、買いシグナルを生成します。

    • 弱気セットアップ

    価格が、ブレイクアウトバッファを加味したレンジ安値を明確に下抜けて確定した場合、売りシグナルを生成します。

    ブレイクアウトバッファ(InpBreakBufferPts)は、浅いレンジ逸脱によるダマシを除外しながら、本物の拡張局面には迅速に追随できるよう設計されています。シグナルはローソク足の確定時にのみ生成されるため、一貫性が高まり、誤検出も抑制されます。

    リスク管理とシグナルの厳格性

    本戦略は、高い選択性を設計思想の中核に据えています。構造、ボラティリティ、幾何学的条件が一致しない限り、無理に取引をおこなうことはありません。また、シグナル間には最小間隔を設けており、特に方向感の乏しい相場で同じ価格帯から繰り返しシグナルが発生することを防ぎます。その結果、より質の高いセットアップに集中し、不要なエントリーを抑えることができます。

    コンテキストを考慮した解釈

    本戦略が生成するシグナルは、将来を予測するものではなく、構造的条件が満たされたことを確認するためのものです。そのため、上位時間足の構造、市場セッションの特性、重要な価格レベルなど、より広い市場コンテキストと組み合わせて活用することで、より高い効果が期待できます。本システムはあくまでも意思決定を支援するフレームワークであり、実際の執行やリスク管理はトレーダー自身がおこないます。

    市場や時間足への適応性

    本戦略は固定値ではなく比例関係に基づいて判定を下すため、さまざまな金融商品や時間足へ柔軟に適応できます。各種パラメータは調整可能ですが、戦略の基本的な考え方を変更する必要はありません。この一貫性と柔軟性の両立により、多様な市場環境でも安定した運用が可能となります。すべてのシグナルを、保ち合いの質と方向性不均衡という構造的根拠に基づいて生成することで、GA Breakout戦略は、均衡状態から拡張局面への移行を高い精度で捉えるための、規律あるフレームワークを提供します。 

    これらすべての要素が連携することで、本戦略は構造的な原因から方向性という結果に至るまで、ブレイクアウトの成立条件を体系的に評価できるようになっています。



    MQL5の実装とコード構造

    このセクションでは、GA Breakout戦略をMQL5でどのように実装しているかについて、包括的に解説します。本コードベースは、可読性、実行効率、そして幅広いカスタマイズ性を重視したモジュール構成を採用しています。各コンポーネントはシステム内の特定の役割を担うよう設計されており、全体として整理されたフレームワークを構成しています。そのため、コードの理解、デバッグ、機能拡張が容易であり、継続的な改良や市場環境への適応にも柔軟に対応できます。さまざまな取引スタイルや運用方針を持つトレーダーに適した設計となっています。

    アーキテクチャの概要

    本実装の中核となるのは、各機能を独立したモジュールへ分離したモジュラー設計です。この設計により、それぞれのモジュールを個別に開発・テスト・保守できるため、システム全体の堅牢性が向上します。主要なモジュールは、フラクタル検出、保ち合いの評価、幾何学的非対称性の検証、エントリーシグナル生成、そしてパターン管理で構成されています。このように責務を明確に分離することで、コード全体の保守性が高まり、市場環境の変化に合わせてロジックを改善・拡張する際にも効率的に対応できます。

    システムの基盤となるのは、ユーザーが自由に設定できる入力パラメータです。これらは戦略の柔軟性を支える重要な要素であり、対象銘柄や時間足、さらには個々のリスク許容度に応じて動作を最適化できます。たとえば、InpMaxScanBarsは解析対象とする過去バーの最大数を制限し、計算負荷を抑えながら処理速度を向上させます。また、InpRangeATRMaxおよびInpRangeATRMaxSoftは、ATRに基づいてレンジサイズを評価し、十分な意味を持つ保ち合いだけを候補として採用するためのフィルタとして機能します。さらに、幾何学的非対称性を評価するための各種パラメータも、本戦略の重要な構成要素です。InpLenRatioMinはスイング距離の優位性を評価し、InpSlopeRatioMinは価格変動の傾き(勢い)の優位性を測定します。また、InpTimeCompressionMaxはスイング形成に要した時間を評価し、より短時間で形成された強い値動きを検出します。

    //====================== INPUTS =====================================
    input int      InpMaxScanBars              = 900;
    input int      InpATRPeriod                = 14;
    input double   InpRangeATRMax              = 2.00;
    input int      InpRangeMinBarsHard         = 8;
    input bool     InpEnableSoftRangeBars      = true;
    input int      InpRangeMinBarsSoft         = 4;
    input double   InpRangeATRMaxSoft          = 1.60;
    input int      InpRangeTestLookback        = 50;
    input double   InpRangeSideZonePct         = 0.35;
    
    input double   InpLenRatioMin              = 1.35;
    input double   InpSlopeRatioMin            = 1.15;
    input double   InpTimeCompressionMax       = 0.95;
    input int      InpMinGeometryVotes         = 2;
    
    input double   InpMaxLegVsRange            = 1.50;
    input int      InpMaxLegBars               = 50;
    input double   InpLegEndNearBoundaryPct    = 0.50;
    
    input int      InpBreakLookbackBars        = 150;
    input double   InpBreakBufferPts           = 5.0;
    input int      InpMaxLockBars              = 30;
    
    input int      InpArrowOffsetPoints        = 15;
    input int      InpArrowLabelOffset         = 30;
    
    input int      InpMinBarsBetweenSignals    = 5;
    
    input bool     InpShowSwings               = true;
    input bool     InpShowLegs                 = true;
    input bool     InpShowTriangle             = true;
    input bool     InpShowLabels               = true;
    input bool     InpShowRangeLines           = true;
    input bool     InpShowCandidateRangeRect   = true;
    input bool     InpShowLockedRangeRects     = true;
    input bool     InpShowArrows               = true;
    input bool     InpShowDebug                = true;
    
    // Arrow code and style
    input int      InpArrowUpCode              = 233;
    input int      InpArrowDownCode            = 234;
    input int      InpArrowSize                = 3;
    input color    InpArrowColorUp             = clrLime;
    input color    InpArrowColorDown           = clrRed;
    input bool     InpShowArrowLabels          = true;
    input int      InpArrowLabelFontSize       = 9;
    input string   InpArrowLabelFont           = "Arial";
    
    // Alert settings
    input bool     InpEnableAlerts             = true;
    input bool     InpAlertOnPatternLock       = true;
    input bool     InpAlertOnBreakout          = true;
    input string   InpAlertSoundPattern        = "alert.wav";
    input string   InpAlertSoundBreakout       = "alert2.wav";
    input bool     InpAlertPopup               = true;
    input bool     InpAlertNotification        = true;
    input bool     InpAlertEmail               = false;

    その他の設定項目としては、小さなヒゲによる誤検出を防ぐためのブレイクアウトバッファ(InpBreakBufferPts)、同一パターンから複数回シグナルが発生することを防ぐための最大ロック期間(InpMaxLockBars)、および過剰なエントリーを抑制するためのシグナル間の最小バー数(InpMinBarsBetweenSignals)が用意されています。また、スイングポイント、スイングレッグ、三角形、レンジ、ラベル、矢印などの表示・非表示を切り替えるビジュアル設定も備えており、トレーダーは自身の分析スタイルに合わせてチャート表示を自由にカスタマイズできます。アラート機能についても、サウンド、ポップアップ通知、プッシュ通知、メール通知を個別に設定できるため、利用環境を問わず重要なシグナルを確実に受け取ることができます。

    パターン検出:転換点の識別

    処理は、保ち合い候補の基礎となるスイングハイ・スイングローを正確に検出することから始まります。IsFractalHigh()およびIsFractalLow()は、Bill Williamsが提唱した厳密な5本足フラクタルの判定ルールを実装しています。高値(または安値)は、左右それぞれ2本のバーよりも高い高値(または低い安値)を形成している場合にのみ、有効なフラクタルとして認識されます。このフィルタにより、意味のない細かな価格変動は除外され、市場構造上で重要な転換点だけが抽出されます。この判定ルールを厳密に適用することで、市場ノイズに起因する誤シグナルを抑え、より信頼性の高い構造解析を実現しています。

    // Check if the bar at index i is a fractal high based on Bill Williams' five-bar rule
    bool IsFractalHigh(int i)
    {
       if(i<3 || i+2>=BarsTotal()) return false;
       double h = iHigh(_Symbol, _Period, i);
       return h > iHigh(_Symbol, _Period, i-1) &&
              h > iHigh(_Symbol, _Period, i+1) &&
              h > iHigh(_Symbol, _Period, i-2) &&
              h > iHigh(_Symbol, _Period, i+2);
    }
    
    // Check if the bar at index i is a fractal low
    bool IsFractalLow(int i)
    {
       if(i<3 || i+2>=BarsTotal()) return false;
       double l = iLow(_Symbol, _Period, i);
       return l < iLow(_Symbol, _Period, i-1) &&
              l < iLow(_Symbol, _Period, i+1) &&
              l < iLow(_Symbol, _Period, i-2) &&
              l < iLow(_Symbol, _Period, i+2);
    }

    これを基盤としてGetLast3AlternatingFractals()は直近の価格データを効率的に検索し、高値・安値が交互に並ぶ最新の3つのフラクタルを抽出します。複数の候補が存在する場合は、周囲のバーと比較して最も大きく乖離した、より顕著なフラクタルを優先的に採用することで、スイングの極値を動的に最適化します。この処理により、市場ノイズの影響を受けにくい堅牢な構造的基盤が形成されます。その結果、後続の解析で評価される保ち合いは、小さな価格変動ではなく、市場における意味のあるスイングに基づいて構築されるため、全体の判定精度が向上します。

    // Retrieve the most recent sequence of three opposing fractals for pattern detection
    bool GetLast3AlternatingFractals(
       int &p0, double &pr0, bool &h0,
       int &p1, double &pr1, bool &h1,
       int &p2, double &pr2, bool &h2)
    {
       int n=0, idx[3];
       double pr[3];
       bool hi[3];
       int maxBars = MathMin(InpMaxScanBars, BarsTotal() - 3);
       
       for(int i=3; i<maxBars; i++)
       {
          double price;
          bool isH;
          if(!GetFractalPoint(i, price, isH))
          {
             // Additional check for prominent fractals
             if(i >= 4 && i+3 < BarsTotal())
             {
                double hh = iHigh(_Symbol, _Period, i);
                double ll = iLow(_Symbol, _Period, i);
                if(hh >= iHigh(_Symbol, _Period, i-1) &&
                   hh >= iHigh(_Symbol, _Period, i+1) &&
                   hh >= iHigh(_Symbol, _Period, i-2) &&
                   hh >= iHigh(_Symbol, _Period, i+2))
                {
                   price = hh;
                   isH = true;
                }
                else if(ll <= iLow(_Symbol, _Period, i-1) &&
                        ll <= iLow(_Symbol, _Period, i+1) &&
                        ll <= iLow(_Symbol, _Period, i-2) &&
                        ll <= iLow(_Symbol, _Period, i+2))
                {
                   price = ll;
                   isH = false;
                }
                else continue;
             }
             else continue;
          }
          // Store the most recent opposing fractals
          if(n==0)
          {
             idx[0]=i; pr[0]=price; hi[0]=isH; n=1;
          }
          else if(hi[n-1]==isH)
          {
             // Update if current fractal is more extreme
             if(isH && price>pr[n-1])
             {
                idx[n-1]=i; pr[n-1]=price;
             }
             if(!isH && price<pr[n-1])
             {
                idx[n-1]=i; pr[n-1]=price;
             }
          }
          else if(n<3)
          {
             idx[n]=i; pr[n]=price; hi[n]=isH; n++;
          }
          if(n>=3) break;
       }
       if(n<3) return false;
    
       p0=idx[0]; pr0=pr[0]; h0=hi[0];
       p1=idx[1]; pr1=pr[1]; h1=hi[1];
       p2=idx[2]; pr2=pr[2]; h2=hi[2];
       return true;
    }

    保ち合い境界の定義

    主要なフラクタルが特定された後、CalcRangeHigh()およびCalcRangeLow()は、それらのフラクタル間に存在するバーを解析し、潜在的な保ち合いゾーンの真の上限および下限を決定します。これらの境界は、ブレイクアウトが発生し得る領域を定義するうえで極めて重要な役割を果たします。

    // Calculate the highest high between two bars
    double CalcRangeHigh(int leftBar, int rightBar)
    {
       int from = MathMin(leftBar, rightBar);
       int to   = MathMax(leftBar, rightBar);
       int bars = to - from + 1;
       int idx  = iHighest(_Symbol, _Period, MODE_HIGH, bars, from);
       return (idx >= 0) ? iHigh(_Symbol, _Period, idx) : iHigh(_Symbol, _Period, from);
    }
    
    // Calculate the lowest low between two bars
    double CalcRangeLow(int leftBar, int rightBar)
    {
       int from = MathMin(leftBar, rightBar);
       int to   = MathMax(leftBar, rightBar);
       int bars = to - from + 1;
       int idx  = iLowest(_Symbol, _Period, MODE_LOW, bars, from);
       return (idx >= 0) ? iLow(_Symbol, _Period, idx) : iLow(_Symbol, _Period, from);
    }

    しかし、単純に算出された境界はそのままでは不十分であり、検証が必要となります。RangeQualifies()関数は厳格な基準を適用し、ゾーンが最低限の継続時間(ハードおよびソフトの閾値の両方)を満たしていること、ATRに対して適切な比率の高さを持つこと、さらに最近の価格がレンジの上下限に実際に接触していることを検証条件としています。これらの条件を満たさないゾーン、すなわち浅すぎるもの、短命すぎるもの、あるいは価格による十分な関与が見られないものはすべて除外されます。このフィルタリング処理により、真にブレイクアウトの可能性が高い環境に焦点が絞られ、小さな市場ノイズや重要性の低い保ち合いによって発生する誤シグナルの発生確率が低減されます。

    // Check if the range qualifies based on duration, ATR, and proximity
    bool RangeQualifies(int p2, int p1, double hi, double lo, string &whyFail, bool &usedSoft)
    {
       whyFail="";
       usedSoft=false;
       int barsSpan = MathAbs(p2 - p1);
       
       if(barsSpan < InpRangeMinBarsSoft)
       {
          whyFail="rangeBars";
          return false;
       }
    
       double atr;
       if(!GetATR(atr))
       {
          whyFail="atr";
          return false;
       }
    
       double height = MathAbs(hi - lo);
       double atrCap = InpRangeATRMax;
       if(barsSpan < InpRangeMinBarsHard)
       {
          if(!InpEnableSoftRangeBars)
          {
             whyFail="rangeBars";
             return false;
          }
          usedSoft=true;
          atrCap = InpRangeATRMaxSoft;
       }
    
       if(height > atr * atrCap)
       {
          whyFail= usedSoft ? "rangeATR_soft" : "rangeATR";
          return false;
       }
    
       // Check proximity to recent high/low
       double zonePct = MathMax(0.08, MathMin(InpRangeSideZonePct, 0.70));
       double zone = height * zonePct;
       bool nearHigh=false, nearLow=false;
       int look = MathMin(InpRangeTestLookback, BarsTotal() - 5);
       for(int i=1; i<=look; i++)
       {
          double h = iHigh(_Symbol, _Period, i);
          double l = iLow(_Symbol, _Period, i);
          if(h >= hi - zone) nearHigh=true;
          if(l <= lo + zone) nearLow=true;
          if(nearHigh || nearLow) break;
       }
       if(!nearHigh && !nearLow)
       {
          whyFail="sideZones";
          return false;
       }
       return true;
    }

    モメンタム不均衡の検出:幾何学的検証

    初期フィルタを通過したゾーンは、幾何学的検証へと進みます。GeometryAsymmetryInsideRangeOK()関数は、ゾーン内部における直近の値動き(ムーブレッグ)を詳細に解析します。この処理では、距離優位性を示すLengthRatio、値動きの鋭さを示すSlopeRatio、および変動速度を示すTimeRatioといった比率を算出します。これらの比率は、直近の値動きが急激に加速しているかどうかを識別するために用いられ、これはモメンタム蓄積の重要なシグナルとなります。

    // Validate the shape and acceleration of the move within the zone
    bool GeometryAsymmetryInsideRangeOK(
       int p0,double pr0,int p1,double pr1,int p2,double pr2,
       double rangeHigh,double rangeLow,
       double &lenRatio,double &slopeRatio,double &timeRatio,int &votes,int &dir,
       string &whyFail)
    {
       whyFail="";
       votes=0;
    
       double lenPrev = MathAbs(pr1 - pr2);
       double lenLast = MathAbs(pr0 - pr1);
       dir = (pr0 > pr1 ? +1 : -1);
    
       double tol = (rangeHigh - rangeLow) * 0.10;
       if(pr0 > rangeHigh+tol || pr0 < rangeLow-tol ||
          pr1 > rangeHigh+tol || pr1 < rangeLow-tol)
       {
          if(pr0 > rangeHigh+tol && pr1 > rangeHigh+tol)
          {
             whyFail="notInsideRange";
             return false;
          }
          if(pr0 < rangeLow-tol && pr1 < rangeLow-tol)
          {
             whyFail="notInsideRange";
             return false;
          }
       }
    
       datetime t0 = iTime(_Symbol, _Period, p0);
       datetime t1 = iTime(_Symbol, _Period, p1);
       datetime t2 = iTime(_Symbol, _Period, p2);
    
       long dtPrevL = (long)t1 - (long)t2;
       long dtLastL = (long)t0 - (long)t1;
    
       double dtPrev = MathAbs((double)dtPrevL);
       double dtLast = MathAbs((double)dtLastL);
    
       if(dtPrev <= 0 || dtLast <= 0)
       {
          whyFail="dt";
          return false;
       }
    
       lenRatio = lenLast / MathMax(1e-10, lenPrev);
       slopeRatio = (lenLast / dtLast) / MathMax(1e-10, (lenPrev / dtPrev));
       timeRatio = dtLast / MathMax(1e-10, dtPrev);
    
       // Voting on pattern strength
       if(lenRatio >= InpLenRatioMin) votes++;
       if(slopeRatio >= InpSlopeRatioMin) votes++;
       if(timeRatio <= InpTimeCompressionMax) votes++;
    
       if(votes < InpMinGeometryVotes)
       {
          if(lenRatio >= InpLenRatioMin * 1.2 || slopeRatio >= InpSlopeRatioMin * 1.2)
             votes = InpMinGeometryVotes;
          else
          {
             whyFail="votes";
             return false;
          }
       }
       return true;
    }

    複数の基準が同時に満たされることを要求する投票システムとして、InpMinGeometryVotesが導入されています。この仕組みにより、パターンの確定には複数の条件が合致する必要があります。さらに、補助的な安全装置として、エンドポイントの価格位置の近接性や、各スイングレッグ間の比例スケーリングが考慮されます。これにより、実際のモメンタム蓄積を反映していない過大な値動きや、構造から乖離した動きを除外することが可能となります。このような厳密な検証プロセスは、単なる対称的な値動きや方向感の乏しい推移と、意味のあるモメンタムの違いを明確に区別します。その結果、ダマシとなるブレイクアウトの発生を抑制し、生成されるシグナルの信頼性が大幅に向上します。

    ブレイクアウトとエントリーシグナルの監視

    // Check recent candles for breakouts and trigger signals
    void CheckAllBreakouts()
    {
       double buffer = InpBreakBufferPts * _Point;
       for(int i=0; i<ArraySize(g_lockedPatterns); i++)
       {
          if(!g_lockedPatterns[i].active) continue;
    
          for(int bar=1; bar<=InpBreakLookbackBars; bar++)
          {
             datetime barTime = iTime(_Symbol, _Period, bar);
             if(barTime <= g_lockedPatterns[i].startTime) continue;
    
             double close = iClose(_Symbol, _Period, bar);
             double high = iHigh(_Symbol, _Period, bar);
             double low = iLow(_Symbol, _Period, bar);
    
             if(g_lockedPatterns[i].dir > 0)
             {
                if(close > g_lockedPatterns[i].high + buffer || high > g_lockedPatterns[i].high + buffer)
                {
                   double breakoutPrice = MathMax(g_lockedPatterns[i].high + buffer, MathMin(close, high));
                   string uid = "BUY_" + g_lockedPatterns[i].key + "_" + Ts(barTime);
                   CreatePermanentArrow(uid, barTime, breakoutPrice, true);
                   if(InpShowDebug) Print("Buy breakout at ", DoubleToString(breakoutPrice,_Digits));
                   RemoveLockedPattern(i);
                   g_lastSignalBar = barTime;
                   break;
                }
             }
             else if(g_lockedPatterns[i].dir < 0)
             {
                if(close < g_lockedPatterns[i].low - buffer || low < g_lockedPatterns[i].low - buffer)
                {
                   double breakoutPrice = MathMin(g_lockedPatterns[i].low - buffer, MathMax(close, low));
                   string uid = "SELL_" + g_lockedPatterns[i].key + "_" + Ts(barTime);
                   CreatePermanentArrow(uid, barTime, breakoutPrice, false);
                   if(InpShowDebug) Print("Sell breakout at ", DoubleToString(breakoutPrice,_Digits));
                   RemoveLockedPattern(i);
                   g_lastSignalBar = barTime;
                   break;
                }
             }
          }
       }
    }

    パターンが強い非対称性を示す場合、システムはブレイクアウト監視フェーズへ移行します。CheckAllBreakouts()関数は、確定済みレンジ境界の明確な突破が生じたかを確認するために、直近のローソク足を監視します。この際、InpBreakBufferPtsによるエントリーバッファが組み込まれており、小さなスパイクや一時的なヒゲへの反応を防止します。ブレイクアウトが確定した場合、CreatePermanentArrow()がチャート上に恒久的な視覚マーカー(矢印、ゾーン、ラベルなど)を配置し、ブレイクアウトの成立を明確に視覚的に示します。

    // Create an arrow marker on the chart for breakouts
    void CreatePermanentArrow(const string uid, const datetime t, const double price, const bool bullish)
    {
       if(!InpShowArrows) return;
       string name = PFX_AR + "PERM_" + uid + "_" + Ts(t);
       if(ObjectFind(0, name) >=0) return; // Already exists
    
       int anchor = bullish ? ANCHOR_BOTTOM : ANCHOR_TOP;
       double arrowPrice = bullish ? (price - InpArrowOffsetPoints * _Point) : (price + InpArrowOffsetPoints * _Point);
       if(!ObjectCreate(0, name, OBJ_ARROW, 0, t, arrowPrice))
       {
          if(InpShowDebug) Print("Failed to create arrow: ", GetLastError());
          return;
       }
       ObjectSetInteger(0, name, OBJPROP_ARROWCODE, bullish ? InpArrowUpCode : InpArrowDownCode);
       ObjectSetInteger(0, name, OBJPROP_COLOR, bullish ? InpArrowColorUp : InpArrowColorDown);
       ObjectSetInteger(0, name, OBJPROP_WIDTH, InpArrowSize);
       ObjectSetInteger(0, name, OBJPROP_ANCHOR, anchor);
       // Add label if enabled
       if(InpShowArrowLabels)
       {
          string lbl = name + "_LBL";
          double lblPrice = arrowPrice + (InpArrowLabelOffset * _Point * (bullish ? 1 : -1));
          if(ObjectCreate(0, lbl, OBJ_TEXT, 0, t, lblPrice))
          {
             ObjectSetString(0, lbl, OBJPROP_TEXT, bullish ? "BUY" : "SELL");
             ObjectSetInteger(0, lbl, OBJPROP_COLOR, bullish ? InpArrowColorUp : InpArrowColorDown);
             ObjectSetInteger(0, lbl, OBJPROP_FONTSIZE, InpArrowLabelFontSize);
             ObjectSetString(0, lbl, OBJPROP_FONT, InpArrowLabelFont);
             ObjectSetInteger(0, lbl, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
          }
       }
    }

    同時に、PlayAlert()はマルチチャネル通知を管理し、サウンド、ポップアップ、プッシュ通知、メールなどを通じて、デバイスや環境に依存せず、高確度のブレイクアウトをトレーダーへ迅速に通知します。各セットアップは、同一パターンからの重複シグナルを防ぐため、短期間(InpMaxLockBars)ロックされます。また、パターンは有効期限を過ぎた場合、またはブレイクアウトが発生した時点で自動的に終了(破棄)されます。この一連の処理により、チャート上の情報は常に整理された状態に保たれ、現在進行中の高確度シグナルのみが強調される設計となっています。

    パターンライフサイクル管理とノイズ制御

    // Check if enough bars have passed since last signal to avoid overtrading
    bool CooldownOK()
    {
       if(g_lastSignalBar == 0)
          return true;
       int barsSince = iBarShift(_Symbol, _Period, g_lastSignalBar, true);
       return (barsSince >= InpMinBarsBetweenSignals);
    }

    さらなる誤検出および過剰取引の抑制のため、本システムではノイズ抑制メカニズムが導入されています。CooldownOK()関数は、シグナル間に最低限のバー数が経過していることを保証し、市場の急激な変動による影響を低減します。また、UpdateLockedPatterns()ルーチンはアクティブなパターンのタイマーを進行させ、最大保持期間に到達したパターンや、すでに条件を満たして完了したパターンを自動的に失効させます。 

    // Update active pattern timers and expire patterns that have reached max lock bars
    void UpdateLockedPatterns()
    {
       datetime currentTime = iTime(_Symbol, _Period, 0);
       for(int i=0; i<ArraySize(g_lockedPatterns); i++)
       {
          if(!g_lockedPatterns[i].active)
             continue;
          
          g_lockedPatterns[i].lockBars++;
          
          // optionally, update visual rectangle for active patterns
          if(g_lockedPatterns[i].rectName != "" && InpShowLockedRangeRects)
             UpdateLockedRangeRectangle(g_lockedPatterns[i].rectName, currentTime,
                                        g_lockedPatterns[i].high, g_lockedPatterns[i].low);
          
          // Expire patterns that have reached max lock bars
          if(g_lockedPatterns[i].lockBars > InpMaxLockBars)
          {
             if(InpShowDebug)
                Print("Pattern expired after ", g_lockedPatterns[i].lockBars, " bars");
             RemoveLockedPattern(i);
          }
       }
    }

    これらの制御機構は、規律ある取引環境を形成し、誤警報の発生頻度を低減するとともに、市場構造が真に有望な機会を示している場合にのみシグナルが生成されるよう設計されています。この規律的なアプローチにより、トレーダーは小さな市場変動やレンジ内のノイズに反応することを避け、より高い確度を持つセットアップに集中できるようになります。

    コア実行:OnTick()

    すべての分析、意思決定、およびシグナル生成処理は、OnTick()関数内で統合的に実行されます。この関数は新しいバーの確定時のみにトリガーされるため、リソース使用を最適化し、システムの安定性を確保します。OnTick()内では、既存パターンの状態更新、新規フラクタルの探索、潜在的なゾーンの評価、幾何学的検証の実行、ブレイクアウト監視、そしてビジュアルオーバーレイの更新といった一連の処理が順次実行されます。

    // Main routine called on each new tick; runs at bar close
    void OnTick()
    {
       // Only proceed at bar close
       datetime closed = iTime(_Symbol, _Period, 1);
       if(closed == 0 || closed == g_lastClosedBar)
          return;
       g_lastClosedBar = closed;
    
       // Update pattern timers and expire old patterns
       UpdateLockedPatterns();
    
       // Check for breakout signals
       CheckAllBreakouts();
    
       // Limit number of active patterns
       if(GetActivePatternCount() >= g_maxSimultaneousLocks)
       {
          if(InpShowDebug)
             Print("Max active patterns reached");
          return;
       }
    
       // Enforce cooldown to prevent overtrading
       if(!CooldownOK())
       {
          if(InpShowDebug)
          {
             int sinceBars = iBarShift(_Symbol, _Period, g_lastSignalBar, true);
             Print("Cooldown active: ", sinceBars, "/", InpMinBarsBetweenSignals, " bars since last signal");
          }
          return;
       }
    
       // Proceed with fractal detection and pattern analysis
       // (Insert fractal detection, range evaluation, geometric validation, etc.)
       // ...
    }

    この構造化されたパイプラインにより、ボラティリティが高い市場や急速に変動する相場環境においても、安定したリアルタイム処理が保証されます。ビジュアルオーバーレイは、フラクタルを示すカラードット、スイングレッグを可視化するトレンドライン、保ち合いを強調するゾーン表示、ブレイクアウトを示す矢印などによって構成されており、これらは現在の市場データに基づいて動的に生成・更新されます。この視覚的フィードバックにより、トレーダーは市場構造および潜在的な取引シグナルを即座かつ直感的に把握することが可能になります。

    リソース管理とシステム安定性

    長期的なシステム安定性を確保するうえで、適切なリソース管理は極めて重要です。OnInit()内ではATRインジケーターハンドルが取得され、再利用のためにキャッシュされます。またOnDeinit()では、メモリリークを防止するために当該ハンドルが解放されます。さらに、トレンドライン、ゾーン、矢印などのグラフィカルオブジェクトは、統一されたプレフィックスと一意の識別子を用いて体系的に命名されており、更新および削除処理を容易にしています。

    // On initialization, acquire indicator handles and prepare environment
    int OnInit()
    {
       g_atrHandle = iATR(_Symbol, _Period, InpATRPeriod);
       if(g_atrHandle == INVALID_HANDLE)
       {
          Print("Failed to create ATR handle. Error=", GetLastError());
          return INIT_FAILED;
       }
    
       // Resize pattern lock array for simultaneous patterns
       ArrayResize(g_lockedPatterns, g_maxSimultaneousLocks);
       for(int i=0; i< g_maxSimultaneousLocks; i++)
          g_lockedPatterns[i].active = false;
    
       // Clean up previous graphical objects
       DeleteByPrefix(PFX_SW);
       DeleteByPrefix(PFX_DOT);
       DeleteByPrefix(PFX_LN);
       DeleteByPrefix(PFX_TX);
       DeleteByPrefix(PFX_TRI);
       ObjectDelete(0, OBJ_CAND);
    
       // Initialize variables
       g_lastClosedBar = 0;
       g_lastSignalBar = 0;
       g_arrowCounter = 0;
    
       // Print startup info
       Print("System initialized successfully");
       return INIT_SUCCEEDED;
    }

    複数のパターンをサポートする配列は動的にリサイズされており、これによりシステムは重複するセットアップや複数銘柄を扱う複雑なシナリオにおいても、パフォーマンス低下を起こすことなく処理を継続できます。適切なクリーンアップ処理と規律あるメモリ管理により、システムは長時間の稼働においても安定性を維持し続けます。また、異なるシンボルや変化する市場環境においても、一貫した動作が保証されます。

    // On deinitialization, release indicator handles and clean up objects
    void OnDeinit(const int reason)
    {
       if(g_atrHandle != INVALID_HANDLE)
          IndicatorRelease(g_atrHandle);
    
       // Remove graphical objects
       DeleteByPrefix(PFX_SW);
       DeleteByPrefix(PFX_DOT);
       DeleteByPrefix(PFX_LN);
       DeleteByPrefix(PFX_TX);
       DeleteByPrefix(PFX_TRI);
       ObjectDelete(0, OBJ_CAND);
    
       // Output stats
       Print("Deinitialization complete");
       Print("Total patterns processed: ", g_totalPatterns);
       Print("Breakouts: ", g_totalBreakouts, " (Buy: ", g_buyBreakouts, ", Sell: ", g_sellBreakouts, ")");
       Print("Patterns expired: ", g_patternsExpired);
    }

    最後に

    本実装は、MQL5におけるプロフェッショナルグレードのエンジニアリングを体現するものです。フラクタル検出、厳格なゾーンフィルタリング、そして詳細な幾何学的検証を組み合わせることで、微細な不均衡を高確度かつ実行可能なシグナルへと変換しています。ビジュアルオーバーレイ、アラート通知、パターンライフサイクル管理は統合的に設計されており、機能性と視認性のバランスが取れた包括的な取引環境を構築しています。

    本システムのモジュラーアーキテクチャは高い拡張性を備えており、トレーダーは必要に応じて閾値の調整、フィルタの追加、あるいは視覚表現の改善をおこなうことができます。この柔軟性により、変化し続ける市場環境や個々の取引スタイルに適応し続けることが可能となります。したがって、GA Breakout戦略は、複雑でダイナミックな市場を自信と精度をもって分析・運用するために設計された、スケーラブルかつ堅牢なフレームワークであると言えます。



    テストと結果

    すべてのツールが安定して一貫した成果を発揮するためには、さまざまな市場環境および時間足における十分なテストが不可欠です。このセクションでは、実施したテスト結果を提示し、最も効果的に機能した条件について説明します。複数の通貨ペアにわたる広範な検証の結果、明確なパフォーマンス傾向が確認されました。本ツールは主要通貨ペアにおいて特に効果的であり、その中でも高い流動性を持つEURUSDのH1時間足で最も安定した優れた結果が得られました。

    以下の図は、EURUSD (H1)に対するストラテジーテスターの実行結果を示しており、本ツールが最も良好かつ実用的なパフォーマンスを発揮した条件を視覚的に示しています。

     


    この結果は、本ツールがこれらの特定条件でのみ機能することを意味するものではありません。観測されたパフォーマンスは、テスト中に使用された入力パラメータの閾値およびシグナルフィルターの設定に大きく依存しています。異なる銘柄はそれぞれ異なる特性を持ち、一部は高ボラティリティであり、また一部は低ボラティリティであるなど、流動性の厚みやボラティリティ特性にも違いがあります。そのため、テストにおいてはパラメータのキャリブレーションが重要な役割を果たしました。入力値は一律に適用されるのではなく、各通貨ペアおよび時間足の挙動に応じて調整されました。市場のボラティリティ、流動性、構造的特性に適合させることで、本ツールはEURUSDのH1に限らず、複数の銘柄において一貫した有効性を示しました。

    別のテストとしてAUDUSDに対しても検証がおこなわれ、同様に安定した良好な結果が得られました。このテストはストラテジーテスターのビジュアルモードで実施され、EAが保ち合いレンジをどのように識別し、内部の方向性バイアスを形成し、ブレイクアウトシグナルをリアルタイムで生成するかを詳細に観察することができました。


    可視化された結果が示す通り、EAはGAレンジを正確に検出し、幾何学的非対称性を確認した上で、価格がロックされたレンジ境界を明確に突破した時点でのみ有効なシグナルを発行しています。この結果は、本ツールが単一の銘柄に限定されることなく、入力パラメータとフィルタが市場のボラティリティおよび構造に適切に調整されている限り、安定して機能することを裏付けています。


    結論

    本記事では、GA Breakoutフレームワークを、意味のあるレンジからトレンドへの移行を識別するための構造化されたルールベース手法として検討しました。本手法は、フラクタルに基づく構造検出、保ち合いの適格性評価、幾何学的非対称性の分析、そして厳格なブレイクアウト確認を組み合わせることで、予測ではなく市場コンテキストと確認プロセスに重点を置いています。

    複数の通貨ペアおよび時間足にわたるテストの結果、パフォーマンスは各市場のボラティリティ、流動性、構造的特性にどれだけ入力パラメータが適合しているかに大きく依存することが明らかになりました。EURUSD (H1)やAUDUSDといった主要通貨ペアで確認された結果は、本フレームワークがノイズや短期的な価格変動によって生じるシグナルを回避しながら、真の価格拡張を捉える能力を持つことを示しています。GAアプローチの強みは、その規律性にあります。シグナルは、有効な保ち合いレンジの確立、幾何学的非対称性による客観的な内部バイアスの特定、そして価格がロックされた境界を明確に終値ベースで突破した場合にのみ生成されます。この段階的フィルタリングにより、ダマシのブレイクアウトが抑制され、シグナルが実際の方向性の意図を反映するようになります。

    GAは、完全自動売買システムではなく、意思決定支援およびシグナル生成のためのフレームワークとして設計されています。そのモジュラー構造により、コアロジックを変更することなく、閾値の調整、フィルタの追加、パラメータの最適化をおこなうことが可能です。この柔軟性により、市場環境の変化に対応しながらも、透明性と再現性を維持した実用的なブレイクアウト分析ツールとなっています。

    GA戦略は、他のあらゆる分析ツールと同様に、十分な検証なしに実際の取引へ適用すべきではありません。ユーザーはデモ口座での十分なフォワードテストを実施し、対象となる銘柄および時間足に合わせてパラメータを調整し、常に適切なリスク管理をおこなうことが推奨されます。過去のパフォーマンス(バックテスト結果を含む)は、将来の成果を保証するものではありません。

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

    添付されたファイル |
    MQL5取引ツールのアクセシビリティ課題を克服する(第1回):MQL5インジケータに状況対応型音声アラートを追加する方法 MQL5取引ツールのアクセシビリティ課題を克服する(第1回):MQL5インジケータに状況対応型音声アラートを追加する方法
    本記事では、標準的なターミナルアラートの枠を超え、MQL5のリソース管理機能を活用して状況に応じた音声フィードバックを実現する、アクセシビリティを重視した拡張手法を紹介します。一般的な通知音ではなく、「何が起こったのか」と「なぜ起こったのか」を音声で伝えることで、トレーダーは視覚情報だけに頼ることなく市場イベントを把握できます。このアプローチは、特に視覚障害のあるトレーダーにとって有用ですが、ハンズフリーで操作したい多忙なユーザーやマルチタスク環境で取引を行うユーザーにもメリットがあります。
    MQL5標準ライブラリエクスプローラー(第8回):CFileTxtによるハイブリッド取引ジャーナルの記録 MQL5標準ライブラリエクスプローラー(第8回):CFileTxtによるハイブリッド取引ジャーナルの記録
    本記事では、MQL5標準ライブラリのファイル操作クラスを活用し、Excel対応のCSVファイルを自動生成する堅牢なレポートモジュールを構築します。その過程において、手動操作とアルゴリズム起点の取引アクションを明確に区別し、信頼性が高く監査可能な取引レポートの基盤を整えます。
    MQL5入門(第39回):MQL5におけるファイル処理入門(I) MQL5入門(第39回):MQL5におけるファイル処理入門(I)
    本記事では、MQL5におけるファイル処理を、実践的なプロジェクトベースのワークフローを用いて紹介します。FileSelectDialogを使用してCSVファイルを選択または作成し、FileOpenでそれを開き、口座名、残高、ログイン、期間、最終更新日時などの構造化されたヘッダーを書き込みます。その結果として、再利用可能な取引ジャーナルと、MetaTrader 5でファイルを安全に扱うための基盤が得られます。
    Python-MetaTrader 5ストラテジーテスター(第5回):複数銘柄および複数時間足ストラテジーテスター Python-MetaTrader 5ストラテジーテスター(第5回):複数銘柄および複数時間足ストラテジーテスター
    銘柄および時間足を横断してスケーラブルに動作するMetaTrader 5互換のバックテストワークフローを紹介します。本システムでは、HistoryManagerを用いてデータ取得を並列化し、すべての時間足からバーおよびティックデータを同期し、スレッドごとに銘柄分離されたOnTickハンドラを実行します。モデリングモードが速度と精度に与える影響、ターミナルデータに依存すべき場面、イベント駆動型更新によるI/O削減手法、そして複数通貨自動売買ロボットの構築方法について学びます。