English Deutsch
preview
MQL5でのスイング極値とプルバック(第2回):EAによる戦略の自動化

MQL5でのスイング極値とプルバック(第2回):EAによる戦略の自動化

MetaTrader 5 |
21 2
Hlomohang John Borotho
Hlomohang John Borotho

目次

  1. はじめに
  2. インジケータの概要
  3. 導入手順
  4. インジケータデモ
  5. 結論


はじめに

反転を早期に捉えたい方、勢いに逆らってエントリーすることが多い方、あるいは価格が本当の極値に達した場所を十分に理解できていない方に向けた記事です。価格が何の根拠もない中途半端な場所で反転することは、ほとんどありません。価格は伸び続け、勢いを増し、市場参加の厚みが薄れてリスクのバランスが静かに変化する極端な水準まで押し進められた後に反転する傾向があります。こうした状況は、多くの場合、まず下位足(LTF)で現れます。スイングハイやスイングローが密集し始め、価格が直近の市場構造を超えて推移し始めるためです。Swing Extremes and Pullbacksインジケータは、この考え方に基づいて設計されています。相場の方向を追いかけるのではなく、価格が直近の市場構造に対して「どれだけ遠く、どれだけ急速に」動いたかというオーバーエクステンション(行き過ぎ)を検出します。

LTFにおけるスイングの挙動へ着目することで、このインジケータは価格が直近の市場構造の境界を上回っている、または下回っている領域を特定します。そして、プルバックや平均回帰が統計的に起こりやすい状況を示します。天井や底を予測するのではなく、構造的な根拠が形成されるにつれてそれに反応し、ミクロな市場構造(マイクロストラクチャ)の情報を売買判断に使える反転ゾーンへ変換します。その結果、このインジケータは反転を単なる推測としてではなく、価格が自身の極端な水準へ到達したことに対する測定可能な反応として捉えるためのフレームワークを提供します。


インジケータの概要と理解

スイングの極値とプルバック:価格の反転は、多くの場合、LTFの直近の市場構造を明確に超えるオーバーエクステンションの後に発生します。このインジケータでは、まず最新のLTFスイングハイとスイングローを検出し、それらを基に短期的な市場構造のレンジを定義します。このレンジは、LTFにおける「適正な価格帯」を表しています。つまり、市場が過去にその価格帯を受け入れ、通常どおりに価格が上下していた領域です。

極端な弱気局面では、価格はLTFの直近安値を大きく下抜け、直近の市場構造を大幅に超えて下方向へ拡大します。この下抜けは、相場継続のシグナルとしてではなく、売り圧力が消耗し始めている証拠として解釈されます。価格が直近のスイングローだけでなく、LTFの市場構造全体よりも大きく下回って推移すると、売り圧力が統計的に行き過ぎた状態であると判断されます。その結果、強気方向へのプルバック、あるいは過去の市場構造へ向かう平均回帰が起こる可能性が高まる状況としてインジケータが検出します。

極端な弱気相場

一方、強い上昇局面では逆の動きが見られます。価格はLTFの直近高値を上抜け、これまでの市場構造を置き去りにして、オーバーエクステンション状態へ入ります。インジケーターは、このブレイクアウトを追いかけるのではなく、この価格の乖離を市場の脆弱性として解釈します。つまり、買い圧力が弱まり始め、弱気方向へのプルバックが発生しやすいゾーンとして判断します。どちらの場合も、このインジケーターは正確な天井や底を予測することを目的としていません。その代わり、市場構造の不均衡を特定することへ重点を置いています。これにより、トレーダーは反転を単なる逆張り取引としてではなく、価格が自らの極端な水準へ到達したことに対する自然な反応として捉えられるようになります。

極端な強気相場


導入手順

//+------------------------------------------------------------------+
//|                                               Swing Extremes.mq5 |
//|                                  Copyright 2025, MetaQuotes Ltd. |
//|                     https://www.mql5.com/ja/users/johnhlomohang/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2025, MetaQuotes Ltd."
#property link      "https://www.mql5.com/ja/users/johnhlomohang/"
#property version   "1.00"
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_plots   2

#property indicator_label1  "Buy Signal"
#property indicator_type1   DRAW_ARROW
#property indicator_color1  clrLime
#property indicator_width1  2

#property indicator_label2  "Sell Signal"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrRed
#property indicator_width2  2

//+------------------------------------------------------------------+
//| Inputs                                                           |
//+------------------------------------------------------------------+
input ENUM_TIMEFRAMES HTF = PERIOD_H1;
input ENUM_TIMEFRAMES LTF = PERIOD_M5;
input int             SwingBars = 3;
input int             ATR_Period      = 14;      // ATR calculation period
input double          ATR_Multiplier  = 1.0;     // How extreme price must move beyond swing
input bool            Visualize = true;

