English Deutsch
preview
プライスアクション分析ツールキットの開発(第27回):移動平均フィルター付き流動性スイープツール

プライスアクション分析ツールキットの開発(第27回):移動平均フィルター付き流動性スイープツール

MetaTrader 5 |
25 0
Christian Benjamin
Christian Benjamin

内容


はじめに

大口の機関投資家は、偶然に市場に影響を与えているわけではありません。彼らの戦略は、多くの場合、よく知られたサポートやレジスタンスレベルを超えて価格を押し上げたり押し下げたりすることにあります。これにより、小口トレーダーのストップロスや未約定のエントリー注文を意図的に作動させるのです。この一時的な動きを「流動性スイープ」と呼び、大口投資家は市場の方向性に即座に逆らうことなく、有利な価格で大量のポジションを取得または解消することができます。

多くのリテールトレーダーにとって、これらの動きは「騙し」のように感じられます。価格が一時的に直近の安値を割り込み、ストップロスを刈り取ったあと、すぐに反発する場合もあります。逆にレジスタンスを上抜け、ストップを発動させてから急反落する場合もあります。大口プレイヤーがこの流動性を吸収し終えると、市場はしばしば元のトレンドに戻り、勢いを増して動き出します。こうした流動性スイープを早期に認識することで、無駄にストップアウトされるのを避け、機関投資家の流れに沿ったポジショニングが可能になります。

本記事では、こうした流動性スイープを検知するためのMQL5エキスパートアドバイザー(EA)を開発していきます。EAはまず、直近のスイングポイントを下抜け/上抜けした後にレンジ内へ戻ってクローズしたローソク足を分析し、流動性吸収の兆候を見つけます。また、任意のフィルター(ローソク足の色の変化や移動平均線によるトレンド確認など)を組み込み、シグナルが相場観に合致するかどうかを精査します。有効なパターンが検出されると、EAはチャート上に矢印やラベルで表示し、アラートも発生させます。

チュートリアルを最後まで読むことで、流動性スイープを即座にハイライトする、シンプルで分かりやすいEAを完成させることができます。EAがどのように特定のパターンを検出するのか、フィルターで誤検知を減らす方法、そして「スマートマネー」の動きにどう追従できるのかを学べるでしょう。このツールを活用すれば、ストップロスを刈り取られる前に機関投資家の仕掛けを先読みし、あらゆる相場環境で戦略的な優位性を手に入れることができます。


戦略の理解

流動性スイープは2つの方法で形成されます。強気のスイープ(サポートを下抜けしたように見せかけるフェイクブレイク)または弱気のスイープ(レジスタンスを上抜けしたように見せかけるフェイクブレイク)です。EAのコア検出ロジックはDetectLiquiditySweep関数にあります。以下では、それが両者をどのように区別するかをステップごとに分解し、その後に真偽値チェックを実行する正確なMQL5コードスニペットを示します。

新しいローソク足が閉じるとすぐにEAは以下を呼び出します。

DetectLiquiditySweep(1);

shift=1を渡すと次のようになります。

  • インデックス1 (shift)は、ちょうど確定したバー(テストしたい「現在の」ローソク足)を指します。
  • インデックス2 (shift+1)は、その直前のバー(「前の」ローソク足)を指します。
  • DetectLiquiditySweep内では、これらの行が両方のバーに対して始値/高値/安値/終値を取得します。

double o   = iOpen(Symbol(), Period(), shift);
double c   = iClose(Symbol(), Period(), shift);
double h   = iHigh(Symbol(), Period(), shift);
double l   = iLow(Symbol(), Period(), shift);

double o1  = iOpen(Symbol(), Period(), shift + 1);
double c1  = iClose(Symbol(), Period(), shift + 1);
double h1  = iHigh(Symbol(), Period(), shift + 1);
double l1  = iLow(Symbol(), Period(), shift + 1);

  • o、h、l、cは新しいバーの始値、高値、安値、終値です。
  • o1、h1、l1、c1は、直前のバーの始値、高値、安値、終値です。

EAは、SignalStrict入力によって制御される2つのやや異なる定義を許容します。

1. LessStrict(デフォルト)

強気スイープ

  • 新しいバーは始値よりも高く引ける:c > o。
  • その安値は前のバーの安値を下回る:l < l1。
  • その終値は前のバーの始値を上回る:c > o1(これにより純粋な十字線や小さすぎる偽のローソク足が条件を満たすのを防ぎます)。
  • 前のバーは十字線ではなかった:c1 != o1。
弱気スイープ

  • 新しいバーは始値よりも高く引ける:c < o。
  • その高値は、前のバーの高値を上回る:h > h1。
  • その終値は前のバーの始値を下回る:c < o1。
  • 前のバーは十字線ではなかった:c1 != o1。

2. Strict

強気スイープ

LessStrictとまったく同じですが、ステップ2が強化され、安値 < 前回の安値に加えて、終値が前の高値を上回る必要があります(c > h1)。言い換えると、新しいバーは一度下に潜った後、前の高値を取り戻して引ける必要があります。

弱気スイープ

同様に、新しいバーは前の高値を上抜けた後(h > h1)、前の安値より下で引ける必要があります(c < l1)。単に前の高値を突き抜けるだけでは不十分です。

これらの真偽判定は、以下のコードブロック内に記述されています。

