SMC (Smart Money Concepts)で取引のレベルアップを実現する:OB、BOS、FVG
はじめに
トレーダーはしばしば戦略の一貫性に課題を抱え、明確な枠組みがないまま、インジケーターやセットアップ、手法を次々と切り替えてしまうことがあります。このような枠組みの欠如は、市場環境が変化する中で混乱や機会の損失、そしてフラストレーションの増大を招きやすく、従来のツールでは対応が難しくなることがあります。本トピックでは、複数のSMC(Smart Money Concepts、スマートマネーコンセプト)戦略を1つの強力なEAに統合するメリットについて説明します。
SMC(Smart Money Concepts、スマートマネーコンセプト)は、OB(Order Blocks、注文ブロック)、BOS(Break of Structure、ブレイクオブストラクチャ)、FVG(Fair Value Gaps、公正価格ギャップ)といった要素を通じて、市場の動きを理解するための体系的なアプローチを提供します。これらを1つのEAに統合することで、ワークフローを簡素化し、意思決定を自動化し、最も強力なプライスアクションシグナルに集中することができます。自動モードでのシームレスな実行はもちろん、個々のコンセプトを選択して使うことも可能で、迷いを減らし、より効率的で一貫性があり、市場の変化に適応した取引を実現します。
EAのロジック
OB
OB(Order Blocks、注文ブロック)とは、大きな価格変動が始まる直前に形成された最後の陽線または陰線を指します。これは、機関投資家が注文を仕込んだ可能性のある重要なゾーンを示すことが多いです。これらのゾーンに再び到達すると、強力なサポートまたはレジスタンスとして機能しやすく、高確率の取引機会を提供します。本EAでは、OBの検出精度をさらに高めるために、フィボナッチインジケーターによる確認を組み合わせています。これにより、エントリーが主要なリトレースメントやエクステンションの水準と一致するように設計されています。前回の記事でも説明したように、OBにフィボナッチによる確認を組み合わせることで、弱いセットアップを排除し、エントリーから取引管理に至るまで、より信頼性の高いフレームワークを提供することができます。

FVG
FVG(Fair Value Gaps、公正価格ギャップ)とは、価格の動きに不均衡が生じた際に発生するもので、通常は勢いのある大きな値動きによって形成され、前後のローソク足のヒゲに隙間が生じることがあります。このギャップは、市場における非効率性、すなわちすべての注文が適切にマッチされなかった領域を示しており、価格は多くの場合、その不均衡を「埋める」ために一度その領域へ戻ってから、元々の方向へと動きを再開します。FVGを検出し、その周囲で取引をおこなうことで、価格がこれらのゾーンへリトレース(戻り)する可能性を先読みし、リスクとリワードが明確な精度の高いエントリー機会を得ることができます。

BOS
BOS(Break of Structure、ブレイクオブストラクチャ)とは、価格が前回のスイングハイを上抜ける、あるいは前回のスイングローを下抜ける際に発生し、市場の方向性が変化する可能性を示すシグナルです。前回の記事では、スイングハイが破られた場合に売りエントリーをおこない、弱気(下落)モメンタムを活用する方法に焦点を当てていました。しかし本アプローチでは、BOSのロジックを市場のトレンドに沿う形で改良しています。具体的には、スイングハイが破られた場合は強気の力を確認し、買いの機会として捉えます。一方、スイングローが破られた場合は、売りの機会として認識します。この変更により、取引は支配的な市場の流れに沿っておこなわれ、逆張りによる無駄なリスクを避けることができます。

システムアーキテクチャ