//+------------------------------------------------------------------+
//| Indicator Buffers                                                |
//+------------------------------------------------------------------+
double BuyBuffer[];
double SellBuffer[];

まず最初に、このインジケータをチャートウィンドウへ表示するインジケーターとして定義し、売買シグナルを表示するための2つの出力バッファを用意します。これらのバッファは、買いシグナルと売りシグナルをそれぞれ上向き矢印と下向き矢印で描画するよう設定されます。さらに、色分けやサイズを明確に設定することで、反転シグナルをチャート上で一目で確認できるようにします。続いて、主要な設定項目を入力パラメータとして公開します。これには、HTF、LTF、スイング検出の深さ、そして表示の有無を切り替えるオプションが含まれます。これらの設定により、市場環境に応じてインジケータを柔軟に調整しながら、市場構造に基づくロジックを維持できます。また、ATR_PeriodとATR_Multiplierは、市場構造に対する価格の極端な伸びを評価するための基準として機能します。最後に、買いシグナル用と売りシグナル用のバッファを宣言します。これらのバッファには、後続のスイング計算および市場構造計算によって生成されるシグナル値が格納され、チャート上へ表示されます。

//+------------------------------------------------------------------+
//| Swing Point Structure                                            |
//+------------------------------------------------------------------+
struct SwingPoint
{
   datetime time;
   double   price;
   int      type;   // 1 = High, -1 = Low
   int               barIndex;

   void Reset()
   {
      time = 0;
      price = 0;
      type = 0;
      barIndex =0;
   }
};

//+------------------------------------------------------------------+
//| Market Structure State                                           |
//+------------------------------------------------------------------+
enum STRUCTURE_STATE
{
   STRUCT_NEUTRAL,
   STRUCT_BULLISH,
   STRUCT_BEARISH
};

//+------------------------------------------------------------------+
//| Globals                                                          |
//+------------------------------------------------------------------+
SwingPoint htf_lastHigh, htf_prevHigh;
SwingPoint htf_lastLow,  htf_prevLow;
SwingPoint ltf_lastHigh, ltf_prevHigh;
SwingPoint ltf_lastLow,  ltf_prevLow;

STRUCTURE_STATE htfBias = STRUCT_NEUTRAL;
STRUCTURE_STATE ltfStructure = STRUCT_NEUTRAL;
int atrHandle;
datetime lastBuySwingTime  = 0;
datetime lastSellSwingTime = 0;
double lastBuySwingPrice = 0;
double lastSellSwingPrice = 0;

このセクションでは、市場構造を簡潔かつ再利用可能な方法でモデル化するために使用される主要なデータ構造を紹介します。SwingPoint構造体は、スイングハイまたはスイングローについてインジケータが必要とするすべての情報を保持します。具体的には、スイングが形成された時刻、その価格水準、高値か安値かを示す情報、そして形成されたバーインデックスが含まれます。これらの情報を1つの構造体へまとめ、さらにReset()メソッドを用意することで、市場構造が無効になった場合や再計算が必要になった場合でも、スイングデータを安全に初期化し、再構築できるようになります。

次に、市場の方向性はSTRUCTURE_STATE列挙型によって表現されます。この列挙型は、連続するスイングハイとスイングローの関係に基づいて、市場の状態を強気(Bullish)、弱気(Bearish)、または中立(Neutral)のいずれかに分類します。この仕組みに基づき、HTFとLTFのそれぞれについて独立したグローバルスイングトラッカーを保持します。これにより、インジケータは上位足の大きな方向性と、下位足の局所的な市場構造を比較しながら分析を行うことができます。さらに、直近で使用した買いシグナルおよび売りシグナルに対応するスイングの時刻と価格を保持するグローバル変数も用意されています。これにより、同じ市場構造イベントに対してシグナルが複数回生成されることを防ぎ、同一のスイング極値から繰り返しシグナルが発生しないよう制御しています。

//+------------------------------------------------------------------+
//| Indicator Init                                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   atrHandle = iATR(_Symbol, LTF, ATR_Period);

   SetIndexBuffer(0, BuyBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, SellBuffer, INDICATOR_DATA);

   PlotIndexSetInteger(0, PLOT_ARROW, 233); // arrow up
   PlotIndexSetInteger(1, PLOT_ARROW, 234); // arrow down

   ArrayInitialize(BuyBuffer, EMPTY_VALUE);
   ArrayInitialize(SellBuffer, EMPTY_VALUE);

   return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Indicator Calculation                                            |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   static datetime lastLtfBar = 0;
   datetime currentLtfBar = iTime(_Symbol, LTF, 0);

   if(currentLtfBar == lastLtfBar)
      return rates_total;

   lastLtfBar = currentLtfBar;

   UpdateHTFStructure();
   UpdateLTFStructure();
   CheckStructureBreak();

   int bar = rates_total - 1;

   BuyBuffer[bar]  = EMPTY_VALUE;
   SellBuffer[bar] = EMPTY_VALUE;

   if(CheckBuyCondition())
      BuyBuffer[bar] = low[bar] - (10 * _Point);

   if(CheckSellCondition())
      SellBuffer[bar] = high[bar] + (10 * _Point);

   if(Visualize)
      UpdateVisualization();

   return rates_total;
}

