English Deutsch
preview
プライスアクション分析ツールキットの開発(第16回):クォーターズ理論の紹介(II) - Intrusion Detector EA

プライスアクション分析ツールキットの開発(第16回):クォーターズ理論の紹介(II) - Intrusion Detector EA

MetaTrader 5トレーディングシステム |
80 0
Christian Benjamin
Christian Benjamin

はじめに

前回の記事では、Quarters Drawerというシンプルなスクリプトを紹介しました。これはチャート上にクォーター(四分割)レベルを視覚的に描画し、市場分析をより直感的におこなうためのツールです。このコンセプトは、イリアン・ヨトフ(Ilian Yotov)によって提唱されたクォーターズ理論(Quarters Theory)に基づいています。クォーターを正確に描画することで、プライスアクションの分析が驚くほどシンプルになります。しかし、価格がこれらのレベルに到達した際にその動きを手動で監視するには、多くの時間と注意力が必要となります。

この課題を解決するために、今回は監視用のエキスパートアドバイザー(EA)であるIntrusion Detectorを紹介します。このEAは、チャート上で価格がどのクォーターレベル(スモール、ラージ、メジャー、オーバーシュート、アンダーシュート)に到達したかを自動的に検出し、リアルタイムでコメントやインサイトを提供します。これにより、イリアンのクォーターズ理論に基づいた市場の反応を、すべてのトレーダーが事前に予測できるようになります。本記事では、まずQuarters Drawerツールを復習し、その後に戦略コンセプトとMQL5での実装、テスト結果の分析、そして最後に主なポイントのまとめへと進んでいきます。以下の目次に従って、構成された内容をご覧ください。

内容



前の記事の振り返り

Quarters Drawerスクリプトについては前回の記事で詳しく説明したので、このセクションについてはあまり詳しく説明しません。まだご覧になっていない方は、ぜひリンク先の記事を読んで、基礎となる概念をしっかり理解しておくことをおすすめします。前回の記事では主に、クォーターレベルの自動描画によって市場分析をより構造的かつ効率的におこなえるようにすることに焦点を当てていました。

すでに触れたように、クォーターズ理論はイリアン・ヨトフによって発見され、その後取引コミュニティに紹介されました。前回の記事であまり強調されていなかった重要なポイントのひとつとして、この理論が特に通貨ペア(FX)に非常に有効であるという点が挙げられます。FXトレーダーにとって必須のツールと言えるでしょう。このクォーターレベルがどのように機能するのかを視覚的に理解するために、以下の図を参考にしてください。理論の構造が視覚的に表現されています。

クォーターレベル

図1:クォーターレベル

Quarters Drawerツールは非常に優れた成果を上げました。ラージクォーターだけでなく、オーバーシュート、アンダーシュート、そしてスモールクォーターも正確に描画することに成功しました。さらに重要なのは、価格がこれらのレベルに一貫して反応・相互作用していたという観察結果です。これは、ヨトフの理論の正当性と有効性を裏付けるものであり、単なる理論的な演習ではなく、実際の市場環境においてクォーター・レベルの力を証明するものでした。それでは、以下の図をもとに、これまでの重要な発見とインサイトを振り返ってみましょう。

結果

図2:結果


戦略コンセプトとMQL5での実装

コアロジック

Intrusion Detector EAは、クォーターズ理論を用いて、重要な心理的価格レベルをマッピングすることを目的としたツールです。このEAでは市場を1000 pipsごとのレンジに区切り、その中でメジャーホールナンバーを基軸として使用します。その内部に、250 pips幅のラージクォーターゾーンを描画し、必要に応じてスモールクォーターを追加すれば、より詳細な分析が可能になります。また、オーバーシュートおよびアンダーシュートゾーンも描画され、これにより一時的な価格の延長や、トレーダーを騙すような偽の動きを捉えることができます。EAはすべてのティックごとに現在価格をスキャンし、設定された許容範囲内でこれらの重要レベルに接近しているかをチェックし、条件を満たした場合にアラートを発します。

このツールの主な目的は、価格が明らかな反転ポイントブレイク(または偽のブレイク)になる前にそれを検出することです。たとえば、価格がメジャーホールナンバー付近で推移していれば、サポートまたはレジスタンスの重要ゾーンとしてEAが警告を出します。ラージクォーターの付近であれば、250 pipsの値動きが起きる可能性があると判断します。さらにオーバーシュート・アンダーシュートゾーンでは、急な反転や罠の可能性を示唆します。EAはアラートを無駄に連発しないように侵入履歴を追跡し、状況をリアルタイムで表示するコメントパネルも備えており、常に最新の相場状況を把握できるようになっています。全体として、クォーターズ理論を実用的かつ行動可能な形に落とし込むことで、群衆の一歩先を行くためのサポートを提供します。

実装

このEAの実装は、まずヘッダーから始まります。ここには、EAの名前(Intrusion Detector)、著作権情報、そして開発者プロファイルへのリンクなどのメタデータが含まれています。#propertyディレクティブを用いて、これらの情報を指定し、厳格なコンパイルルールを適用することで、最新のMQL5規格に準拠したコードの構築を保証します。

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

