Indicator · Pine Script v5 · On the chart

Volume Dots

by regalouisei· code credited to KioseffTrading

This indicator highlights high volume and open interest (OI) extremities directly on the price chart.

View on GitHub

GitHub stars2on the repository
Pine versionv5runs as is
Lines of code1,574
Settings36inputs
LicenceNone statedfile: MPL-2.0
Last update8 months ago25 Feb 2026

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

1,574 lines of Pine Script v5 from pinescript/editors_picks/liquidation-ranges-volumeoi-dots-kioseff-trading.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

liquidation-ranges-volumeoi-dots-kioseff-trading.pine
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © KioseffTrading

//@version=5
indicator("Volume Dots", overlay = true, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500)
// 
import            PineCoders/Time/4              as pct
import HeWhoMustNotBeNamed/BinaryInsertionSort/1 as HeWho
// 
action2                        = input.bool  (defval = true, title = "Show Liquidation Ranges",    group = "Show Liquidation Ranges" , inline = "0")
showValues                     = input.bool  (defval = true, title = "Show VOL/OI Values in Grid", group = "Show Liquidation Ranges", inline = "0")
action                         = input.string(defval = "Find As Many Vol/OI Extremities As Possible", title = "Volume/OI Dots Should: ", options = ["Find As Many Vol/OI Extremities As Possible", "Show Extremities For The Data On My Screen", "Show The Highest Vol./OI Per Bar", "Off"], group = "Volume/OI Dots Main Settings"), stringBool =  "Find As Many Vol/OI Extremities As Possible"
data                           = input.bool  (defval = false, title = "Use OI Instead Of Volume For Dots?", group = "Use OI Instead")
agg                            = 100 - input.float (minval = 0.01, title = "Aggressiveness", maxval = 10, defval = 0.01, group =  'If You Selected "Find As Many Vol/OI Extremities As Possible"', tooltip = "Corresponds To The Required Percent Rank For A Value To Be Distinguished. Sclaed 0.01-9.99 For Simplicity; However, Uses Percent Rank (100 Subtracted By Number In This Box) - Works Only For 'Find As Many Vol/OI Extremities As Possible'") 
nake                           = input.bool  (defval = false, title = "Naked Levels Only?", group = "Naked Levels")
actint                         = input.int   (defval = 20, title = 'If You Selected "Show Extremities For The Data On My Screen: How Many Levels Should Be Shown?',  group = "Volume/OI Dots (Screen Only)", minval = 1, maxval = 500)
ti                             = input.bool  (defval = false, title = "Use Fixed Range", inline = "1",  group = "Volume/OI Dots Misc Settings")
ti2                            = input.time  (defval = timestamp("04 Apr 2022 00:00 +0300"), title = "Start", inline = "1",  group = "Volume/OI Dots Misc Settings")
dt                             = input.int   (defval = 15, title = "Levels In Table Shown", minval = 0, maxval = 99,  group = "Volume/OI Dots Misc Settings" , inline = "Levels")
showl                          = input.int   (defval = 10, title = "Lines Shown", minval = 0, maxval = 500,  group = "Volume/OI Dots Misc Settings", inline = "Levels")
linebool                       = input.bool  (defval = false, title = "Only Show Lines Within a % of Price", group = "Volume/OI Dots Misc Settings", inline = "line" )
linedist                       = input.float (defval = 3, title = "%", minval = 0, group = "Volume/OI Dots Misc Settings", inline = "line") / 100
srcol                          = input.bool  (defval = false, title = "Color Lines As Support/Resistance", group = "Volume/OI Dots Misc Settings"), atr = ta.atr(14)
upcol                          = input.color (defval = color.lime, title = "Up Color Vol                ", inline = "20",             group = "Volume/OI Dots Misc Settings")
dncol                          = input.color (defval = color.red , title = "Down Color Vol            ", inline = "20",           group = "Volume/OI Dots Misc Settings")
upcoloi                        = input.color (defval = color.blue, title = "OI Up + Price Up      ", inline = "21",        group = "Volume/OI Dots Misc Settings")
dncoloi                        = input.color (defval = color.orange , title = "OI Down + Price Down", inline = "21",  group = "Volume/OI Dots Misc Settings")
upcoloi2                       = input.color (defval = color.yellow, title = "OI Up + Price Down ", inline = "21",    group = "Volume/OI Dots Misc Settings")
dncoloi2                       = input.color (defval = color.purple , title = "OI Down + Price Up     ", inline = "21",    group = "Volume/OI Dots Misc Settings")
sort                           = input.string(defval = "Descending", title = "Sort Table Data", options = ["Occurrence", "Descending"],  group = "Volume/OI Dots Misc Settings", inline = "Misc")
siz                            = input.string(defval = "Normal", title = "Table Size", options = ["Auto", "Tiny", "Small", "Normal", "Large", "Huge"],  inline = "Misc", group = "Volume/OI Dots Misc Settings")
move                           = input.bool  (defval = true, title = "Show OI Delta Lines", group = "Liquidation Range")
showcir                        = input.bool  (defval = true, title = "Show Violation Circles", group = "Liquidation Range")
box2Line                       = input.bool (defval = false,         title = "Change Liquidation Boxes to Lines", group = "Liquidation Range")
minlevl                        = input.string(defval = "Auto", title = "Min Liquidation Level Shown", options = ["Auto", "10", "20", "30", "40", "50"], group = "Liquidation Range")
userDefinedTime                = input.bool  (defval = false, title = "Fixed Range", group = "Liquidation Range")
timtest                        = input.time  (defval = timestamp("04 Apr 2023 00:00 +0300"), title = "Start Liquidation Range"),
timtest2                       = input.time  (defval = timestamp("20 Apr 2023 00:00 +0300"), title = "End Liquidation Range")

method equal                   (string id, value) => id == value
method notEqual                (string id, value) => id != value


req() => 
    
    cont = switch str.contains(syminfo.ticker, ".P")
        
        true => syminfo.ticker + "_OI"
        =>      str.endswith(syminfo.ticker, "USDT") ? syminfo.ticker + ".P_OI" : syminfo.ticker + "T.P_OI"

        

    switch syminfo.type
        "crypto" =>          cont
        =>                   string(na)

r3() => 
    [R1, R2, R3, R4, R5] = switch syminfo.type
        "crypto" => [  close-close[1], high, low, open, close    ]
        => [float(na), float(na), float(na), float(na), float(na)]


// Thank you PineCoders

float chartTfInMinutes = timeframe.in_seconds() / 60.
int  ONE_MIN    = 1
int  ONE_SEC    = ONE_MIN  / 60
int  ONE_HOUR   = ONE_MIN  * 60
int  ONE_DAY    = ONE_HOUR * 24
int  ONE_WEEK   = ONE_DAY  * 7
bool  marketIs24Hours  = syminfo.type == "crypto" or syminfo.type == "forex"

ltfint = switch 

    chartTfInMinutes <= ONE_MIN * 30  => "1"
    chartTfInMinutes <= ONE_HOUR      => "3"
    chartTfInMinutes <  ONE_HOUR * 2  => "5"
    chartTfInMinutes <  ONE_HOUR * 4  => "15"
    chartTfInMinutes <  ONE_HOUR * 6  => "30"
    chartTfInMinutes <  ONE_HOUR * 12 => marketIs24Hours ? "60"  : "30"
    chartTfInMinutes <  ONE_DAY       => marketIs24Hours ? "120" : "30"
    chartTfInMinutes <  ONE_WEEK      => marketIs24Hours ? "720" : "120"

// Thank you PineCoders

[oic, oih, oil, oio, trueOic    ] = request.security_lower_tf(req(), ltfint, r3()    )
timeframeDiff                     = request.security(req(),   timeframe.period, close-close[1]     )
[vol, clo, clo1, hi, lo, clocalc, ltf, volSign] = request.security_lower_tf(syminfo.tickerid, ltfint, 
             [
             volume, close, close[1], high, low, close - close[1], time, close >= open ? volume : volume * -1
             ])


comparison(id, value, string ) =>
    
    switch string 
        
        ">"  => id >  value
        ">=" => id >= value
        "==" => id == value
        "<=" => id <= value
        "<"  => id <  value
        "!=" => id != value


method floatGreatEqual   (float  id, value) => comparison(id, value, ">=") 
method floatEqual        (float  id, value) => comparison(id, value, "==") 
method floatLessEqual    (float  id, value) => comparison(id, value, "<=") 
method floatNotEqual     (float  id, value) => comparison(id, value, "!=") 


