English
preview
無効化されたオーダーブロックをミティゲーションブロックとして再利用する(SMC)

無効化されたオーダーブロックをミティゲーションブロックとして再利用する(SMC)

MetaTrader 5 |
11 0
Hlomohang John Borotho
Hlomohang John Borotho

目次

  1. はじめに
  2. ミティゲーションオーダーブロックの理解
  3. 導入手順
  4. バックテスト結果
  5. 結論


はじめに

SMC(スマートマネーコンセプト)取引において、オーダーブロックは、機関投資家が特定方向に価格を動かす前にポジションを積み上げたり分配したりする重要なエリアを表します。しかし、すべてのオーダーブロックが常に有効であるわけではなく、市場状況の変化に伴い一部は無効化されることがあります。オーダーブロックが維持されなかった場合でも、その重要性が完全に失われるわけではありません。むしろ、潜在的なミティゲーションブロックに変化する可能性があります。この概念は、価格がしばしば無効化されたゾーンに戻り、残された注文を「ミティゲート」した後に新しいトレンド方向に進むことに着目しており、トレーダーが機関投資家の意図や市場構造の動きをより深く理解できるようになります。

無効化されたオーダーブロックをミティゲーションブロックとして再利用する考え方により、トレーダーは市場のバイアスが変化した後にスマートマネーがどこで再参入する可能性があるかを認識できます。価格がこれらのエリアとどのように相互作用するかを研究することで、新しい方向性の流れに沿った高確率のエントリーポイントを見極めることができます。オーダーブロックからミティゲーションブロックへのこの変換を理解することで、エントリータイミングの精度向上とリスク管理に役立ちます。


ミティゲーションオーダーブロックの理解

通常、価格が上昇すると、そのスイングロー付近またはその位置に強気のオーダーブロックが残される傾向があります。これは、買い手が価格を上昇させる前に最後に介入したポイントを示しています。ラリーが続くと、価格はしばしば旧高値、過去のオーダーブロック、あるいはブレーカーストラクチャーなどの抵抗レベルに接近します。このレベルでは、買い手から売り手へ注文フローがシフトしていることを示す明確なサイン、例えば上ヒゲによる拒否や弱気ローソク足の出現を確認します。市場がそのレベルから下落を始め、その後再び同じ抵抗ゾーンに戻ってくる場合、次に注目すべきは市場がそのレベルから再度下落する意思を示すかどうかです。これにより売り圧力が機能していることが確認でき、弱気の動きが継続する可能性を示唆します。

市場が下落し始めると、一般的に「M字型」のパターンが形成されます。これは、抵抗レベル付近で強気の勢いが枯渇したことを示す失敗スイングを表します。M字の2つ目のピークは通常、新しい高値を形成できず、買い手の力が衰えている一方で売り手が介入し始めていることを示します。この価格の動きは、キーとなる安値を下抜けすることで市場構造が変化したと確認されます。これは「マーケットストラクチャーシフト(MSS)」と呼ばれ、スマートマネーや機関投資家が価格を下げるためにポジションを取っていることを裏付けます。市場は強気環境から弱気環境へと移行します。

このシナリオでは、短期安値から短期高値までの範囲に注目し、以前に買いが集中したエリアを特定します。価格の短期ラリーは、ミティゲーションブロックとして知られる特定の機関参照ポイントに注目を集めます。このブロック内またはブロックに戻る価格の戻りは、スマートマネーが優勢な弱気バイアスに沿って再参入する機会を提供します。このゾーンが重要となる理由は、元の強気オーダーブロックが維持されなかったためです。強い売り圧力によって尊重されず、機関の注文フローが蓄積(買い)から分配(売り)へシフトしたことを示しています。これにより、価格はこのブロックをミティゲートした後、さらに下落する可能性が高くなります。


導入手順

//+------------------------------------------------------------------+
//|                                            OB_&_MitigationOB.mq5 |
//|                                  Copyright 2024, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"

#include <Trade/Trade.mqh>
#include <Arrays\ArrayObj.mqh>

CTrade trade;
CArrayObj obList;

#define BullOB clrLime
#define BearOB clrRed
#define ViolatedBullOB clrBlue
#define ViolatedBearOB clrMagenta

