Pine Script

8 Fair Value Gap (FVG) Pine Script Templates — Non-Repainting, Copy-Paste Ready

|12 min read

Eight Pine Script fair value gap templates — bullish, bearish, mitigated, inverse FVGs. Non-repainting, ready to paste into TradingView.

A fair value gap is one of the most cited concepts in ICT-style trading and one of the worst-coded indicators on TradingView. Most public FVG scripts repaint, miss the inverse case, or break on multi-timeframe charts. The detection logic is actually simple — three candles, one inequality — and the scripts that get it right fit in under 50 lines.

This guide gives you eight working templates. Each is non-repainting, copy-paste ready, and isolated enough to combine. Start with template 1, layer in any of the others as your edge requires.

What an FVG Actually Is

A fair value gap is a three-candle pattern where the wicks of candles 1 and 3 fail to overlap. The space between them is the gap — price moved through that range so quickly that no two-sided trading occurred there.

A bullish FVG: the high of candle 1 is below the low of candle 3. The gap sits between those two levels.

A bearish FVG: the low of candle 1 is above the high of candle 3. The gap sits between those two levels.

The thesis: institutions move price aggressively enough to skip a level, and price often returns to that level later to balance. FVGs act as magnets, support, or resistance — depending on which version of ICT theory you read.

3-candle FVG anatomy showing both bullish and bearish FVG patterns with the gap region highlighted

Template 1: Basic 3-Candle FVG

The simplest version. Detects every bullish and bearish FVG, draws a translucent box, leaves it on the chart until removed manually.

//@version=6
indicator("Basic FVG", overlay=true, max_boxes_count=200)

bullColor = input.color(color.new(color.green, 80), "Bullish FVG")
bearColor = input.color(color.new(color.red, 80), "Bearish FVG")
extend = input.int(20, "Extend box right (bars)", minval=1)

// Bullish FVG: high[2] < low[0]
bullFVG = high[2] < low[0]
// Bearish FVG: low[2] > high[0]
bearFVG = low[2] > high[0]

if bullFVG
    box.new(bar_index - 2, low[0], bar_index + extend, high[2],
            bgcolor=bullColor, border_color=color.new(color.green, 50))

if bearFVG
    box.new(bar_index - 2, low[2], bar_index + extend, high[0],
            bgcolor=bearColor, border_color=color.new(color.red, 50))

That is it. Every other template builds on this base.

Template 2: Mitigated FVG (Hides Filled Gaps)

A mitigated FVG has been retraced into. The thesis says it is no longer a clean entry. This version removes the box once price returns to the gap.

//@version=6
indicator("Mitigated FVG", overlay=true, max_boxes_count=200)

var array<box> bullBoxes = array.new<box>()
var array<box> bearBoxes = array.new<box>()

bullFVG = high[2] < low[0]
bearFVG = low[2] > high[0]

if bullFVG
    b = box.new(bar_index - 2, low[0], bar_index + 50, high[2],
                bgcolor=color.new(color.green, 80))
    array.push(bullBoxes, b)

if bearFVG
    b = box.new(bar_index - 2, low[2], bar_index + 50, high[0],
                bgcolor=color.new(color.red, 80))
    array.push(bearBoxes, b)

// Remove mitigated boxes
if array.size(bullBoxes) > 0
    for i = array.size(bullBoxes) - 1 to 0
        b = array.get(bullBoxes, i)
        if low <= box.get_top(b)
            box.delete(b)
            array.remove(bullBoxes, i)

if array.size(bearBoxes) > 0
    for i = array.size(bearBoxes) - 1 to 0
        b = array.get(bearBoxes, i)
        if high >= box.get_bottom(b)
            box.delete(b)
            array.remove(bearBoxes, i)

The chart stays clean. Only unmitigated FVGs remain visible — the higher-probability ones for retest entries.

Template 3: Inverse FVG (IFVG)

An inverse FVG is an FVG that has been broken in the opposite direction. The thesis: a bullish FVG that gets aggressively broken to the downside flips polarity — it now acts as resistance.

//@version=6
indicator("Inverse FVG", overlay=true)

bullFVG = high[2] < low[0]
bearFVG = low[2] > high[0]

var float lastBullTop = na
var float lastBullBot = na
var float lastBearTop = na
var float lastBearBot = na

if bullFVG
    lastBullTop := low[0]
    lastBullBot := high[2]
if bearFVG
    lastBearTop := low[2]
    lastBearBot := high[0]

// Bullish FVG broken below — flip to bearish IFVG
ifvgBear = not na(lastBullBot) and close < lastBullBot
// Bearish FVG broken above — flip to bullish IFVG
ifvgBull = not na(lastBearTop) and close > lastBearTop

if ifvgBear
    box.new(bar_index - 1, lastBullTop, bar_index + 30, lastBullBot,
            bgcolor=color.new(color.purple, 80), border_color=color.purple)
    lastBullBot := na

if ifvgBull
    box.new(bar_index - 1, lastBearTop, bar_index + 30, lastBearBot,
            bgcolor=color.new(color.purple, 80), border_color=color.purple)
    lastBearTop := na

IFVGs are a premium ICT concept — most traders never code them because the logic gets confusing. The trick: store the last unbroken FVG, then watch for a strong opposite close.

Template 4: FVG With Volume Filter

Not every FVG is institutional. A real liquidity gap shows up alongside elevated volume. This filter only draws FVGs where volume on the third candle exceeds the 20-period average.

//@version=6
indicator("Volume-Filtered FVG", overlay=true)

volMult = input.float(1.5, "Volume multiplier", minval=1.0, step=0.1)
avgVol = ta.sma(volume, 20)

bullFVG = high[2] < low[0] and volume > avgVol * volMult
bearFVG = low[2] > high[0] and volume > avgVol * volMult

if bullFVG
    box.new(bar_index - 2, low[0], bar_index + 30, high[2],
            bgcolor=color.new(color.green, 80))
if bearFVG
    box.new(bar_index - 2, low[2], bar_index + 30, high[0],
            bgcolor=color.new(color.red, 80))

A 1.5x volume multiplier filters out roughly 70% of FVGs on most charts. The remaining 30% have a much higher hit rate as support and resistance.

Template 5: Multi-Timeframe FVG

Plot daily FVGs on a 15-minute chart, or 1-hour FVGs on a 5-minute chart. Higher-timeframe FVGs have outsized importance — most retail traders only see them on the chart they trade.

//@version=6
indicator("Multi-Timeframe FVG", overlay=true)

htf = input.timeframe("60", "Higher timeframe")

[h2, l2, h0, l0] = request.security(syminfo.tickerid, htf,
     [high[2], low[2], high, low], lookahead=barmerge.lookahead_off)

bullFVG = h2 < l0
bearFVG = l2 > h0

if bullFVG and not bullFVG[1]
    box.new(bar_index, l0, bar_index + 50, h2,
            bgcolor=color.new(color.green, 75))
if bearFVG and not bearFVG[1]
    box.new(bar_index, l2, bar_index + 50, h0,
            bgcolor=color.new(color.red, 75))

Note lookahead_off — this is what prevents the script from repainting. Most multi-timeframe FVG scripts on TradingView use lookahead_on and look great in backtests but fail live.

Template 6: FVG With Order Block Confluence

The strongest version. An FVG that aligns with a recent order block is a high-probability institutional level. This template only draws FVGs that overlap a recently-formed order block.

//@version=6
indicator("FVG + OB Confluence", overlay=true)

// Order block detection (simplified)
obLookback = input.int(3, "OB lookback")
isBearishOB = close[obLookback] < open[obLookback]
isBullishOB = close[obLookback] > open[obLookback]

// FVG detection
bullFVG = high[2] < low[0]
bearFVG = low[2] > high[0]

// Confluence
bullConfluence = bullFVG and isBearishOB[2]
              and high[obLookback + 2] >= high[2] and low[obLookback + 2] <= low[0]
bearConfluence = bearFVG and isBullishOB[2]
              and high[obLookback + 2] >= high[0] and low[obLookback + 2] <= low[2]

if bullConfluence
    box.new(bar_index - 2, low[0], bar_index + 40, high[2],
            bgcolor=color.new(color.lime, 70), border_color=color.lime)
if bearConfluence
    box.new(bar_index - 2, low[2], bar_index + 40, high[0],
            bgcolor=color.new(color.maroon, 70), border_color=color.maroon)

Confluence cuts the FVG count by another 60-80%. The remaining setups are the highest-quality entries any ICT-style script will produce.

Template 7: FVG With Alerts