導入手順
//+------------------------------------------------------------------+ //| SMC_ALL_IN_1.mq5 | //| GIT under Copyright 2025, MetaQuotes Ltd. | //| https://www.mql5.com/ja/users/johnhlomohang/ | //+------------------------------------------------------------------+ #property copyright "GIT under Copyright 2025, MetaQuotes Ltd." #property link "https://www.mql5.com/ja/users/johnhlomohang/" #property version "1.01" #property description "Unified SMC: FVG + Order Blocks + BOS. Detect + Draw + Trade." #include <Trade/Trade.mqh> #include <Trade/PositionInfo.mqh> CTrade trade; CPositionInfo pos; enum ENUM_STRATEGY { STRAT_OB, // Use Order Blocks Only STRAT_FVG, // Use FVGs Only STRAT_BOS, // Use Break of Structure Only STRAT_AUTO // Auto (All SMC Concepts) }; enum SWING_TYPE{ SWING_OB, SWING_BOS, };
本コードでは、MetaTraderの取引ライブラリ、特にCTradeとCPositionInfoを利用しており、EAはこれにより取引の発注や管理、さらに保有ポジションの情報取得が可能になります。コードの冒頭でこれらのクラスを準備することで、EAは注文の実行やポジション追跡に必要な基本的なツールを確実に利用できるようになっています。
次に、戦略の柔軟性とスイングポイントの分類を列挙型で定義しています。ENUM_STRATEGYは、EAがどの手法に従って動作するかをトレーダーが選択できるように設計されており、OBのみ、FVGのみ、BOSのみ、あるいは3つのコンセプトを動的に活用する完全自動モード(STRAT_AUTO)を選ぶことができます。同様に、SWING_TYPEの列挙型は、OB用に使用するスイングポイントとBOS解析用のスイングポイントを区別するために用意されています。この構造により、コードはモジュール化されて柔軟性が高く、トレーダーは異なるSMCアプローチを試したり、市場状況に応じてEAに自動判断させたりすることが可能です。
//----------------------------- Inputs ------------------------------// input ENUM_STRATEGY TradeStrategy = STRAT_AUTO; input double In_Lot = 0.02; input double StopLoss = 3500; // points input double TakeProfit = 7500; // points input long MagicNumber = 76543; input int SwingPeriod = 5; // bars each side to confirm swing input int SwingProbeBar = 5; // bar index we test for swings (>=SwingPeriod) input double Fib_Trade_lvls = 61.8; // OB retrace must reach this % input bool DrawBOSLines = true; input int FVG_MinPoints = 3; // minimal gap in points input int FVG_ScanBars = 20; // how many bars to scan for FVGs input bool FVG_TradeAtEQ = true; // trade at 50% of the gap (EQ) input bool OneTradePerBar = true; //---------------------------- Colors -------------------------------// #define BullOB clrLime #define BearOB clrRed #define BullFVG clrPaleGreen #define BearFVG clrMistyRose #define BOSBull clrDodgerBlue #define BOSBear clrTomato
このコードのセクションでは、トレーダーが自身の取引スタイルやリスク管理の好みに合わせてカスタマイズできる入力パラメータが定義されています。TradeStrategy入力パラメータにより、EAがOBのみ、FVGのみ、BOSのみで取引するか、あるいは3つのコンセプトを自動的に組み合わせるかを選択することができます。リスクおよび資金管理はIn_Lot、StopLoss、TakeProfit、MagicNumberで制御され、適切なロットサイズ、保護用ストップ、利益確定ポイント、ユニークな取引IDが確保されます。さらに、スイング検出パラメータであるSwingPeriodやSwingProbeBarは、スイングハイやスイングローを特定する際に考慮するバーの本数を指定し、Fib_Trade_lvlsはOB取引がフィボナッチリトレースメントによる確認と整合するように設定されています。DrawBOSLinesオプションは、チャート上でBOSのレベルを直接表示できるようにすることで、視覚的な柔軟性を提供します。
次にFVG設定があり、ギャップの検出方法と取引方法をより細かく制御できます。FVG_MinPointsは、重要な不均衡のみを対象とすることを保証し、FVG_ScanBarsはEAが過去どの範囲まで有効なギャップを検索するかを決定します。FVG_TradeAtEQオプションを有効にすると、FVGの均衡点(ギャップの50%)で取引することが可能となり、SMC理論においてバランスの取れたエントリーポイントと見なされます。最後に、BullOB、BearOB、BullFVG、BearFVG、BOSBull、BOSBearなどの色設定により、チャート上のオブジェクトが視覚的に直感的に識別でき、強気と弱気のパターンを一目で判別することができます。
//---------------------------- Globals ------------------------------// double Bid, Ask; datetime g_lastBarTime = 0; // OB state class COrderBlock : public CObject { public: int direction; // +1 bullish, -1 bearish datetime time; // OB candle time double high; // OB candle high double low; // OB candle low string Key() const { return TimeToString(time, TIME_DATE|TIME_MINUTES); } void draw(datetime tmS, datetime tmE, color clr){ string objOB = " OB REC" + TimeToString(time); ObjectCreate( 0, objOB, OBJ_RECTANGLE, 0, time, low, tmS, high); ObjectSetInteger( 0, objOB, OBJPROP_FILL, true); ObjectSetInteger( 0, objOB, OBJPROP_COLOR, clr); string objtrade = " OB trade" + TimeToString(time); ObjectCreate( 0, objtrade, OBJ_RECTANGLE, 0, tmS, high, tmE, low); // trnary operator ObjectSetInteger( 0, objtrade, OBJPROP_FILL, true); ObjectSetInteger( 0, objtrade, OBJPROP_COLOR, clr); } }; COrderBlock* OB = NULL; // OB fib state // Track if an OB has already been traded datetime lastTradedOBTime = 0; bool tradedOB = false; double fib_low, fib_high; datetime fib_t1, fib_t2; bool isBullishOB = false; bool isBearishOB = false; datetime T1; datetime T2; color OBClr; #define FIB_OB_BULL "FIB_OB_BULL" #define FIB_OB_BEAR "FIB_OB_BEAR" #define FIBO_OBJ "Fibo Retracement" // BOS state datetime lastBOSTradeTime = 0; bool Bull_BOS_traded, Bear_BOS_traded; int lastBOSTradeDirection = 0; // 1 for buy, -1 for sell double swng_High = -1.0, swng_Low = -1.0; datetime bos_tH = 0, bos_tL = 0;
このコードのグローバルセクションでは、市場の状態、取引機会、チャートオブジェクトを管理するための基本的な変数と構造が定義されています。まず、Bid、Ask、g_lastBarTimeといったシンプルなグローバル値が設定され、リアルタイムの価格追跡を可能にし、EAが新しいバーごとに一度だけロジックを実行することを保証します。そこから、COrderBlockという専用クラスが作成され、OBのプロパティをまとめて管理できるようになっています。このクラスは、方向(強気か弱気か)、時間、価格レベル(高値/安値)といった情報を保持し、さらにdraw()関数を備えているため、チャート上に自動で長方形を描画し、色分けすることが可能です。これにより、トレーダーは視覚的にアクティブなOBや取引ゾーンを容易に確認できます。関数をクラス内にまとめることで、コードは整理され再利用性も高くなります。
基本的なOBの識別に加え、フィボナッチによるOBの検証を扱うための追加変数も定義されています。たとえば、fib_low、fib_high、時間マーカーのfib_t1やfib_t2はフィボナッチリトレースメントレベルの境界を格納し、isBullishOB、isBearishOB、tradedOBといったフラグは、各OBが一度だけ検証や取引されることを保証します。これらの変数により、EAはフィボナッチレベルを動的に参照でき、入力で定義された61.8%などの有効なリトレースメントのみを取引のエントリー条件として考慮します。さらに、FIB_OB_BULL、FIB_OB_BEAR、FIBO_OBJといった定数を用いることで、チャートオブジェクトの名称が統一され、描画されたフィボナッチリトレースメントが視覚的に区別しやすくなっています。
最後に、BOSの状態管理も導入されています。lastBOSTradeTime、Bull_BOS_traded、Bear_BOS_tradedといった変数により、BOSシグナルが発生した際に重複した取引が実行されるのを防ぎます。lastBOSTradeDirectionは直近のBOS取引が強気(+1)か弱気(-1)かを記録し、swng_Highとswng_Lowは最新のスイングレベルを保持して市場構造の検出に使用されます。さらに、bos_tHやbos_tLといった時間マーカーを用いて、スイングが確定した正確なタイミングを特定し、市場構造のルールに沿うようにしています。このようにグローバルに状態を管理することで、EAはOB、BOS、FVGといった異なる戦略間で一貫性を保ち、重複取引を防ぐことができます。これにより、SMC取引の自動化におけるしっかりとした基盤が構築されます。
//--------------------------- Helpers -------------------------------// double getHigh(int i) { return iHigh(_Symbol, _Period, i); } double getLow(int i) { return iLow(_Symbol, _Period, i); } double getOpen(int i) { return iOpen(_Symbol, _Period, i); } double getClose(int i) { return iClose(_Symbol, _Period, i); } datetime getTimeBar(int i){ return iTime(_Symbol, _Period, i); } bool IsNewBar() { datetime lastbar_time = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE); if(g_lastBarTime == 0) { g_lastBarTime = lastbar_time; return false; } if(g_lastBarTime != lastbar_time) { g_lastBarTime = lastbar_time; return true; } return false; } void ExecuteTrade(ENUM_ORDER_TYPE type) { double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); double price = (type==ORDER_TYPE_BUY) ? SymbolInfoDouble(_Symbol, SYMBOL_ASK) : SymbolInfoDouble(_Symbol, SYMBOL_BID); double sl = (type==ORDER_TYPE_BUY) ? price - StopLoss*point : price + StopLoss*point; double tp = (type==ORDER_TYPE_BUY) ? price + TakeProfit*point : price - TakeProfit*point; sl = NormalizeDouble(sl, _Digits); tp = NormalizeDouble(tp, _Digits); trade.SetExpertMagicNumber(MagicNumber); trade.PositionOpen(_Symbol, type, In_Lot, price, sl, tp, "SMC"); }
ここで定義されているヘルパー関数は、チャートデータとやり取りする際に、コードを整理し再利用可能にする役割を持っています。コード全体で直接iHigh、iLow、iCloseといった組み込み関数を呼び出す代わりに、getHigh()、getLow()、getClose()などのラッパー関数を使用することで、バー情報にアクセスしやすくなると同時に、可読性も向上します。IsNewBar()関数は、EAが各ローソク足ごとに一度だけロジックを処理することを保証する重要な役割を果たします。これは、g_lastBarTimeに最後のバーの時刻を保存し、現在のバーのタイムスタンプと比較することで実現されます。この効率的な方法により、シグナルや取引は新しいバーが始まったときにのみ考慮され、確定済みのローソク足に基づく戦略において必須の機能となります。
ExecuteTrade()関数は、取引実行のロジック全体をまとめて扱います。この関数は、注文タイプ(買い/売り)に応じて適切なエントリー価格、ストップロス、テイクプロフィットを自動計算し、値が銘柄の小数桁精度に合わせて正規化されることを保証します。このロジックを集中させることで、EAは重複するコードを避け、エントリーする際のミスを減らすことができます。また、この関数ではEAの固有のMagicNumberを設定し、すべての取引に「SMC」というコメントを付与することで、トレーダーがこのシステムによって開かれたポジションを簡単に識別できるようになっています。
//----------------------- Unified Swing Detection -------------------// // Detects if barIndex is a swing high and/or swing low using len bars on each side. // If swing high found -> updates fib_high/fib_tH (and swng_High/bos_tH if for BOS). // If swing low found -> updates fib_low/fib_tL (and swng_Low/bos_tL if for BOS). // return: true if at least one swing found. void DetectSwingForBar(int barIndex, SWING_TYPE type) { const int len = 5; bool isSwingH = true, isSwingL = true; for(int i = 1; i <= len; i++){ int right_bars = barIndex - i; int left_bars = barIndex + i; if(right_bars < 0) { isSwingH = false; isSwingL = false; break; } if((getHigh(barIndex) <= getHigh(right_bars)) || (left_bars < Bars(_Symbol, _Period) && getHigh(barIndex) < getHigh(left_bars))) isSwingH = false; if((getLow(barIndex) >= getLow(right_bars)) || (left_bars < Bars(_Symbol, _Period) && getLow(barIndex) > getLow(left_bars))) isSwingL = false; } // Assign with ternary operator depending on swing type if(isSwingH){ if(type == SWING_OB) { fib_high = getHigh(barIndex); fib_t1 = getTimeBar(barIndex); } else { swng_High = getHigh(barIndex); bos_tH = getTimeBar(barIndex); } } if(isSwingL){ if(type == SWING_OB) { fib_low = getLow(barIndex); fib_t2 = getTimeBar(barIndex); } else { swng_Low = getLow(barIndex); bos_tL = getTimeBar(barIndex); } } }
統合スイング検出関数は、指定したバーを中心に左右の一定本数のバーと比較することで、チャート上の潜在的なスイングハイおよびスイングローを特定することを目的としています。左右に同じ本数(len)のバーを設定し、現在のバーが局所的な極値を形成しているかどうかを判定します。すなわち、現在のバーの高値が左右のバーよりも高ければスイングハイ、安値が左右よりも低ければスイングローとみなします。左右いずれかのバーがこの条件を満たさない場合、そのスイングは無効とされます。この方法により、微細な価格変動による誤検出を避け、市場構造における重要なピボットポイントに焦点を当てることができます。
スイングが確定すると、スイングの種類に応じて主要な変数が更新されます。OB (SWING_OB)の検出では、スイングハイおよびスイングローがフィボナッチのアンカーポイント(fib_high、fib_low)およびタイムスタンプに割り当てられ、後にリトレースメントの検証に使用されます。一方、BOSのロジックでは、同じスイングレベルがswng_Highまたはswng_Lowに割り当てられ、それぞれ対応するタイムスタンプ(bos_tHまたはbos_tL)と組み合わされます。OBとBOSの両方を1つの関数で扱うことで、EAはスイング検出を効率的におこない、冗長なロジックを避けながら、市場構造の検証とフィボナッチリトレースメントセットアップの両方で一貫したスイング識別プロセスを共有することができます。
void DetectAndDrawOrderBlocks() { static datetime lastDetect = 0; datetime lastBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE); // Reset OB detection on new bar if(lastDetect != lastBar) { if(OB != NULL) { delete OB; OB = NULL; } lastDetect = lastBar; } // Only detect new OB if we don't have one already if(OB == NULL) { for(int i = 1; i < 100; i++) { // Bullish OB candidate if(getOpen(i) < getClose(i) && getOpen(i+2) < getClose(i+2) && getOpen(i+3) > getClose(i+3) && getOpen(i+3) < getClose(i+2)) { OB = new COrderBlock(); OB.direction = 1; OB.time = getTimeBar(i+3); OB.high = getHigh(i+3); OB.low = getLow(i+3); OBClr = BullOB; T1 = OB.time; Print("Bullish Order Block detected at: ", TimeToString(OB.time)); break; } // Bearish OB candidate if(getOpen(i) > getClose(i) && getOpen(i+2) > getClose(i+2) && getOpen(i+3) < getClose(i+3) && getOpen(i+3) > getClose(i+2)) // Fixed condition { OB = new COrderBlock(); OB.direction = -1; OB.time = getTimeBar(i+3); OB.high = getHigh(i+3); OB.low = getLow(i+3); OBClr = BearOB; T1 = OB.time; Print("Bearish Order Block detected at: ", TimeToString(OB.time)); break; } } } if(OB == NULL) return; // Check if we already traded this OB if(lastTradedOBTime == OB.time) return; // If price retraces inside OB zone Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); bool inBullZone = (OB.direction > 0 && Ask <= OB.high && Ask >= OB.low); bool inBearZone = (OB.direction < 0 && Bid >= OB.low && Bid <= OB.high); if(!inBullZone && !inBearZone) return; // Use your DetectSwing function to find swings // We need to call it multiple times to find the most recent swings double mostRecentSwingHigh = 0; double mostRecentSwingLow = EMPTY_VALUE; datetime mostRecentSwingHighTime = 0; datetime mostRecentSwingLowTime = 0; // Scan recent bars to find the most recent swings for(int i = 0; i < 20; i++) // Check the last 20 bars { // Reset swing variables fib_high = 0; fib_low = 0; fib_t1 = 0; fib_t2 = 0; DetectSwingForBar(i, SWING_OB); if(fib_high > 0 && (mostRecentSwingHighTime == 0 || fib_t1 > mostRecentSwingHighTime)) { mostRecentSwingHigh = fib_high; mostRecentSwingHighTime = fib_t1; } if(fib_low < EMPTY_VALUE && (mostRecentSwingLowTime == 0 || fib_t2 > mostRecentSwingLowTime)) { mostRecentSwingLow = fib_low; mostRecentSwingLowTime = fib_t2; } } // Ensure we found both swing points if(mostRecentSwingHighTime == 0 || mostRecentSwingLowTime == 0) return; // Draw Fibonacci before trading to validate if(OB.direction > 0 && inBullZone) { // Draw Fibonacci from recent swing low to recent swing high ObjectDelete(0, "FIB_OB_BULL"); if(ObjectCreate(0, "FIB_OB_BULL", OBJ_FIBO, 0, mostRecentSwingLowTime, mostRecentSwingLow, mostRecentSwingHighTime, mostRecentSwingHigh)) { // Format Fibonacci ObjectSetInteger(0, "FIB_OB_BULL", OBJPROP_COLOR, clrBlack); for(int i = 0; i < ObjectGetInteger(0, "FIB_OB_BULL", OBJPROP_LEVELS); i++) { ObjectSetInteger(0, "FIB_OB_BULL", OBJPROP_LEVELCOLOR, i, clrBlack); } double entLvlBull = mostRecentSwingHigh - (mostRecentSwingHigh - mostRecentSwingLow) * (Fib_Trade_lvls / 100.0); if(Ask <= entLvlBull) { T2 = getTimeBar(0); OB.draw(T1, T2, BullOB); ExecuteTrade(ORDER_TYPE_BUY); lastTradedOBTime = OB.time; // Mark this OB as traded delete OB; OB = NULL; } } } else if(OB.direction < 0 && inBearZone) { // Draw Fibonacci from recent swing high to recent swing low ObjectDelete(0, "FIB_OB_BEAR"); if(ObjectCreate(0, "FIB_OB_BEAR", OBJ_FIBO, 0, mostRecentSwingHighTime, mostRecentSwingHigh, mostRecentSwingLowTime, mostRecentSwingLow)) { // Format Fibonacci ObjectSetInteger(0, "FIB_OB_BEAR", OBJPROP_COLOR, clrBlack); for(int i = 0; i < ObjectGetInteger(0, "FIB_OB_BEAR", OBJPROP_LEVELS); i++) { ObjectSetInteger(0, "FIB_OB_BEAR", OBJPROP_LEVELCOLOR, i, clrBlack); } double entLvlBear = mostRecentSwingLow + (mostRecentSwingHigh - mostRecentSwingLow) * (Fib_Trade_lvls / 100.0); if(Bid >= entLvlBear) { T2 = getTimeBar(0); OB.draw(T1, T2, BearOB); ExecuteTrade(ORDER_TYPE_SELL); lastTradedOBTime = OB.time; // Mark this OB as traded delete OB; OB = NULL; } } } }
DetectAndDrawOrderBlocks()関数は、OBを識別、検証し、さらに取引を実行する役割を担っており、フィボナッチの一致(コンフルエンス)を意思決定プロセスに統合しています。関数は新しいバーごとに検出をリセットするところから始まり、直近のローソク足をスキャンして強気または弱気のOBパターンを探します。有効なOBが見つかると、現在の価格がそのゾーンにリトレースしているかを確認し、潜在的な取引機会を示します。取引を実行する前に、関数はスイング検出を呼び出して直近のスイングハイとスイングローを特定し、その間にフィボナッチリトレースメントを描画できる状態にします。
次に、フィボナッチツールを用いてエントリーを検証し、価格が事前に設定されたリトレースメントレベルに一致することを確認してからエントリーを確定します。これにより、早まったエントリーを避け、OBゾーンとフィボナッチリトレースメントの両方が整合した場合にのみ取引が執行されるようにすることで、取引の検証精度と一貫性を高めています。
//============================== FVG ================================// // Definition (ICT-style): // Let C=i, B=i+1, A=i+2. // Bullish FVG if Low(A) > High(C) -> gap [High(C), Low(A)] // Bearish FVG if High(A) < Low(C) -> gap [High(A), Low(C)] struct SFVG { int dir; // +1 bull, -1 bear datetime tLeft; // left time anchor double top; // zone top price double bot; // zone bottom price string Name() const { string k = TimeToString(tLeft, TIME_DATE|TIME_MINUTES); return (dir>0 ? "FVG_B_" : "FVG_S_") + k + "_" + IntegerToString((int)(top*1000.0)); } }; bool FVGExistsAt(const string &name){ return ObjectFind(0, name) != -1; } void DetectAndDrawFVGs() { double point = SymbolInfoDouble(_Symbol, SYMBOL_POINT); int counted = 0; for(int i=2; i<MathMin(FVG_ScanBars, Bars(_Symbol, _Period))-2; i++) { // Build A,B,C double lowA = getLow(i+2); double highA = getHigh(i+2); double highC = getHigh(i); double lowC = getLow(i); // Bullish FVG: Low of A > High of C if(lowA > highC && (lowA - highC >= FVG_MinPoints * point)) { SFVG z; z.dir = +1; z.tLeft = getTimeBar(i+2); // Changed from getTimeBar to getTime z.top = lowA; z.bot = highC; DrawFVG(z); counted++; } // Bearish FVG: High of A < Low of C else if(highA < lowC && (lowC - highA >= FVG_MinPoints * point)) { SFVG z; z.dir = -1; z.tLeft = getTimeBar(i+2); // Changed from getTimeBar to getTime z.top = lowC; // Fixed: should be lowC for bearish FVG top z.bot = highA; // Fixed: should be highA for bearish FVG bottom DrawFVG(z); counted++; } if(counted > 15) break; // avoid clutter } // --- Simplified trading for FVGs --- Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // scan drawn objects and trade on first valid touch of EQ (50%) int total = ObjectsTotal(0, 0, -1); static datetime lastTradeBar = 0; if(OneTradePerBar) { datetime barNow = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE); if(lastTradeBar == barNow) return; // already traded this bar } for(int idx=0; idx<total; idx++) { string name = ObjectName(0, idx); if(StringFind(name, "FVG_", 0) != 0) continue; // only our FVGs // Get object coordinates datetime t1 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 0); double y1 = ObjectGetDouble(0, name, OBJPROP_PRICE, 0); datetime t2 = (datetime)ObjectGetInteger(0, name, OBJPROP_TIME, 1); double y2 = ObjectGetDouble(0, name, OBJPROP_PRICE, 1); double top = MathMax(y1, y2); double bot = MathMin(y1, y2); bool isBull = (StringFind(name, "FVG_B_", 0) == 0); double mid = (top + bot) * 0.5; if(isBull) { // trade when Ask is inside the gap and at/under EQ if(Ask <= top && Ask >= bot && (!FVG_TradeAtEQ || Ask <= mid)) { ExecuteTrade(ORDER_TYPE_BUY); lastTradeBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE); break; } } else { // trade when Bid is inside the gap and at/over EQ if(Bid <= top && Bid >= bot && (!FVG_TradeAtEQ || Bid >= mid)) { ExecuteTrade(ORDER_TYPE_SELL); lastTradeBar = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE); break; } } } }
この関数は、ICTロジックに基づきFVGを検出、描画、取引する役割を担っています。まず直近のバーをスキャンし、強気のギャップではローソク足Aの安値がローソク足Cの高値を上回っている場合、弱気のギャップではローソク足Aの高値がローソク足Cの安値を下回っている場合を特定し、ギャップが指定された最小ポイント数以上であることを条件とします。FVGが検出されると、その情報は構造体に保存され、チャート上に視覚的に描画され、取引対象として追跡されます。その後、システムはアクティブなFVGゾーンを監視し、価格がギャップ内に入ったタイミングや、オプションが有効であれば均衡点(ゾーンの50%)との整合を確認します。条件が整った場合、関数は取引を執行し、強気FVGでは買い、弱気FVGでは売りをおこないます。なお、1バーにつき1取引に制限することで、過剰取引を防いでいます。このように、検出、視覚化、取引を組み合わせることで、FVGツールは分析的であると同時に、直接取引に活用できる機能となっています。
//=============================== BOS ===============================// // Use unified swings (no RSI). Trading logic mirrors your earlier code: // - Sell when price breaks above last swing high (liquidity run idea) // - Buy when price breaks below last swing low void DetectAndDrawBOS() { // Use DetectSwingForBar to find the most recent swing points double mostRecentSwingHigh = 0; double mostRecentSwingLow = EMPTY_VALUE; datetime mostRecentSwingHighTime = 0; datetime mostRecentSwingLowTime = 0; // Scan recent bars to find the most recent swings for BOS for(int i = 0; i < 20; i++) // Check the last 20 bars { // Reset swing variables swng_High = 0; swng_Low = 0; bos_tH = 0; bos_tL = 0; // Detect swing at this bar for BOS DetectSwingForBar(i, SWING_BOS); if(swng_High > 0 && (mostRecentSwingHighTime == 0 || bos_tH > mostRecentSwingHighTime)) { mostRecentSwingHigh = swng_High; mostRecentSwingHighTime = bos_tH; } if(swng_Low < EMPTY_VALUE && (mostRecentSwingLowTime == 0 || bos_tL > mostRecentSwingLowTime)) { mostRecentSwingLow = swng_Low; mostRecentSwingLowTime = bos_tL; } } // Update the global BOS variables with the most recent swings if(mostRecentSwingHighTime > 0) { if(mostRecentSwingHighTime != bos_tH) Bull_BOS_traded = false; swng_High = mostRecentSwingHigh; bos_tH = mostRecentSwingHighTime; } if(mostRecentSwingLowTime > 0) { if(mostRecentSwingLowTime != bos_tL) Bear_BOS_traded = false; swng_Low = mostRecentSwingLow; bos_tL = mostRecentSwingLowTime; } // Now check for break of structure Bid = SymbolInfoDouble(_Symbol, SYMBOL_BID); Ask = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Get current bar time to prevent multiple trades on same bar datetime currentBarTime = iTime(_Symbol, _Period, 0); // SELL on break above swing high if(swng_High > 0 && Ask > swng_High && Bull_BOS_traded == false) { // Check if we haven't already traded this breakout if(lastBOSTradeTime != currentBarTime || lastBOSTradeDirection != -1) { if(DrawBOSLines) DrawBOS("BOS_H_" + TimeToString(bos_tH), bos_tH, swng_High, TimeCurrent(), swng_High, BOSBear, -1); ExecuteTrade(ORDER_TYPE_BUY); // Update trade tracking lastBOSTradeTime = currentBarTime; lastBOSTradeDirection = -1; Bull_BOS_traded = true; // Reset the swing high to prevent immediate re-trading swng_High = -1.0; } } // BUY on break below swing low if(swng_Low > 0 && Bid < swng_Low && Bear_BOS_traded == false) { // Check if we haven't already traded this breakout if(lastBOSTradeTime != currentBarTime || lastBOSTradeDirection != 1) { if(DrawBOSLines) DrawBOS("BOS_L_" + TimeToString(bos_tL), bos_tL, swng_Low, TimeCurrent(), swng_Low, BOSBull, +1); ExecuteTrade(ORDER_TYPE_SELL); // Update trade tracking Bear_BOS_traded = true; lastBOSTradeTime = currentBarTime; lastBOSTradeDirection = 1; // Reset the swing low to prevent immediate re-trading swng_Low = -1.0; } } }このBOS関数は、統合スイングロジックを用いてBOSイベントを検出し、取引を実行する役割を担っています。直近20本のバーをスキャンして最新のスイングハイおよびスイングローを特定し、グローバルなBOS変数を更新しながら、同じバーでの重複取引を防ぎます。価格が直近のスイングハイを上抜けた場合は買い(高値上の流動性確保)、直近のスイングローを下抜けた場合は売りを実行します。必要に応じてBOSラインをチャート上に描画して視覚化することも可能で、内部フラグによって同じブレイクアウトでの即時再エントリーを防止しています。
//---------------------------- BOS UI -------------------------------// void DrawBOS(const string name, datetime t1, double p1, datetime t2, double p2, color col, int dir) { if(ObjectFind(0, name) == -1) { ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2); ObjectSetInteger(0, name, OBJPROP_COLOR, col); ObjectSetInteger(0, name, OBJPROP_WIDTH, 2); string lbl = name + "_lbl"; ObjectCreate(0, lbl, OBJ_TEXT, 0, t2, p2); ObjectSetInteger(0, lbl, OBJPROP_COLOR, col); ObjectSetInteger(0, lbl, OBJPROP_FONTSIZE, 10); ObjectSetString(0, lbl, OBJPROP_TEXT, "Break"); ObjectSetInteger(0, lbl, OBJPROP_ANCHOR, (dir>0)?ANCHOR_RIGHT_UPPER:ANCHOR_RIGHT_LOWER); } }
DrawBOS関数は、チャート上でBOSを視覚的に表示する役割を担っています。指定された2点(t1, p1)と(t2, p2)の間にトレンドラインを作成し、色や線の太さを設定します。指定された名前のオブジェクトがまだ存在しない場合は、まずトレンドラインを作成し、色と太さを設定した後、終点に「Break」と表示するテキストラベルを追加します。ラベルの位置は方向(dir)に応じて固定され、強気の場合は右上、弱気の場合は右下に配置されます。これにより、チャート上で市場構造の変化を直感的に確認できる明確な視覚的指標が提供されます。
void DrawFVG(const SFVG &z) { string name = z.Name(); datetime tNow = (datetime)SeriesInfoInteger(_Symbol, _Period, SERIES_LASTBAR_DATE); // Delete existing object if it exists if(ObjectFind(0, name) != -1) ObjectDelete(0, name); // Create rectangle object for FVG if(!ObjectCreate(0, name, OBJ_RECTANGLE, 0, z.tLeft, z.bot, tNow, z.top)) { Print("Error creating FVG object: ", GetLastError()); return; } // Set object properties ObjectSetInteger(0, name, OBJPROP_COLOR, z.dir>0 ? BullFVG : BearFVG); ObjectSetInteger(0, name, OBJPROP_FILL, true); ObjectSetInteger(0, name, OBJPROP_BACK, true); ObjectSetInteger(0, name, OBJPROP_WIDTH, 1); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); // Set Z-order to make sure it's visible ObjectSetInteger(0, name, OBJPROP_ZORDER, 0); }
DrawFVG関数は、チャート上でFVGを視覚化する役割を担っています。まず、同じ名前のオブジェクトが既に存在するかを確認し、重複を避けるために削除します。その後、ギャップの左端時刻tLeftから現在のバーまで、またギャップの下限botから上限topまでの範囲に長方形を作成します。この長方形はギャップの方向に応じた色で塗りつぶされ、チャートの背面に配置され、境界線は実線で描かれます。Zオーダーを0に設定することで、FVGは他のチャート要素の背後に表示され、トレーダーが価格の不均衡をリアルタイムで直感的に確認できる視覚的参照として機能します。
void OnTick() { if(!IsNewBar()) return; // Strategy switch if(TradeStrategy == STRAT_FVG || TradeStrategy == STRAT_AUTO) DetectAndDrawFVGs(); if(TradeStrategy == STRAT_OB || TradeStrategy == STRAT_AUTO) DetectAndDrawOrderBlocks(); if(TradeStrategy == STRAT_BOS || TradeStrategy == STRAT_AUTO) DetectAndDrawBOS(); }
OnTick関数はティックごとに実行されますが、新しいバーが形成された場合にのみ処理を進めるようになっており、計算はローソク足ごとに一度だけおこなわれます。その後、選択された取引戦略を確認し、FVGまたは自動モードが有効な場合はDetectAndDrawFVGsを呼び出し、OBまたは自動モードが有効な場合はDetectAndDrawOrderBlocksを呼び出します。さらに、BOSまたは自動モードが有効な場合はDetectAndDrawBOSを呼び出します。この構造により、EAやインジケーターは選択した戦略に基づいて、リアルタイムで異なる市場構造を動的に検出、視覚化することが可能となります。
バックテスト結果
バックテストは、1時間足(1H)を対象に、約2か月間(2025年7月1日から2025年9月1日まで)の期間で評価されました。設定はデフォルトのままで、戦略にはBOSが使用されています。


結論
3つの主要要素であるOB、BOS、FVGを統合した統一型SMC取引フレームワークを開発しました。それぞれのコンセプトには、検出、描画、取引のロジックが組み込まれています。OBはフィボナッチリトレースメントで検証される機関投資家の足跡として識別され、BOSは価格がスイングハイやスイングローを抜けた際の流動性を捉え、FVGは価格形成の非効率性を示し、反応の強いゾーンとして機能します。これらの要素を1つのシステムに統合することで、チャート上で高確率のセットアップを動的に検出でき、視覚的補助や自動実行機能も備えたEAが実現しました。
この統合アプローチにより、トレーダーはSMCの原則を体系的に適用でき、従来の手動チャート分析に伴う推測を排除できます。EAは市場構造の変化をマーク、追跡するだけでなく、価格が事前に定義されたSMC条件に整合するとリアルタイムで取引を実行します。これにより、トレーダーは規律あるルールベースの手法を利用でき、感情的バイアスを減らし、一貫性を高め、さまざまな市場環境において機関投資家スタイルの取引機会を捉える専門的な優位性を得ることが可能となります。
MetaQuotes Ltdにより英語から翻訳されました。
元の記事: https://www.mql5.com/en/articles/16340
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
MQL5での取引戦略の自動化(第30回):視覚的フィードバックによるプライスアクションAB-CDハーモニックパターンの作成
初心者からエキスパートへ:MQL5を使ったアニメーションニュース見出し(X) - ニュース取引のための多銘柄チャート表示
カスタム市場センチメント指標の開発
プライスアクション分析ツールキットの開発(第39回):MQL5でBOSとChoCHの検出を自動化する
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索
フェア・バリュー・ギャップの説明と描写が間違っています。
あなたの記事は素晴らしい。ただ、記事の中にインドネシア人がいることは知っています。僕らのコンセプトは同じだけど、違うのは僕がSMC+GPTにPythonを使っていることだね。
FVGゾーンの内側にすでに価格/キャンドルスティックが戻っている状態でFVGを描きました。
あなたは間違っているか、図形の準備が不十分です。左から 1番目と3番目のローソク足が赤で示されているように、これらは弱気であり、2番目のローソク足(おそらくFVGが発生した場所)の前後に 2つの大きなギャップを形成しています。

インターネットでFVGを検索すると、このフォーメーションは価格の大きな一方向のジャンプ/ギャップであり、ジグザグのジャンプではないことがわかります。