Pine Script V5 Liquidity Targets Free / Open Source

Fair Value Gaps (FVG)

Overview

Price does not move through the market at a uniform pace. When one side of the order book is overwhelmed — typically by an institutional-size order sweeping through available liquidity — price displaces faster than the opposing side can respond. The result is a three-candle footprint where the wicks of the first and third candle fail to overlap, leaving a void: a Fair Value Gap.

This void represents an inefficiency. Because the gap formed on low two-sided participation, it is an area many institutional execution algorithms are understood to reference when rebalancing inventory — price frequently returns to partially or fully "fill" the gap before the prevailing move continues. Fair Value Gaps (FVG) detects this three-candle signature in real time, filters it against a minimum size threshold so only genuine imbalances are plotted, and tracks each zone until it is mitigated.

Features of This Indicator

How to Use

  1. Copy the script into TradingView's Pine Editor and click Add to Chart.
  2. Scan your chart for unmitigated (still-highlighted) FVG zones above or below current price.
  3. Wait for price to return into the zone and observe how it reacts — a clean fill and reversal carries more weight than a slow grind through it.
  4. Use the gap's near edge as your structural reference level, not as a standalone entry signal.
Pro Tip: FVGs carry the most weight when they overlap with an unmitigated Order Block. A gap sitting inside — or immediately adjacent to — an institutional order block means two independent structural signatures are pointing to the same origin of displacement, which meaningfully raises the conviction of a reaction at that level.

The Pine Script V5 Code

fair-value-gaps.pine
//@version=5
indicator("Fair Value Gaps (FVG) — Crypto Indicator Pro", shorttitle="FVG [CIP]", overlay=true, max_boxes_count=500)

// ============================================================================
// FAIR VALUE GAPS (FVG)
// Detects three-candle price imbalances where the wicks of candle 1 and
// candle 3 fail to overlap, leaving a liquidity void that institutional
// execution algorithms are frequently understood to reference when
// rebalancing price. Confirms strictly on bar close — no repainting.
// ============================================================================

// ---------------------------- INPUTS ----------------------------
minGapATRMult = input.float(0.25, "Minimum Gap Size (x ATR)", minval=0.0, step=0.05, group="Imbalance Filter",
     tooltip="Gap must be at least this multiple of ATR to be plotted, filtering out negligible imbalances.")
atrLength     = input.int(14, "ATR Length", minval=1, group="Imbalance Filter")
maxBoxAge     = input.int(150, "Auto-Delete Unfilled Gaps After (bars)", minval=10, group="Display")
extendBoxes   = input.bool(false, "Extend Unfilled Zones to Current Bar", group="Display")
bullColor     = input.color(color.new(color.teal, 82), "Bullish FVG Color", group="Display")
bearColor     = input.color(color.new(color.red, 82), "Bearish FVG Color", group="Display")

// ---------------------------- IMBALANCE SIZE FILTER ----------------------------
atrVal = ta.atr(atrLength)
minGap = atrVal * minGapATRMult

// ---------------------------- THREE-CANDLE IMBALANCE DETECTION ----------------------------
// Candle 1 = two bars back [2]   Candle 2 = displacement candle [1]   Candle 3 = current bar [0]
bullGapSize  = low - high[2]     // void between candle 1's high and candle 3's low
bearGapSize  = low[2] - high     // void between candle 3's high and candle 1's low

isBullishFVG = bullGapSize > minGap
isBearishFVG = bearGapSize > minGap

// ---------------------------- STORAGE ----------------------------
var array<box> bullBoxes  = array.new<box>()
var array<box> bearBoxes  = array.new<box>()
var array<int> bullBoxBar = array.new<int>()
var array<int> bearBoxBar = array.new<int>()

// ---------------------------- GAP ZONE CREATION ----------------------------
if isBullishFVG
    leftBar = bar_index - 2
    newBox = box.new(left=leftBar, top=low, right=bar_index, bottom=high[2],
         border_color=color.teal, bgcolor=bullColor,
         extend=extendBoxes ? extend.right : extend.none)
    array.push(bullBoxes, newBox)
    array.push(bullBoxBar, bar_index)

if isBearishFVG
    leftBar = bar_index - 2
    newBox = box.new(left=leftBar, top=low[2], right=bar_index, bottom=high,
         border_color=color.red, bgcolor=bearColor,
         extend=extendBoxes ? extend.right : extend.none)
    array.push(bearBoxes, newBox)
    array.push(bearBoxBar, bar_index)

// ---------------------------- FILL / MITIGATION TRACKING ----------------------------
// A bullish gap (void sits above price) fills once a later bar's low trades down into it.
if array.size(bullBoxes) > 0
    for i = array.size(bullBoxes) - 1 to 0
        b = array.get(bullBoxes, i)
        if low <= box.get_bottom(b)
            box.delete(b)
            array.remove(bullBoxes, i)
            array.remove(bullBoxBar, i)

// A bearish gap (void sits below price) fills once a later bar's high trades up into it.
if array.size(bearBoxes) > 0
    for i = array.size(bearBoxes) - 1 to 0
        b = array.get(bearBoxes, i)
        if high >= box.get_top(b)
            box.delete(b)
            array.remove(bearBoxes, i)
            array.remove(bearBoxBar, i)

// ---------------------------- AGE-BASED CLEANUP ----------------------------
if array.size(bullBoxes) > 0
    for i = array.size(bullBoxes) - 1 to 0
        if bar_index - array.get(bullBoxBar, i) > maxBoxAge
            box.delete(array.get(bullBoxes, i))
            array.remove(bullBoxes, i)
            array.remove(bullBoxBar, i)

if array.size(bearBoxes) > 0
    for i = array.size(bearBoxes) - 1 to 0
        if bar_index - array.get(bearBoxBar, i) > maxBoxAge
            box.delete(array.get(bearBoxes, i))
            array.remove(bearBoxes, i)
            array.remove(bearBoxBar, i)

// ---------------------------- ALERTS ----------------------------
alertcondition(isBullishFVG, title="Bullish FVG Formed", message="Bullish fair value gap detected.")
alertcondition(isBearishFVG, title="Bearish FVG Formed", message="Bearish fair value gap detected.")
Risk Notice: This script is provided for educational and research purposes only and does not constitute financial advice. Always backtest on a demo account before any live deployment. See our full Institutional Risk Disclaimer for details.

03 Related Modules

PINE SCRIPT V5

Automated Order Blocks

Calculates institutional order accumulation zones with real-time market structure verification.

View Module
ALGORITHMIC FLOW

Liquidity Grab Engine

Isolates structural stop-hunts, internal sweeps, and retail liquidity pools by session.

View Module
VOLUME PROFILE

Session Volume Profile

Maps real-time Point of Control (POC), Value Area High (VAH), and high-density volume nodes.

View Module

04 Related Reading

Three Fair Value Gaps Are Stacked on Your Chart — Which One Do You Trade?

Category: Technical Guides
Read Guide →

Inversion Fair Value Gaps Explained: The Setup Most Retail Traders Miss

Category: Technical Guides
Read Guide →

FVG Indicator That Doesn't Repaint: What to Look for Before You Pay

Category: Risk Protocols
Read Guide →

Why Price Keeps 'Filling the Gap' and Reversing on You

Category: Market Intelligence
Read Guide →