method intGreatEqual     (int    id, value) => comparison(id, value, ">=") 
method intEqual          (int    id, value) => comparison(id, value, "==") 
method intLessEqual      (int    id, value) => comparison(id, value, "<=") 
method intNotEqual       (int    id, value) => comparison(id, value, "!=") 
   
method HeWhoSort(float [] id, value, order) => 
    
    HeWho.binary_insertion_sort(id, value, order)


method determine   (bool id, value, value2) =>
    
    switch id 
        
        true => value
        =>      value2

type timeValues

    float [] Difference 
    float [] Close
    int   [] Time

type dotValues 
    
    float [] Volume
    float [] Close
    float [] Difference
    float [] OI 
    float [] OI2
    int   [] Time
    label [] Circle
    line  [] Zone
    float [] Rank
    float [] perBar
    float [] volSignArr
    int      Start

type sortedMat
    
    float   volM
    float   oicM
    int     timM
    float   cloM
    float   clcM
    float   toiM
    color   colM
    string  valM


var timeMat        = timeValues.new(array.new_float(), array.new_float(), array.new_int())

timeMat.Difference.unshift(timeframeDiff), timeMat.Close.unshift(close), timeMat.Time.unshift(time)


var dotMat = dotValues.new(
             
             array.new_float(), array.new_float(),
             array.new_float(), array.new_float(),
             array.new_float(), array.new_int  (), 
             array.new_label(), array.new_line (), 
             array.new_float(), array.new_float(),
             array.new_float(),        0
             
             )

obj = switch syminfo.type.equal("crypto")
    
    true  => oic
    false => vol

if action.notEqual("Off")

    conditionX = switch action
        
        "Find As Many Vol/OI Extremities As Possible"   => last_bar_index - bar_index <= (4.5e6)/(timeframe.in_seconds(timeframe.period)) and not ti or 
                 time >= ti2 and ti and vol.size().intGreatEqual(1)
        
        "Show Extremities For The Data On My Screen" => time >= chart.left_visible_bar_time and time <= chart.right_visible_bar_time 
                     and not ti and vol.size().intGreatEqual(1) or time >= ti2 and ti and vol.size().intGreatEqual(1)
        
        => last_bar_index - bar_index <= 500

    if conditionX
        
        if obj.size().intGreatEqual(1)
            
            if action.notEqual("Show The Highest Vol./OI Per Bar")

                if nake and dotMat.Volume.size().intGreatEqual(1)

                    for i = 0 to dotMat.Volume.size() - 1
                        if not na(dotMat.Close.get(i))
                            
                            if     dotMat.Close.get(i).floatLessEqual(high) 
                                if dotMat.Close.get(i).floatGreatEqual(low)
                                
                                    dotMat.Close.set(i, na)

                for i = math.min(obj.size() - 1, clo.size() - 1) to 0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             // In Honor of Spunky - Rest Comfortably Sweet Princess. You'll be Loved and Cherished Forever & Always <3            

                    dotMat.Volume     .unshift(vol.get(i))     , dotMat.Close    .unshift(clo.get(i))
                    dotMat.Difference .unshift(clocalc.get(i)) , dotMat.OI       .unshift(math.abs(oic.get(i)))
                    dotMat.Time       .unshift(ltf.get(i))     , dotMat.OI2      .unshift(oic.get(i)),
                                             dotMat.volSignArr.unshift(volSign.get(i))
            
            else 

                for i = math.min(obj.size() - 1, clo.size() - 1) to 0                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    

                    cond = switch data 

                        false => vol.get(i).floatEqual(vol.max())
                        =>       oic.get(i).floatEqual(math.max(oic.max(), math.abs(oic.min())))

                    if cond 
                        
                        col = switch data 
                            
                            false => color.new(math.sign(clocalc.get(i)).floatEqual(-1).determine(color.lime , color.red), 33)
                            
                            =>       color.new(math.sign(oic.get(i)).floatEqual(-1).determine  (
                                     math.sign(clocalc.get(i))      .floatEqual(-1).determine  (
                                     dncoloi, dncoloi2),  math.sign(clocalc.get(i)).floatEqual (
                                     -1).determine(upcoloi2, upcoloi)), 33)


                        OItxt = switch syminfo.type.equal("crypto")
                            true => "\nOI: " +  str.tostring(oic.get(i), format.mintick)
                            => ""

                        dotMat.Circle.unshift( 
                            
                             label.new(
                                         time, clo.get(i), "⬤",
                                         tooltip   =  "VOL: " + (math.sign(clocalc.get(i)).floatEqual(-1).determine("-" , "")) + 
                                         str.tostring(vol.get(i), format.volume)  +  OItxt, 
                                         xloc      = xloc.bar_time, 
                                         color     = #000000, 
                                         textcolor = col,
                                         size      = size.tiny,
                                         style     = label.style_text_outline
                                         ))

                        dotMat.Zone.unshift(
                                             line.new(time, clo.get(i), time, clo.get(i), 
                                                             color = col, 
                                                             xloc = xloc.bar_time
                                                             ))

                        dotMat.perBar.unshift(data.determine(vol.get(i) , oic.get(i)))               


    if vol.size().intEqual(0)
        dotMat.Start := time
    

var grid     = matrix.new<box>(5, 20)

