Edgoro
Volatility indicator

Fair value gap (FVG): definition, fill rules and Pine Script v6 code

What a fair value gap is in the three-candle pattern, how traders read gaps and fills, the settings that change the result, and a free FVG script.

What it measures

A fair value gap is a three-candle pattern in which the middle candle moves so far that the first and third candles don't overlap. In a bullish gap, the low of the third candle is above the high of the first: the price range between them was crossed by one candle, with little trading inside it. Traders call it an imbalance and watch whether price comes back to fill it.

The term was popularised by smart money concepts (SMC) educators, but the pattern itself is ordinary price action and can be defined exactly. For the wider vocabulary, see smart money concepts explained without the hype.

The formula in words

Number the candles 1, 2 and 3, where 3 is the bar that just closed.

  1. Bullish FVG: the low of candle 3 is above the high of candle 1. The gap runs from candle 1's high (bottom) to candle 3's low (top).
  2. Bearish FVG: the high of candle 3 is below the low of candle 1. The gap runs from candle 3's high (bottom) to candle 1's low (top).
  3. Minimum size: tiny gaps appear constantly. A common filter keeps only gaps at least a fraction of the ATR wide, such as 0.25 × ATR(14).
  4. Fill: a gap is considered closed when price comes back to it. Definitions differ: some close it when price enters it, some when price reaches its middle (often called consequent encroachment), and some only when price fills it completely.

A gap is known only once candle 3 has closed, which is why the script waits for the bar to close before drawing anything.

How traders read it

  • Retracement zones. Traders who use FVGs often expect price to come back to an open gap and react there: a bullish gap below price as possible support, a bearish gap above price as possible resistance.
  • Displacement. A large gap shows a fast, one-sided move. Several gaps in a row describe strong momentum.
  • Filled gaps. A gap that's filled completely and broken through is read as the imbalance having been absorbed. Some traders then expect the zone to switch roles.
  • Context. Gaps are usually combined with market structure or order blocks: a bullish gap left by a break of structure gets more attention than one in the middle of a range.

Common settings

SettingTypical valueWhat changing it does
Minimum size0.1 to 0.5 × ATRHigher keeps only gaps left by large moves.
Fill ruleFills it completely"Enters it" closes gaps sooner; "reaches its middle" is in between.
Open gaps kept5 to 20 per sideOld unfilled gaps far from price add clutter.

The fill rule decides how long each gap stays on the chart, and so how often price appears to "react" to one. Choose it before reading anything into reactions.

Pine Script v6 code

The complete, self-contained script is below. Copy it into a new indicator in TradingView's Pine Editor, or download the .pine file.

Pine Script v6
//@version=6
indicator("Fair value gaps (FVG)", shorttitle = "FVG", overlay = true, max_boxes_count = 500)

minSize    = input.float(0.25, "Minimum gap size (x ATR)", minval = 0, step = 0.05, group = "Detection")
atrLen     = input.int(14, "ATR length", minval = 1, group = "Detection")
fillRule   = input.string("Fills it completely", "Close a gap when price", options = ["Enters it", "Reaches its middle", "Fills it completely"], group = "Detection")
maxOpen    = input.int(10, "Open gaps to keep per side", minval = 1, maxval = 50, group = "Display")
keepFilled = input.bool(false, "Keep closed gaps on the chart", group = "Display")
bullCol    = input.color(color.new(color.teal, 80), "Bullish gap", group = "Display")
bearCol    = input.color(color.new(color.red, 80), "Bearish gap", group = "Display")

atr = ta.atr(atrLen)

// Three-candle pattern. Bullish: this bar's low is above the high two bars back. Bearish: this bar's high is below the low two bars back.
bullGap = low > high[2] and low - high[2] >= minSize * atr
bearGap = high < low[2] and low[2] - high >= minSize * atr

// Price level that closes a gap, depending on the rule chosen.
closeLevel(box b, bool bullish) =>
    top = b.get_top()
    bot = b.get_bottom()
    mid = (top + bot) / 2
    fillRule == "Enters it" ? (bullish ? top : bot) : fillRule == "Reaches its middle" ? mid : (bullish ? bot : top)

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