次に、コードを編集することなくEAの動作をカスタマイズできるように、入力パラメータのセットを定義します。パラメータには、MajorStepのような数値が含まれており、これはメジャーレベル間の間隔を決定するもので、事実上1000 pipsの範囲を定義します。また、AlertToleranceでは、価格が特定のレベルに「接触」したとみなすための許容距離を設定できます。ブール型の入力では、ラージクォーターラインおよびスモールクォーターライン、さらにメジャーレベル周辺のオーバーシュート領域など、追加のラインを描画するかどうかを制御できます。

さらに、色設定は16進数の値(または定義済みのカラー名)を用いて定義されており、メジャー、ラージクォーター、スモールクォーター、オーバーシュートといった各種ラインがチャート上で意図された視覚スタイルで表示されるようにします。続いて、ラインのスタイルや太さの設定が行われ、これによって描画されるラインの外観をさらに細かくカスタマイズできます。

設定項目

input double MajorStep          = 0.1000;   // Difference between Major Whole Numbers (defines the 1000-PIP Range)
input bool   DrawLargeQuarters  = true;     // Draw intermediate Large Quarter lines.
input bool   DrawSmallQuarters  = false;    // Draw Small Quarter lines.
input bool   DrawOvershootAreas = true;     // Mark overshoot/undershoot areas for Large Quarter lines.
input double AlertTolerance     = 0.0025;   // Tolerance for detecting a "touch"

  • MajorStep:メジャーレベル間の間隔を定義します(例:1000 pipsの範囲)
  • DrawLargeQuartersDrawSmallQuarters:レンジ内に追加のライン(ラージクォーターおよびスモールクォーター)を描画するかどうかを制御するブール値です。
  • DrawOvershootAreas:ラージクォーターレベル付近に「オーバーシュート」および「アンダーシュート」ラインを追加描画するかどうかを決定します。
  • AlertTolerance:価格が特定のレベルに「接触した」とみなすために、どれだけ近づく必要があるかを指定します(例:0.0025以内)。

色の設定

input color  MajorColor         = 0x2F4F4F; // Dark Slate Gray for Major lines.
input color  LargeQuarterColor  = 0x8B0000; // Dark Red for Large Quarter lines.
input color  SmallQuarterColor  = 0x00008B; // Dark Blue for Small Quarter lines.
input color  OvershootColor     = clrRed;   // Red for overshoot/undershoot lines.

各ラインの種類ごとに色が定義されており、チャート上に描画された際に説明どおりの視覚的なスタイルになるようになっています。

ラインのスタイルおよび太さの設定

input ENUM_LINE_STYLE MajorLineStyle       = STYLE_SOLID;
input int    MajorLineWidth                 = 4;
input ENUM_LINE_STYLE LargeQuarterLineStyle  = STYLE_DOT;
input int    LargeQuarterLineWidth          = 3;
input ENUM_LINE_STYLE OvershootLineStyle     = STYLE_DASH;
input int    OvershootLineWidth             = 1;
input ENUM_LINE_STYLE SmallQuarterLineStyle  = STYLE_SOLID;
input int    SmallQuarterLineWidth          = 1;

各ラインタイプ(メジャー、ラージクォーター、オーバーシュート、スモールクォーター)には、それぞれに対応するスタイル(実線、点線、破線)と太さの設定があります。

カスタマイズ可能なコメントメッセージ

これらのメッセージは、価格が特定のレベルに「接触」した際のそのレベルの重要性を説明するもので、後にコメントテーブルの作成に利用されます。

  • MajorSupportReason:これは市場において重要なサポートレベルが存在することを示します。価格がこのレベルを下回った場合、取引レンジの変化が起こり、さらなる価格下落につながる可能性があることを意味します。
  • MajorResistanceReason:これは重要なレジスタンスレベルを示します。価格がこのレジスタンスを上抜けた場合、新たな取引レンジの始まりを示唆し、上昇トレンドが発生する可能性があります。
  • LargeQuarterReason:このメッセージは、このレベルでの価格の決定的なブレイクが、最大で250 pipsもの大きな値動きにつながる可能性を指摘しています。トレーダーはこのレベルに注目し、トレードの機会を見逃さないようにすべきです。
  • OvershootReason:価格がブレイクアウトレベルを試していることを示しますが、勢いを維持できなければ価格が反転する可能性が高いことを意味します。重要なレベルを強い買い支持なしに一時的に超えた場合は、注意が必要です。
  • UndershootReason:市場に十分な強気の買いが入っていないことを示し、弱気トレンドへの反転の可能性があることを示唆しています。トレーダーはショートポジションの機会として注視すべき信号です。
  • SmallQuarterReason:市場内での小さな価格変動を示しています。価格が小幅に推移しており、大きな取引機会やトレンドの変化が見られない可能性があります。

input string MajorSupportReason    = "Key support level. Break below signals range shift.";
input string MajorResistanceReason = "Pivotal resistance. Breakout above may start new range.";
input string LargeQuarterReason    = "Decisive break could trigger next 250-PIP move.";
input string OvershootReason       = "Test of breakout; reversal likely if momentum fails.";
input string UndershootReason      = "Insufficient bullish force; possible bearish reversal.";
input string SmallQuarterReason    = "Minor fluctuation.";

グローバルブール変数intrusionAlertedは、価格があるレベル付近で停滞している間にアラートが繰り返し発生するのを防ぎ、侵入イベントごとにアラートが一度だけ発生するようにするために使用されます。

// Global flag to avoid repeated alerts while price lingers at a level
bool intrusionAlerted = false;

DrawHorizontalLineは、EAの視覚的出力の中核を担っています。この関数は、ラインの名前、価格、色、太さ、スタイルといったパラメータを受け取ります。まず、その名前のラインが既に存在するかをチェックし、存在すれば既存のラインを削除します。その後、指定された価格に新しい水平ラインを作成し、プロパティを設定してラインがチャートの右端まで延びるようにします。このモジュール化された設計により、新たなレベルを描画する際に関数を簡単に再利用できるようになっています。

void DrawHorizontalLine(string name, double price, color lineColor, int width, ENUM_LINE_STYLE style)
{
   if(ObjectFind(0, name) != -1)
      ObjectDelete(0, name);

   if(!ObjectCreate(0, name, OBJ_HLINE, 0, 0, price))
   {
      Print("Failed to create line: ", name);
      return;
   }
   ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
}

DrawQuarters関数は、DrawHorizontalLineを活用して、現在の価格を基に計算された1000 pips範囲の主要な境界線およびその範囲内の追加ラインを描画します。設定が有効になっていれば、EAは範囲を4つの区間に分割して「ラージクォーター」ラインを描きます。これらの各ラインについて、オーバーシュート領域が有効であれば、主要レベルのやや上と下にラインを描画し、潜在的なオーバーシュートやアンダーシュートを示します。さらにオプションがオンの場合、EAは範囲をさらに細かく分割し、スモールクォーターラインも描画して、価格構造に関するより詳細な視覚的手がかりを提供します。

void DrawQuarters(double currentPrice)
{
   // Calculate the boundaries of the current 1000-PIP Range
   double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
   double upperMajor = lowerMajor + MajorStep;

   // Draw Major Whole Number lines (defining the 1000-PIP Range)
   DrawHorizontalLine("MajorLower", lowerMajor, MajorColor, MajorLineWidth, MajorLineStyle);
   DrawHorizontalLine("MajorUpper", upperMajor, MajorColor, MajorLineWidth, MajorLineStyle);

   // Draw Large Quarter lines and their overshoot/undershoot areas if enabled
   if(DrawLargeQuarters)
   {
      double LQIncrement = MajorStep / 4.0;
      for(int i = 1; i < 4; i++)
      {
         double level = lowerMajor + i * LQIncrement;
         string objName = "LargeQuarter_" + IntegerToString(i);
         DrawHorizontalLine(objName, level, LargeQuarterColor, LargeQuarterLineWidth, LargeQuarterLineStyle);

         if(DrawOvershootAreas)
         {
            double offset = MajorStep / 40.0; // approximately 25 pips if MajorStep=0.1000
            DrawHorizontalLine("Overshoot_" + IntegerToString(i) + "_up", level + offset, OvershootColor, OvershootLineWidth, OvershootLineStyle);
            DrawHorizontalLine("Undershoot_" + IntegerToString(i) + "_down", level - offset, OvershootColor, OvershootLineWidth, OvershootLineStyle);
         }
      }
   }

   // Draw Small Quarter lines if enabled (optional, finer divisions)
   if(DrawSmallQuarters)
   {
      double segStep = MajorStep / 10.0;
      double smallQuarter = segStep / 4.0;
      for(int seg = 0; seg < 10; seg++)
      {
         double segStart = lowerMajor + seg * segStep;
         for(int j = 1; j < 4; j++)
         {
            double level = segStart + j * smallQuarter;
            string objName = "SmallQuarter_" + IntegerToString(seg) + "_" + IntegerToString(j);
            DrawHorizontalLine(objName, level, SmallQuarterColor, SmallQuarterLineWidth, SmallQuarterLineStyle);
         }
      }
   }
}

もうひとつ重要な関数がCreateOrUpdateLabelで、これはチャート上にテキストを表示する役割を担います。この関数は、指定したラベルが既に存在するかどうかを確認し、存在しなければ新規作成します。その後、ラベルのテキスト、色、フォントサイズなどのプロパティを更新します。テーブル状のデータがきれいに揃うように、等幅フォント(Courier New)を使用している点も特徴です。この関数は、市場状況を説明するコメントを更新する際に特に重要となります。

void CreateOrUpdateLabel(string name, string text, int corner, int xdist, int ydist, color txtColor, int fontSize)
{
   if(ObjectFind(0, name) == -1)
   {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xdist);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, ydist);
      // Set a monospaced font for tabular display (Courier New)
      ObjectSetString(0, name, OBJPROP_FONT, "Courier New");
   }
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, txtColor);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
}

EAが初期化されるとき(OnInit関数内)、チャートの左上隅に「Intrusion Detector Initialized」というメッセージを表示するラベルを作成します。逆に、EAが削除されるとき(OnDeinit関数内)には、このラベルを削除してチャートをすっきりと保ちます。

int OnInit()
{
   // Create a persistent commentary label in the top-left corner
   CreateOrUpdateLabel("IntrusionCommentary", "Intrusion Detector Initialized", CORNER_LEFT_UPPER, 10, 10, clrWhite, 14);
   return(INIT_SUCCEEDED);
}

EAの心臓部は、OnTick関数にあります。この関数は市場の新しいティックごとに実行されます。EAが新しいティックを受け取ると、OnTick関数が呼び出されます。関数内の最初のステップでは、intrusionDetectedというフラグをfalseに初期化し、SymbolInfoDouble(_Symbol, SYMBOL_BID)を使って現在の市場のBid価格を取得します。もし取得した価格が0(無効または利用不可の値を示す)であれば、関数は即座に終了します。

void OnTick()
{
   bool intrusionDetected = false;
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(currentPrice == 0)
      return;

次に、EAは現在の価格を引数としてDrawQuarters関数を呼び出します。この関数は、チャート上に主要なレベル、ラージクォーターライン、そして設定が有効であればスモールクォーターラインを描画し、価格範囲の視覚的構造を提供します。その後、EAは現在の1000 pips範囲の境界を再計算します。まず、MathFloor(currentPrice / MajorStep) * MajorStepを使用してlowerMajorを算出します。次に、upperMajorを求めるために、lowerMajorにMajorStepを加算します。

   // Draw the quarter lines first
   DrawQuarters(currentPrice);

EAが検知している内容をわかりやすく伝えるために、表形式の文字列が作成されます。この表は、まず「Zone」「Price」「Reason」の3つの列を定義するヘッダーから始まります。これらの列は、価格が各レベルに接近した際の重要性を一覧表示するために使用されます。

   double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
   double upperMajor = lowerMajor + MajorStep;

次のステップでは、価格が重要なレベル付近にあるかどうかを確認します。EAはまず、価格が下限(メジャーサポート)または上限(メジャーレジスタンス)のいずれかに、設定された許容範囲内にあるかどうかをテストします。いずれかの条件が満たされると、関数は適切なメッセージ(サポートの場合は「Key support level.Break below signals range shift.」などの定義済みメッセージ)を使ってコメント表に行を追加し、intrusionDetectedをtrueに設定します。

// Check for Major Support
if(MathAbs(currentPrice - lowerMajor) <= AlertTolerance)
{
   table += StringFormat("%-18s | %-8s | %s\n", "Major Support", DoubleToString(lowerMajor,4), MajorSupportReason);
   intrusionDetected = true;
}
// Check for Major Resistance
if(MathAbs(currentPrice - upperMajor) <= AlertTolerance)
{
   table += StringFormat("%-18s | %-8s | %s\n", "Major Resistance", DoubleToString(upperMajor,4), MajorResistanceReason);
   intrusionDetected = true;
}

ラージクォーターラインの描画が有効になっている場合、EAは範囲を4分割し、これらの中間レベルを順にチェックします。各ラージクォーターについて、価格が許容範囲内にあるかを確認し、該当すれば「Decisive break could trigger next 250-PIP move.」といったメッセージとともにコメント表に行を追加します。さらに、オーバーシュート領域が有効な場合は、各ラージクォーターの上下に小さなオフセットを計算し、価格がこれらのオーバーシュートまたはアンダーシュートゾーンに接触しているかをチェックします。条件を満たせば、こちらもコメント表に行を追加します。

if(DrawLargeQuarters)
{
   double LQIncrement = MajorStep / 4.0;
   for(int i = 1; i < 4; i++)
   {
      double level = lowerMajor + i * LQIncrement;
      if(MathAbs(currentPrice - level) <= AlertTolerance)
      {
         table += StringFormat("%-18s | %-8s | %s\n", "Large Quarter", DoubleToString(level,4), LargeQuarterReason);
         intrusionDetected = true;
      }
      if(DrawOvershootAreas)
      {
         double offset = MajorStep / 40.0; // ~25 pips
         double overshootUp = level + offset;
         double undershootDown = level - offset;
         if(MathAbs(currentPrice - overshootUp) <= AlertTolerance)
         {
            table += StringFormat("%-18s | %-8s | %s\n", "Overshoot", DoubleToString(overshootUp,4), OvershootReason);
            intrusionDetected = true;
         }
         if(MathAbs(currentPrice - undershootDown) <= AlertTolerance)
         {
            table += StringFormat("%-18s | %-8s | %s\n", "Undershoot", DoubleToString(undershootDown,4), UndershootReason);
            intrusionDetected = true;
         }
      }
   }
}

オプションで、EAがスモールクォーターラインの描画を設定されている場合は、範囲をさらに細かく分割します。関数はこれらの細分化されたレベルを順にチェックし、価格がスモールクォーターのいずれかに接近した際には、「Minor fluctuation」というメッセージを付けてコメント表に行を追加します。

if(DrawSmallQuarters)
{
   double segStep = MajorStep / 10.0;
   double smallQuarter = segStep / 4.0;
   for(int seg = 0; seg < 10; seg++)
   {
      double segStart = lowerMajor + seg * segStep;
      for(int j = 1; j < 4; j++)
      {
         double level = segStart + j * smallQuarter;
         if(MathAbs(currentPrice - level) <= AlertTolerance)
         {
            table += StringFormat("%-18s | %-8s | %s\n", "Small Quarter", DoubleToString(level,4), SmallQuarterReason);
            intrusionDetected = true;
         }
      }
   }
}

どのレベルでも侵入が検知されなかった場合(つまりintrusionDetectedがfalseのままの場合)、EAはデフォルトメッセージを作成します。このメッセージは、重要な侵入は検出されず、市場はレンジ内での調整局面にあることをユーザーに伝えるとともに、現在の価格を表示します。

   // If no zones were triggered, still provide full information
   if(!intrusionDetected)
   {
      table = StringFormat("No significant intrusion detected.\nCurrent Price: %s\nMarket consolidating within established quarters.\n", DoubleToString(currentPrice,4));
   }

コメント表を作成した後、EAはCreateOrUpdateLabel関数を使ってチャート上のラベルを更新し、最新の分析結果を明確に表示します。最後に、侵入が検知され、かつこれまでにアラートが送信されていなければ(intrusionAlertedフラグで管理)、EAはコメント表の内容を含むアラートを発し、フラグをtrueに設定して重複通知を防ぎます。新たに作成されたオブジェクトや更新内容が即座に反映されるように、処理の最後にChartRedraw関数を呼び出してチャートの再描画をおこないます。

   // Update the label with the commentary table.
   CreateOrUpdateLabel("IntrusionCommentary", table, CORNER_LEFT_UPPER, 10, 10, clrWhite, 14);

   // Trigger an alert only once per intrusion event.
   if(intrusionDetected && !intrusionAlerted)
   {
      Alert(table);
      intrusionAlerted = true;
   }
   if(!intrusionDetected)
      intrusionAlerted = false;

   ChartRedraw();
}