//+------------------------------------------------------------------+
//|                           Input parameters                       |
//+------------------------------------------------------------------+
input double Lots = 0.11;
input int takeProfit = 3000;
input double stopLoss = 2000;
input int Time1Hstrt = 1;
input int Time1Hend = 10;

まず、エキスパートアドバイザー(EA)の基盤と構造を定義します。このEAは、SMCフレームワーク内でオーダーブロックとそのミティゲーションブロックを識別し管理するよう設計されています。次に、重要なライブラリを2つインクルードします。Trade/Trade.mqhは注文実行などの取引操作を処理するため、Arrays/ArrayObj.mqhはオブジェクトの動的コレクションを管理するために使用されます。本ケースでは、検出されたオーダーブロックデータを効率的に格納するために利用されます。「CTrade trade」の宣言により取引ハンドラが初期化され、「CArrayObj obList」がランタイム中に検出されたすべてのオーダーブロックを格納するコンテナを作成します。

次に、チャート上で視覚的にわかりやすくするための色定数を定義します。緑色(clrLime)は強気オーダーブロック、赤色(clrRed)は弱気オーダーブロック、さらに青やマゼンタなどの異なる色は無効化されたオーダーブロック(ミティゲーションブロック)を表します。これらの視覚的な手がかりは、有効なゾーンと無効化されたゾーンを区別する上で非常に重要です。続く入力パラメータでは、トレーダーがEAの挙動をカスタマイズできます。ポジションサイズ(Lots)、利益および損失目標(takeProfitおよびstopLoss)、および取引を特定の市場セッションに限定する時間ベースのフィルタ(Time1HstrtおよびTime1Hend)などです。この初期設定により、EAがオーダーブロックを検出、分類し、ミティゲーションゾーンとして再利用するための視覚的、論理的、運用上の基盤が整います。

//+------------------------------------------------------------------+
//|                         OrderBlock Class                         |
//+------------------------------------------------------------------+
class COrderBlock : public CObject
{
public:
   int direction;
   datetime time;
   double high;
   double low;
   bool violated;
   datetime violatedTime;
   bool traded;
   bool inTrade;
   string identifier;
   string tradeComment;

   COrderBlock()
   {
      traded = false;
      violated = false;
      inTrade = false;
      tradeComment = "";
   }

   void UpdateIdentifier()
   {
      identifier = IntegerToString(time) + IntegerToString(direction) + IntegerToString(violated);
   }

   void draw(datetime tmS, datetime tmE, color clr)
   {
      UpdateIdentifier();
      string objOB = "OB_REC_" + identifier;
      if(ObjectFind(0, objOB) == -1)
      {
         ObjectCreate(0, objOB, OBJ_RECTANGLE, 0, time, low, tmS, high);
         ObjectSetInteger(0, objOB, OBJPROP_FILL, true);
         ObjectSetInteger(0, objOB, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objOB, OBJPROP_BACK, true);
      }

      string objtrade = "OB_TRADE_" + identifier;
      if(ObjectFind(0, objtrade) == -1)
      {
         ObjectCreate(0, objtrade, OBJ_RECTANGLE, 0, tmS, high, tmE, low);
         ObjectSetInteger(0, objtrade, OBJPROP_FILL, true);
         ObjectSetInteger(0, objtrade, OBJPROP_COLOR, clr);
         ObjectSetInteger(0, objtrade, OBJPROP_BACK, true);
      }
   }

   void RemoveObjects()
   {
      UpdateIdentifier();
      string objOB = "OB_REC_" + identifier;
      string objtrade = "OB_TRADE_" + identifier;
      if(ObjectFind(0, objOB) >= 0) ObjectDelete(0, objOB);
      if(ObjectFind(0, objtrade) >= 0) ObjectDelete(0, objtrade);
   }
};

//+------------------------------------------------------------------+
//|                           Global variables                       |
//+------------------------------------------------------------------+
COrderBlock *activeMitigationOB = NULL;
int activeMitigationDirection = 0;