// Only closed bars change the zones, so nothing appears or disappears on the live bar.
if barstate.isconfirmed
    if bullBoxes.size() > 0
        for i = bullBoxes.size() - 1 to 0
            b = bullBoxes.get(i)
            b.set_right(bar_index)
            if low <= b.get_top()
                enteredBull := true
            if low <= closeLevel(b, true)
                if keepFilled
                    b.set_bgcolor(color.new(color.gray, 90))
                    b.set_border_color(color.new(color.gray, 75))
                else
                    b.delete()
                bullBoxes.remove(i)
    if bearBoxes.size() > 0
        for i = bearBoxes.size() - 1 to 0
            b = bearBoxes.get(i)
            b.set_right(bar_index)
            if high >= b.get_bottom()
                enteredBear := true
            if high >= closeLevel(b, false)
                if keepFilled
                    b.set_bgcolor(color.new(color.gray, 90))
                    b.set_border_color(color.new(color.gray, 75))
                else
                    b.delete()
                bearBoxes.remove(i)
    if bullGap
        bullBoxes.push(box.new(bar_index - 2, low, bar_index, high[2], border_color = bullCol, bgcolor = bullCol))
        if bullBoxes.size() > maxOpen
            bullBoxes.shift().delete()
    if bearGap
        bearBoxes.push(box.new(bar_index - 2, low[2], bar_index, high, border_color = bearCol, bgcolor = bearCol))
        if bearBoxes.size() > maxOpen
            bearBoxes.shift().delete()

alertcondition(bullGap, "New bullish FVG", "A bullish fair value gap formed on {{ticker}} ({{interval}})")
alertcondition(bearGap, "New bearish FVG", "A bearish fair value gap formed on {{ticker}} ({{interval}})")
alertcondition(enteredBull, "Price enters a bullish FVG", "Price traded into an open bullish fair value gap on {{ticker}} ({{interval}})")
alertcondition(enteredBear, "Price enters a bearish FVG", "Price traded into an open bearish fair value gap on {{ticker}} ({{interval}})")

Gaps are drawn as boxes that extend to the right until the fill rule you picked closes them; closed gaps are then removed, or greyed out if you choose to keep them. Gaps smaller than the ATR threshold are ignored, and only the most recent open gaps on each side are kept.

The zones are updated on closed bars only (barstate.isconfirmed), so a gap never appears and disappears on the live bar. Alert conditions cover new bullish and bearish gaps and price trading into an open gap; use Once per bar close.

Add it to your chart

  1. Open any chart on TradingView and open the Pine Editor.
  2. Create a new indicator, delete the template code and paste the script.
  3. Click Save, then Add to chart. Change lengths and levels from the indicator's settings.

Stuck on an error? See how to add a Pine Script indicator to TradingView.

Pitfalls

  • There's always a gap somewhere. Without a size filter, small gaps appear constantly, and some will always look like they worked. Use a minimum size and look at every gap, not just the memorable ones.
  • Choosing the fill rule in hindsight. Picking the rule after looking at a chart makes the zones fit the past. Decide it first.
  • One timeframe only. A gap on a 5-minute chart is often invisible on the hourly. Know which timeframe you're reading.
  • Session gaps. Weekend and overnight gaps also create three-candle gaps. Some traders exclude them; the script doesn't.
  • The story isn't proof. "Imbalance" describes the pattern, not who traded it or why.

The script is an educational example: test it on your own markets before relying on it. Indicators describe the past; they don't predict the future. Trading involves substantial risk of loss and is not suitable for every investor. Edgoro is a software tool, not a broker or financial adviser: nothing it produces is investment advice, and past or backtested performance does not guarantee future results.

Make it yours with Edgoro

Describe the change in plain English and Edgoro writes the Pine Script v6 for you, ready to paste. For example:

“Draw bullish and bearish fair value gaps larger than 0.3 × ATR(14), close them when price reaches their middle, and alert when price enters an open bullish gap.”

Build your FVG indicator