//--- Liquidity sweep logic (LessStrict vs Strict)
bool bullSweep, bearSweep;
if (SignalStrict == LessStrict)
  {
    bullSweep = (c > o &&    // 1) Bullish candle
                 l < l1 &&   // 2) Low dipped below previous low
                 c > o1 &&   // 3) Close above previous open
                 c1 != o1);  // 4) Previous bar was not a doji

    bearSweep = (c < o &&    // 1) Bearish candle
                 h > h1 &&   // 2) High spiked above previous high
                 c < o1 &&   // 3) Close below previous open
                 c1 != o1);  // 4) Previous bar was not a doji
  }
else // Strict
  {
    bullSweep = (c > o &&    // 1) Bullish candle
                 l < l1 &&   // 2) Low dipped below previous low
                 c > h1 &&   // 3) Close above previous high (stricter)
                 c1 != o1);  // 4) Previous bar was not a doji

    bearSweep = (c < o &&    // 1) Bearish candle
                 h > h1 &&   // 2) High spiked above previous high
                 c < l1 &&   // 3) Close below previous low (stricter)
                 c1 != o1);  // 4) Previous bar was not a doji
  }

  • LessStrictステップ3は強気の場合c > o1、弱気の場合c < o1で十分です。
  • Strictモードでは、ステップ3は強気の場合c > h1、弱気の場合c < l1に変更されます。

ユーザーがColorChangeOnlyをtrueに設定している場合、EAは新しいバーが前のバーと逆の色であることを要求します。具体的には、以下のようになります。

bool bullCC = (c > o && c1 < o1);   // New bar bullish, old bar bearish
bool bearCC = (c < o && c1 > o1);   // New bar bearish, old bar bullish

if (ColorChangeOnly)
  {
    bullSweep &= bullCC;  // Only a bullish sweep if also a bull‐after‐bear color flip
    bearSweep &= bearCC;  // Only a bearish sweep if also a bear‐after‐bull color flip
  }

ColorChangeOnlyがfalseの場合、これら2行は影響せず、bullSweepおよびbearSweepは前の価格判定によって決定されたままです。

価格および任意の色判定の後、bullSweepまたはbearSweepがtrueになると、EAはさらに移動平均に基づいてフィルタリングすることができます。これはUseMAFilterおよびPriceAboveMAによって制御されます。基本的には次の通りです。

  • インデックスshift(ちょうど確定したバー)で単一の移動平均値を計算します。計算方法は、組み込みのiMA()ハンドル(SMA、EMA、LWMA、RMA用)またはカスタム関数(CalcVWMAまたはCalcHMA)によります。

bool cond = PriceAboveMA ? (c > maValue) : (c < maValue);

  • PriceAboveMA == true」の場合、EAは「c > maValue」の場合にのみbullSweepを維持し、強制的に「bearSweep = false」とします。
  • PriceAboveMA == false」の場合、EAは「c < maValue」の場合にのみbearSweepを維持し、強制的に「bullSweep = false」とします。

if (UseMAFilter)
  {
    double maValue = 0.0;

    if (MAType == VWMA)
         maValue = CalcVWMA(shift);
    else if (MAType == HMA)
         maValue = CalcHMA(shift);
    else
      {
        double buf[];
        if (CopyBuffer(MAHandle, 0, shift, 1, buf) != 1)
          return;  // not enough MA data yet
        maValue = buf[0];
      }

    // Keep only bullish sweeps if price > MA, or only bearish if price < MA
    bool cond = PriceAboveMA ? (c > maValue) : (c < maValue);
    bullSweep &= cond;
    bearSweep &= !cond;
  }

まとめ

強気スイープ

  1. ローソク足が始値より高く引け、前の安値を下回り、前の始値(LessStrict)または前の高値(Strict)より上で終値を付ける。
  2. 任意で、前のバーが弱気で新しいバーが強気になる色変化である必要がある。
  3. 任意で、終値が移動平均より上である必要がある。
  4. すべての条件を満たした場合、EAはそのローソク足の安値より数ポイント下に緑色の上向き矢印(OBJ_ARROW_UP)を表示する。

図1:強気スイープ

弱気スイープ

  1. ローソク足が始値より低く引け、前の高値を上抜け、前の始値(LessStrict)または前の安値(Strict)より下で終値を付ける。
  2. 任意で、前のバーが強気で新しいバーが弱気になる色変化である必要がある。
  3. 任意で、終値が移動平均より下である必要がある。
  4. すべての条件を満たした場合、EAはそのローソク足の高値より数ポイント上に赤色の下向き矢印(OBJ_ARROW_DOWN)を表示する。

図2:弱気スイープ


コード解説

Liquidity Sweep with MA filter EAは、MetaTrader 5の任意のチャート上で、フェイクブレイク(しばしば流動性スイープと呼ばれる)を識別し、視覚的にマークするよう設計されています。EAのコアは、前のバーのスイング安値(または高値)を下抜け(または上抜け)した後、トラップされた注文を示唆する形で終値を付けるバーを監視することにあります。この検出ロジックに任意の移動平均フィルターを組み合わせることで、トレーダーはシグナルをより広いトレンドの文脈に合わせて調整できます。以下の段落では、EAの主要な構成要素をすべて順を追って説明し、各セクションが何をしているのか、なぜ重要なのかをステップごとに解説します。

