OPEN-SOURCE SCRIPT

Volumetric Inverse Fair Value Gap (VIFVG) [UAlgo]

7 206
Volumetric Inverse Fair Value Gap is an imbalance analysis tool that tracks the full lifecycle of a Fair Value Gap and then focuses on what happens after that gap fails. Instead of stopping at the initial gap detection, the script stores qualifying bullish and bearish FVGs, waits for price to invalidate them from the opposite side, and then converts those failed imbalances into active Inverse Fair Value Gaps.

The main idea is rooted in role reversal. A bullish Fair Value Gap may initially represent an area of inefficiency below price, but if price later trades through that gap in the opposite direction, the same zone can flip into a bearish inverse area. The same logic applies in reverse for bearish gaps that later fail to the upside. This script automates that transition and keeps the resulting IFVG visible on the chart as long as it remains active.

What makes this version more distinctive is the volumetric overlay inside the inverse zone. Once an IFVG is created, the script attaches three internal metrics to it. The first estimates bullish participation, the second estimates bearish participation, and the third measures relative volume strength using percentile rank. These values are then displayed inside the box as horizontal progress bars, turning the IFVG into both a structural level and a compact participation summary.

The indicator also supports a ghost box that preserves the original FVG location from the moment it was created until the moment it became inverse. This gives the user a clearer narrative of how the imbalance formed and where the role reversal occurred. Combined with the active box, the result is a visually informative workflow for traders who want to study failed imbalances, structure flips, and how strong the inversion candle was when the role change happened.

In practical use, the script can help identify zones where a former inefficiency has turned into a reaction area, while also showing whether the inversion event carried more bullish pressure, more bearish pressure, or unusually strong participation relative to recent volume history.

🔹 Features

🔸 Fair Value Gap Detection With ATR Filtering
The script first detects classic three candle FVG structures, then filters them using a minimum gap size expressed in ATR units. This helps reduce noise and removes smaller gaps that may be less meaningful.

🔸 Strict and Non Strict Detection Modes
Strict mode requires actual wick separation between the first and third candle. Non strict mode allows close based confirmation instead. This gives the user control over how precise the gap definition should be.

🔸 Pending FVG Lifecycle Tracking
Detected FVGs are not immediately turned into inverse zones. They are first stored as pending gaps and monitored until price later crosses them in the opposite direction.

🔸 Automatic FVG to IFVG Conversion
When price invalidates a pending gap from the opposite side, the script creates a new Inverse Fair Value Gap object and begins tracking it as an active zone.

🔸 Ghost Box Support
The original FVG can be preserved visually as a dashed ghost box from the creation time of the imbalance to the inversion time. This makes it easier to see the original gap and the later role reversal event together.

🔸 Volumetric Breakdown Inside the IFVG
Each active inverse gap includes three stacked internal bars:
estimated bullish participation,
estimated bearish participation,
and relative strength.

This gives the zone more context than a normal box alone.

🔸 Participation Estimation From Candle Anatomy
Bullish and bearish participation are estimated from the inversion candle’s structure and volume. This creates a practical volume split model that helps describe how the inversion occurred.

🔸 Strength Metric From Volume Percentile Rank
The script measures how strong the inversion candle’s volume is relative to the last one hundred bars. This is displayed as a separate strength bar inside the IFVG.

🔸 Live Box Expansion
As long as an IFVG remains active, its container extends forward in time. The internal volumetric bars and text labels are updated continuously so the zone remains clear and readable.

🔸 Automatic Invalidation
A bullish IFVG is removed if price closes below its bottom. A bearish IFVG is removed if price closes above its top. This keeps the display focused on still valid inverse zones.

🔸 Controlled History Size
The script limits how many active IFVGs remain on the chart. Older ones are removed once the display exceeds the selected history count.

🔹 Calculations

1) Defining the Pending Gap and Active IFVG Objects

Pine Script®
type PendingFVG float top float btm bool is_bull_gap bool processed int created_time type IFVG int start_time int origin_time float top float btm bool is_bull_ifvg float pct_bull float pct_bear float pct_strength box container box ghost_box box bg_bull box bar_bull box bg_bear box bar_bear box bg_str box bar_str label lbl_bull label lbl_bear label lbl_str bool active


This is the structural foundation of the script.

A PendingFVG stores an imbalance that has been detected but has not yet inverted. It contains the gap boundaries, whether the original gap was bullish or bearish, whether it has already been processed into an inverse gap, and the time when it was created.

An IFVG stores the full active inverse gap state. In addition to the price boundaries and direction, it also stores the three internal metrics, the container box, the optional ghost box, the internal background and progress bars, the labels, and the active state.

So the script is not just drawing boxes. It is managing two linked object lifecycles:
pending FVGs,
and active inverse FVGs.

2) ATR Filter for Gap Significance

Pine Script®
float atr_val = ta.atr(14)


The ATR value is used as the script’s minimum significance filter.

Instead of accepting every visible gap, the script compares gap size against a fraction of ATR. This is useful because a fixed price threshold would behave very differently across markets and timeframes, while ATR gives a volatility aware reference.

So ATR acts as the noise filter that decides whether a newly found gap deserves to be tracked.

3) Estimating Bullish and Bearish Participation

Pine Script®
calc_metrics(float o, float h, float l, float c, float v) => float rng = h - l float buy_v = 0.0 if rng == 0 buy_v := v * 0.5 else if c >= o buy_v := v * ((math.abs(c - o) + (math.min(o, c) - l)) / rng) else buy_v := v * ((h - math.max(o, c)) / rng) float sell_v = v - buy_v float total = buy_v + sell_v float p_bull = total > 0 ? buy_v / total : 0 float p_bear = total > 0 ? sell_v / total : 0 float p_str = ta.percentrank(v, 100) / 100.0 [p_bull, p_bear, p_str]


This function is one of the most important parts of the whole script.

Its goal is to turn one candle into three interpretable metrics:
bullish share,
bearish share,
and strength.

First, the script measures the candle range. If the candle has zero range, volume is split evenly.

If the candle has a real range, the script estimates buying pressure differently depending on candle direction.

For bullish candles, buy volume is influenced by the candle body plus the lower section of the candle.
For bearish candles, buy volume is approximated from the remaining upper section.

The result is not true exchange level aggressor volume, but it is a practical candle anatomy based estimate of how much of the inversion bar behaved more like buying versus selling.

Then the script converts those raw buy and sell estimates into proportions:
p_bull
and
p_bear

Finally, it calculates p_str using the percentile rank of current volume over the last one hundred bars. That means the strength value is not just raw volume. It describes how relatively strong the inversion candle was compared with recent history.

4) Reading the Current Candle Metrics

Pine Script®
[curr_p_bull, curr_p_bear, curr_p_str] = calc_metrics(open, high, low, close, volume)


This line applies the volumetric function to the current bar.

These three values are later attached to a new IFVG at the moment of inversion. So each active inverse gap inherits the participation and strength profile of the candle that caused the role reversal.

That is important conceptually. The internal bars inside the IFVG are not random decorations. They represent the inversion event itself.

5) Detecting Bullish and Bearish FVGs

Pine Script®
bool bull_cond = strict_mode ? (low[0] > high[2]) : (close[0] > high[2]) bool bear_cond = strict_mode ? (high[0] < low[2]) : (close[0] < low[2])


This block defines the actual Fair Value Gap logic.

In strict mode:
a bullish gap exists only when the current low is above the high from two bars ago,
and a bearish gap exists only when the current high is below the low from two bars ago.

That means actual wick separation is required.

In non strict mode:
the script relaxes this and allows close based confirmation instead.

So the user can choose whether the script should only accept clean wick gaps or allow a softer close based definition.

6) Measuring the Gap Size

Pine Script®
float gap_size = 0.0 if bull_cond and close[1] > open[1] gap_size := low[0] - high[2] if bear_cond and close[1] < open[1] gap_size := low[2] - high[0] bool is_significant = gap_size >= (atr_val * fvg_threshold_atr)


Once a candidate FVG is found, the script measures how large the gap actually is.

For bullish gaps, the size is the distance between the current low and the high from two bars ago.
For bearish gaps, the size is the distance between the low from two bars ago and the current high.

The script also adds a candle direction filter on the middle bar:
bullish gaps require the middle candle to be bullish,
and bearish gaps require the middle candle to be bearish.

Finally, the measured gap must be at least as large as:
ATR × threshold

This removes smaller gaps that may simply be noise.

7) Storing a Pending FVG

Pine Script®
if is_significant PendingFVG p = PendingFVG.new() p.created_time := time[1] p.processed := false if bull_cond p.is_bull_gap := true p.top := low[0] p.btm := high[2] else p.is_bull_gap := false p.top := low[2] p.btm := high[0] array.push(pending_fvgs, p)