このセクションでは、インジケータの初期化処理と実行ロジックを実装します。OnInitでは、まずATRインジケータを初期化します。続いて、買いシグナル用および売りシグナル用のバッファを、それぞれ対応するインジケータプロットへ関連付けます。これらのバッファは、上向き矢印と下向き矢印を表示するよう設定され、不要なシグナルが表示されないよう、あらかじめ空の値で初期化されます。その後、OnCalculate関数では、新しいLTFバーが形成された場合にのみインジケータを処理します。これにより、毎ティックごとに同じ計算を繰り返すことを避け、効率的に更新処理を行うことができます。新しいLTFバーが形成されるたびに、インジケータはHTFおよびLTFの市場構造を更新し、市場構造のブレイクが発生しているかを確認します。続いて、買い条件および売り条件を評価し、有効なセットアップが検出された場合には、視認性を高めるため、現在のバーの少し上または下へシグナル矢印を描画します。必要に応じて、チャート上の視覚要素も更新します。

//+------------------------------------------------------------------+
//| Update Higher Timeframe Structure                                |
//+------------------------------------------------------------------+
void UpdateHTFStructure()
{
   // Get HTF data
   MqlRates htfRates[];
   ArraySetAsSeries(htfRates, true);
   CopyRates(_Symbol, HTF, 0, 100, htfRates);
   
   if(ArraySize(htfRates) < SwingBars * 2 + 1) return;
   
   // Detect swing points on HTF
   for(int i = SwingBars; i < ArraySize(htfRates) - SwingBars; i++)
   {
      bool isHigh = true;
      bool isLow = true;
      
      // Check if current bar is swing high
      for(int j = 1; j <= SwingBars; j++)
      {
         if(htfRates[i].high <= htfRates[i-j].high || 
            htfRates[i].high <= htfRates[i+j].high)
         {
            isHigh = false;
         }
         
         if(htfRates[i].low >= htfRates[i-j].low || 
            htfRates[i].low >= htfRates[i+j].low)
         {
            isLow = false;
         }
      }
      
      // Update swing highs
      if(isHigh)
      {
         htf_prevHigh = htf_lastHigh;
         htf_lastHigh.time = htfRates[i].time;
         htf_lastHigh.price = htfRates[i].high;
         htf_lastHigh.type = 1;
         htf_lastHigh.barIndex = i;
      }
      
      // Update swing lows
      if(isLow)
      {
         htf_prevLow = htf_lastLow;
         htf_lastLow.time = htfRates[i].time;
         htf_lastLow.price = htfRates[i].low;
         htf_lastLow.type = -1;
         htf_lastLow.barIndex = i;
      }
   }
   
   // Update HTF bias
   htfBias = DetectStructure(htf_lastHigh, htf_prevHigh, htf_lastLow, htf_prevLow);
}

//+------------------------------------------------------------------+
//| Update Lower Timeframe Structure                                 |
//+------------------------------------------------------------------+
void UpdateLTFStructure()
{
   // Get LTF data
   MqlRates ltfRates[];
   ArraySetAsSeries(ltfRates, true);
   CopyRates(_Symbol, LTF, 0, 100, ltfRates);
   
   if(ArraySize(ltfRates) < SwingBars * 2 + 1) return;
   
   // Detect swing points on LTF
   for(int i = SwingBars; i < ArraySize(ltfRates) - SwingBars; i++)
   {
      bool isHigh = true;
      bool isLow = true;
      
      // Check if current bar is swing high
      for(int j = 1; j <= SwingBars; j++)
      {
         if(ltfRates[i].high <= ltfRates[i-j].high || 
            ltfRates[i].high <= ltfRates[i+j].high)
         {
            isHigh = false;
         }
         
         if(ltfRates[i].low >= ltfRates[i-j].low || 
            ltfRates[i].low >= ltfRates[i+j].low)
         {
            isLow = false;
         }
      }
      
      // Update swing highs
      if(isHigh)
      {
         ltf_prevHigh = ltf_lastHigh;
         ltf_lastHigh.time = ltfRates[i].time;
         ltf_lastHigh.price = ltfRates[i].high;
         ltf_lastHigh.type = 1;
         ltf_lastHigh.barIndex = i;
      }
      
      // Update swing lows
      if(isLow)
      {
         ltf_prevLow = ltf_lastLow;
         ltf_lastLow.time = ltfRates[i].time;
         ltf_lastLow.price = ltfRates[i].low;
         ltf_lastLow.type = -1;
         ltf_lastLow.barIndex = i;
      }
   }
   
   // Update LTF structure
   ltfStructure = DetectStructure(ltf_lastHigh, ltf_prevHigh, ltf_lastLow, ltf_prevLow);
}

これら2つの関数は、HTFとLTFにおけるスイングハイおよびスイングローを検出することで、市場構造を抽出する役割を担います。各関数はまず、一定数の過去ローソク足データを読み込み、設定されたSwingBarsの深さに基づいてスイングを判定するために十分なデータが存在することを確認します。その後、スイング検出ロジックでは、判定対象となる各バーについて、その高値と安値を左右に位置する近隣のローソク足と比較します。価格が周囲の市場構造と比べて明確に突出している場合にのみ、そのバーをスイングハイまたはスイングローとして確定します。新しいスイングが検出されると、直前のスイングは保持され、最新のスイング情報が更新されます。これにより、単一のスイングポイントだけではなく、継続的に更新される市場構造の履歴が維持されます。

スイングポイントが更新されると、これらの関数は取得した価格情報を市場の方向性へ変換します。HTFでは、最新のスイングとその1つ前のスイングを比較することで、より大きな市場の方向性を判断します。一方、LTFでは市場構造を独立して分類し、短期的な値動きを把握します。このようにHTFとLTFの市場構造を分けて評価することで、ミクロな反転をマクロな構造と整合させて捉えることができます。また、異なる時間足に対して同じスイング検出ロジックを適用することで、遅行系インジケータへ依存することなく、トレンド、レンジ(保ち合い)、オーバーエクステンションを一貫した視点で把握できるようになります。

//+------------------------------------------------------------------+
//| Detect Market Structure                                          |
//+------------------------------------------------------------------+
STRUCTURE_STATE DetectStructure(SwingPoint &lastHigh, SwingPoint &prevHigh,
                                SwingPoint &lastLow, SwingPoint &prevLow)
{
   // Need at least two highs and two lows
   if(lastHigh.price == 0 || prevHigh.price == 0 || 
      lastLow.price == 0 || prevLow.price == 0)
      return STRUCT_NEUTRAL;
   
   bool isHigherHigh = lastHigh.price > prevHigh.price;
   bool isHigherLow = lastLow.price > prevLow.price;
   bool isLowerLow = lastLow.price < prevLow.price;
   bool isLowerHigh = lastHigh.price < prevHigh.price;
   
   if(isHigherHigh && isHigherLow)
      return STRUCT_BULLISH;
   
   if(isLowerLow && isLowerHigh)
      return STRUCT_BEARISH;
   
   return STRUCT_NEUTRAL;
}

//+------------------------------------------------------------------+
//| New Sell Condition                                               |
//+------------------------------------------------------------------+
bool CheckSellCondition()
{
   // Only allow sells if HTF bias is BEARISH
   if(htfBias != STRUCT_BEARISH)
      return false;

   // Need valid LTF swing points
   if(ltf_lastHigh.price == 0 || ltf_lastLow.price == 0)
      return false;
   
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   
   double atr = GetCurrentATR();
   if(atr == 0) return false;
   
   bool priceAboveHigh = currentPrice > (ltf_lastHigh.price + atr * ATR_Multiplier);
   bool priceAboveLow  = currentPrice > ltf_lastLow.price;
   
   bool isNewSwing = (ltf_lastHigh.time != lastSellSwingTime) || 
                     (ltf_lastHigh.price != lastSellSwingPrice);
   
   return (priceAboveHigh && priceAboveLow && isNewSwing);
}

//+------------------------------------------------------------------+
//| New Buy Condition                                                |
//+------------------------------------------------------------------+
bool CheckBuyCondition()
{
   // Only allow buys if HTF bias is BULLISH
   if(htfBias != STRUCT_BULLISH)
      return false;

   // Need valid LTF swing point
   if(ltf_lastLow.price == 0)
      return false;
   
   double currentPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   
   double atr = GetCurrentATR();
   if(atr == 0) return false;
   
   bool priceBelowLow = currentPrice < (ltf_lastLow.price - atr * ATR_Multiplier);
   
   bool isNewSwing = (ltf_lastLow.time != lastBuySwingTime) || 
                     (ltf_lastLow.price != lastBuySwingPrice);
   
   return (priceBelowLow && isNewSwing);
}

