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
-
Multi-Timeframe Detection The same three-candle logic runs identically on any timeframe, from 1-minute scalping charts to 4-hour swing structure, with no lookahead or repainting.
-
Imbalance Efficiency Filtering Every gap is measured against a dynamic ATR-based threshold, so negligible, low-conviction gaps are filtered out and only genuine institutional-scale voids are plotted.
-
Auto-Fill Zone Tracking Each gap is monitored bar-by-bar and automatically removed the moment price trades back through it, keeping the chart limited to zones that remain genuinely unmitigated.
-
Lightweight, Non-Repainting Calculation Gaps confirm strictly on bar close using array-managed box objects, keeping the script fast on high-timeframe-count charts without any historical repainting.
How to Use
- Copy the script into TradingView's Pine Editor and click Add to Chart.
- Scan your chart for unmitigated (still-highlighted) FVG zones above or below current price.
- 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.
- Use the gap's near edge as your structural reference level, not as a standalone entry signal.
The Pine Script V5 Code
//@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.")