If the gap is significant, the script stores it as a pending FVG.

The gap is not drawn yet as an inverse zone. Instead, it is placed into the pending list with:
its direction,
its boundaries,
its creation time,
and a flag showing it has not yet been processed.

This is important because an FVG only becomes an IFVG after it fails. The pending list is the waiting room for that future role reversal.

8) Detecting the Inversion Event

Pine Script®
if array.size(pending_fvgs) > 0 for i = array.size(pending_fvgs) - 1 to 0 PendingFVG p = array.get(pending_fvgs, i) if not p.processed bool inverted = false bool to_bull = false if not p.is_bull_gap and close > p.top inverted := true to_bull := true if p.is_bull_gap and close < p.btm inverted := true to_bull := false


This is the core IFVG transition logic.

A pending bearish FVG becomes a bullish IFVG if price closes above its top.
A pending bullish FVG becomes a bearish IFVG if price closes below its bottom.

That is the actual role reversal event. Price has invalidated the original imbalance from the opposite side, so the gap flips into an inverse form.

The to_bull flag determines the direction of the new inverse zone.

9) Creating the IFVG Object

Pine Script®
if inverted IFVG obj = IFVG.new() obj.start_time := time obj.origin_time := p.created_time obj.top := p.top obj.btm := p.btm obj.is_bull_ifvg := to_bull obj.pct_bull := curr_p_bull obj.pct_bear := curr_p_bear obj.pct_strength := curr_p_str obj.active := true obj.create_drawings() array.push(active_ifvgs, obj) p.processed := true


Once inversion is confirmed, the script creates the active IFVG.

The new object inherits:
the original FVG boundaries,
the original creation time,
the inversion start time,
and the new inverse direction.

It also stores:
the bullish participation percentage,
the bearish participation percentage,
and the strength percentage from the inversion candle.

So the IFVG is a structural object with a built in event profile. It tells the user not only where the failed gap is located, but also what the inversion bar looked like in participation terms.

10) Creating the Ghost Box and Main Container

Pine Script®
if show_ghost this.ghost_box := box.new( left=this.origin_time, top=this.top, right=this.start_time, bottom=this.btm, border_color=color.new(c_border, 20), border_width=1, border_style=line.style_dashed, bgcolor=c_ghost, xloc=xloc.bar_time ) this.container := box.new( left=this.start_time, top=this.top, right=time, bottom=this.btm, border_color=c_border, border_width=1, bgcolor=color(na), xloc=xloc.bar_time )


This is the first part of the IFVG drawing engine.

If ghost mode is enabled, the script draws a dashed box from the original FVG creation time to the inversion time. This visually represents the original gap before it failed.

Then it creates the main IFVG container box starting from the inversion time and extending to the current bar.

So the chart can show both:
where the original gap existed,
and where the inverse zone now lives.

11) Building the Internal Volumetric Bar Areas

Pine Script®
this.bg_bull := box.new(this.start_time, this.top, time, this.top, border_width=0, bgcolor=c_bg_dark, xloc=xloc.bar_time) this.bar_bull := box.new(this.start_time, this.top, this.start_time, this.top, border_width=0, bgcolor=c_bull_bar, xloc=xloc.bar_time) this.bg_bear := box.new(this.start_time, this.top, time, this.top, border_width=0, bgcolor=c_bg_dark, xloc=xloc.bar_time) this.bar_bear := box.new(this.start_time, this.top, this.start_time, this.top, border_width=0, bgcolor=c_bear_bar, xloc=xloc.bar_time) this.bg_str := box.new(this.start_time, this.top, time, this.top, border_width=0, bgcolor=c_bg_dark, xloc=xloc.bar_time) this.bar_str := box.new(this.start_time, this.top, this.start_time, this.top, border_width=0, bgcolor=c_str_bar, xloc=xloc.bar_time)


Inside every IFVG, the script creates three horizontal rows.

Each row has:
a dark background box,
and a colored progress bar box.

The three rows represent:
bullish participation,
bearish participation,
and strength.

Initially these boxes are created with minimal size. Their real geometry is set later during updates.

So the IFVG is designed as a mini information panel embedded directly inside the zone.

12) Slicing the IFVG Into Three Metric Rows

Pine Script®
float total_h = this.top - this.btm float h_slice = total_h / 3 float y1 = this.top float y2 = this.top - h_slice float y3 = this.top - 2 * h_slice float y4 = this.btm


This block divides the IFVG vertically into three equal sections.