このセクションでは、生のスイングデータを意味のある市場構造へ変換する方法を定義します。DetectStructure関数は、連続するスイングハイとスイングローの関係を評価し、市場構造を強気(Bullish)、弱気(Bearish)、または中立(Neutral)のいずれかに分類します。強気構造と判定されるのは、高値が切り上がる(Higher High)と同時に安値も切り上がる(Higher Low)場合のみです。一方、弱気構造と判定されるには、安値が切り下がる(Lower Low)と同時に高値も切り下がる(Lower High)必要があります。これらの条件を満たさない場合、市場構造は中立と判断されます。これにより、市場構造が不明確または不完全な状況で方向性を決めつけることを防ぎます。

買い条件および売り条件を判定する関数は、この市場構造の判定を基盤としながら、HTFの方向性とATRによるオーバーエクステンション判定を組み合わせています。売り条件は、HTFの方向性が弱気である場合にのみ評価されます。さらに、価格が最新のLTFスイングハイを、設定されたATR倍率を超える距離まで上抜けていること(なお、価格は対象となるスイング構造より上に位置している必要があります)が条件となります。これにより、小さなブレイクアウトではなく、ボラティリティを考慮した価格の行き過ぎ(オーバーエクステンション)だけを売りシグナルとして認識します。同様に、買い条件はHTFの方向性が強気の場合にのみ有効です。価格が最新のLTFスイングローをATRで調整された距離だけ下抜けた場合に、有意な下方向へのオーバーエクステンションとして判断します。 どちらの場合も、過去に記録したスイングの時刻と価格を確認することで、同じスイング極値からシグナルが複数回発生しないよう制御しています。これにより、このインジケータは小さな価格変動へ反応するのではなく、市場構造に基づいた、一度限りの反転機会だけを検出します。

シグナルが表示された場合は、まずチャート上に表示されているHTFの方向性と一致していることを確認してください。売買は、その方向性に一致する場合のみ有効です。つまり、強気バイアスでは買いのみ、弱気バイアスでは売りのみを検討します。売買条件は、価格がインジケータに表示されているLTFのスイング極値を超えた時点で有効になります。買いの場合は価格がLTFの直近安値を下抜けたとき、売りの場合は価格がLTFの直近高値(および該当するスイング構造)を上抜けたときです。さらに、条件ラベルがアクティブなセットアップを示していることも確認してください。リスク管理では、ストップロスは対応するLTFスイング極値の外側へ配置できます。たとえば、売りポジションではLTFの直近高値の上、買いポジションではLTFの直近安値の下へストップを配置します。これらの水準は、このインジケータのロジックにおいて市場構造が否定されるポイントを表しています。利益確定については、売りポジションではLTFの直近安値、またはスイング間の中間価格帯を目標とできます。買いポジションでは、LTFの直近高値、またはスイング間の中間価格帯を利益目標として設定できます。どちらを選択するかは、トレーダー自身のリスク許容度に応じて判断してください。

//+------------------------------------------------------------------+
//| Check Structure Break                                            |
//+------------------------------------------------------------------+
void CheckStructureBreak()
{
   MqlRates current[];
   ArraySetAsSeries(current, true);
   CopyRates(_Symbol, LTF, 0, 1, current);
   
   if(ArraySize(current) < 1) return;
   
   // Bullish structure break
   if(ltfStructure == STRUCT_BULLISH && current[0].close < ltf_lastLow.price)
   {
      ResetLTFStructure();
   }
   
   // Bearish structure break
   if(ltfStructure == STRUCT_BEARISH && current[0].close > ltf_lastHigh.price)
   {
      ResetLTFStructure();
   }
}

//+------------------------------------------------------------------+
//| Reset LTF Structure                                              |
//+------------------------------------------------------------------+
void ResetLTFStructure()
{
   ltf_lastHigh.Reset();
   ltf_prevHigh.Reset();
   ltf_lastLow.Reset();
   ltf_prevLow.Reset();
   ltfStructure = STRUCT_NEUTRAL;
   
   // Also reset trade tracking
   lastBuySwingTime = 0;
   lastSellSwingTime = 0;
   lastBuySwingPrice = 0;
   lastSellSwingPrice = 0;
}

//+------------------------------------------------------------------+
//| Update Visualization                                             |
//+------------------------------------------------------------------+
void UpdateVisualization()
{
   ObjectsDeleteAll(0, "MS_");
   
   // Draw HTF swing points
   if(htf_lastHigh.price > 0)
      DrawSwingPoint(htf_lastHigh, "HTF_High", clrRed, HTF);
   
   if(htf_prevHigh.price > 0)
      DrawSwingPoint(htf_prevHigh, "HTF_PrevHigh", clrRed, HTF);
   
   if(htf_lastLow.price > 0)
      DrawSwingPoint(htf_lastLow, "HTF_Low", clrBlue, HTF);
   
   if(htf_prevLow.price > 0)
      DrawSwingPoint(htf_prevLow, "HTF_PrevLow", clrBlue, HTF);
   
   // Draw LTF swing points
   if(ltf_lastHigh.price > 0)
      DrawSwingPoint(ltf_lastHigh, "LTF_High", clrOrange, LTF);
   
   if(ltf_prevHigh.price > 0)
      DrawSwingPoint(ltf_prevHigh, "LTF_PrevHigh", clrOrange, LTF);
   
   if(ltf_lastLow.price > 0)
      DrawSwingPoint(ltf_lastLow, "LTF_Low", clrGreen, LTF);
   
   if(ltf_prevLow.price > 0)
      DrawSwingPoint(ltf_prevLow, "LTF_PrevLow", clrGreen, LTF);
   
   // Draw current price lines for reference
   double currentBid = SymbolInfoDouble(_Symbol, SYMBOL_BID);
   double currentAsk = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
   
   CreateHorizontalLine("Current_Bid", currentBid, clrYellow, STYLE_DASH);
   CreateHorizontalLine("Current_Ask", currentAsk, clrYellow, STYLE_DASH);
   
   // Draw trade condition zones
   if(ltf_lastHigh.price > 0)
      CreateHorizontalLine("Sell_Zone_Min", ltf_lastHigh.price, clrRed, STYLE_SOLID);
   
   if(ltf_lastLow.price > 0)
   {
      CreateHorizontalLine("Buy_Zone_Max", ltf_lastLow.price, clrBlue, STYLE_SOLID);
      CreateHorizontalLine("Sell_TP_Level", ltf_lastLow.price, clrGreen, STYLE_DOT);
   }
   
   // Draw structure labels
   DrawStructureLabels();
}

このセクションでは、市場構造の有効性を検証し、インジケータが常に現在の市場状況と一致した状態を維持できるようにします。CheckStructureBreak関数は、最新のLTFバーを継続的に監視し、その終値を直近のスイング水準と比較します。強気構造では、価格が直近のスイングローを終値で下抜けた場合、その市場構造は無効になったと判断されます。一方、弱気構造では、価格が直近のスイングハイを終値で上抜けた場合、市場構造が崩れたと判断されます。このように市場構造が無効になった場合、既存の構造は成立しなくなります。その際には、ResetLTFStructure 関数が実行され、市場構造をリセットするとともにシグナルを更新します。これにより、すでに無効となったスイング構造が以後のシグナル判定へ影響を与えることを防ぎ、有効な市場構造だけを使用してシグナルを生成できるようになります。

市場構造が確認または再構築されると、UpdateVisualization関数が内部で計算されたすべての情報を、リアルタイムで分かりやすくチャートへ反映します。HTFおよびLTFのスイングポイント、現在の価格基準、および売買ゾーンを定義する主要な構造レベルを再描画します。さらに、HTFとLTFのスイングを視覚的に区別し、売買条件に対応する価格水準やラベルを重ねて表示することで、トレーダーはシグナルがどこで発生したのかだけでなく、なぜそのシグナルが発生したのかを市場構造に基づいて直感的に理解できるようになります。その結果、このインジケータは抽象的な売買シグナルを表示するのではなく、実際の市場構造に裏付けられた売買機会を視覚的に示します。

//+------------------------------------------------------------------+
//| Draw Swing Point                                                 |
//+------------------------------------------------------------------+
void DrawSwingPoint(SwingPoint &sp, string name, color clr, ENUM_TIMEFRAMES tf)
{
   ObjectCreate(0, "MS_" + name, OBJ_ARROW, 0, sp.time, sp.price);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_ARROWCODE, sp.type == 1 ? 218 : 217);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_WIDTH, 3);
   ObjectSetString(0, "MS_" + name, OBJPROP_TOOLTIP, 
                  StringFormat("%s: %.5f", sp.type == 1 ? "High" : "Low", sp.price));
}