まず、EAファイルでは、メタデータ(タイトル、著作権、作者リンク、バージョン)が宣言され、その直後に「#property strict」が記述されています。これらの行は単なる装飾ではなく、2つの目的を持っています。1つ目は、複数のEAを閲覧する人に対して、このスクリプトが誰によるものか、またどこで他の作品を確認できるかを明示することです。2つ目は、 #property strictを指定することで、コンパイラがより厳密な構文および型チェックをおこない、コードが実行される前に未宣言変数や型の不一致といった一般的なミスを検出できるようにすることです。このように明確なメタデータと厳密なコンパイルを行うことは、保守性と信頼性を確保するプロフェッショナルな標準を反映しています。

//+------------------------------------------------------------------+
//|                                    Liquidity Sweep with MA filter|
//|                                   Copyright 2025, MetaQuotes Ltd.|
//|                           https://www.mql5.com/ja/users/lynnchris|
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ja/users/lynnchris"
#property version   "1.0"
#property strict

次に、EAはTrade.mqhライブラリを読み込みます。この特定のバージョンでは自動的に実取引をおこなうわけではありませんが、<Trade\Trade.mqh>を含めることで将来的な拡張に備えています。これにより、CTradeクラスが利用可能になり、将来的にポジションのオープン、変更、クローズをおこなう機能を追加できます。チュートリアルの文脈では、読者に対してこのEAが、検出ロジック内でtrade.Buy(...)やtrade.Sell(...)を呼び出すだけで、完全自動化戦略として容易に進化させられることを示しています。このライブラリの導入を最初に示すことで、著者は読者に分析と実行の両方を意識させています。

#include <Trade\Trade.mqh>

ライブラリの読み込みに続いて、EAは入力パラメータのセットを定義します。これらはEAをチャートに適用した際、MetaTraderの「Inputs」ダイアログに表示されます。入力パラメータは大きく3つの論理グループに分かれます。最初のグループは移動平均の設定です。ユーザーはMAフィルターのON/OFF(UseMAFilter)を切り替えたり、チャート上にMAを表示するかどうか(ShowMA)を選択したり、MAの期間(MALength)を設定したり、6種類のMAタイプ(SMA、EMA、LWMA、VWMA、RMA、HMA)から選択したりできます。さらに、ブール値PriceAboveMAにより、EAが価格がMAの上にある場合のみ強気スイープを採用するか(またはMAの下にある場合のみ弱気スイープを採用するか)を指定できます。

実際の運用では、このフィルターによりトレーダーは主要トレンドの方向に沿った流動性スイープのみを取引することができ、市場が逆方向に動いている場合の誤検知を減らすことができます。

//--- Inputs: Moving Average Filter
input bool   UseMAFilter       = false;    // Enable Moving Average Filter
input bool   ShowMA            = false;    // Show MA on chart
input int    MALength          = 20;       // MA period (must be >=1)
enum MA_Type {SMA=0, EMA, LWMA, VWMA, RMA, HMA};
input MA_Type MAType           = SMA;      // Moving Average type
input bool   PriceAboveMA      = true;     // Filter: price above MA?

入力パラメータの第2グループは、EAがスイープをどの程度厳密に定義するかを制御します。列挙型SignalStrictには2つのモードがあります。LessStrictは、新しいバーの安値(強気シグナルの場合)が前のバーの安値を下回るだけでよく(弱気の場合は逆)、一方「Strict」はさらに前のバーの高値(または安値)の突破を要求します。このフラグを公開することで、読者は感度を調整する方法を学ぶことができます。多くのシグナルを拾いたい場合は緩めの設定、明確なブレイクが出るまで待ちたい場合は保守的な設定を選ぶことが可能です。

補助的なブール値ColorChangeOnlyは条件をさらに厳しくし、新しいバーの色(強気か弱気か)が前のバーの色と逆であることを要求します。多くの取引理論では、色の変化は勢いの転換を示すため、このオプションは、ローソク足の明確な反転と一致する場合のみマークしたいトレーダーに適しています。

//--- Inputs: Sweep Definition Strictness
enum Strictness {LessStrict=0, Strict};
input Strictness SignalStrict  = LessStrict; // Signal strictness
input bool   ColorChangeOnly   = false;      // Only on color-change candles

入力パラメータの第3グループは、チャート上の表示に関する設定です。列挙型LabelTypeにより、ラベルなし、短い2文字ラベル(強気スイープは「BS」、弱気スイープは「SS」)、またはフルテキストラベル(「Bull Sweep」「Bear Sweep」)を選択できます。同時にPlotArrowによって、テキストの代わりに、あるいはテキストと併用して矢印を描画することも可能です。ラベルがローソク足と重ならないようにするため、ArrowOffsetPoints入力で、各矢印やテキストオブジェクトをローソク足の上(弱気の場合)または下(強気の場合)に指定ポイントだけ移動させます。

さらに、2つの色入力BullishColorBearishColorにより、これらのマーカーの色を完全に制御でき、ユーザーは自分のチャートテーマに合わせて調整できます。表示オプションを検出ロジックから分離することで、EAは柔軟性を保ちます。トレーダーは、チャート上にオブジェクトを表示しない「サイレントアラート」モードや、フルの「チャート注釈」モードを、自分の好みやシステムのパフォーマンスに応じて設定できます。

//--- Inputs: Label/Arrow & Color Customization
enum LabelType {None=0, Short, Full};
input LabelType LblType        = Full;       // Label type (None/Short/Full)
input bool      PlotArrow      = true;       // Draw arrow on signal
input int       ArrowOffsetPoints = 10;      // Offset (in points) above/below candles
input color     BullishColor   = clrLime;    // Color for bullish signals
input color     BearishColor   = clrRed;     // Color for bearish signals