完全なMQL5コード

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

//---- Input parameters -------------------------------------------------
input double MajorStep          = 0.1000;   // Difference between Major Whole Numbers (defines the 1000-PIP Range)
input bool   DrawLargeQuarters  = true;     // Draw intermediate Large Quarter lines.
input bool   DrawSmallQuarters  = false;    // Draw Small Quarter lines.
input bool   DrawOvershootAreas = true;     // Mark overshoot/undershoot areas for Large Quarter lines.
input double AlertTolerance     = 0.0025;   // Tolerance for detecting a "touch" (e.g., ~25 pips for a pair where 1 pip=0.0001)

//---- Color settings ---------------------------------------------------
input color  MajorColor         = 0x2F4F4F; // Dark Slate Gray for Major lines.
input color  LargeQuarterColor  = 0x8B0000; // Dark Red for Large Quarter lines.
input color  SmallQuarterColor  = 0x00008B; // Dark Blue for Small Quarter lines.
input color  OvershootColor     = clrRed;   // Red for overshoot/undershoot lines.

//---- Line style and thickness settings -------------------------------
input ENUM_LINE_STYLE MajorLineStyle       = STYLE_SOLID;
input int    MajorLineWidth                 = 4;
input ENUM_LINE_STYLE LargeQuarterLineStyle  = STYLE_DOT;
input int    LargeQuarterLineWidth          = 3;
input ENUM_LINE_STYLE OvershootLineStyle     = STYLE_DASH;
input int    OvershootLineWidth             = 1;
input ENUM_LINE_STYLE SmallQuarterLineStyle  = STYLE_SOLID;
input int    SmallQuarterLineWidth          = 1;

//---- Commentary Messages (customizable) -----------------------------
input string MajorSupportReason    = "Key support level. Break below signals range shift.";
input string MajorResistanceReason = "Pivotal resistance. Breakout above may start new range.";
input string LargeQuarterReason    = "Decisive break could trigger next 250-PIP move.";
input string OvershootReason       = "Test of breakout; reversal likely if momentum fails.";
input string UndershootReason      = "Insufficient bullish force; possible bearish reversal.";
input string SmallQuarterReason    = "Minor fluctuation.";

// Global flag to avoid repeated alerts while price lingers at a level
bool intrusionAlerted = false;

//+------------------------------------------------------------------+
//| DrawHorizontalLine: Creates or replaces a horizontal line        |
//+------------------------------------------------------------------+
void DrawHorizontalLine(string name, double price, color lineColor, int width, ENUM_LINE_STYLE style)
  {
   if(ObjectFind(0, name) != -1)
      ObjectDelete(0, name);

   if(!ObjectCreate(0, name, OBJ_HLINE, 0, 0, price))
     {
      Print("Failed to create line: ", name);
      return;
     }
   ObjectSetInteger(0, name, OBJPROP_COLOR, lineColor);
   ObjectSetInteger(0, name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, name, OBJPROP_WIDTH, width);
   ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, true);
  }

//+------------------------------------------------------------------+
//| DrawQuarters: Draws all quarter lines based on the current price |
//+------------------------------------------------------------------+
void DrawQuarters(double currentPrice)
  {
// Calculate the boundaries of the current 1000-PIP Range
   double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
   double upperMajor = lowerMajor + MajorStep;

// Draw Major Whole Number lines (defining the 1000-PIP Range)
   DrawHorizontalLine("MajorLower", lowerMajor, MajorColor, MajorLineWidth, MajorLineStyle);
   DrawHorizontalLine("MajorUpper", upperMajor, MajorColor, MajorLineWidth, MajorLineStyle);

// Draw Large Quarter lines and their overshoot/undershoot areas if enabled
   if(DrawLargeQuarters)
     {
      double LQIncrement = MajorStep / 4.0;
      for(int i = 1; i < 4; i++)
        {
         double level = lowerMajor + i * LQIncrement;
         string objName = "LargeQuarter_" + IntegerToString(i);
         DrawHorizontalLine(objName, level, LargeQuarterColor, LargeQuarterLineWidth, LargeQuarterLineStyle);

         if(DrawOvershootAreas)
           {
            double offset = MajorStep / 40.0; // approximately 25 pips if MajorStep=0.1000
            DrawHorizontalLine("Overshoot_" + IntegerToString(i) + "_up", level + offset, OvershootColor, OvershootLineWidth, OvershootLineStyle);
            DrawHorizontalLine("Undershoot_" + IntegerToString(i) + "_down", level - offset, OvershootColor, OvershootLineWidth, OvershootLineStyle);
           }
        }
     }

// Draw Small Quarter lines if enabled (optional, finer divisions)
   if(DrawSmallQuarters)
     {
      double segStep = MajorStep / 10.0;
      double smallQuarter = segStep / 4.0;
      for(int seg = 0; seg < 10; seg++)
        {
         double segStart = lowerMajor + seg * segStep;
         for(int j = 1; j < 4; j++)
           {
            double level = segStart + j * smallQuarter;
            string objName = "SmallQuarter_" + IntegerToString(seg) + "_" + IntegerToString(j);
            DrawHorizontalLine(objName, level, SmallQuarterColor, SmallQuarterLineWidth, SmallQuarterLineStyle);
           }
        }
     }
  }

//+------------------------------------------------------------------+
//| CreateOrUpdateLabel: Creates or updates a label with given text  |
//+------------------------------------------------------------------+
void CreateOrUpdateLabel(string name, string text, int corner, int xdist, int ydist, color txtColor, int fontSize)
  {
   if(ObjectFind(0, name) == -1)
     {
      ObjectCreate(0, name, OBJ_LABEL, 0, 0, 0);
      ObjectSetInteger(0, name, OBJPROP_CORNER, corner);
      ObjectSetInteger(0, name, OBJPROP_XDISTANCE, xdist);
      ObjectSetInteger(0, name, OBJPROP_YDISTANCE, ydist);
      // Set a monospaced font for tabular display (Courier New)
      ObjectSetString(0, name, OBJPROP_FONT, "Courier New");
     }
   ObjectSetString(0, name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, name, OBJPROP_COLOR, txtColor);
   ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fontSize);
  }

//+------------------------------------------------------------------+
//| OnInit: Initialization function for the EA                       |
//+------------------------------------------------------------------+
int OnInit()
  {
// Create a persistent commentary label in the top-left corner
   CreateOrUpdateLabel("IntrusionCommentary", "Intrusion Detector Initialized", CORNER_LEFT_UPPER, 10, 10, clrWhite, 14);
   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| OnDeinit: Deinitialization function for the EA                   |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// Remove the commentary label on exit
   ObjectDelete(0, "IntrusionCommentary");
  }