次に、COrderBlockクラスを定義します。このクラスは、EA内でオーダーブロックを表現し、管理するための中核データ構造として機能します。CObjectを継承しているため、インスタンスはオブジェクト配列(CArrayObj)内に格納、操作可能です。各オーダーブロックは、方向(強気または弱気)、時間、最高値および最安値、さらにviolated、traded、inTradeといった状態を格納します。identifierやtradeCommentなどの追加識別子は、チャート上で各オーダーブロックを一意に参照し、関連する取引ロジックを追跡するのに役立ちます。コンストラクタはデフォルト状態を初期化し、新たに検出されたオーダーブロックが未取引かつ有効な状態で開始されるようにし、チャートや取引との相互作用前にクリーンな基準を確立します。

このクラスには、視覚化とライフサイクル管理を扱う強力なメソッドも含まれています。UpdateIdentifier()メソッドは、時間、方向、無効化状態に基づいて一意の文字列を生成し、複数のオーダーブロックを区別するために不可欠です。draw()関数は、オーダーブロックゾーンおよび関連する取引領域をチャート上に色付き矩形として描画し、各ブロックが視覚的に明確で重ならないようにします。一方、RemoveObjects()は、これらのグラフィカルオブジェクトがもはや有効でない場合に削除し、チャートの視認性とパフォーマンスを維持します。最後に、現在アクティブなミティゲーションオーダーブロックとその方向性を追跡するためのグローバル変数activeMitigationOBとactiveMitigationDirectionが宣言され、オーダーブロックの検出、無効化、取引ロジックをEA内で連携させる橋渡しとなります。

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   trade.SetExpertMagicNumber(76543);
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   for(int i = obList.Total()-1; i >= 0; i--)
   {
      COrderBlock* ob = obList.At(i);
      ob.RemoveObjects();
      delete ob;
   }
   obList.Clear();
}

このセクションでは、EAの初期化および初期化解除処理を定義し、取引およびグラフィカルリソースを適切に管理できるようにします。OnInit()関数は、システムがおこなった取引を識別するための一意のマジックナンバーを割り当て、ポジションの正確な追跡と管理を可能にします。一方、OnDeinit()関数は、チャート上のすべてのグラフィカルオーダーブロックオブジェクトを削除し、各オーダーブロックインスタンスに割り当てられたメモリを解放することで、クリーンな終了処理を保証します。この構造により、データの漏れやチャートの混雑を防ぎ、EAの再起動やシャットダウン時にも安定かつ効率的な取引環境を維持できます。

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   if(isNewBar())
   {
      CheckForClosedTradesAndCleanup();
      CheckForViolations();
      getOrderB();
      CheckForTradeEntries();
   }
}

//+------------------------------------------------------------------+
//| Helpers to find bars and swings safely                           |
//+------------------------------------------------------------------+
int BarIndexFromTimeSafely(datetime t)
{
   int idx = iBarShift(_Symbol, _Period, t, true);
   if(idx < 0) idx = Bars(_Symbol, _Period) - 1;
   return(idx);
}

int FindHighestAfter(datetime t, int lookbackBars)
{
   int obIdx = BarIndexFromTimeSafely(t);
   int start = MathMax(1, obIdx - lookbackBars);
   int count = obIdx - start;
   if(count <= 0) return(-1);
   int idx = iHighest(_Symbol, _Period, MODE_HIGH, count, start);
   return(idx);
}

int FindLowestAfter(datetime t, int lookbackBars)
{
   int obIdx = BarIndexFromTimeSafely(t);
   int start = obIdx + 1;
   int maxPossible = Bars(_Symbol, _Period) - start;
   int count = MathMin(lookbackBars, maxPossible);
   if(count <= 0) return(-1);
   int idx = iLowest(_Symbol, _Period, MODE_LOW, count, start);
   return(idx);
}

OnTick()関数は、EAのメイン実行ループとして機能し、各市場ティックにおけるリアルタイムの意思決定を担当します。主に、isNewBar()条件を使用して新しいバーが確定した時にのみロジックを実行し、同じローソク足内での冗長な処理を防ぎます。新しいバーごとに、決済済み取引のクリーンアップ、無効化されたオーダーブロックの確認、新たな潜在オーダーブロックの特定、有効な取引エントリーチャンスのスキャンなどの重要なタスクを実行します。この構造化されたアプローチにより、リソースの効率的な使用が保証され、システムに過負荷をかけることなく戦略の論理的な流れを維持できます。