入力セクションの直後、EAは2つのグローバル変数を宣言します。1つ目の「datetime lastBarTime」は、直近で処理されたバーのタイムスタンプを格納します。この変数は非常に重要です。EAは各ティックごとに、現在のバーの開始時刻(iTime(Symbol(),Period(),0))とlastBarTimeを比較します。異なれば新しいローソク足が形成されたことを意味し、その時に初めてEAは検出ルーチンを実行します。このチェックがないと、形成途中のバーのすべてのティックでロジックが走り、1本のローソク足で複数のシグナルが発生して、一般に「リペイント」と呼ばれる問題が生じます。

2つ目のグローバル変数「int MAHandle」は、組み込みの移動平均を作成した際にiMA(...)が返すインジケーターハンドルを保持します。初期値をINVALID_HANDLEに設定しておくことで、後で有効なハンドルが存在するかを確認してから読み取りを行うことができます。

//--- Globals
datetime   lastBarTime = 0;               // Timestamp of the last processed bar
int        MAHandle    = INVALID_HANDLE;  // Handle for built‐in MA indicator

OnInit()関数は、EAのセットアップルーチンです。EAが最初にチャートに適用されたとき、または誰かが入力パラメータを変更したときに、プラットフォームはOnInit()を呼び出します。最初のタスクはMALengthの検証です。MQL5ではコンパイル後に入力は定数として扱われるため、安全な値を単純に再代入することはできません。その代わり、コードは「if(MALength < 1)」をチェックし、無効な期間が指定された場合はエラーメッセージを表示してINIT_FAILEDを返します。この防御的なプログラミングにより、EAがゼロや負のバーに対して移動平均を計算しようとして、実行時エラーや意味のない結果を生じることを防ぎます。次にlastBarTimeが現在のバーの開始時刻に設定されます。これにより、ティックハンドラーのロジックが初期化直後のバーで即座に動作しないように準備されます。

int OnInit()
  {
   // Validate MALength (cannot assign to an input directly)
   if(MALength < 1)
     {
      Print("ERROR: MALength must be at least 1. Current value = ", MALength);
      return(INIT_FAILED);
     }

   // Initialize timing so we only run when a new bar closes
   lastBarTime = iTime(Symbol(), Period(), 0);

   // … (rest of OnInit follows) …
   return(INIT_SUCCEEDED);
  }

OnInit()内の続きとして、EAは組み込みの移動平均ハンドルを作成する必要があるかどうかを判断します。ユーザーが4種類の標準的なMAタイプ(SMA、EMA、LWMA、RMA)のいずれかを選択し、かつUseMAFilterまたはShowMAが有効になっている場合、コードは「iMA(Symbol(), Period(), MALength, 0, (ENUM_MA_METHOD)MAType, PRICE_CLOSE)」を呼び出してインジケーターハンドルを作成します。もしこの呼び出しが失敗した場合、EAは診断用エラーを表示して初期化を中止します。成功した場合は、「if(ShowMA == true)」をチェックし、条件を満たす場合は「ChartIndicatorAdd(0, 0, MAHandle)」を呼び出してチャート上にMAを描画します。初期化の最後には、「Liquidity Sweep EA initialized successfully.」というメッセージが表示され、すべての前提条件(有効な期間、MAハンドル)が整い、EAが実行準備完了であることを確認します。

// Create MA handle if using a built-in MA (SMA, EMA, LWMA, RMA) 
if((MAType != VWMA && MAType != HMA) && (UseMAFilter || ShowMA))
  {
   ENUM_MA_METHOD method = (ENUM_MA_METHOD)MAType;
   MAHandle = iMA(Symbol(), Period(), MALength, 0, method, PRICE_CLOSE);
   if(MAHandle == INVALID_HANDLE)
     {
      Print("Failed to create MA handle (type=", EnumToString(MAType), 
            ", length=", MALength, ")");
      return(INIT_FAILED);
     }
   if(ShowMA)
      ChartIndicatorAdd(0, 0, MAHandle);
  }

ユーザーがEAを削除するかMetaTraderを終了するかした際には、OnDeinit()関数が実行されます。この関数の唯一の役割は、ハウスキーピングです。MAHandleが設定されている場合(つまりINVALID_HANDLEでない場合)、「IndicatorRelease(MAHandle)」を呼び出してメモリを解放し、残存している参照を削除します。これにより、EAはリソースリークを防ぐことができます。インジケータハンドルを実行時に作成する場合、これは重要なベストプラクティスです。現代のMetaTraderではある程度自動管理されますが、ハンドルを明示的に解放することで、長時間稼働するセッションや、パラメータ最適化のためにEAを頻繁に再適用する場合でも、チャートの膨張を防ぐことができます。

void OnDeinit(const int reason)
  {
   if(MAHandle != INVALID_HANDLE)
      IndicatorRelease(MAHandle);
  }

OnTick()関数は、EAの心臓部にあたり、MetaTraderは価格ティックが到着するたびにこの関数を呼び出します。しかし、流動性スイープの確認は確定したローソク足ごとに1回だけおこないたいため、OnTick()はまず「iTime(Symbol(),Period(),0)」で最新バーの時刻を取得し、lastBarTimeと比較します。両者が一致する場合、EAはまだ同じバー内にいることを意味するため、何も実行されません。