if barstate.islast and action.notEqual("Off")
    
    if action.notEqual("Show The Highest Vol./OI Per Bar")
        
        if dotMat.Circle.size().intGreatEqual(1) 
            
            for i = 0 to dotMat.Circle.size() - 1
                dotMat.Circle.shift().delete()
        
        if dotMat.Zone.size()  .intGreatEqual(1)
            
            for i = 0 to dotMat.Zone.size() - 1
                dotMat.Zone.shift().delete()
                          
        if ti
            
            line.new(            ti2, high, ti2, low, 
                                 xloc = xloc.bar_time  , extend = extend.both, 
                                 color = chart.fg_color, width  = 2   
                                  )    

        var count = 1, vcopy = dotMat.Volume.copy(), ocopy = dotMat.OI.copy(), var SM = matrix.new<sortedMat>(8, 0), var tan3 = array.new_float()

        switch data
        
            false => vcopy.sort(order.descending)
            =>       ocopy.sort(order.descending)
        
        for i = 0 to 249

            [bool1, bool2, index] = switch data

                false => [dotMat.Volume.includes(vcopy.get(i)), action.equal(stringBool).determine( 
                         vcopy.percentrank(i).floatGreatEqual(agg) , i.intLessEqual(actint - 2)), dotMat.Volume.indexof(vcopy.get(i))]
                
                =>       [dotMat.OI.includes(ocopy.get(i))    , action.equal(stringBool).determine( 
                         ocopy.percentrank(i).floatGreatEqual(agg) , i.intLessEqual(actint - 2)),     dotMat.OI.indexof(ocopy.get(i))]


            if bool1
                if bool2
                    if dotMat.Close.indexof(dotMat.Close.get(index)).intNotEqual(-1)
                    
                        append = switch data
                        
                            false         => vcopy.get(i)
                            true          => ocopy.get(i)


                        [coloR, strinG] = if not data

                            switch math.sign(dotMat.Difference.get(index))

                                -1 => [dncol, "-"]
                                =>    [upcol, "" ]

                        else 

                            switch math.sign(dotMat.Difference.get(index))

                                -1 => [math.sign(dotMat.OI2.get(index)).floatEqual(-1).determine(dncoloi , upcoloi2) ,  "-"]
                                =>    [math.sign(dotMat.OI2.get(index)).floatEqual(-1).determine(upcoloi , dncoloi2) ,  "" ]


                        SM.add_col(SM.columns(), 

                                         array.from(

                                                     sortedMat.new(volM =  dotMat.Volume    .get(index))  , 
                                                     sortedMat.new(oicM =  dotMat.OI        .get(index))  , 
                                                     sortedMat.new(timM =  dotMat.Time      .get(index))  , 
                                                     sortedMat.new(cloM =  dotMat.Close     .get(index))  , 
                                                     sortedMat.new(clcM =  dotMat.Difference.get(index))  ,
                                                     sortedMat.new(toiM =  dotMat.OI2       .get(index))  , 
                                                     sortedMat.new(colM =  coloR                       )  , 
                                                     sortedMat.new(valM =  strinG                      )
                                                    
                                                     ))
                        
                        count += 1, dotMat.Rank.push(append), 
                       
                        tan3.push(sort.equal("Occurrence")
                                             .determine(dotMat.Time.get(index), data
                                             .determine(dotMat.OI.get(index)  , dotMat.Volume.get(index)
                                             )))
                
                else 
                    
                    break

        var uniArr   = array.from("❶","❷","❸","❹","❺","❻","❼","❽","❾","❿","⓫","⓬","⓭","⓮","⓯","⓰","⓱","⓲","⓳","⓴")
        colorr       = array.new_color(), var tan2 = array.new_float(), var tan4 = array.new_int()

        for i = 0 to SM.columns() - 1 

            condition = switch action

                "Find As Many Vol/OI Extremities As Possible" => data.determine(ocopy.percentrank(i).floatGreatEqual(agg),
                                                                 vcopy.percentrank(i).floatGreatEqual               (agg))
                
                => i.intNotEqual(-1)

            if condition

                rank = dotMat.Rank.percentrank(i)

                SIZE = switch rank.floatNotEqual(-1)

                    rank <= 10 => size.tiny
                    rank <= 25 => size.small
                    rank <= 50 => size.normal
                    rank <= 75 => size.large
                    =>            size.huge

                OItxt = switch syminfo.type.equal("crypto")
                    
                    true => "\nOI: " +  str.tostring(SM.row(5).get(i).toiM, format.mintick)
                    =>      ""
                    

                dotMat.Circle.push(label.new(int(SM.row(2).get(i).timM), SM.row(3).get(i).cloM, 
                                                 
                                                 text      = action.equal(stringBool) or i.intGreatEqual(uniArr.size() - 1) ? "⬤" : uniArr.get(i),
                                                 tooltip   = "VOL: " + (math.sign(SM.row(4).get(i).clcM).floatEqual(-1).determine("-" , ""))     + 
                                                 str.tostring(SM.row(0).get(i).volM, format.volume) + OItxt,
                                                 
                                                 xloc      = xloc.bar_time, 
                                                 color     = #000000, 
                                                 textcolor = color.new(SM.row(6).get(i).colM, 33),
                                                 size      = action.equal(stringBool) ? SIZE : size.normal,
                                                 style     = label.style_text_outline
                                                 ))

                colorr.push(color.new(SM.row(6).get(i).colM, 33)), tan4.push(SM.row(2).get(i).timM)
                tan2.HeWhoSort(sort.equal("Occurrence").determine(SM.row(2).get(i).timM, data.determine(SM.row(1).get(i).oicM, SM.row(0).get(i).volM)), -1)

        if not nake
            
            if showl.intNotEqual(0)
                
                countLab = 0
                
                for i = 0 to dotMat.Circle.size() - 1
                    
                    if dotMat.Circle.get(i).get_x().intGreatEqual(tan4.max(math.min(showl, tan4.size()-1)))
                        
                        if linebool  and dotMat.Circle.get(i).get_y().floatGreatEqual(close * (1 - linedist)) 
                                     and dotMat.Circle.get(i).get_y().floatLessEqual (close * (1 + linedist))
                                                             or not linebool

                            finCol = switch srcol
                                
                                true => dotMat.Circle.get(i).get_y().floatGreatEqual(close).determine(dncol , upcol) 
                                =>      colorr.get(i)

                            dotMat.Zone.push(line.new(
                                             
                                     dotMat.Circle.get(i).get_x(), dotMat.Circle.get(i).get_y(), pct.timeFrom("close", 40, "chart"),  dotMat.Circle.get(i).get_y(),
                                                 color = finCol,
                                                 xloc = xloc.bar_time
                                                 ))
                            countLab += 1
                            if countLab.intEqual(showl)
                                break


        else 
            
            if action.equal(stringBool) or action.equal("Show Extremities For The Data On My Screen")
                
                if dotMat.Circle.size().intGreatEqual(1) 
                    
                    for i = 0 to dotMat.Circle.size() - 1
                        
                        if linebool  and dotMat.Circle.get(i).get_y().floatGreatEqual(close * (1 - linedist)) 
                                     and dotMat.Circle.get(i).get_y().floatGreatEqual(close * (1 + linedist))
                                                     or not linebool
                            
                            dotMat.Zone.push(line.new(
                                         
                                         dotMat.Circle.get(i).get_x(),     dotMat.Circle.get(i).get_y(), 
                                         pct.timeFrom("bar", 40, "chart"), dotMat.Circle.get(i).get_y(), 
                                         xloc  = xloc.bar_time, color = color.new(SM.row(6).get(i).colM, 33)
                                         ))

        sz = switch siz
        
            "Auto"   =>  size.auto
            "Tiny"   =>  size.tiny
            "Small"  =>  size.small
            "Normal" =>  size.normal
            "Large"  =>  size.large
            "Huge"   =>  size.huge


        if float(dt).floatGreatEqual(1)
        
            var tab    = table.new(position.top_right, 4, dt+2, frame_width = 1, 
                                                         frame_color  = color.white , 
                                                         border_width = 1, 
                                                         border_color = color.white), 
                                                         nakeCount    = 0
    
            for i = 0 to nake.determine(dotMat.Circle.size() - 1, math.min(dt - 1, SM.columns() - 1))

                occCond = tan3.indexof(tan2.get(i)) 
                
                if sort.equal("Occurrence")
                    tan3.set(occCond, 0)
                
                if SM.row(0).get(i).volM.floatNotEqual(0)

                    tab.cell(0, i + 1, text = SM.row(7).get(occCond).valM + str.tostring(SM.row(0).get(occCond).volM, format.volume), 
                                                         bgcolor          = color.new(#000000, 50), 
                                                         text_color       = SM.row(6).get(occCond).colM, 
                                                         text_size        = sz, 
                                                         text_font_family = font.family_monospace
                                                         )

                    tab.cell(1, i + 1, text = str.tostring(SM.row(3).get(occCond).cloM, format.mintick), 
                                                         bgcolor          = color.new(#000000, 50), 
                                                         text_color       = SM.row(6).get(occCond).colM, 
                                                         text_size        = sz, 
                                                         text_font_family = font.family_monospace
                                                         )

                    if not na(oic.sum()) and syminfo.type.equal("crypto")
                        
                        tab.cell(2, i + 1, text = str.tostring(SM.row(5).get(occCond).toiM, format.mintick), 
                                                             bgcolor           = color.new(#000000, 50), 
                                                             text_color        = data.determine(SM.row(6).get(occCond).colM , 
                                                                                 math.sign(SM.row(5).get(occCond).toiM).floatEqual(-1).determine(dncol , upcol)),
                                                             text_size         = sz, 
                                                             text_font_family  = font.family_monospace
                                                             )
                    nakeCount += 1

                    if nakeCount.intEqual(dt)
                        break

            tab.cell(0, 0, "Vol." , text_color = color.white, bgcolor = color.new(#000000, 50), text_size = sz, text_font_family  = font.family_monospace)
            tab.cell(1, 0, "Price", text_color = color.white, bgcolor = color.new(#000000, 50), text_size = sz, text_font_family  = font.family_monospace)

            if not na(oic.sum()) and syminfo.type.equal("crypto")
                tab.cell(2, 0, "OI", text_color = color.white, bgcolor = color.new(#000000, 50), text_size = sz, text_font_family = font.family_monospace)

            if dotMat.Circle.size().intEqual(0) and nake
                
                tab.cell(0, 1, "No Naked Levels Detected", text_color = color.white, text_font_family = font.family_monospace, bgcolor = #000000)
                tab.merge_cells(0, 1, 2, 1)


            if action.notEqual(stringBool)
                
                line.new(dotMat.Start, high, dotMat.Start, low, 
                                             extend = extend.both, 
                                             color  = color.white, 
                                             width  = 4, 
                                             xloc   = xloc.bar_time
                                             )
                
                label.new(dotMat.Start, high * 1.01, 
                                     style     = label.style_label_right, 
                                     color     = #000000,
                                     xloc      = xloc.bar_time, 
                                     textcolor = color.white, 
                                     size      = size.small, 
                                     text      = "Earliest Start Date"
                                     )

    else 
        
        if dotMat.Circle.size().intGreatEqual(1) 
            
            for i = 0 to dotMat.Circle.size() - 1
                
                dotMat.perBar.set(i, math.abs(dotMat.perBar.get(i)))
            
            for i = 0 to dotMat.Circle.size() - 1

                rank = dotMat.perBar.percentrank(i)
                
                SIZE = switch rank.floatNotEqual(-1)

                    rank <= 10 => size.tiny
                    rank <= 25 => size.small
                    rank <= 50 => size.normal
                    rank <= 75 => size.large
                    =>            size.huge

                dotMat.Circle.get(i).set_size(SIZE)
            

                if i.intLessEqual(showl - 2)
                    
                    if linebool
                        if dotMat.Circle.get(i).get_y().floatGreatEqual(close * (1 - linedist)) 
                       and dotMat.Circle.get(i).get_y().floatLessEqual (close * (1 + linedist))
                        
                            dotMat.Zone.get(i).set_x2(pct.timeFrom("bar", 40, "chart"))
                    
                    else 
                        
                        dotMat.Zone.get(i).set_x2    (pct.timeFrom("bar", 40, "chart"))
                    
                    switch srcol

                        true =>  dotMat.Zone.get(i).set_color(dotMat.Circle.get(i).get_y().
                                 floatGreatEqual(close).determine(dncol , upcol))
                        
                        =>      continue



// Start
// Thank you Donovan Wall for the great range filter code!! 

Cond_EMA(x, cond, n)=>
    var val     = array.new_float(0)
    var ema_val = array.new_float(1)
    if cond
        val.push(x)
        if val.size( )> 1
            val.remove(0)
        if na(ema_val.get(0))
            ema_val.fill(val.get(0))
        ema_val.set(0, (val.get(0) - ema_val.get(0))*(2/(n + 1)) + ema_val.get(0))
    EMA = ema_val.get(0)
    EMA

rng_size(x, scale, qty, n)=> 
    ATR      = Cond_EMA(ta.tr(true), 1, n)
    AC       = Cond_EMA(math.abs(x - x[1]), 1, n)
    rng_size = qty*AC  

rng_filt(h, l, rng_, n, type, smooth, sn, av_rf, av_n)=>
    rng_smooth = Cond_EMA(rng_, 1, sn)
    r          = smooth ? rng_smooth : rng_
    var rfilt  = array.new_float(2, (h + l)/2)
    rfilt.set(1, rfilt.get(0))
    
    if h - r > rfilt.get(1)
        rfilt.set(0, h - r)
    if l + r < rfilt.get(1)
        rfilt.set(0, l + r)

    rng_filt1 = rfilt.get(0)
    
    hi_band1  = rng_filt1 + r, lo_band1  = rng_filt1 - r
    rng_filt2 = Cond_EMA(rng_filt1, rng_filt1 != rng_filt1[1], av_n)
    hi_band2  = Cond_EMA(hi_band1,  rng_filt1 != rng_filt1[1], av_n)
    lo_band2  = Cond_EMA(lo_band1,  rng_filt1 != rng_filt1[1], av_n)
    
    [rng_filt, hi_band, lo_band] = switch av_rf 
     
        true => [rng_filt2, hi_band2, lo_band2]               
        =>      [rng_filt1, hi_band1, lo_band1]
   
    [hi_band, lo_band, rng_filt]


[h_band, l_band, filt] = rng_filt(close, close, rng_size((close + close)/2, "Average Change", 20.618, 500), 500, "Type 1", true, 500, 0,0)

// Thank you Donovan Wall for great range filter code!! 
// End

var int boxCount = 0

type liqValues

    matrix <float> Average
    matrix <int>   Violations
    matrix <float> TickVol
    matrix <float> TickOI
    float []       Levels
    line  []       boxLines

    line           rightUp
    line           rightDn
    line           leftUp
    line           leftDn
    line           startU
    line           startD

var liquidation = liqValues.new(
                                 
                                 matrix.new<float>(5 , 0   ), matrix.new<int>  (2 , 11, 0),
                                 matrix.new<float>(21, 1000), matrix.new<float>(21, 1000 ),
                                             array.new_float(), array.new_line()), 
                                             orientation = matrix.new<float>(2, 5)

method expCalc(array <float> id)                   => 
    math.exp(id.avg())

method multiply(float id, string PM, float )       =>
    
    switch PM
       
        "+"  => id * (1 + ((1./( float ))  + 0.004))
        "-"  => id * (1 - ((1./( float ))  + 0.004))

method reduced(array <float> id, float , string )  => 

    switch string 
    
        "P" => id.expCalc().multiply("+", float )
        "N" => id.expCalc().multiply("-", float )

method divSubtract(array<float> id, float, float2) =>

    (id.reduced(float , "P") - id.reduced(float2, "N")) / 5


method double_binary_search_leftmost(array <float> id, column)                =>

    n  = id.binary_search_leftmost (orientation.get(0, column))
    n1 = id.binary_search_leftmost (orientation.get(1, column))

    [n, n1]

method finSetVol(matrix <float> id, int i, int row, int start, int end)       =>
    
    for x = start to end 
        id.set (row, i, id.get(row, i) + liquidation.TickVol.get(i, x))


method finSetOI (matrix <float> id, int i, int row, int start, int end)       =>
    
    for x = start to end 
        id.set (row, i, id.get(row, i) + liquidation.TickOI.get(i, x))


method tickSet(matrix<float> id, float value, int column, int loop, int calc) => 
    
    id.set(math.max(boxCount, 0), column, nz(nz(id.get(math.max(boxCount, 0), column)) + (value / math.max(1, (calc)))))



method netAdjust(array <float> id) => 

    for i = 0 to id.size() - 1
        if na(id.get(i))
            id.set(i, 0)

    for i = id.size() - 2 to 0
        id.set(i, id.get(i) + id.get(i + 1))

    if id.size().intGreatEqual(201)
        for i = id.size() - 1 to 200
            id.pop()


if action2 and syminfo.type.equal("crypto")

    var oiclab  = matrix.new<label>(2)

    OIgrid     = input.bool (defval = true ,         title = "Use OI For Grid Color (Instead of Volume) (Crypto Only)", group = "Grid Settings") 
    heatmap    = input.bool (defval = true ,         title = "Show Heatmap (Grid)",       group = "Grid Settings")
    ext        = input.bool (defval = false,         title = "Extend Grid Boxes",  group = "Grid Settings")
    gridWidth  = input.int  (defval = 0,             title = "Grid Width",         group = "Grid Settings")
    gridCol    = input.color(defval = color.white, title = "Border Color",       group = "Grid Settings", inline = "Grid")
    gridUp     = input.color(defval = color.lime,  title = '"Up" Color',         group = "Grid Settings", inline = "Grid")
    gridDn     = input.color(defval = color.red,   title = '"Down" Color',       group = "Grid Settings", inline = "Grid")


    var float div = 0

    if action.equal("Off") and not userDefinedTime 
        bx = box.all, la = label.all, li = line.all, lf = linefill.all
    
        if bx.size().intGreatEqual(1) 
            
            row = grid.rows(), col = grid.columns()
            grid.reshape(grid.elements_count(), 1)
            
            for i = 0 to bx.size() - 1
                if not grid.col(0).includes(bx.get(i))
                    bx.get(i).delete()

            grid.reshape(row, col) 

        if la.size().intGreatEqual(1) 
            for i = 0 to la.size() - 1
                
                if not oiclab.row(0).includes(la.get(i)) 
               and not oiclab.row(1).includes(la.get(i))
                    
                    la.get(i).delete()
        
        if li.size().intGreatEqual(1) 
            for i = 0 to li.size() - 1  
                li.get(i).delete()
        
        if lf.size().intGreatEqual(1) 
            for i = 0 to lf.size() - 1
                lf.shift().delete()
    
    var oicsum    = matrix.new<float>              (2, 0)
    var int stime = 0, var int sbar = 0
    

    if obj.size().intGreatEqual(1) and clocalc.size().intGreatEqual(1)

        if sbar.intEqual(0) 
            
            sbar := bar_index, stime := time

        cond = switch userDefinedTime 
            
            false => filt.floatGreatEqual(filt[1] - atr) and filt.floatLessEqual(filt[1] + atr)
            =>       time >= timtest and time <= timtest2

        if cond

            liquidation.Average.add_col(0, array.from(high, low, math.log(close), close > open ? volume : volume * -1, time))
            
            for i = 0 to math.min(obj.size(), clocalc.size()) - 1

                if syminfo.type.equal("crypto") and OIgrid

                    if math.sign(clocalc.get(i)).floatEqual(1) 

                        switch math.sign(oic.get(i))

                            1  => oicsum.add_col(oicsum.columns(), array.from(oic.get(i), float(na)))
                            -1 => oicsum.add_col(oicsum.columns(), array.from(float(na), oic.get(i)))

                    else

                        switch math.sign(oic.get(i))

                            -1  => oicsum.add_col(oicsum.columns(), array.from(oic.get(i), float(na)))
                            1   => oicsum.add_col(oicsum.columns(), array.from(float(na), oic.get(i)))
                    
                else 


                    switch math.sign(clocalc.get(i))
                       
                        1  => oicsum.add_col(oicsum.columns(), array.from(vol.get(i), float(na)))
                        -1 => oicsum.add_col(oicsum.columns(), array.from(float(na), vol.get(i)))

                    
 
            if liquidation.Levels.size().intGreatEqual(1)

                for i = 0 to math.min(obj.size(), clocalc.size()) - 1

                    highest =        liquidation.Levels.binary_search_leftmost (hi.get(i))
                    lowest  =        liquidation.Levels.binary_search_leftmost (lo.get(i))

                    for x = lowest to highest
                        
                        volx = switch math.sign(clocalc.get(i))
                            
                            1 => vol.get(i)
                            =>   vol.get(i) * -1

                        liquidation.TickVol.tickSet(volx      , x, i, highest - lowest)
                        liquidation.TickOI .tickSet(obj.get(i), x, i, highest - lowest)


            for i = 0 to 19
                if not barstate.islast

                    con = div.floatGreatEqual(500)

                    [index, pIndex, startIndex] = switch con
                        
                        true => [   time    ,  time  [1], stime]
                        =>       [bar_index, bar_index[1], sbar]


                    if index >= math.round(startIndex + (div * (i + 1))) and pIndex < math.round(startIndex + (div * (i + 1))) 

                        boxCount += 1
                        for z = 0 to 4

                            liquidation.TickVol.set(math.max(boxCount, 0), z, liquidation.TickVol.get(math.max(boxCount - 1, 0), z))
                            liquidation.TickOI .set(math.max(boxCount, 0), z, liquidation.TickOI .get(math.max(boxCount - 1, 0), z))


                        for x = 0 to 4

                            grid.set(x, boxCount,
                                     box.new(
                                                     grid.get(x, boxCount - 1).get_right(),
                                                     liquidation.Average.row(2).reduced(100, "N") + (liquidation.Average.row(2).divSubtract(100, 100) * (x + 1)), 
                                                     math.round(index + div), 
                                                     liquidation.Average.row(2).reduced(100, "N") + (liquidation.Average.row(2).divSubtract(100, 100) *     (x)),
                                                     xloc         = div.floatGreatEqual(500).determine(xloc.bar_time, xloc.bar_index),  
                                                     border_color = gridCol, 
                                                     border_width = gridWidth, 
                                                     bgcolor      = color.white

                                                 ))


                        break
                       
            if oicsum.elements_count().intGreatEqual(90001)
                for i = 0 to 10000
                    oicsum.remove_col(i)
        
        else    

            if filt < filt[1] - atr and not userDefinedTime or filt > filt[1] + atr and not userDefinedTime or userDefinedTime and time < timtest

                liquidation.TickVol.fill(0, 0, liquidation.TickVol.rows(), 0, liquidation.TickVol.columns())
                liquidation.TickOI .fill(0, 0, liquidation.TickOI .rows(), 0, liquidation.TickOI .columns())
                liquidation.Levels.clear()
                        

                for i = 0 to grid.rows() - 1
                    for x = 0 to grid.columns() - 1
                        grid.get(i, x).delete()
                
                if oicsum.columns().intGreatEqual(1) 
                    for i = 0 to oicsum.columns() - 1
                        oicsum.remove_col()
                
                if oiclab.columns().intGreatEqual(1) 
                    
                    for i = 0 to oiclab.columns() - 1
                        oiclab.get(0, i).delete()
                        oiclab.get(1, i).delete()
                    
                    for i = 0 to oiclab.columns() - 1
                        oiclab.remove_col()

                stime := time, sbar := bar_index, div := switch (last_bar_index - sbar) / 20 >= 500
                    
                    false => (last_bar_index - sbar) / 20
                    =>       (last_bar_time - stime) / 20

                startIndex = div.floatGreatEqual(500).determine(time, bar_index)

                for x = 0 to 4
                                            
                    grid.set(x, 0,
                             box.new(
                                             startIndex,
                                             liquidation.Average.row(2).reduced(100, "N") + (liquidation.Average.row(2).divSubtract(100, 100) * (x + 1)), 
                                             math.round(startIndex + div), 
                                             liquidation.Average.row(2).reduced(100, "N") + (liquidation.Average.row(2).divSubtract(100, 100) *     (x)),
                                             xloc         = div.floatGreatEqual(500).determine(xloc.bar_time, xloc.bar_index),  
                                             border_color = gridCol, 
                                             border_width = gridWidth, 
                                             bgcolor      = color.white

                                         ))
    



                if liquidation.Average.columns().intGreatEqual(1) 
                    for i = 0 to liquidation.Average.columns() - 1
                        liquidation.Average.remove_col()
                    
                    liquidation.Average.add_col(0 ,  array.from(
                                                     high, 
                                                     low, 
                                                     math.log(close), 
                                                     close > open ? volume : volume * -1, 
                                                     time
                                                     
                                                     ))

                for i = 0 to 499
                    
                    liquidation.Levels.HeWhoSort(close * (1 + (i/1000)), 1)
                    liquidation.Levels.HeWhoSort(close * (1 - (i/1000)), 1)

                boxCount := 0


    if barstate.islast and not userDefinedTime or time[1] < timtest2 and time >= timtest2 and userDefinedTime 

        [endTime, endIndex] = switch userDefinedTime
            
            false => [last_bar_time, last_bar_index]
            true  => [timtest2, bar_index          ]


        if userDefinedTime  
            
            x = timeMat.Time.get(timeMat.Time.indexof(timtest) + 1)
            
            line.new(x, high, x, low,    xloc = xloc.bar_time,   extend = extend.both,   color = color.new(color.white, 50)  ,  width = 2)
            line.new(timtest2, high, timtest2, low, xloc = xloc.bar_time, extend = extend.both, color = color.new(#000000, 50), width = 2)
            

        posSum = oicsum.row(0).sum()
        negSum = oicsum.row(1).sum()
        
        minlvlu = 50, minlvld = 50,  minlvl = 50, var corners = matrix.new<liqValues>(6, 1)

        if minlevl.notEqual("Auto")
            
            minlvlu := int(str.tonumber(minlevl))
            minlvld := int(str.tonumber(minlevl))
        
        else 
            
            for i = 50 to 20 by 10
                
                if liquidation.Average.row(0).max().floatGreatEqual(liquidation.Average.row(2).reduced(i, "P"))
                    minlvlu := i + 10
                
                if liquidation.Average.row(1).min().floatLessEqual (liquidation.Average.row(2).reduced(i, "N"))
                    minlvld := i - 10
        
        minlvl    := math.min(minlvlu, minlvld)    


        if syminfo.type.equal("crypto")
            
            for i = 0 to liquidation.Average.columns() - 1
                for x = 0 to 10
                    if liquidation.Average.get(0, i).floatGreatEqual(liquidation.Average.row(2).reduced(x * 10, "P"))
                        liquidation.Violations.set(0, x, nz(liquidation.Violations.get(0, x) + 1))

            for i = 0 to liquidation.Average.columns() - 1
                for x = 0 to 10
                    if liquidation.Average.get(1, i).floatLessEqual(liquidation.Average.row(2).reduced(x * 10, "N"))
                        liquidation.Violations.set(1, x, nz(liquidation.Violations.get(1, x) + 1))


            if showcir

                uCalc = liquidation.Average.row(2).reduced(100, "P")
                lCalc = liquidation.Average.row(2).reduced(100, "N")

                for i = liquidation.Average.columns() - 1 to 1

                    if liquidation.Average.get(0, i - 1).floatGreatEqual(uCalc) 
                             and liquidation.Average.get(0, i) < uCalc

                        oiclab.add_col(oiclab.columns())

                        oiclab.set(0, oiclab.columns() - 1, label.new(math.round(liquidation.Average.get(4, i - 1)), 
                                                 liquidation.Average.get(0, i - 1) , "⬤", 
                                     textcolor = color.new(gridUp, 50), 
                                     color     = #000000,  
                                     style     = label.style_text_outline, 
                                     xloc      = xloc.bar_time, 
                                     size      = size.tiny
                                     ))

                    if liquidation.Average.get(1, i - 1).floatLessEqual(lCalc) 
                             and liquidation.Average.get(1, i).floatGreatEqual(lCalc)

                        oiclab.add_col(oiclab.columns())

                        oiclab.set(1, oiclab.columns() - 1, label.new(math.round(liquidation.Average.get(4, i - 1)),
                                                 liquidation.Average.get(1, i - 1) , "⬤", 
                                     textcolor = color.new(gridDn, 50), 
                                     color     = #000000, 
                                     style     = label.style_text_outline, xloc = xloc.bar_time, 
                                     size      = size.tiny
                                     ))


            for i = minlvl to 90 by 10 

                transp = array.from(45, 50, 55, 60, 65, 70, 75, 80, 85, 90)

                txt = switch i.intNotEqual(90)

                    true => str.tostring(i, "###x")
                    =>      box2Line ? "90x" : "90 - 100x" 

                switch box2Line

                    false => 
                    
                     box.new(
                             stime, liquidation.Average.row(2).expCalc().multiply("-", i + 10), endTime, liquidation.Average.row(2).expCalc().multiply("-", i), 
                                                         xloc         = xloc.bar_time, 
                                                         bgcolor      = color.new( gridUp, transp.get((i - minlvl) / 10)),  
                                                         text_color   = color.white, 
                                                         text         = txt, 
                                                         border_color = color.new( gridUp, transp.get((i - minlvl) / 10)),  
                                                         text_halign  = text.align_left, 
                                                         text_size    = size.auto

                                                         ),

                     box.new(
                             stime, liquidation.Average.row(2).expCalc().multiply("+", i + 10), endTime, liquidation.Average.row(2).expCalc().multiply("+", i),
                                                         xloc         = xloc.bar_time, 
                                                         bgcolor      = color.new( gridDn, transp.get((i - minlvl) / 10)),  
                                                         text_color   = color.white, 
                                                         text         = txt, 
                                                         border_color = color.new( gridDn, transp.get((i - minlvl) / 10)), 
                                                         text_halign  = text.align_left,
                                                         text_size    = size.auto
                                                         )

                    =>
                     line.new(
                             stime, liquidation.Average.row(2).expCalc().multiply("-", i), endTime + (time - time[3]), 
                                                         liquidation.Average.row(2).expCalc().multiply("-", i), 
                                                         xloc         = xloc.bar_time, 
                                                         color        = color.new( gridUp, transp.get((i - minlvl) / 10))

                                                         ),

                     line.new(
                             stime, liquidation.Average.row(2).expCalc().multiply("+", i), endTime + (time - time[3]), 
                                                         liquidation.Average.row(2).expCalc().multiply("+", i),
                                                         xloc          = xloc.bar_time, 
                                                         color         = color.new( gridDn, transp.get((i - minlvl) / 10)) 
                                                         )
                
                if box2Line

                    label.new(bar_index + 7, liquidation.Average.row(2).expCalc().multiply("+", i), 
                                         style     = label.style_label_left, 
                                         textcolor = gridDn, 
                                         color = #00000000,
                                         text = txt
                                         )

                    label.new(bar_index + 7, liquidation.Average.row(2).expCalc().multiply("-", i), 
                                         style     = label.style_label_left,
                                         textcolor = gridUp, 
                                         color = #00000000,
                                         text = txt
                                         )
                    if i.intEqual(90)
                     
                        line.new(
                                 stime, liquidation.Average.row(2).expCalc().multiply("-", i + 10), endTime + (time - time[3]), 
                                                             liquidation.Average.row(2).expCalc().multiply("-", i + 10), 
                                                             xloc         = xloc.bar_time, 
                                                             color        = color.new( gridUp, transp.get((i - minlvl) / 10))

                                                             )

                        line.new(
                                 stime, liquidation.Average.row(2).expCalc().multiply("+", i + 10), endTime + (time - time[3]), 
                                                             liquidation.Average.row(2).expCalc().multiply("+", i + 10),
                                                             xloc          = xloc.bar_time, 
                                                             color         = color.new( gridDn, transp.get((i - minlvl) / 10)) 
                                                             )
                
                        label.new(bar_index + 7, liquidation.Average.row(2).expCalc().multiply("+", i + 10), 
                                             style     = label.style_label_left, 
                                             textcolor = gridDn, 
                                             text = str.tostring(i + 10, "###x"),
                                             color = #00000000
                                             )

                        label.new(bar_index + 7, liquidation.Average.row(2).expCalc().multiply("-", i + 10), 
                                             style     = label.style_label_left,
                                             textcolor = gridUp, 
                                             text = str.tostring(i + 10, "###x"),
                                             color = #00000000
                                             )


                y1     = (liquidation.Average.row(2).divSubtract(minlvl, 100) / ((90 - minlvl) / 10) * ((i - minlvl) / 10))

                colarr = array.from(11, 11, 22, 22, 33, 33, 44, 44, 55, 66, 66)

                txtc   = switch liquidation.Average.row(2).expCalc().multiply("+", i + 10).floatGreatEqual(liquidation.Average.row(0).max())

                    true => #ffffff50
                    =>      color.new(gridDn, colarr.get((i - minlvl) / 10)) 

                if not box2Line
                    
                    label.new(endIndex + 25, liquidation.Average.row(2).reduced(minlvl, "P") - y1,

                                     "▰", 

                                     textcolor = txtc,
                                     size      = size.tiny, 
                                     color     = #000000, 
                                     style     = label.style_text_outline
                                     )

                txtc2 = switch liquidation.Average.row(2).expCalc().multiply("-", i + 10).floatLessEqual(liquidation.Average.row(1).min())

                    true => #ffffff50
                    =>        color.new(gridUp, colarr.get((i - minlvl) / 10))
                
                if not box2Line
                    
                    label.new(endIndex + 25, liquidation.Average.row(2).reduced(minlvl, "N") + y1, 
                                     "▰", 
                                     textcolor = txtc2 ,
                                     size      = size.tiny, 
                                     color     = #000000, 
                                     style     = label.style_text_outline
                                     )
                            
        [mult, mult2, mult3] = switch minlvl
           
            50 => [35  , 40, 30    ]
            40 => [30  , 35, 25    ]
            30 => [23  , 25.5, 20.5]
            20 => [15  , 17, 13    ]
            10 => [8   , 9 , 7     ]

        liqd  = math.avg(liquidation.Average.row(2).reduced(100, "N"), liquidation.Average.row(2).reduced(minlvl, "N"))
        liqu  = math.avg(liquidation.Average.row(2).reduced(100, "P"), liquidation.Average.row(2).reduced(minlvl, "P"))
        
        liqd2 = liquidation.Average.row(2).reduced(mult, "N")
        liqu2 = liquidation.Average.row(2).reduced(mult, "P")


        sbartime = timeMat.Time.indexof(stime)

        switchCond = syminfo.type.equal("crypto")

        [longText, shortText, totCalc] = switch switchCond
            
            true => [OIgrid.determine("\nNet Longs In Range",  "\nBuying Vol. In Range" ), 
                     OIgrid.determine("\nNet Shorts In Range", "\nSelling Vol. In Range"),
                     OIgrid.determine          (posSum+negSum, posSum-negSum)]
            
            =>      ["\nBuying Vol. In Range", "\nSelling Vol. In Range", posSum-negSum]


        label.new(bar_index + 15, liquidation.Average.row(2).expCalc(), str.tostring(totCalc, "###,###,###,###.00"), 
                                         color     = color.white, 
                                         textcolor = color.rgb(0, 0, 0), 
                                         style     = label.style_text_outline, 
                                         size      = size.small
                                         )


        b = box.new(
                    
                     int(
                             math.avg(stime, stime, endTime)), 
                             liquidation.Average.row(2).reduced(mult2, "N"), 
                        
                                 int(
                                             math.avg(stime, endTime, endTime)), 
                                             liquidation.Average.row(2).reduced(mult3, "N"),
                                             bgcolor      = #00000050, 
                                             border_color = #ffffff, 
                                             text         = str.tostring(posSum, "###,###,###,###,###,###.00") 
                                                                                   + longText , 
                                             text_color   = color.white, 
                                             text_size    = size.auto, 
                                             xloc         = xloc.bar_time
                                             )


        o = box.new(
                    
                     int(
                             math.avg(stime, stime, endTime)), 
                             liquidation.Average.row(2).reduced(mult2, "P"), 
                        
                                 int(
                                         math.avg(stime, endTime, endTime)), 
                                             liquidation.Average.row(2).reduced(mult3, "P"),
                                             bgcolor      = #00000050, 
                                             border_color = #ffffff, 
                                             text         = str.tostring(negSum, "###,###,###,###,###,###.00") +
                                                                                     + shortText , 
                                             text_color   = color.white, 
                                             text_size    = size.auto, 
                                             xloc         = xloc.bar_time
                                             )

        

        if switchCond


            liquidation.boxLines.push(line.new(timeMat.Time.get(sbartime), liqd, timeMat.Time.get(sbartime + 10), 
                                                     liqd, 
                                                     color = gridUp, 
                                                     style = line.style_dashed, 
                                                     xloc  = xloc.bar_time
                                                     ))

            corners.set(2, 0, 
                         liqValues.new(
                                 leftUp = line.new(timeMat.Time.get(sbartime + 10), liqd2, timeMat.Time.get(sbartime + 10), 
                                                         liqd, 
                                                         color = gridUp, 
                                                         style = line.style_dashed, 
                                                         xloc  = xloc.bar_time
                                                         )))


            liquidation.boxLines.push(line.new(timeMat.Time.get(sbartime + 10),  liqd2, b.get_left(),liqd2, 
                                                 color = gridUp, 
                                                 style = line.style_dashed, 
                                                 xloc  = xloc.bar_time
                                                 ))


            corners.set(0, 0, 
                             liqValues.new(
                                     rightUp = line.new(endIndex + 10,  liqd2, endIndex + 10, liqd, 
                                                 color = gridUp, 
                                                 style = line.style_dashed
                                                 )))


            liquidation.boxLines.push(line.new(int(math.avg(sbar, endIndex, endIndex)),  liqd2, endIndex + 10, liqd2, 
                                                 color = gridUp, 
                                                 style = line.style_dashed
                                                 ))


            liquidation.boxLines.push(line.new(endIndex, liqd, endIndex + 10, 
                                                 liqd, 
                                                 color = gridUp, 
                                                 style = line.style_dashed
                                                 ))


            liquidation.boxLines.push(line.new(stime, liqu, timeMat.Time.get(sbartime + 10), 
                                                 liqu, 
                                                 color = gridDn, 
                                                 style = line.style_dashed, 
                                                 xloc  = xloc.bar_time
                                                 ))


            corners.set(3, 0, 

                         liqValues.new(

                                 leftDn = line.new(timeMat.Time.get(sbartime + 10), liqu, timeMat.Time.get(sbartime + 10), 
                                                 liqu2, 
                                                 color = gridDn, 
                                                 style = line.style_dashed, 
                                                 xloc  = xloc.bar_time
                                                 )))

            liquidation.boxLines.push(line.new(timeMat.Time.get(sbartime + 10),  liqu2, o.get_left(),liqu2, 
                                                 color = gridDn, 
                                                 style = line.style_dashed, 
                                                 xloc  = xloc.bar_time
                                                 ))


            corners.set(1, 0, 
                         liqValues.new(
                                 rightDn = line.new(endIndex + 10,  liqu, endIndex + 10, liqu2, 
                                                 color = gridDn, 
                                                 style = line.style_dashed
                                                 )))


            liquidation.boxLines.push(line.new(
                     int(
                         math.avg(sbar, endIndex, endIndex)),  liqu2, endIndex + 10, liqu2, 
                                                 color = gridDn, 
                                                 style = line.style_dashed
                                                 ))



            liquidation.boxLines.push(line.new(endIndex, liqu, endIndex + 10, 
                                                 liqu, 
                                                 color = gridDn, 
                                                 style = line.style_dashed
                                                 ))
    
            var table liqtab = table.new(
                                                 position.bottom_right, 20, 20, 
                                                 bgcolor      = na, 
                                                 frame_color  = color.white, 
                                                 border_color = color.white, 
                                                 frame_width  = 1, 
                                                 border_width = 1
                                                 )

            liqtab.cell(0, 0, "Violations", text_color = color.white), liqtab.cell(0, 1, "Positive", text_color = gridDn), 
            liqtab.cell(2, 1, "Negative"  , text_color = gridUp), liqtab.merge_cells(0, 0, 3, 0), 
            
            liqtab.merge_cells(0, 1, 1, 1)
            liqtab.merge_cells(2, 1, 3, 1)

            strArr  = array.from("100x: ", "90x: ", "80x: ", "70x: ", "60x: ", "50x: ", "40x: ", "30x: ", "20x: ", "10x: " )
            strArr2 = array.from("100x: ", "90x: ", "80x: ", "70x: ", "60x: ", "50x: ", "40x: ", "30x: ", "20x: ", "10x: " )

            intMat = matrix.new<int>(0, 10)

            dnRow = liquidation.Violations.row(0), dnRow.reverse()    
            upRow = liquidation.Violations.row(1), upRow.reverse()    

            for i = 0 to strArr.size() - 1

                strArr .set(i, strArr.get(i)  + str.tostring(dnRow.get(i)))
                strArr2.set(i, strArr2.get(i) + str.tostring(upRow.get(i)))

            intMat.add_row(0, array.from(0, 1, 0, 1, 0, 1, 0, 1, 0, 1))
            intMat.add_row(1, array.from(2, 3, 2, 3, 2, 3, 2, 3, 2, 3))
            intMat.add_row(2, array.from(2, 2, 3, 3, 4, 4, 5, 5, 6, 6))

            for i = 0 to strArr.size() - 1

                liqtab.cell(intMat.get(0, i), intMat.get(2, i), strArr.get (i), text_color = gridDn  )
                liqtab.cell(intMat.get(1, i), intMat.get(2, i), strArr2.get(i), text_color = gridUp)

        if move 

            netLong  = oicsum.row(0), netShort = oicsum.row(1)


            for i = bar_index - sbar to 0
                
                if timeMat.Close.get(i).floatGreatEqual(timeMat.Close.get(i + 1))
                    
                    switch math.sign(timeMat.Difference.get(i)).floatEqual(1)
                        
                        true =>  netLong .set(i, timeMat.Difference.get(i)), 
                                 netShort.set(i, 0)
                        
                        =>       netLong .set(i, 0), 
                                 netShort.set(i, timeMat.Difference.get(i))

                else 
                    
                    switch math.sign(timeMat.Difference.get(i)).floatEqual(1)
                        
                        true =>  netLong .set(i, 0),
                                 netShort.set(i, timeMat.Difference.get(i))
                        
                        =>       netLong .set(i, timeMat.Difference.get(i)),
                                 netShort.set(i, 0)
          
            netLong.netAdjust(), netShort.netAdjust()

            b.set_top(liquidation.Average.row(2).reduced(mult, "N")) 
            o.set_top(liquidation.Average.row(2).reduced(mult, "P"))
            
            if switchCond

                linefill.new(corners.get(2, 0).leftUp, corners.get(0, 0).rightUp, #00000050) 
                linefill.new(corners.get(3, 0).leftDn, corners.get(1, 0).rightDn, #00000050)

                avgN = (math.avg(liquidation.Average.row(2).reduced(mult + 1, "N"), liquidation.Average.row(2).reduced(minlvl - 1, "N")))
                avgP = (math.avg(liquidation.Average.row(2).reduced(mult + 1, "P"), liquidation.Average.row(2).reduced(minlvl - 1, "P")))

                for i = 1 to math.min(bar_index - sbar, netLong.size() - 1)


                    netCalc   = (liquidation.Average.row(2).reduced(mult + 1, "N")) + ((liquidation.Average.row(2).reduced(minlvl - 1, "N")) 
                                                         - (liquidation.Average.row(2).reduced(mult + 1, "N"      ))) 
                                                         * (netLong.get(i) - netLong.min())  / (netLong.range     ())

                    snetCalc  = (liquidation.Average.row(2).reduced(minlvl - 1, "P"))   -   ((liquidation.Average.row(2).reduced(minlvl - 1, "P")) 
                                                         - (liquidation.Average.row(2).reduced(mult + 1, "P"      ))) 
                                                         * (netShort.get(i) - netShort.min())  / (netShort.range  ())

                    netCalc2  =   (liquidation.Average.row(2) .reduced(mult + 1, "N"))  + ((liquidation.Average.row(2) .reduced  (minlvl - 1, "N")) 
                                                         - (liquidation.Average.row(2).reduced(mult + 1, "N"       ))) 
                                                         * (netLong.get(i - 1) - netLong.min())  / (netLong.range  ()), 

                    snetCalc2 =   (liquidation.Average.row(2).reduced(minlvl-1, "P"))  - ((liquidation.Average.row(2)   .reduced(minlvl - 1, "P"  )) 
                                                         - (liquidation.Average.row(2).reduced(mult + 1, "P"       ))) 
                                                         * (netShort.get(i - 1) - netShort.min()) / (netShort.range())




                    line.new(timeMat.Time.get(i + 1), snetCalc, timeMat.Time.get(i), 
                                     snetCalc2,
                                     color = gridDn,
                                     xloc  = xloc.bar_time, 
                                     width = 2
                                     )

                    line.new(timeMat.Time.get(i + 1), netCalc, timeMat.Time.get(i), 
                                     netCalc2, 
                                     color = gridUp,
                                     xloc  = xloc.bar_time, 
                                     width = 2
                                     )
                    
                       

        if not move and switchCond

            
            if liquidation.boxLines.size().intGreatEqual(1) 
                for i = 0 to liquidation.boxLines.size() - 1
                    liquidation.boxLines.shift().delete()   
            
            for i = 0 to 3
                
                if not na(corners.get(i, 0))
                    
                    switch i % 4
                        
                        0 => corners.get(0, 0).rightUp.delete()
                        1 => corners.get(1, 0).rightDn.delete()
                        2 => corners.get(2, 0).leftUp .delete()
                        3 => corners.get(3, 0).leftDn .delete()

            if linefill.all.size().intGreatEqual(1) 
                for i = 0 to linefill.all.size() - 1
                    linefill.all.shift().delete()
        
        if heatmap
            
            count = 0
            for i = 0 to grid.rows() - 1
                for x = 0 to grid.columns() - 1

                    grid.get(i, x).set_bottom(liquidation.Average.row(2).reduced(100, "N") + (liquidation.Average.row(2).divSubtract(100, 100) * i))
                    grid.get(i, x).set_top   (liquidation.Average.row(2).reduced(100, "N") + (liquidation.Average.row(2).divSubtract(100, 100) * (i + 1)))


            for i = 0 to 4

                orientation.set(0, i, grid.get(i, 0).get_top   ())
                orientation.set(1, i, grid.get(i, 0).get_bottom())


            [up1, dn1] = liquidation.Levels.double_binary_search_leftmost (0), [up2, dn2] = liquidation.Levels.double_binary_search_leftmost (1),
            [up3, dn3] = liquidation.Levels.double_binary_search_leftmost (2), [up4, dn4] = liquidation.Levels.double_binary_search_leftmost (3),

                                         [up5, dn5] = liquidation.Levels.double_binary_search_leftmost (4)

            var finTick = matrix.new<float>(5, 1, 0), var finTick2 = matrix.new<float>(5, 1, 0)

            for i = 0 to 20

                finTick .finSetVol(i, 0, dn1, up1), finTick .finSetVol(i, 1, dn2, up2)
                finTick2.finSetOI (i, 0, dn1, up1), finTick2.finSetOI (i, 1, dn2, up2)

                finTick .finSetVol(i, 2, dn3, up3), finTick .finSetVol(i, 3, dn4, up4)
                finTick2.finSetOI (i, 2, dn3, up3), finTick2.finSetOI (i, 3, dn4, up4),

                                     finTick .finSetVol(i, 4, dn5, up5),
                                     finTick2.finSetOI (i, 4, dn5, up5)

                finTick .add_col(finTick .columns(), finTick .col(finTick .columns() - 1))
                finTick2.add_col(finTick2.columns(), finTick2.col(finTick2.columns() - 1))

            for i = 0 to grid.rows() - 1
                for x = 0 to grid.columns() - 1

                    [datax, datamin, datamax] = switch OIgrid

                        false                                    =>          [finTick .get(i, x), finTick .min(), finTick .max()]
                        true and syminfo.type.notEqual("crypto") =>          [finTick .get(i, x), finTick .min(), finTick .max()]
                        =>                                                   [finTick2.get(i, x), finTick2.min(), finTick2.max()] 

                    col = switch math.sign(datax)

                        0  => color.new(color.gray, 80)
                        1  =>   color.from_gradient(datax, 0, datamax, color.new(gridUp, 90), color.new(gridUp, 50))
                        -1 =>   color.from_gradient(datax, datamin, 0, color.new(gridDn, 50), color.new(gridDn, 90))

                    grid.get(i, x).set_bgcolor(col)

                    if showValues

                        oiTxt = switch switchCond
                            true => "\n\nOI: " +
                                             (finTick2.get(i, x).floatGreatEqual(0).determine("+" , "")) + str.tostring(finTick2.get(i, x), "###,###,###.##")
                            =>      ""

                        grid.get(i, x).set_text( 

                                                 "VOL: " + str.tostring(finTick.get(i, x), format.volume) + oiTxt)

                        grid.get(i, x).set_text_color(color.white)


            if ext
                for i = 0 to grid.rows() - 1
                    for x = 1 to grid.columns() - 1

                        [dataCur, dataPrev] = switch OIgrid

                            false                                    => [finTick .get(i, x), finTick .get(i, x - 1)]
                            true and syminfo.type.notEqual("crypto") => [finTick .get(i, x), finTick .get(i, x - 1)]
                            =>                                          [finTick2.get(i, x), finTick2.get(i, x - 1)]

                        if dataCur.floatEqual(dataPrev)

                            grid.get(i, x).set_left(grid.get(i, x - 1).get_left())
                            grid.get(i, x - 1).delete()

            if not switchCond

                distanceTop = o.get_bottom() - o.get_top()
                distanceBot = b.get_top() - b.get_bottom()

                o.set_bottom(grid.get(4, 0) .get_top())   , o.set_top(o.get_bottom() + distanceTop)
                b.set_top   (grid.get(0, 0) .get_bottom()), b.set_bottom(b.get_top() - distanceBot)


        else 
            
            for i = 0 to grid.rows() - 1
                for x = 0 to grid.columns() - 1
                    grid.get(i, x).delete()

Overview

Volume Dots identifies potential support and resistance zones, naked levels, and liquidation ranges using visual dots, lines, and boxes. An optional on-screen table displays sorted volume and open interest values. Users can toggle between volume and OI data, adjust percentile aggressiveness, filter lines by price percentage distance, and set fixed evaluation time ranges.

Details

Published by

regalouisei on GitHub

Code credit

KioseffTrading, as written in the script's header

Draws

On the price chart

Markets

Crypto

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
Show Liquidation Rangesbooltrueaction2
Show VOL/OI Values in GridbooltrueshowValues
Volume/OI Dots Should: stringFind As Many Vol/OI Extremities As Possibleaction
Use OI Instead Of Volume For Dots?boolfalsedata
Aggressivenessfloat0.01Aggressiveness
Naked Levels Only?boolfalsenake
If You Selected "Show Extremities For The Data On My Screen: How Many Levels Should Be Shown?int20actint
Use Fixed Rangeboolfalseti
Starttimetimestamp("04 Apr 2022 00:00 +0300")ti2
Levels In Table Shownint15dt
Lines Shownint10showl
Only Show Lines Within a % of Priceboolfalselinebool
Show all 36 settings
SettingTypeDefaultVariable
%float3linedist
Color Lines As Support/Resistanceboolfalsesrcol
Up Color Vol                colorcolor.limeupcol
Down Color Vol            colorcolor.reddncol
OI Up + Price Up      colorcolor.blueupcoloi
OI Down + Price Downcolorcolor.orangedncoloi
OI Up + Price Down colorcolor.yellowupcoloi2
OI Down + Price Up     colorcolor.purpledncoloi2
Sort Table DatastringDescendingsort
Table SizestringNormalsiz
Show OI Delta Linesbooltruemove
Show Violation Circlesbooltrueshowcir
Change Liquidation Boxes to Linesboolfalsebox2Line
Min Liquidation Level ShownstringAutominlevl
Fixed RangeboolfalseuserDefinedTime
Start Liquidation Rangetimetimestamp("04 Apr 2023 00:00 +0300")timtest
End Liquidation Rangetimetimestamp("20 Apr 2023 00:00 +0300")timtest2
Use OI For Grid Color (Instead of Volume) (Crypto Only)booltrueOIgrid
Show Heatmap (Grid)booltrueheatmap
Extend Grid Boxesboolfalseext
Grid Widthint0gridWidth
Border Colorcolorcolor.whitegridCol
"Up" Colorcolorcolor.limegridUp
"Down" Colorcolorcolor.redgridDn
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

This overlay indicator builds a horizontal momentum profile over a chosen lookback. It splits price into rows and plots the average WaveTrend of each zone as a bar colored by direction, plus an optional MFI reference band that stays gray when real trade volume

Indicator v6 Oscillators & momentumVolume
Updated 3 days ago · MIT GitHub repository of Momentum Profile [WavesUnchained]
5 stars

This indicator builds a frozen volume profile over a fixed, half-open timestamp range and draws it on the price chart. It aggregates one-minute source bars into output rows sized by a chosen tick count, marks a configurable value-area percentage, and can plot

Indicator v6 VolumeSupport & resistance
Updated 4 days ago · MPL-2.0 GitHub repository of VP RSD Fixed Range Shelf Diagnostic
4 stars

This overlay indicator draws an on-chart depth-of-market table that splits volume at each price tick into estimated buy volume, sell volume, delta, and session volume. Growing text bars compare buyers and sellers, and recent bars approximate resting bid and as

Indicator v5 VolumeScreeners & dashboards
Updated 6 months ago · None stated GitHub repository of Custom DOM - Bookmap Style
4 stars

This indicator overlays an order flow intelligence suite directly on the price chart. It renders a liquidity heatmap across configured price zones, alongside an on-chart information panel displaying volume delta, cumulative volume delta, a momentum compass bas

Indicator v6 VolumeScreeners & dashboards
Updated 2 weeks ago · MIT GitHub repository of GHOST — OrderFlow Intelligence

More Volume scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers