Indicator · Pine Script v5 · Own pane

FVG Histogram [DojiEmoji]

by kevinhhl· code credited to DojiEmoji

Calculates Fair Value Gaps (FVGs) by comparing price action across three bars and plots their magnitude in a separate pane.

View on GitHub

GitHub stars4on the repository
Pine versionv5runs as is
Lines of code179
Settings12inputs
LicenceNone statedfile: MPL-2.0
Last update3 years ago6 Oct 2023

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

179 lines of Pine Script v5 from Scripts/FVG Histogram.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

FVG Histogram.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © DojiEmoji 


// This indicator uses a bar chart*** to represent "fair value gaps" ("FVG"). FVG is a popular pattern among modern traders.

// *** Typo in the write-up: The bars plotted as time series are a bar chart (not a histogram). ***
//     Rather: The histogram that depicts the distribution of magnitudes of FVGs is plotted to the right-hand side of the last bar.

//@version=5
indicator("FVG Histogram [DojiEmoji]", overlay=false, max_labels_count = 500)

//-----------
// Settings
//-----------

var string GROUP0 = "Bar Chart - Percent Rank (Lookback)"
var string GROUP2 = "Histogram - Recent gaps, ignoring time interval"

var color col_up    = input.color(color.new(color.blue,0), title="Up move / Down move:", inline="ln0" )
var color col_dn    = input.color(color.new(color.red,0), title="/", inline="ln0" )

var int n_compare = input.int(20, title="Lookback: Largest FVG", group=GROUP0, inline="barchart_ln0",
 tooltip="If a new FVG is larger than all past FVGs over X bars, then indicate it with a label.", minval=2)
var bool show_label1 = true//input.bool(false, title="Show signal label", inline="barchart_ln2", group=GROUP0)
var bool transp_reduct = input.bool(true, title="Make bars transparent (Insignificant FVGs)", inline="barchart_ln3", group=GROUP0)
var bool show_label2 = input.bool(false, title="Show info label", inline="barchart_ln4", group=GROUP0)
var color info_txtcol = input.color(color.new(color.purple,0), title="", inline="barchart_ln4", group=GROUP0)

var bool show_histogram = input.bool(true, title="Show histogram", group=GROUP2)
var int n_histogram = input.int(100, title="No. of observations", minval=100, maxval=200, tooltip="Ignores time interval")
var int n_bins = input.int(10, title="No. of bins", minval=2, maxval=50)
var bool show_hist_info = input.bool(false, title="Show frequency table info", group=GROUP2)
var color col_hist = input.color(color.new(color.orange,20), title="Histogram color", group=GROUP2, 
 tooltip="Visualize the distribution of FVGs. Note that most FVGs have low magnitudes and tend to fall on the left tail of the distribution.\n\nTo indicate the bin associated with the most recent FVG, the symbol '^' will be used directly below the corresponding bin.")
var bool signal_filter_hist = input.bool(false, title="Filter signals (If FVG is at the first bin)")

//==============================================================================================================//
// FVGs
//==============================================================================================================//
// {
type gap                                    // Attributes to describe a FVG 
    float pivot_upper   = na
    float pivot_lower   = na
    float pivot_mid     = na
    color hist_color    = na
    int barindex        = na

var gap[] ao_gaps = array.new<gap>()       // Array of gaps, to keep track of them. 

// @function insert_gap(price[2], price[0]) 
// @returns void
insert_gap(gap g, float price_t_minustwo, float price_t_zero) =>
    g.pivot_upper := math.max(price_t_minustwo, price_t_zero)
    g.pivot_lower := math.min(price_t_minustwo, price_t_zero)
    g.pivot_mid := (g.pivot_upper+g.pivot_lower)/2
    
    g.hist_color := price_t_minustwo < price_t_zero ? col_up : price_t_minustwo > price_t_zero ? col_dn : na

    g.barindex := bar_index
    array.unshift(ao_gaps, g)

// @function get_recent_gap()
// @returns an instance of gap object
get_recent_gap() =>
    gap result = na 
    if array.size(ao_gaps) == 0
        result := gap.new()
    else
        result := array.get(ao_gaps,0) 
    result

// }

//==============================================================================================================//
// Tests for FVGs:
// Requirement 1: Displacement test;    upward FVG must > 0, and downward FVG must < 0
// Requirement 2: Threshold test;       distance of FVG must > threshold==0
//==============================================================================================================//

// FVG is valid iff both requirements (1 & 2) are met
float test1_displacement_up = low[0] - high[2] 
float test2_displacement_dn = high[0] - low[2]  
bool is_fvg_up = false, bool is_fvg_dn = false // for alerts

if math.abs(test1_displacement_up) > 0 and test1_displacement_up > 0
    insert_gap(gap.new(), high[2], low[0])
    is_fvg_up := true
if math.abs(test2_displacement_dn) > 0 and test2_displacement_dn < 0
    insert_gap(gap.new(), low[2], high[0])
    is_fvg_dn := true

//==============================================================================================================//
// Histogram
//==============================================================================================================//
crnt_fvg_magnitude = (get_recent_gap().pivot_upper-get_recent_gap().pivot_lower)[1]
var int _offset = 10 // No. of bars to offset the histogram, relative to bar.last, right hand side 
var label lbl_hist_info = na, label.delete(lbl_hist_info[1])
var label lbl_crnt_fvg_bin = na, label.delete(lbl_crnt_fvg_bin[1]) 
int crnt_fvg_hist_loc = na 

if array.size(ao_gaps) > n_histogram

    // Initi array of float containing magnitudes of recent FVGs
    float[] aof_magnitude = array.new_float()
    for i=0 to n_histogram-1
        array.push(aof_magnitude, array.get(ao_gaps, i).pivot_upper - array.get(ao_gaps, i).pivot_lower)

    float bin_size = (array.max(aof_magnitude)-array.min(aof_magnitude))/n_bins
    
    // Init. frequency table
    int[] freq_table = array.new_int(size=n_bins+1, initial_value=0)

    // Sum up occurence via freq_table[_i]++
    for i=0 to n_histogram-1
        float data_point = array.get(aof_magnitude,i)
        int _i = math.floor(data_point/bin_size)
        if data_point == crnt_fvg_magnitude
            crnt_fvg_hist_loc := _i
        array.set(freq_table, _i, array.get(freq_table,_i)+1)

    _debug_msg = "Frequency Table {\n"
    
    for i=0 to array.size(freq_table)-1 
        _debug_msg := _debug_msg + str.format("    {0} : {1}{2}\n",str.tostring(i,"#"), str.tostring(array.get(freq_table,i),"#"), crnt_fvg_hist_loc==i ? "<--Current Bin" : "")
    
    _debug_msg += "}"
    
    // if barstate.islast

    var box[] plots = array.new_box()        
    while(array.size(plots)!=0)
        box.delete(array.pop(plots))

    var float _total_area = 10*n_bins
    if show_histogram
        for i=0 to array.size(freq_table)-1
            _i = bar_index+_offset+i
            bin_plot = box.new(_i, 0, _i+1, _total_area*(array.get(freq_table,i)/array.sum(freq_table)), xloc=xloc.bar_index, bgcolor=col_hist, border_color=col_hist)
            array.push(plots, bin_plot)
        lbl_crnt_fvg_bin := label.new(x=bar_index+_offset+crnt_fvg_hist_loc, y=0, text="^", style=label.style_label_up, size=size.huge, textcolor=col_hist, textalign=text.align_center, color=color.new(color.black,99))
    
    if show_hist_info
        lbl_hist_info := label.new(bar_index+_offset-5, 50, _debug_msg + "\nBin Size=" + str.tostring(bin_size,"#.#"),xloc=xloc.bar_index, yloc=yloc.price, style=label.style_label_left, textcolor=color.white, textalign = text.align_left)

//==============================================================================================================//
// Bar Chart:
//==============================================================================================================//

_filtersignal = not signal_filter_hist or (crnt_fvg_hist_loc==0 and signal_filter_hist)
vg_shock = ta.highest(crnt_fvg_magnitude,n_compare)[1] < crnt_fvg_magnitude and _filtersignal

if vg_shock and show_label1
    if get_recent_gap().hist_color == col_up
        label.new(x=bar_index, y=0, text="💡", style=label.style_label_up, size=size.tiny, color=color.new(col_up,50))
    else if get_recent_gap().hist_color == col_dn
        label.new(x=bar_index, y=0, text="💡", style=label.style_label_up, size=size.tiny, color=color.new(col_dn,50))

var label lbl_lookback =na, label.delete(lbl_lookback[1])
if barstate.islast and show_label2
    _msg = str.format("💡 = {0} bars", str.tostring(n_compare,"#"))
    lbl_lookback := label.new(x=bar_index+10, y=0, text=_msg, style=label.style_label_up, size=size.normal, textcolor=info_txtcol, textalign=text.align_left, color=color.new(color.black,99))

relative_magnit = ta.percentrank(crnt_fvg_magnitude, n_compare)
hline(0, color=color.new(color.gray,50), editable=false)
hline(100, color=color.new(color.gray,50), editable=false)