Every FVG worth trading deserves an alert. This template adds clean alert messages and webhook-ready JSON.

//@version=6
indicator("FVG Alerts", overlay=true)

bullFVG = high[2] < low[0]
bearFVG = low[2] > high[0]

if bullFVG
    box.new(bar_index - 2, low[0], bar_index + 30, high[2],
            bgcolor=color.new(color.green, 80))
    alert('{"type": "bullish_fvg", "ticker": "{{ticker}}", "top": ' +
          str.tostring(low[0]) + ', "bottom": ' + str.tostring(high[2]) + '}',
          alert.freq_once_per_bar_close)

if bearFVG
    box.new(bar_index - 2, low[2], bar_index + 30, high[0],
            bgcolor=color.new(color.red, 80))
    alert('{"type": "bearish_fvg", "ticker": "{{ticker}}", "top": ' +
          str.tostring(low[2]) + ', "bottom": ' + str.tostring(high[0]) + '}',
          alert.freq_once_per_bar_close)

The JSON payload includes top and bottom of the gap so a downstream system can place limit orders at the FVG boundary. The full webhook setup is covered in the webhook alerts guide.

Template 8: FVG Strategy (Tradeable, With Stops and Targets)

The conversion from indicator to strategy. Enters on FVG retest, stops below the gap (long) or above (short), targets the next opposing FVG.

//@version=6
strategy("FVG Strategy", overlay=true,
         initial_capital=10000,
         default_qty_type=strategy.percent_of_equity,
         default_qty_value=10,
         commission_type=strategy.commission.percent,
         commission_value=0.1)

bullFVG = high[2] < low[0]
bearFVG = low[2] > high[0]

var float bullTop = na
var float bullBot = na
var float bearTop = na
var float bearBot = na

if bullFVG
    bullTop := low[0]
    bullBot := high[2]
if bearFVG
    bearTop := low[2]
    bearBot := high[0]

// Long when price retraces into bullish FVG and closes above bottom
longEntry = not na(bullBot) and low <= bullTop and close > bullBot
             and strategy.position_size == 0

// Short when price retraces into bearish FVG and closes below top
shortEntry = not na(bearTop) and high >= bearBot and close < bearTop
              and strategy.position_size == 0

if longEntry
    strategy.entry("Long", strategy.long)
    strategy.exit("Long Exit", "Long",
                  stop=bullBot * 0.998,
                  limit=close + (close - bullBot) * 2)
    bullBot := na

if shortEntry
    strategy.entry("Short", strategy.short)
    strategy.exit("Short Exit", "Short",
                  stop=bearTop * 1.002,
                  limit=close - (bearTop - close) * 2)
    bearTop := na

The strategy uses a 2:1 reward-to-risk on every trade. Stop is just outside the gap; target is twice the distance from entry to stop. Adjust position size, commission, and the entry trigger to match your edge.

grid showing all 8 FVG templates with one-line description and brand-colored mini icon for each

Validating the Templates

All eight templates use confirmed historical bars only — no lookahead_on, no future references, no implicit forward indexing. To verify any one is non-repainting, load it on a live chart, take a screenshot, wait an hour, screenshot again. If any boxes have moved or deleted (other than mitigation in template 2), throw it out.

The mitigated FVG removal in template 2 is intentional — it removes filled gaps as price interacts with them. That is a real-time event, not repainting.

Build a Custom FVG Variant Without the Manual Code

The eight templates cover 90% of ICT FVG use cases. Most traders end up wanting one more variant: a session-filtered FVG that only appears during London, an FVG with breaker block confluence, an FVG strategy that scales position size based on gap width.

Each one is another debugging cycle. The faster path: describe the variant in PineWiz. "FVG indicator, but only show gaps formed during the first hour of the New York session, and only if the gap width is at least 0.3 ATR." You get a working script in under a minute.

The same applies if you want to combine FVG with order blocks, liquidity sweeps, or BOS — the SMC concepts covered in our order block and liquidity sweep guides. Stack them in a single script with one prompt.

Share this article
P

PineWiz Team

The PineWiz team specializes in Pine Script and algorithmic trading. We build AI tools that help retail traders turn their ideas into production-ready TradingView strategies and indicators — no coding required.

Ready to bring your idea to life?

Turn your trading ideas into Pine Script code without writing a single line.

Start Building