The full height of the box is measured, then split into thirds:
the first slice for bullish participation,
the second slice for bearish participation,
the third slice for strength.

This makes the internal visualization clean and consistent regardless of zone height.

13) Converting Percentages Into Horizontal Width

Pine Script®
int now = time int dur = now - this.start_time if dur <= 0 dur := timeframe.in_seconds() * 1000 int w_bull = math.round(dur * this.pct_bull) int w_bear = math.round(dur * this.pct_bear) int w_str = math.round(dur * this.pct_strength)


This is how the script turns percentages into visible progress bars.

The available horizontal width is the elapsed time from the IFVG start to the current bar. That duration becomes the maximum usable width.

Then each stored metric is multiplied by that duration:
bullish percentage controls the bullish bar width,
bearish percentage controls the bearish bar width,
strength percentage controls the strength bar width.

So the internal bars behave like proportion meters stretched across the live duration of the zone.

14) Updating the Bull, Bear, and Strength Bars

Pine Script®
this.bg_bull.set_left(this.start_time) this.bg_bull.set_right(now) this.bg_bull.set_top(y1) this.bg_bull.set_bottom(y2) this.bar_bull.set_left(this.start_time) this.bar_bull.set_right(this.start_time + w_bull) this.bar_bull.set_top(y1) this.bar_bull.set_bottom(y2)


Pine Script®
this.bg_bear.set_left(this.start_time) this.bg_bear.set_right(now) this.bg_bear.set_top(y2) this.bg_bear.set_bottom(y3) this.bar_bear.set_left(this.start_time) this.bar_bear.set_right(this.start_time + w_bear) this.bar_bear.set_top(y2) this.bar_bear.set_bottom(y3)


Pine Script®
this.bg_str.set_left(this.start_time) this.bg_str.set_right(now) this.bg_str.set_top(y3) this.bg_str.set_bottom(y4) this.bar_str.set_left(this.start_time) this.bar_str.set_right(this.start_time + w_str) this.bar_str.set_top(y3) this.bar_str.set_bottom(y4)


These blocks physically place the three metric layers inside the IFVG.

Each background row spans the full current width of the active zone.
Each colored bar spans only the proportional amount determined by the stored metric.

So if bullish participation is high, the bullish bar stretches farther across its row. If strength is low, the strength bar remains shorter.

This gives the zone an at a glance internal profile.

15) Updating the Text Labels

Pine Script®
this.lbl_bull.set_xy(center_x, mid_bull) this.lbl_bull.set_text(str.format("Bull: {0}%", math.round(this.pct_bull * 100))) this.lbl_bear.set_xy(center_x, mid_bear) this.lbl_bear.set_text(str.format("Bear: {0}%", math.round(this.pct_bear * 100))) this.lbl_str.set_xy(center_x, mid_str) this.lbl_str.set_text(str.format("Str: {0}%", math.round(this.pct_strength * 100)))


The script also prints the numerical values inside the three rows.

Each label is placed at the center of its row and updated with the rounded percentage value.

So the user sees both:
the visual bar length,
and the exact stored percentage.

This makes the IFVG readable even when box width is large or when color alone is not enough.

16) IFVG Invalidation Logic

Pine Script®
bool broken = false if this.is_bull_ifvg and close < this.btm broken := true if not this.is_bull_ifvg and close > this.top broken := true if broken this.active := false this.remove()


An active IFVG only remains valid while price stays on the correct side of its structure.

For bullish IFVG:
if close falls below the bottom, the zone is broken.

For bearish IFVG:
if close rises above the top, the zone is broken.

When that happens, the IFVG is marked inactive and all associated objects are deleted.

So the indicator is not just drawing inverse gaps indefinitely. It actively monitors whether they continue to behave as valid reaction zones.

17) Display Limit Management

Pine Script®
while array.size(active_ifvgs) > show_last_n IFVG d = array.shift(active_ifvgs) d.remove()


This final block controls how many IFVGs remain visible.

If the number of active inverse gaps exceeds the selected display limit, the oldest one is removed from the front of the array and all of its drawings are deleted.

This keeps the chart focused on the most recent inverse gaps and prevents excessive visual clutter.

Declinazione di responsabilità

Le informazioni e le pubblicazioni non sono intese come, e non costituiscono, consulenza o raccomandazioni finanziarie, di investimento, di trading o di altro tipo fornite o approvate da TradingView. Per ulteriori informazioni, consultare i Termini di utilizzo.