//+------------------------------------------------------------------+
//| Draw Structure Labels                                            |
//+------------------------------------------------------------------+
void DrawStructureLabels()
{
   string biasText = "HTF Bias: ";
   switch(htfBias)
   {
      case STRUCT_BULLISH: biasText += "BULLISH"; break;
      case STRUCT_BEARISH: biasText += "BEARISH"; break;
      default: biasText += "NEUTRAL"; break;
   }
   
   string ltfText = "LTF Structure: ";
   switch(ltfStructure)
   {
      case STRUCT_BULLISH: ltfText += "HH+HL"; break;
      case STRUCT_BEARISH: ltfText += "LL+LH"; break;
      default: ltfText += "NEUTRAL"; break;
   }
   
   // Current trade conditions
   string conditionText = "";
   if(CheckBuyCondition())
      conditionText = "Potential BUY Condition: ACTIVE (Price < LTF Low)";
   else if(CheckSellCondition())
      conditionText = "Potential SELL Condition: ACTIVE (Price > LTF High & Low)";
   else
      conditionText = "No Anticipated Trade Condition Met";
   
   // Create label objects
   CreateLabel("MS_BiasLabel", biasText, 10, 20, clrDarkOrange);
   CreateLabel("MS_LTFLabel", ltfText, 10, 40, clrDarkOrange);
   CreateLabel("MS_ConditionLabel", conditionText, 10, 60, 
               (CheckBuyCondition() || CheckSellCondition()) ? clrLime : clrGray);
   
   // Display LTF swing prices
   if(ltf_lastHigh.price > 0)
      CreateLabel("MS_LTFHighLabel", StringFormat("LTF High: %.5f", ltf_lastHigh.price), 10, 80, clrOrange);
   
   if(ltf_lastLow.price > 0)
      CreateLabel("MS_LTFLowLabel", StringFormat("LTF Low: %.5f", ltf_lastLow.price), 10, 100, clrGreen);
}

//+------------------------------------------------------------------+
//| Create Text Label                                                |
//+------------------------------------------------------------------+
void CreateLabel(string name, string text, int x, int y, color clr)
{
   ObjectCreate(0, "MS_" + name, OBJ_LABEL, 0, 0, 0);
   ObjectSetString(0, "MS_" + name, OBJPROP_TEXT, text);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_XDISTANCE, x);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_YDISTANCE, y);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_FONTSIZE, 10);
}

//+------------------------------------------------------------------+
//| Create Horizontal Line                                           |
//+------------------------------------------------------------------+
void CreateHorizontalLine(string name, double price, color clr, ENUM_LINE_STYLE style)
{
   ObjectCreate(0, "MS_" + name, OBJ_HLINE, 0, 0, price);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_COLOR, clr);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_STYLE, style);
   ObjectSetInteger(0, "MS_" + name, OBJPROP_WIDTH, 1);
}

//+------------------------------------------------------------------+
//|  Get Current ATR                                                 |
//+------------------------------------------------------------------+
double GetCurrentATR()
{
   double atr[];
   ArraySetAsSeries(atr, true);
   
   if(CopyBuffer(atrHandle, 0, 0, 1, atr) <= 0)
      return 0;
      
   return atr[0];
}

この最後のセクションでは、内部で保持されているスイング情報および市場構造データを、トレーダーがチャート上で直感的に理解できる視覚情報へ変換します。DrawSwingPoint関数は、検出された各スイングハイおよびスイングローに対して矢印オブジェクトを作成します。矢印の方向、色、ツールチップは、それが山(ピーク)なのか谷(トラフ)なのかを示すよう設定されます。これにより、市場が重要なスイングを形成した正確なタイミングを簡単に確認でき、数値データと実際のトレード判断の間にあるギャップを埋めることができます。各スイングポイントには識別しやすい表示が付けられるため、トレーダーはローソク足パターンを1つずつ確認することなく、価格の極値とその形成タイミングを理解できます。

スイングポイントの描画を補完する形で、DrawStructureLabelsおよびCreateLabel、CreateHorizontalLine などのヘルパー関数は、市場状態と潜在的なトレード条件を動的に表示します。HTFおよびLTFのバイアスが表示されることで、現在の市場が強気、弱気、または中立のどの状態にあるか、さらに現在価格が買いゾーンまたは売りゾーンへ到達しているかを確認できます。水平ラインは重要なスイングハイおよびスイングローを示し、色分けされたラベルによって各ゾーンや条件が視覚的に強調されます。また、GetCurrentATR 関数はATRインジケータのバッファから最新のATR値を取得して返します。データへアクセスできない場合は、値として0を返します。これらの要素を組み合わせることで、市場構造をリアルタイムで確認できるインタラクティブなマップが形成され、トレーダーは反転やプルバックの可能性を事前に判断しやすくなります。



インジケータデモ



結論

まとめると、Swing Extremes and Pullbacksインジケータは、マルチタイムフレームのスイング検出とリアルタイムのLTF市場分析を組み合わせることで開発されました。このインジケータは、HTFおよびLTFの両方で重要なスイングハイとスイングローを検出し、現在の市場構造を強気、弱気、または中立として評価します。さらに、価格が過度に伸びた状態(オーバーエクステンション)を検出し、反転が発生する可能性があるゾーンを示します。スイングポイントを示す矢印、重要な価格水準を示す水平ライン、そしてHTFバイアス・LTF構造・現在のトレード条件を表示する動的ラベルなどの視覚要素により、市場内部のリズムを簡単に把握できます。また、市場構造の検証、オーバーエクステンション判定、スイングリセット機能を統合することで、シグナルが適切なタイミングで発生し、同じスイング極値から繰り返し表示されないよう制御されています。

このインジケータは、生のプライスアクションデータを、反転やプルバックの可能性を示す明確で実用的な市場マップへ変換することで、トレード分析を向上させることができます。トレーダーは、極端なスイングポイントで発生する価格反応を予測し、現在の状況が強気セットアップまたは弱気セットアップのどちらに適しているかを迅速に判断できます。また、複数時間足にわたるスイング分析を手動で行う必要がなくなり、より効率的で判断しやすいトレード分析が可能になります。市場構造とトレードゾーンをリアルタイムで可視化することで、推測に頼る部分を減らし、分析作業を簡略化するとともに、より確信度の高いエントリーおよび決済判断を行えるようになります。つまり、このインジケータは市場観察と実際のトレード実行の間をつなぐ役割を果たします。この記事を読めば、少なくともLTFの極値を特定し、HTFバイアスを用いてフィルタリングし、構造化されたエントリー、ストップ、エグジットのルールを適用できるようになるはずです。

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

添付されたファイル |
Swing_Extremes.mq5 (17.93 KB)
最後のコメント | ディスカッションに移動 (2)
Educator Jay
Educator Jay | 28 2月 2026 において 12:18
矢印が表示されたり消えたりする。まだ安定していない
Jacryptoking
Jacryptoking | 1 3月 2026 において 11:48
良いアイデアですが、適切に実装されていません。

売買の矢印が表示されません。
フラクタル オブジェクトが表示されません。

LTF構造用。

内部チョックを使用してHH/LLを特定します。

その後、チョックによるLH/HLを特定し、そのチョックがHTF内でHH/LLを形成している可能性が高いと判断します。トレンドは、LTFの流動性スイープの後に発生します。

新しいチョックが出現しなくなると、LH/HLでのエントリー機会は増えなくなる

Lux Algo Ultimate Smart Concept MT5インジケーターは、優れた内部チョック/BOSの計算式を備えている。


HTFが 強気シグナルを出したとき
LTFは中立を示す が、チャート上にはHHとHLが存在する……


私は利益を出しているトレーダーだ。 私は、自分のトレード環境をサポートしてくれる自動化やツールが大好きです。
あなたはとても優秀なプログラマーですから、いつか確固たるものを構築することになるでしょう。

ありがとうございます。
初心者からエキスパートへ: トレンドフィルタによる流動性戦略の拡張 初心者からエキスパートへ: トレンドフィルタによる流動性戦略の拡張
流動性ベースの戦略にシンプルなトレンド条件を追加します。具体的には、EMA(50)の方向に沿って流動性ゾーンを取引するというフィルタを導入します。記事では、不要なセットアップを除外するためのルールを説明し、再利用可能なTrendFilter.mqhクラスと、MQL5でのEA統合方法を紹介します。また、フィルタなしの基本戦略と、フィルタを適用した戦略のテスト結果を比較します。読者は、明確な方向性の判断基準、逆トレンド局面での過剰取引の抑制方法、そしてすぐに利用できるソースファイルを得ることができます。
MQL5入門(第42回):MQL5におけるファイル処理入門(IV) MQL5入門(第42回):MQL5におけるファイル処理入門(IV)
CSV形式の取引履歴を読み込み、Profit($)の値と総取引数を抽出し、累積残高推移を計算するMQL5インジケータの作成方法を紹介します。算出した累積残高曲線は、インジケータ専用のサブウィンドウにプロットし、Y軸は値の範囲に応じて自動的にスケーリングされます。また、表示位置を分かりやすくするために、水平軸と垂直軸も描画します。インジケータはタイマーによって定期的に更新され、新しい取引が追加された場合にのみ再描画します。さらに、オプションとして各取引の利益または損失を数値ラベルとして表示できるため、パフォーマンスやドローダウンをチャート上で直接確認・分析できます。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
ラリー・ウィリアムズの『市場の秘密』(第12回):相場環境に基づくスマッシュデー反転トレード ラリー・ウィリアムズの『市場の秘密』(第12回):相場環境に基づくスマッシュデー反転トレード
体系化されたコンテキストの中で、ラリー・ウィリアムズのスマッシュデー反転パターンをMQL5で自動化する方法を解説します。限定された有効期間内でセットアップを検証し、Supertrendを用いた曜日フィルタを備え、エントリーはレベル突破時またはバー確定時のいずれにも対応するエキスパートアドバイザー(EA)を実装します。エントリー方式としては、レベル突破時の即時エントリー、またはローソク足確定後のエントリーに対応しています。また、同時保有ポジション数を1つに制限し、リスクベースまたは固定ロットによるポジションサイジングをサポートします。さらに、段階的な開発手順、バックテスト方法、再現可能な設定も併せて提示します。