The Anatomy of a 3-Candle Fair Value Gap (FVG)

In Smart Money Concepts (SMC), a Fair Value Gap represents an acute price imbalance caused by large institutional market orders exhausting resting limit orders. The imbalance is mathematically defined across a 3-candle sequence:

  • Bullish FVG: Candle 1 High is strictly less than Candle 3 Low (Gap = Low_3 - High_1 > 0).
  • Bearish FVG: Candle 1 Low is strictly greater than Candle 3 High (Gap = Low_1 - High_3 > 0).

When the intervening Candle 2 possesses significant displacement (long candle body with minimal wicks), market makers are obligated to re-auction price into this imbalance to deliver equilibrium before continuation.

Production Python Script: Multi-Pair FVG Scanner

import MetaTrader5 as mt5
import pandas as pd
from datetime import datetime

def scan_fvgs(symbol: str, timeframe=mt5.TIMEFRAME_H1, num_candles: int = 100):
    rates = mt5.copy_rates_from_pos(symbol, timeframe, 0, num_candles)
    if rates is None or len(rates) < 3:
        return []
        
    df = pd.DataFrame(rates)
    df['time'] = pd.to_datetime(df['time'], unit='s')
    
    unmitigated_fvgs = []
    
    for i in range(1, len(df) - 1):
        c1 = df.iloc[i - 1]
        c2 = df.iloc[i]
        c3 = df.iloc[i + 1]
        
        # Bullish FVG check
        if c3['low'] > c1['high']:
            gap_size = (c3['low'] - c1['high']) / mt5.symbol_info(symbol).point
            # Check subsequent mitigation
            subsequent_lows = df.iloc[i + 2:]['low'] if i + 2 < len(df) else []
            mitigated = any(low <= c1['high'] for low in subsequent_lows)
            
            if not mitigated and gap_size >= 10.0:
                unmitigated_fvgs.append({
                    'type': 'BULLISH',
                    'symbol': symbol,
                    'top': c3['low'],
                    'bottom': c1['high'],
                    'gap_points': gap_size,
                    'created_at': c2['time']
                })
                
        # Bearish FVG check
        elif c1['low'] > c3['high']:
            gap_size = (c1['low'] - c3['high']) / mt5.symbol_info(symbol).point
            subsequent_highs = df.iloc[i + 2:]['high'] if i + 2 < len(df) else []
            mitigated = any(high >= c1['low'] for high in subsequent_highs)
            
            if not mitigated and gap_size >= 10.0:
                unmitigated_fvgs.append({
                    'type': 'BEARISH',
                    'symbol': symbol,
                    'top': c1['low'],
                    'bottom': c3['high'],
                    'gap_points': gap_size,
                    'created_at': c2['time']
                })
                
    return unmitigated_fvgs

if __name__ == "__main__":
    mt5.initialize()
    watchlist = ["EURUSD", "GBPUSD", "USDJPY", "XAUUSD", "US30"]
    for sym in watchlist:
        gaps = scan_fvgs(sym)
        print(f"[{sym}] Active Unmitigated FVGs: {len(gaps)}")
        for g in gaps[-3:]:
            print(f"  -> {g['type']} Gap: {g['bottom']:.5f} to {g['top']:.5f} ({g['gap_points']:.1f} pts)")
    mt5.shutdown()

High-Probability FVG Execution Confluence Checklist

  • Time-of-Day Filter: The imbalance must originate within London Killzone (07:00–10:00 GMT) or New York Open (13:00–16:00 GMT).
  • Higher-Timeframe Alignment: Only enter Bullish FVGs when Daily and H4 Market Structure reflects bullish Change of Character (CHoCH).
  • Discount / Premium Arrays: Long entries into Bullish FVGs must reside below the 50% dealing range equilibrium of the current swing leg.