これらの主要処理をサポートするヘルパー関数として、BarIndexFromTimeSafely()、FindHighestAfter()、FindLowestAfter()が設計されています。BarIndexFromTimeSafely()は、異常な市場状況やデータ欠損があっても、指定された時間から正しいバーインデックスを安全に取得できるようにします。FindHighestAfter()およびFindLowestAfter()は、定義された遡及期間内で主要なスイングハイとスイングローを特定し、市場構造の把握やオーダーブロックの挙動確認に不可欠です。これらの関数は連携して、精密かつ適応的なオーダーブロック分析の基盤を形成します。

//+------------------------------------------------------------------+
//| Check for violations of existing orderblocks                     |
//+------------------------------------------------------------------+
void CheckForViolations()
{
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   for(int i = 0; i < obList.Total(); i++)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.violated || ob.traded) continue;

      if(ob.direction == 1 && currentBid < ob.low)
      {
         if(activeMitigationOB != NULL)
         {
            activeMitigationOB = NULL;
            activeMitigationDirection = 0;
         }
         
         ob.violated = true;
         ob.violatedTime = iTime(_Symbol, PERIOD_CURRENT, 0);
         ob.traded = false;
         ob.UpdateIdentifier();
         activeMitigationOB = ob;
         activeMitigationDirection = -1;
         Print("Bullish OB violated and becomes BEARISH mitigation: ", TimeToString(ob.time));
      }
      else if(ob.direction == -1 && currentAsk > ob.high)
      {
         if(activeMitigationOB != NULL)
         {
            activeMitigationOB = NULL;
            activeMitigationDirection = 0;
         }
         
         ob.violated = true;
         ob.violatedTime = iTime(_Symbol, PERIOD_CURRENT, 0);
         ob.traded = false;
         ob.UpdateIdentifier();
         activeMitigationOB = ob;
         activeMitigationDirection = 1;
         Print("Bearish OB violated and becomes BULLISH mitigation: ", TimeToString(ob.time));
      }
   }
}

CheckForViolations()関数は、既存のすべてのオーダーブロックを継続的に監視し、現在の市場価格の動きによってどれが無効化されたかを判断する役割を持ちます。まず現在のBid価格とAsk価格を取得し、格納されているすべてのオーダーブロックを順番に確認します。この際、すでに取引済みまたは無効化済みとしてマークされているブロックはスキップされます。

強気オーダーブロックの場合、Bid価格がブロックの最安値を下回ると無効とみなされます。弱気オーダーブロックの場合は、Ask価格がブロックの最高値を上回ると無効と判定されます。無効化が検出されると、システムはオーダーブロックの状態を更新し、無効化時刻を記録します。その後、アクティブなミティゲーションブロック参照をリセットし、無効化されたブロックを反対方向のバイアスを持つアクティブなミティゲーションオーダーブロックとして再割り当てします。

このように市場状態が動的に変化する中で、EAは価格動向の変化に適応し、以前は強力だったゾーンを新しいミティゲーションエリアとして扱うことができます。これにより、オーダーフローのシフトに対する戦略の応答性が効果的に向上します。

//+------------------------------------------------------------------+
//| Check for trade entries in orderblocks                           |
//+------------------------------------------------------------------+
void CheckForTradeEntries()
{
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);

   for(int i = 0; i < obList.Total(); i++)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.traded) continue;

      bool priceInZone = false;
      bool isBullishTrade = false;
      bool isMitigation = ob.violated;

      if(!ob.violated)
      {
         if(ob.direction == 1 && currentAsk < ob.high && currentAsk > ob.low)
         {
            priceInZone = true;
            isBullishTrade = true;
         }
         else if(ob.direction == -1 && currentBid > ob.low && currentBid < ob.high)
         {
            priceInZone = true;
            isBullishTrade = false;
         }
      }
      else
      {
         if(ob.direction == 1 && currentBid > ob.low && currentBid < ob.high)
         {
            priceInZone = true;
            isBullishTrade = false;
         }
         else if(ob.direction == -1 && currentAsk < ob.high && currentAsk > ob.low)
         {
            priceInZone = true;
            isBullishTrade = true;
         }
      }

      if(priceInZone)
      {
         if(isMitigation)
         {
            if(activeMitigationOB == NULL || activeMitigationOB != ob)
            {
               continue;
            }
         }

         if(isBullishTrade)
         {
            ExecuteBuyTrade(ob);
         }
         else
         {
            ExecuteSellTrade(ob);
         }
         ob.traded = true;
         break;
      }
   }
}