//+------------------------------------------------------------------+
//| OnTick: Main function called on every tick                       |
//+------------------------------------------------------------------+
void OnTick()
  {
   bool intrusionDetected = true;
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   if(currentPrice == 0)
      return;

// Draw the quarter lines first
   DrawQuarters(currentPrice);

// Calculate boundaries of the current 1000-PIP Range
   double lowerMajor = MathFloor(currentPrice / MajorStep) * MajorStep;
   double upperMajor = lowerMajor + MajorStep;

// Build a tabular commentary string with a header.
   string header = StringFormat("%-18s | %-8s | %s\n", "Zone", "Price", "Reason");
   string separator = "----------------------------------------------\n";
   string table = header + separator;

// Check for Major Support
   if(MathAbs(currentPrice - lowerMajor) <= AlertTolerance)
     {
      table += StringFormat("%-18s | %-8s | %s\n", "Major Support", DoubleToString(lowerMajor,4), MajorSupportReason);
      intrusionDetected = true;
     }
// Check for Major Resistance
   if(MathAbs(currentPrice - upperMajor) <= AlertTolerance)
     {
      table += StringFormat("%-18s | %-8s | %s\n", "Major Resistance", DoubleToString(upperMajor,4), MajorResistanceReason);
      intrusionDetected = true;
     }

// Check Large Quarter Levels and Overshoot/Undershoot Zones
   if(DrawLargeQuarters)
     {
      double LQIncrement = MajorStep / 4.0;
      for(int i = 1; i < 4; i++)
        {
         double level = lowerMajor + i * LQIncrement;
         if(MathAbs(currentPrice - level) <= AlertTolerance)
           {
            table += StringFormat("%-18s | %-8s | %s\n", "Large Quarter", DoubleToString(level,4), LargeQuarterReason);
            intrusionDetected = true;
           }
         if(DrawOvershootAreas)
           {
            double offset = MajorStep / 40.0; // ~25 pips
            double overshootUp = level + offset;
            double undershootDown = level - offset;
            if(MathAbs(currentPrice - overshootUp) <= AlertTolerance)
              {
               table += StringFormat("%-18s | %-8s | %s\n", "Overshoot", DoubleToString(overshootUp,4), OvershootReason);
               intrusionDetected = true;
              }
            if(MathAbs(currentPrice - undershootDown) <= AlertTolerance)
              {
               table += StringFormat("%-18s | %-8s | %s\n", "Undershoot", DoubleToString(undershootDown,4), UndershootReason);
               intrusionDetected = true;
              }
           }
        }
     }

// Check Small Quarter Levels (if enabled)
   if(DrawSmallQuarters)
     {
      double segStep = MajorStep / 10.0;
      double smallQuarter = segStep / 4.0;
      for(int seg = 0; seg < 10; seg++)
        {
         double segStart = lowerMajor + seg * segStep;
         for(int j = 1; j < 4; j++)
           {
            double level = segStart + j * smallQuarter;
            if(MathAbs(currentPrice - level) <= AlertTolerance)
              {
               table += StringFormat("%-18s | %-8s | %s\n", "Small Quarter", DoubleToString(level,4), SmallQuarterReason);
               intrusionDetected = true;
              }
           }
        }
     }

// If no zones were triggered, still provide full information
   if(!intrusionDetected)
     {
      table = StringFormat("No significant intrusion detected.\nCurrent Price: %s\nMarket consolidating within established quarters.\n", DoubleToString(currentPrice,4));
     }

// Update the label with the commentary table.
   CreateOrUpdateLabel("IntrusionCommentary", table, CORNER_LEFT_UPPER, 10, 10, clrWhite, 14);

// Trigger an alert only once per intrusion event.
   if(intrusionDetected && !intrusionAlerted)
     {
      Alert(table);
      // Alternatively, you could use: PlaySound("alert.wav");
      intrusionAlerted = true;
     }
   if(!intrusionDetected)
      intrusionAlerted = false;

   ChartRedraw();
  }
//+------------------------------------------------------------------+


結果

ここでは、実際の市場環境でツールをテストした際の結果をご紹介します(テストはデモ口座を使用して行いました)。以下の図は、ニュージーランドドル(NZD)対米ドル(USD)のチャートを示しています。価格がアンダーシュートレベルに接近し、アラートが発動しました。 

このアラートは重要な情報を提供しており、特定されたゾーン(この場合はアンダーシュートゾーン)が示されました。検知された具体的な価格レベルは0.5725です。さらに、アラートにはそのレベルでの市場状況の分析も含まれており、強気の勢いが不十分であり、弱気への反転の可能性があることを示しています。

NZDUSD

図3:NZD対USD

以下は、MetaTrader 5の[エキスパート]タブに記録された情報です。

2025.02.25 16:55:37.188 Intrusion Detector EA (NZDUSD,H1)       Alert: Zone               | Price    | Reason
2025.02.25 16:55:37.188 Intrusion Detector EA (NZDUSD,H1)       ----------------------------------------------
2025.02.25 16:55:37.188 Intrusion Detector EA (NZDUSD,H1)       Undershoot         | 0.5725   | Insufficient bullish force; possible bearish reversal.
2025.02.25 16:55:37.188 Intrusion Detector EA (NZDUSD,H1)       

それでは、この検知とさらなる分析に基づいて私が実行した取引を見てみましょう。

実行した取引

図3:取引テスト

以下は、私が早めに取引をクローズしたにもかかわらず、市場が最終的に辿ったポジションです。

市場の動き

図5:市場の動き

以下の図6は、USD/CAD通貨ペアでおこなったテストのGIFです。価格がオーバーシュートゾーンとラージクォーターレベルの2つのゾーンで反応した様子が示されています。

USDCAD

図6:USDCAD



結論

私たちのEAは、クォーターズ理論に沿った価格ゾーンを監視するために特化した強力な分析アシスタントとして機能します。このツールは、クォーターズ理論を市場分析に取り入れているトレーダーにとって特に価値があります。幅広いテストの結果、このEAは重要な価格ゾーンの検出、タイムリーなアラートの発信、効果的な市場背景のモニタリングに優れていることが確認されました。この開発は、クォーターズ理論を用いた市場分析の自動化において大きな前進を意味します。以前はクォーターの描画自動化に注力していましたが、今回はリアルタイムでのクォーター監視に進化しました。この強化により、価格がこれらの重要なレベルに触れるたびに、トレーダーに即座に通知が届き、潜在的な市場変化について簡潔な説明が提供されるようになりました。