新しいローソク足が現れた場合(つまり「iTime(...) != lastBarTime」)、コードは「DetectLiquiditySweep(1)」を呼び出します。ここでshift=1を渡すことで、直近で確定したバーをその前のバーと比較して検証します。その直後、lastBarTimeは新しい値に更新され、次のバーが確定するまで再検出されないようにします。この厳密な手法により、1本のバーにつき1回だけシグナルを出すことが保証され、ノイズの除去や1本のローソク足で複数のアラートが発生するのを防ぎます。

void OnTick()
  {
   // Retrieve the current bar’s start time
   datetime current = iTime(Symbol(), Period(), 0);

   // If the bar start time changed, call the detection routine once
   if(current != lastBarTime)
     {
      DetectLiquiditySweep(1);
      lastBarTime = current;
     }
  }

主要なルーチンである「DetectLiquiditySweep(int shift)」は、このEAを単純なパターンマーカーと区別するロジックを実装しています。まず、カスタム移動平均を計算するのに十分な履歴データがあるかを確認します。「requiredBars = shift + MALength」を計算し、「if(Bars(Symbol(),Period()) <= requiredBars ) return;」で確認することで、インデックスの範囲外アクセスを防ぎます。たとえば、チャート上にバーが15本しかないのにユーザーがMALength=20を設定している場合、EAは移動平均の計算やパターンチェックを試みません。履歴が不足しているためです。このガード句は、堅牢なEAを作るうえで不可欠な、慎重なエラー防止の一例を示しています。

void DetectLiquiditySweep(int shift)
  {
   // Ensure there are at least (shift + MALength) bars of history
   int requiredBars = shift + MALength;
   if(Bars(Symbol(), Period()) <= requiredBars)
     {
      // Not enough bars to compute custom MA or compare prices
      return;
     }

   // … (next steps in the function) …
  }

履歴チェックを通過すると、DetectLiquiditySweepは8つの価格データを取得します。現在確定したバー(インデックス = shift)と、前のバー(インデックス = shift + 1)のそれぞれの始値、終値、高値、安値です。これらの値は、現在のバー用にo, c, h, l、前のバー用にo1, c1, h1, l1の8つのdouble型変数に格納されます。これらの値を用いることで、EAは流動性スイープが発生したかどうかを判断できます。

重要なのは、2つのブールフラグbullCC(強気の色変化)とbearCC(弱気の色変化)も計算される点です。計算方法は単純で、現在のバーの終値が始値より上で、かつ前のバーの終値が始値より下であればbullCCをtrueに、逆の場合はbearCCをtrueに設定します。これらのフラグは、ColorChangeOnlyが有効な場合に使用され、EAがローソク足の色変化と一致するスイープのみをフラグ付けすることを保証します。

//--- Bar data for the current completed candle (index = shift)
double o  = iOpen(Symbol(), Period(), shift);
double c  = iClose(Symbol(), Period(), shift);
double h  = iHigh(Symbol(), Period(), shift);
double l  = iLow(Symbol(), Period(), shift);

//--- Bar data for the prior candle (index = shift + 1)
double o1 = iOpen(Symbol(), Period(), shift + 1);
double c1 = iClose(Symbol(), Period(), shift + 1);
double h1 = iHigh(Symbol(), Period(), shift + 1);
double l1 = iLow(Symbol(), Period(), shift + 1);

次のセクションでは、「LessStrict」と「Strict」のスイープ定義を実装しています。ユーザーがLessStrictを選択した場合、強気スイープは現在のバーの終値が始値より上であり(c > o)、その安値が前のバーの安値を下回り(l < l1)、終値が前のバーの始値を上回り(c > o1)、かつ前のバーが十字線でない(c1 != o1)場合にシグナルが出ます。弱気スイープは対称的で、現在のバーの終値が始値より下で、高値が前のバーの高値を上回り、終値が前のバーの始値より下で、前のバーが十字線でない場合に発生します。

一方、ユーザーがStrictを選択した場合、EAは条件を厳しくし、強気では現在の終値が前のバーの高値を突破すること、弱気では前の終値が前のバーの安値を下回ることを要求します。このStrictモードは浅い戻りでの「偽の」シグナルを減らし、サポートやレジスタンスの明確な突破を強制します。両方のモードを用意することで、EAは読者に、ロジックの微調整がシグナルの頻度や品質にどのように影響するかを示しています。

// Compute color-change flags
bool bullCC = (c > o && c1 < o1);
bool bearCC = (c < o && c1 > o1);

// Liquidity sweep flags (LessStrict or Strict)
bool bullSweep, bearSweep;
if(SignalStrict == LessStrict)
  {
   bullSweep = (c > o && l < l1 && c > o1 && c1 != o1);
   bearSweep = (c < o && h > h1 && c < o1 && c1 != o1);
  }
else // Strict
  {
   bullSweep = (c > o && l < l1 && c > h1 && c1 != o1);
   bearSweep = (c < o && h > h1 && c < l1 && c1 != o1);
  }

生のスイープ判定の後、EAは任意で色変化フィルターを適用します。ColorChangeOnlyがtrueの場合、「bullSweep &= bullCC; bearSweep &= bearCC;」が実行されます。つまり、ローソク足の色変化と一致しないスイープは即座に破棄されます。多くのトレーダーは、色の変化(たとえば、緑のローソク足の後の赤のローソク足)を反転の勢いの確認と見なすため、この条件でスイープを制御することで、目に見える反発を伴わないスイープを誤ってマークする可能性を減らせます。このシンプルなビット単位のAND演算により、価格構造とローソク足心理という2つの補完的なシグナルが1つのフィルターに巧みに統合されます。