CheckForTradeEntries()関数は、EAがアクティブなオーダーブロックやミティゲーションブロックとの価格の相互作用に基づき、いつどこで取引を実行するかを決定するコアロジックを担当します。まず現在のBid価格とAsk価格を取得し、格納されているすべてのオーダーブロックを順番に確認します。この際、すでに取引に使用されたブロックはスキップされます。次に、現在の価格が有効なオーダーブロックゾーン内で取引されているかを確認し、ブロックの性質(強気または弱気)に基づいて取引方向を特定します。無効化されていないオーダーブロックの場合、Ask価格が強気ゾーンに入り込むと買い取引が発動され、Bid価格が弱気ゾーンに上昇すると売り取引が発動されます。

一方、無効化されたオーダーブロック(現在はミティゲーションブロックとして機能)では、取引ロジックが逆方向に適用され、市場構造における反応の変化を反映します。システムは、現在アクティブなミティゲーションブロックのみが取引を発動できるようにし、不要な再エントリーや衝突を回避します。条件が満たされると、対応するオーダーブロックのパラメータを使用して買いまたは売り取引を実行し、そのブロックを「traded」とマークして重複ポジションを防ぎます。この二層構造のロジック(有効ブロックとミティゲートブロックの両方を扱うこと)は、スマートマネー取引において動的かつ適応的な構造を提供し、市場状況の変化に応じてアルゴリズムが知的に反応できるようにします。

//+------------------------------------------------------------------+
//| Execute a buy trade                                              |
//+------------------------------------------------------------------+
void ExecuteBuyTrade(COrderBlock* ob)
{
   double entry = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   int swingHighIdx = FindHighestAfter(ob.time, 20);
   double tp = (swingHighIdx>0) ? getHigh(swingHighIdx) : entry + takeProfit * _Point;
   double sl = NormalizeDouble(ob.low - stopLoss * _Point, _Digits);

   string comment = "Bull_OB_" + ob.identifier;
   ob.tradeComment = comment;

   if(trade.Buy(Lots, _Symbol, entry, sl, tp, comment))
   {
      color obColor = ob.violated ? ViolatedBullOB : BullOB;
      ob.draw(ob.time, iTime(_Symbol, PERIOD_CURRENT, 0), obColor);
      ob.inTrade = true;
      Print("Buy trade executed. Type: ", ob.violated ? "Mitigation" : "Regular", 
            " OB Time: ", TimeToString(ob.time), " Entry: ", entry, " SL: ", sl, " TP: ", tp);
   }
}

//+------------------------------------------------------------------+
//| Execute a sell trade                                             |
//+------------------------------------------------------------------+
void ExecuteSellTrade(COrderBlock* ob)
{
   double entry = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   int swingLowIdx = FindLowestAfter(ob.time, 20);
   double tp = (swingLowIdx>0) ? getLow(swingLowIdx) : entry - takeProfit * _Point;
   double sl = NormalizeDouble(ob.high + stopLoss * _Point, _Digits);

   string comment = "Bear_OB_" + ob.identifier;
   ob.tradeComment = comment;

   if(trade.Sell(Lots, _Symbol, entry, sl, tp, comment))
   {
      color obColor = ob.violated ? ViolatedBearOB : BearOB;
      ob.draw(ob.time, iTime(_Symbol, PERIOD_CURRENT, 0), obColor);
      ob.inTrade = true;
      Print("Sell trade executed. Type: ", ob.violated ? "Mitigation" : "Regular", 
            " OB Time: ", TimeToString(ob.time), " Entry: ", entry, " SL: ", sl, " TP: ", tp);
   }
}