しかし、これは旅の終わりではありません。今後もクォーターズ理論の市場自動化への応用においてさらなる革新が期待されます。その一方で、このツールを活用するすべてのトレーダーには、提供されるシグナルだけに頼らず、自身の戦略を組み合わせることを強くお勧めします。自動化と個人の専門知識を融合させたバランスの取れたアプローチこそが、より賢明な取引判断につながるのです。

日付ツール名 詳細バージョン アップデート 備考
01/10/24ChartProjector前日のプライスアクションをゴースト効果でオーバーレイするスクリプト1.0初回リリースツール番号1
18/11/24Analytical Comment前日の情報を表形式で提供し、市場の将来の方向性を予測する1.0初回リリースツール番号2
27/11/24Analytics Master2時間ごとに市場指標を定期的に更新 1.01v.2ツール番号3
02/12/24Analytics Forecaster Telegram統合により、2時間ごとに市場指標を定期的に更新1.1v.3ツール番号4
09/12/24Volatility Navigatorボリンジャーバンド、RSI、ATR指標を使用して市場の状況を分析するEA1.0初回リリースツール番号5
19/12/24Mean Reversion Signal Reaper 平均回帰戦略を用いて市場を分析し、シグナルを提供する 1.0 初回リリース ツール番号6 
9/01/25 Signal Pulse 多時間枠分析ツール1.0 初回リリース ツール番号7 
17/01/25 Metrics Board 分析用のボタン付きパネル 1.0 初回リリースツール番号8 
21/01/25External Flow外部ライブラリによる分析1.0 初回リリースツール番号9 
27/01/25VWAP出来高加重平均価格  1.3 初回リリース ツール番号10 
02/02/25 Heikin Ashi トレンドの平滑化と反転シグナルの識別 1.0 初回リリース ツール番号11
04/02/25 FibVWAP Python分析によるシグナル生成 1.0 初回リリース ツール番号12
14/02/25 RSI DIVERGENCE プライスアクションとRSIのダイバージェンス 1.0 初回リリース ツール番号13 
17/02/25 Parabolic Stop and Reverse (PSAR) PSAR戦略の自動化1.0初回リリース ツール番号14
20/02/25 Quarters Drawerスクリプトチャートにクォーターレベルを描く 1.0 初回リリース ツール番号15 
27/02/25Intrusion Detector価格がクォーターレベルに達したときに検出して警告する1.0初回リリースツール番号16

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

添付されたファイル |
プライスアクション分析ツールキットの開発(第17回):TrendLoom EAツール プライスアクション分析ツールキットの開発(第17回):TrendLoom EAツール
プライスアクションを観察し、取引をおこなう立場から言うと、複数の時間枠でトレンドが確認された場合、その方向にトレンドが継続することがよくあります。ただし、トレンドがどれくらい続くかは一定ではなく、ポジションを長期で保有するのか、それともスキャルピングのような短期取引をおこなうのかといったトレーダーのスタイルによって異なります。トレンド確認に使用する時間枠の選択は非常に重要な役割を果たします。以下の記事では、ワンクリックや定期的な更新によって、複数の時間足にわたる全体的なトレンドを自動で分析できる便利なシステムを紹介しています。ぜひご覧ください。
知っておくべきMQL5ウィザードのテクニック(第56回):ビル・ウィリアムズフラクタル 知っておくべきMQL5ウィザードのテクニック(第56回):ビル・ウィリアムズフラクタル
ビル・ウィリアムズによるフラクタルは、最初にチャート上で目にしたときには見落とされがちな強力なインジケーターです。一見するとチャートが煩雑に見え、鋭さに欠けるように思えるかもしれません。この記事では、このインジケーターの覆いを取り払い、そのさまざまなパターンがどのように機能するのかを、MQL5ウィザードで組み上げたエキスパートアドバイザー(EA)によるフォワードウォークテストを通じて検証していきます。
MQL5取引ツールキット(第8回):コードベースにHistory Manager EX5ライブラリを実装して使用する方法 MQL5取引ツールキット(第8回):コードベースにHistory Manager EX5ライブラリを実装して使用する方法
MetaTrader 5口座の取引履歴を処理するために、MQL5ソースコード内で「History Manager EX5」ライブラリを簡単にインポートして活用する方法を、本連載の最終回となるこの記事で解説します。MQL5ではシンプルな1行の関数呼び出しで、取引データの管理や分析を効率的におこなうことが可能です。さらに、取引履歴の分析スクリプトを複数作成する方法や、実用的なユースケースとして、価格ベースのエキスパートアドバイザー(EA)の開発方法についても学んでいきます。このEAは、価格データとHistory Manager EX5ライブラリを活用し、過去のクローズ済み取引に基づいて取引判断をおこない、取引量の調整やリカバリーストラテジーの実装をおこないます。
HarmonyOS NEXTデバイスにMetaTrader 5などのMetaQuotesアプリをインストールする HarmonyOS NEXTデバイスにMetaTrader 5などのMetaQuotesアプリをインストールする
HarmonyOS NEXTデバイスでMetaTrader 5やその他のMetaQuotesアプリをDroiTong(卓易通)を使って簡単にインストールできます。スマートフォンやノートパソコン向けの詳細なステップバイステップガイドです。