// If only color-change sweeps are desired, AND‐combine with the raw sweep flags
if(ColorChangeOnly)
  {
   bullSweep &= bullCC;    // must also be a bullish color reversal
   bearSweep &= bearCC;    // must also be a bearish color reversal
  }

生のスイープ条件と任意の色変化フィルターを適用した後、EAはUseMAFilterがtrueの場合に移動平均フィルターを確認します。まず「double maValue = 0.0;」を宣言し、MAを3通りの方法のいずれかで計算します。ユーザーが「VWMA」を選択していればカスタムヘルパーCalcVWMA(shift)を呼び出し、「HMA」を選択していればCalcHMA(shift)を呼び出します。それ以外の場合は、組み込みのMAタイプ(SMA、EMA、LWMA、RMA)であると仮定し、「CopyBuffer(MAHandle, 0, shift, 1, buf)」を実行して1つのMA値を取得します。CopyBufferが正確に1つの結果を返さなければ、EAはそのバーでの残りのロジックをスキップして処理を終了します。

maValueが取得できたら、ブール値で「PriceAboveMA ? (c > maValue) : (c < maValue)」をテストします。実際には、PriceAboveMAがtrueの場合、EAはMAより上で終値を付けた強気スイープのみを維持し(弱気スイープは「bearSweep = false」に設定して破棄)、PriceAboveMAがfalseの場合はMAより下で終値を付けた弱気スイープのみを残します。このフィルターにより、流動性スイープはトレンドの文脈で評価され、強気スイープは価格がMAより上の場合のみ重要であり、弱気スイープは価格がMAより下の場合のみ意味を持つようになります。

if(UseMAFilter)
  {
   double maValue = 0.0;

   // Compute MA value at the same 'shift'
   if(MAType == VWMA)
      maValue = CalcVWMA(shift);
   else if(MAType == HMA)
      maValue = CalcHMA(shift);
   else
     {
      // Built‐in MA handle (SMA, EMA, LWMA, RMA)
      double buf[];
      if(CopyBuffer(MAHandle, 0, shift, 1, buf) != 1)
         return;  // no valid MA data available
      maValue = buf[0];
     }

   // Only allow bullish sweeps above MA or bearish sweeps below MA
   bool cond = PriceAboveMA ? (c > maValue) : (c < maValue);
   bullSweep &= cond;
   bearSweep &= !cond;
  }

最後に、すべてのフィルターを適用した後、DetectLiquiditySweepは2つのifブロックで「if(bullSweep)」と「if(bearSweep)」を確認します。bullSweepがtrueの場合、「DrawSignal(shift, true)」を呼び出してチャート上に強気マーカーを表示し、「Bullish sweep detected at [time], price=[close].」のようにEAログにメッセージを書き込みます。bearSweepがtrueの場合も同様に、「DrawSignal(shift, false)」を呼び出し、対応するメッセージを出力します。

どちらかが発生した場合、最後に「Alert("Liquidity Sweep detected on ", Symbol(), " ", EnumToString(Period()));」が呼び出され、画面上にアラートがポップアップ表示されます。「チャート上に描画すること」と「アラートを送信すること」を分離することで、トレーダーは音声やポップアップ通知のみを受け取るか、チャート上に持続的な視覚的参照を表示するかを自由に選択できるようになっています。

// If a bullish sweep remains true after all filters, draw and log it
if(bullSweep)
  {
   DrawSignal(shift, true);
   PrintFormat("Bullish sweep detected at %s, price=%.5f",
               TimeToString(iTime(Symbol(),Period(),shift)), c);
  }

// If a bearish sweep remains true after all filters, draw and log it
if(bearSweep)
  {
   DrawSignal(shift, false);
   PrintFormat("Bearish sweep detected at %s, price=%.5f",
               TimeToString(iTime(Symbol(),Period(),shift)), c);
  }

// In either case, fire a pop‐up alert
if(bullSweep || bearSweep)
  {
   Alert("Liquidity Sweep detected on ", Symbol(), " ", EnumToString(Period()));
  }

DrawSignal(int shift, bool bullish)」関数は、チャートオブジェクトを配置する役割のみを担当します。まずバーのタイムスタンプを「t = iTime(Symbol(),Period(),shift)」で取得し、表示価格を計算します。強気の矢印の場合は安値からArrowOffsetPoints * _Pointを引き、弱気の場合は高値に同じオフセットを加えます。_Pointを使用することで、たとえばEURUSD(0.0001単位)やUSDJPY(0.01単位)など、銘柄に応じた実際の価格刻みでオフセットが測定されます。次に「StringFormat("LS_%I64u", (long)t)」で一意のオブジェクト名を作成します。(long)tはバーの正確な開始時刻を表す64ビット整数であるため、どのバーも同じオブジェクト名を生成することはありません。

描画をおこなう前に、「ObjectFind(0, name)」を呼び出して、既存の同名オブジェクトを削除します。これにより、EAを再適用した場合やチャートのリフレッシュで同じ検出が繰り返されても、チャート上が散乱するのを防ぎます。最後にPlotArrowがtrueの場合、ObjectCreate(...)により計算した価格に色付き矢印(強気は上向き、弱気は下向き)を描画します。PlotArrowがfalseでLblTypeがNoneでない場合は、代わりに短いラベルまたはフルテキストラベル(たとえば「BS」または「Bull Sweep」)を描画します。すべてのチャートオブジェクトコードを1つのルーチンにまとめることで、EAは検出ロジックと視覚的注釈を分離しており、これがクリーンでモジュール化された設計の特徴です。

void DrawSignal(int shift, bool bullish)
  {
   // Get the exact bar start time for this sweep
   datetime t = iTime(Symbol(), Period(), shift);

   // Determine the on‐chart Y‐coordinate: offset a few points above/below the candle
   double price = bullish
                  ? (iLow(Symbol(), Period(), shift)  - ArrowOffsetPoints * _Point)
                  : (iHigh(Symbol(), Period(), shift) + ArrowOffsetPoints * _Point);

   // Compose a unique name using the 64-bit timestamp
   string name = StringFormat("LS_%I64u", (long)t);

   // If an object with that name already exists, delete it first
   if(ObjectFind(0, name) >= 0)
      ObjectDelete(0, name);

   // (Next lines will choose arrow vs. text drawing)
}

内部では、非標準の移動平均を計算する2つのヘルパー関数が用意されています。「CalcVWMA(int shift)」は典型的な出来高加重移動平均(VWMA)を実装しています。まず、分子と分母という2つの集計変数を0.0に初期化します。その後、バーのインデックスがshiftから「shift + MALength – 1」までの各バーについて、終値を「price = iClose(...)」、ティックボリュームを「vol = iVolume(...)」(64ビット整数として格納)で読み取ります。計算時に「numerator += price * (double)vol、denominator += (double)vol」のように明示的にdoubleにキャストすることで、意図しない整数から浮動小数点への変換に関する警告を回避しています。

ループが終了したら、分母が0.0より大きければ「numerator / denominator」を返し、そうでなければ安全のため0.0を返します(ゼロ除算を避けるため)。これにより、各VWMAポイントは過去MALength本のバーの(価格 × 出来高)の合計を出来高の合計で割った値となり、トレーダーが期待する典型的な出来高加重平均が正確に計算されます。

double CalcVWMA(int shift)
  {
   double numerator   = 0.0;
   double denominator = 0.0;

   // Loop over 'MALength' bars, starting at 'shift'
   for(int i = shift; i < shift + MALength; i++)
     {
      double price = iClose(Symbol(), Period(), i);
      long   vol   = iVolume(Symbol(), Period(), i);

      // Accumulate (price × volume) and sum of volume
      numerator   += price * (double)vol;
      denominator += (double)vol;
     }

   // Return VWMA = sum(price×volume) / sum(volume), or 0 if volume = 0
   return (denominator > 0.0) ? (numerator / denominator) : 0.0;
  }

最後に、「CalcHMA(int shift)」はHull移動平均(HMA)を計算します。HMAはラグを減らすために設計された二段階の加重平均です。まず、「half = MALength / 2」を設定し、「i = shift」から「i < shift + half」までループして、各バーに半期間から1までの降順の重みを割り当てます。これにより、加重合計w1と重みの合計sw1を累積し、最終的に「w1 / sw1」で半期間加重移動平均を求めます。次に、「i = shift」から「i < shift + MALength」までループし、各バーにMALengthから1までの重みを割り当て、加重合計w2と重みの合計sw2を計算して「w2 / sw2」により全期間加重移動平均を求めます。

最終的なHMA値は「2 * w1 – w2」です。半期間MAを2倍して全期間MAを引くことで、移動平均は直近の価格変動に対してより敏感になり、多くのトレーダーはこれを動的な市場で優れた指標と見なします。VWMAと同様に、EAは履歴不足に対して保護をおこない、「Bars(Symbol(),Period()) > shift + MALength」が成立しない限りCalcHMAを呼び出しません。

//+------------------------------------------------------------------+
//| Compute Hull Moving Average (HMA)                                |
//+------------------------------------------------------------------+
double CalcHMA(int shift)
  {
   int half = MALength / 2;
   double w1   = 0.0, sw1 = 0.0;
   double w2   = 0.0, sw2 = 0.0;

   // 1) Weighted MA over half period
   for(int i = shift; i < shift + half; i++)
     {
      double p = iClose(Symbol(), Period(), i);
      int    weight = half - (i - shift);
      w1   += p * weight;
      sw1 += weight;
     }
   w1 = (sw1 > 0.0) ? (w1 / sw1) : 0.0;

   // 2) Weighted MA over full period
   for(int i = shift; i < shift + MALength; i++)
     {
      double p = iClose(Symbol(), Period(), i);
      int    weight = MALength - (i - shift);
      w2   += p * weight;
      sw2 += weight;
     }
   w2 = (sw2 > 0.0) ? (w2 / sw2) : 0.0;

   // 3) Final HMA value = 2 * (MA over half) – (MA over full)
   return 2.0 * w1 - w2;
  }


テストと結果

ツールの性能を評価し理解するために、包括的なテストを実施しました。このプロセスでは、過去データを用いたバックテストに加え、実際の市場での即時パフォーマンスも検証しました。以下に、これらのテストから得られた結果の例を示します。

図3:GBPUSDにおけるライブテスト

このチャートは、ツールが流動性スイープを識別する能力を効果的に示しています。明確な弱気スイープの後に強気スイープが続いており、市場の反転や機関投資家の動きを正確に検出する可能性を示しています。このチャートは、ツールが流動性スイープを識別する能力を効果的に示しています。明確な弱気スイープの後に強気スイープが続いており、市場の反転や機関投資家の動きを正確に検出する可能性を示しています。

図4:Step Indexにおけるライブテスト

上記の結果は、ツールが流動性スイープを特定する効果を示しており、明確な視覚マーカーで重要な市場反転を捉えることができ、戦略的な意思決定に役立つ可能性があります。この結果は、ツールが流動性スイープを特定する有効性を示しており、明確な視覚マーカーで主要な市場反転を捉えることができ、戦略的な意思決定に役立つ可能性があります。

図5:EURUSDにおけるバックテスト

このバックテストチャートは、ツールが過去の市場データ内で流動性スイープパターンを正確に検出する能力を示しています。過去の価格変動を分析することで、価格が一時的にサポートまたはレジスタンスレベルを突破した後に反転した事例を浮き彫りにし、潜在的な機関投資家の活動を示唆します。こうして特定されたポイントは、将来の市場反転やトレンド継続を予測するための有用な手掛かりとなります。このチャートは検出メカニズムの有効性を裏付け、ライブ取引での応用に対する信頼性を提供します。


結論

このEAは、過去のバックテストでもライブシミュレーションでも一貫して流動性スイープを特定し、価格が以前のスイングの安値または高値を突破してから再度回復した地点に正確に矢印をマークします。複数のMAフィルター設定においても、シグナルはその後のトレンド継続とよく一致し、実際の市場環境で高い信頼性を示しました。過去のパフォーマンスを確認する場合でも、ライブで動作を観察する場合でも、このEAが機関投資家スタイルの「ストップハント」を捉える能力は一貫して検証されています。これらの知見を活用することで、スイープ検出を自身の戦略に統合する際、再生環境およびリアルタイム環境の両方で、その精度と応答性が実証済みであることを前提に利用できます。





   
ChartProjector
Analytical Comment
Analytics Master
Analytics Forecaster 
Volatility Navigator
Mean Reversion Signal Reaper
Signal Pulse 
Metrics Board 
External Flow
VWAP
Heikin Ashi   FibVWAP  
RSI DIVERGENCE
Parabolic Stop and Reverse (PSAR) 
Quarters Drawerスクリプト
Intrusion Detector
TrendLoom Tool  Quarters Board 
ZigZag Analyzer  Correlation Pathfinder  Market Structure Flip Detector Tool
Correlation Dashboard   Currency Strength Meter 
PAQ Analysis Tool 
Dual EMA Fractal Breaker
Pin bar, Engulfing and RSI divergence
Liquidity Sweep      

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

添付されたファイル |
Liquidity_Sweep.mq5 (19.66 KB)
MQL5での取引戦略の自動化(第19回):Envelopes Trend Bounce Scalping - 取引執行とリスク管理(その2) MQL5での取引戦略の自動化(第19回):Envelopes Trend Bounce Scalping - 取引執行とリスク管理(その2)
この記事では、MQL5でEnvelopes Trend Bounce Scalping戦略の取引実行とリスク管理を実装します。注文の発注、ストップロスやポジションサイズなどのリスク制御をおこないます。最後に、第18回の基盤をもとにバックテストと最適化をおこないます。
知っておくべきMQL5ウィザードのテクニック(第68回): コサインカーネルネットワークでTRIXとWPRのパターンを使用する 知っておくべきMQL5ウィザードのテクニック(第68回): コサインカーネルネットワークでTRIXとWPRのパターンを使用する
前回の記事では、TRIXとWilliams Percent Range (WPR)の指標ペアを紹介しましたが、今回はこの指標ペアを機械学習で拡張する方法について検討します。TRIXとWPRは、トレンド指標とサポート/レジスタンス補完ペアとして組み合わせられます。本機械学習アプローチでは、畳み込みニューラルネットワーク(CNN)を使用し、予測精度を微調整する際にコサインカーネルをアーキテクチャに組み込んでいます。これは常に、MQL5ウィザードと連携してエキスパートアドバイザー(EA)を組み立てるカスタムシグナルクラスファイル内で行われます。。
知っておくべきMQL5ウィザードのテクニック(第69回):SARとRVIのパターンの使用 知っておくべきMQL5ウィザードのテクニック(第69回):SARとRVIのパターンの使用
パラボリックSAR (SAR)と相対活力指数(RVI)は、MQL5のエキスパートアドバイザー(EA)内で併用可能なもう一つのインジケーターペアです。このインジケーターペアは、これまでに取り上げたものと同様に補完的で、SARはトレンドを定義し、RVIはモメンタムを確認します。通常通り、MQL5ウィザードを使用してこのインジケーターペアリングを構築し、その可能性をテストします。
MetaTrader 5機械学習の設計図(第1回):データリーケージとタイムスタンプの修正 MetaTrader 5機械学習の設計図(第1回):データリーケージとタイムスタンプの修正
MetaTrader 5で機械学習を取引に活用する以前に、最も見落とされがちな落とし穴の一つであるデータリーケージに対処することが極めて重要です。本記事では、データリーケージ、特にMetaTrader 5のタイムスタンプの罠がどのようにモデルのパフォーマンスを歪め、信頼性の低い売買シグナルにつながるのかを解説します。この問題の仕組みに踏み込み、その防止戦略を提示することで、実取引環境で信頼できる予測を提供する堅牢な機械学習モデルを構築するための道を切り開きます。