ExecuteBuyTrade()およびExecuteSellTrade()関数は、オーダーブロックまたはミティゲーションブロックがエントリー条件を満たした際に、取引を正確に実行する役割を担います。買いシナリオでは、システムは現在のAsk価格をエントリーレベルとして使用し、次に検出されたスイングハイに基づいてテイクプロフィットターゲットを計算します。有効なスイングハイが見つからない場合は、takeProfit入力で定義された固定ピップ距離をデフォルトとして使用します。ストップロスはオーダーブロックの最安値のすぐ下に設定され、リスク管理された構成となります。その後、取引には追跡用の説明コメントが付与され、チャート上には適切な色で視覚的に反映されます。通常の強気ブロックには緑色、ミティゲートされたブロックには青色が使用されます。この構造により、論理上およびチャート上で通常の取引とミティゲーションベースの取引を明確に区別できます。

売り取引ロジックも同様に反転したプロセスで実行されます。エントリーレベルは現在のBid価格を使用し、テイクプロフィットは最も近いスイングロー、または固定のフォールバック値によって決定されます。ストップロスはオーダーブロックの最高値のすぐ上に配置され、買い設定と対称のリスクリワードバランスを維持します。識別用に明確な取引コメントが生成され、対応する弱気またはミティゲートされた弱気の色(赤またはマゼンタ)がチャートに適用され、アクティブな取引ゾーンを強調します。両関数とも、inTradeフラグをtrueに設定して、各オーダーブロックが一度だけポジションを発動するようにします。このモジュール設計により、買いと売りの実行ロジックが明確に分離され、スマートマネーコンセプトに沿った構造化され、チャート上で視覚的に追跡可能かつルールに基づいた取引動作を実現します。

//+------------------------------------------------------------------+
//| Detect new orderblocks                                           |
//+------------------------------------------------------------------+
void getOrderB()
{
   MqlDateTime structTime;
   TimeCurrent(structTime);
   static int prevDay = 0;

   if(structTime.hour >= Time1Hstrt && structTime.hour < Time1Hend)
   {
      if(prevDay != structTime.day)
      {
         prevDay = structTime.day;

         for(int i = 3; i < 100; i++)
         {
            if(i + 3 >= Bars(_Symbol, _Period)) continue;

            if(getOpen(i+2) > getClose(i+2) &&
               getClose(i+1) > getOpen(i+1) &&
               getClose(i) > getOpen(i))
            {
               COrderBlock* ob = new COrderBlock();
               ob.direction = 1;
               ob.time = getTime(i+2);
               ob.high = getHigh(i+2);
               ob.low = getLow(i+2);
               ob.violated = false;
               ob.traded = false;
               ob.UpdateIdentifier();
               obList.Add(ob);
               Print("Bullish Order Block detected at: ", TimeToString(ob.time));
            }
            else if(getOpen(i+2) < getClose(i+2) &&
                    getClose(i+1) < getOpen(i+1) &&
                    getClose(i) < getOpen(i))
            {
               COrderBlock* ob = new COrderBlock();
               ob.direction = -1;
               ob.time = getTime(i+2);
               ob.high = getHigh(i+2);
               ob.low = getLow(i+2);
               ob.violated = false;
               ob.traded = false;
               ob.UpdateIdentifier();
               obList.Add(ob);
               Print("Bearish Order Block detected at: ", TimeToString(ob.time));
            }
         }
      }
   }
}

//+------------------------------------------------------------------+
//| Check for closed trades and cleanup objects after closure        |
//+------------------------------------------------------------------+
void CheckForClosedTradesAndCleanup()
{
   for(int i = obList.Total()-1; i >= 0; i--)
   {
      COrderBlock* ob = obList.At(i);
      if(ob.inTrade)
      {
         if(!PositionWithCommentExists(ob.tradeComment))
         {
            Print("Trade closed for OB: ", ob.identifier, " -> cleaning up drawings");
            ob.RemoveObjects();
            ob.inTrade = false;
            ob.traded = false;
            ob.tradeComment = "";

            if(activeMitigationOB == ob)
            {
               activeMitigationOB = NULL;
               activeMitigationDirection = 0;
               Print("Active mitigation OB cleared after trade closure");
            }

            obList.Delete(i);
            delete ob;
         }
      }
   }
}

getOrderB()関数は、シンプルな3本ローソク足パターンのロジックに基づき、新規のオーダーブロックを検出する役割を担っています。この関数は、ユーザーが指定したTime1HstrtおよびTime1Hendによって定義される特定の時間帯内のみをスキャンすることで、管理された取引時間中にのみ検出がおこなわれるよう設計されています。まず、強気のオーダーブロック条件を確認します。これは、1本の陰線の後に2本連続した陽線が形成されるパターンであり、市場における蓄積(アキュムレーション)とセンチメントの転換を示唆します。このパターンが検出されると、新しいCOrderBlockオブジェクトが生成され、方向、時間、高値、安値などの属性が初期化されたうえで、グローバルリストに追加されます。

同様に、弱気のオーダーブロックは、1本の陽線の後に2本連続した陰線が形成された場合に検出されます。この構成は、分配および今後の価格下落の可能性を示すものです。このように、時間制御されモジュール化された検出ロジックにより、1日において重要なオーダーブロックのみが登録され、不要な情報の増加を抑えつつ、実用性の高い価格ゾーンに集中できるようになっています。

CheckForClosedTradesAndCleanup()関数は、この検出プロセスを補完し、チャートの視認性とシステムの整合性を維持します。オーダーブロックに紐づく取引が決済されると、この関数は当該ブロックの取引コメントを持つポジションが存在するかを確認することで、その取引状態を検証します。取引がすでに存在しない場合、関連するすべての視覚要素(矩形オブジェクトやハイライト表示)がチャートから削除され、アクティブなセットアップとの混同を防止します。さらに、オーダーブロックの内部状態をリセットし、必要に応じてミティゲーション参照をクリアしたうえで、該当するCOrderBlockオブジェクトをグローバルリストから削除します。これによりメモリが解放されます。このクリーンアップサイクルにより、システムは常に動的かつ効率的に保たれ、関連性のある有効なオーダーブロックのみが追跡される一方で、不要となったものはメモリおよびチャート環境の両方から安全に除去されます。

//+------------------------------------------------------------------+
//| Check if position with specific comment exists                   |
//+------------------------------------------------------------------+
bool PositionWithCommentExists(string comment)
{
   if(StringLen(comment) == 0) return(false);
   for(int i = PositionsTotal()-1; i >= 0; i--)
   {
      ulong ticket = PositionGetTicket(i);
      if(ticket > 0 && PositionSelectByTicket(ticket))
      {
         string c = PositionGetString(POSITION_COMMENT);
         if(c == comment) return(true);
      }
   }
   return(false);
}

//+------------------------------------------------------------------+
//| Helper functions                                                 |
//+------------------------------------------------------------------+
bool isNewBar()
{
   static datetime lastBar;
   datetime currentBar = iTime(_Symbol, _Period, 0);
   if(lastBar != currentBar)
   {
      lastBar = currentBar;
      return true;
   }
   return false;
}

double getHigh(int index) { return iHigh(_Symbol, _Period, index); }
double getLow(int index) { return iLow(_Symbol, _Period, index); }
double getOpen(int index) { return iOpen(_Symbol, _Period, index); }
double getClose(int index) { return iClose(_Symbol, _Period, index); }
datetime getTime(int index) { return iTime(_Symbol, _Period, index); }

PositionWithCommentExists()関数は、特定の識別用コメントを持つポジションが現在も存在しているかを確認することで、取引状態の整合性を維持する重要な役割を果たします。各オーダーブロックに基づく取引には一意のコメントが付与されているため、関数はすべてのポジションを反復処理し、それぞれのコメントを取得して、入力として与えられたコメントと比較します。一致するものが見つかった場合はtrueを返し、取引が依然として有効であることを示します。一方、一致しない場合はfalseを返します。この検証プロセスは、取引のクリーンアップ処理やミティゲーションロジックなどの機能において不可欠です。これにより、関連する取引が実際に決済された後にのみ、オブジェクトの削除や内部状態のリセットといった処理が実行されることが保証されます。この仕組みを採用することで、EAは同一取引に対する二重処理を防止し、進行中の取引と完了済み取引を正確に区別した状態を維持できます。

続くヘルパー関数は、データ取得およびバー管理を支援するためのユーティリティとして機能します。isNewBar()関数は、新しいバーが形成されたかどうかを判定し、オーダーブロックの検出や無効化チェックといった主要な処理が、1バーにつき1回のみ実行されるようにします。これにより、毎ティックごとの不要な重複処理を防ぐことができます。また、getHigh()、getLow()、getOpen()、getClose()、getTime()といったヘルパー関数は、チャート上の特定のローソク足属性を取得するための抽象化されたインターフェースとして機能します。これにより、コードの可読性が向上し、EA全体にわたって一貫したデータ処理が可能となります。これらの小規模ながら不可欠なツール群が基盤となることで、オーダーブロックの識別や取引執行といった、より高度で複雑な処理が円滑かつ効率的に動作するよう支えられています。


バックテスト結果

バックテストは、デフォルト設定を使用し、1時間足(1H)において、約2か月間の検証期間(2025年6月2日から2025年7月29日まで)で実施されました。



結論

要約すると、本EAは、オーダーブロックおよびミティゲーションオーダーブロックといったスマートマネーコンセプト(SMC)を、構造化されたルールベースの取引システムとして統合しています。まず、価格構造およびローソク足の形成に基づいて、有効な強気および弱気のオーダーブロックを特定し、その後、無効化を監視することで、無効となったオーダーブロックをミティゲーションゾーンへと移行させます。検出、検証、執行の各メカニズムを通じて、価格が重要な水準へ再侵入した際に買いまたは売りのポジションを実行し、取引を動的に管理します。また、本EAはオーダーブロックに関するすべての視覚表現を管理し、取引の開始および終了に応じて矩形オブジェクトの描画および削除をおこないます。これにより、チャート上には常に最も関連性の高い機関投資家由来のゾーンのみが表示されるようになっています。

結論として、本システムは、市場構造および機関投資家の価格行動をMQL5環境下で体系的に解釈するための、極めて方法論的なアプローチを示しています。オーダーブロックの認識、それらがミティゲーションゾーンへと移行する過程、そしてその後の取引管理を自動化することで、スマートマネーが無効化されたゾーンを流動性確保やトレンド継続のセットアップとして再利用する本質的な考え方を的確に捉えています。さらに、クリーンアップ処理、ヘルパー関数、厳格な検証条件を組み込むことで、信頼性と実運用に近い挙動が強化されています。総合的に見て、このプロジェクトは、技術的な実装と市場構造に基づく取引の概念を橋渡しするものであり、プロフェッショナルトレーダーがオーダーフローの動態をどのように解釈し、行動に移すかを再現する、完全自律型の取引フレームワークを提供しています。

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

添付されたファイル |
EAのサンプル EAのサンプル
一般的なMACDを使ったEAを例として、MQL4開発の原則を紹介します。
知っておくべきMQL5ウィザードのテクニック(第83回): ストキャスティクスとFrAMAのパターンの使用 - 行動アーキタイプ 知っておくべきMQL5ウィザードのテクニック(第83回): ストキャスティクスとFrAMAのパターンの使用 - 行動アーキタイプ
ストキャスティクスとフラクタル適応型移動平均(FrAMA: Fractal Adaptive Moving Average)は、互いに補完し合う特性を持っており、MQL5のエキスパートアドバイザー(EA)で使える指標ペアの1つです。ストキャスティクスはモメンタムの変化を捉えるために使用し、FrAMAは現在のトレンドを確認するために利用します。本記事では、これら2つのインジケーターの組み合わせについて、MQL5ウィザードを活用して構築およびテストをおこない、その有効性を検証します。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
プライスアクション分析ツールキットの開発(第44回):MQL5でVWMAクロスオーバーシグナルEAを構築する プライスアクション分析ツールキットの開発(第44回):MQL5でVWMAクロスオーバーシグナルEAを構築する
本記事では、MetaTrader 5向けに開発されたVWMA(出来高加重移動平均)クロスオーバーシグナルツールを紹介します。このツールは、価格動向と出来高を組み合わせることで、強気および弱気の反転ポイントを特定することを目的としています。このエキスパートアドバイザー(EA)は、チャート上に明確な買いと売りシグナルを直接表示し、豊富な情報を持つパネルを備えるとともに、ユーザーによる詳細なカスタマイズが可能で、実践的な取引戦略の強力な補助となります。