// Default color = input with 50% transparancy, if it's a large FVG, then use original color
plot(relative_magnit, style=plot.style_histogram, color=color.new(get_recent_gap().hist_color,transp_reduct?50:0), linewidth = 4)
plot(relative_magnit, style=plot.style_histogram, color=vg_shock?color.new(get_recent_gap().hist_color,0):na, linewidth = 4)


//==============================================================================================================//
// Alerts:
//==============================================================================================================//
alertcondition(vg_shock, title="Large FVG", message="{{exchange}}:{{ticker}} A wild FVG has appeared!, timeframe={{interval}}")
alertcondition(is_fvg_up, title="FVG Up", message="{{exchange}}:{{ticker}} FVG Up, timeframe={{interval}}")
alertcondition(is_fvg_dn, title="FVG Down", message="{{exchange}}:{{ticker}} FVG Down, timeframe={{interval}}")
alertcondition(is_fvg_dn or is_fvg_up, title="FVG [either] Up or Down", message="{{exchange}}:{{ticker}} FVG, timeframe={{interval}}")

Overview

Upward and downward gaps are displayed as colored bars, with smaller, insignificant gaps rendered transparent based on settings. In addition to the time-series plot, the script creates a right-hand histogram showing the statistical distribution and frequency of recent FVG sizes. Users can adjust lookback lengths for detecting exceptionally large gaps, configure histogram bin counts, and filter signals based on gap distribution bins.

Details

Published by

kevinhhl on GitHub

Code credit

DojiEmoji, as written in the script's header

Type

Indicator, Pine Script v5

Draws

Separate pane below the chart

Category

SMC / ICT / price action

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Up move / Down move:colorcolor.new(color.blue,0)col_up
/colorcolor.new(color.red,0)col_dn
Lookback: Largest FVGint20n_compare
Make bars transparent (Insignificant FVGs)booltruetransp_reduct
Show info labelboolfalseshow_label2
info_txtcolcolorcolor.new(color.purple,0)info_txtcol
Show histogrambooltrueshow_histogram
No. of observationsint100n_histogram
No. of binsint10n_bins
Show frequency table infoboolfalseshow_hist_info
Histogram colorcolorcolor.new(color.orange,20)col_hist
Filter signals (If FVG is at the first bin)boolfalsesignal_filter_hist
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

Calculates continuous structural market bias by detecting swing high and low pivots (higher highs, higher lows, lower highs, and lower lows) using configurable left and right bar lookbacks. It assigns weighted values to classified pivots within a rolling windo

Indicator v6 SMC / ICT / price actionTrend
Updated yesterday · MIT GitHub repository of Market Structure Advanced [WavesUnchained]

Nexural Transform

by JasonTeixeira
9 stars

Nexural Transform is an oscillator indicator calculated in a separate pane that combines Fisher Transform mechanics with Cumulative Volume Delta momentum. It integrates volume-weighted price transforms, CVD acceleration, and order flow features like absorption

Indicator v6 Oscillators & momentumVolume
Updated 12 days ago · Custom licence GitHub repository of Nexural Transform

CRT + Turtle Soup Scanner

by alboogycOdR
6 stars

Calculates CRT session range boundaries and detects Turtle Soup setups to identify potential breakout signals. The script tracks session high, low, and midpoint values to determine intraday bias while measuring price break distances against ATR thresholds. It

Indicator v5 SMC / ICT / price actionSessions & time
Updated 7 months ago · None stated GitHub repository of CRT + Turtle Soup Scanner
5 stars

This indicator calculates and plots virtual daily candlestick OHLC values shifted forward by several hours relative to the current time. By aggregating four-hour bar data from higher-timeframe requests, it constructs custom 24-hour periods and renders them as

Indicator v6 Candlestick patternsSessions & time
Updated 7 months ago · MIT GitHub repository of Day Candle Closed Hour(s) Ago
4 stars

Relative Structure Strength Index is an oscillator indicator that plots in a separate pane. It calculates the Relative Strength Index (RSI) using bar structure power derived from an external library, then smooths the resulting values with a customizable moving

Indicator v6 Oscillators & momentumMoving averages
Updated today · None stated GitHub repository of Relative Structure Strength Index

BTMM Performance Tracker

by jcortes254
3 stars

BTMM Performance Tracker is a trade analytics indicator designed for Steve Mauro's Beat The Market Maker methodology. It tracks simulated trade executions driven by automated, manual, or external signal inputs to compute account performance, win rates, and ris

Indicator v5 Screeners & dashboardsSMC / ICT / price action
Updated 1 year ago · None stated GitHub repository of BTMM Performance Tracker

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers