Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
- // © LuxAlgo
- //@version=5
- indicator("Liquidity Levels/Voids (VP) [LuxAlgo]", "LuxAlgo - Liquidity Levels/Voids (VP)", true, max_bars_back = 5000, max_boxes_count = 500) // , max_labels_count = 500, max_lines_count = 500
- //------------------------------------------------------------------------------
- // Settings
- //-----------------------------------------------------------------------------{
- mdTT = 'The mode option controls the number of visual objects presented, where\n\n- Historical, takes into account all data available to the user\n- Present, takes into account only the last X bars specified in the \'# Bars\' option\n\nPossible \'# Bars\' values [100-5000]'
- mode = input.string('Present', title = 'Mode', options =['Present', 'Historical'], inline = 'MOD')
- back = input.int (360, ' # Bars', minval = 100, maxval = 5000, step = 10, inline = 'MOD', tooltip = mdTT)
- grpLQ = 'Liquidity Levels / Voids'
- liqUC = input.color(color.new(#1848cc, 79), 'Liquidity Levels/Voids', inline = 'UFL', group = grpLQ, tooltip = 'Color customization option for Unfilled Liquidity Levels/Voids')
- ppLen = input.int(47, "Detection Length", minval = 1, group = grpLQ, tooltip = 'Lookback period used for the calculation of Swing Levels\n\nMinimum value [1]')
- liqT = input.int(21, 'Threshold %', minval = 1, maxval = 51, group = grpLQ, tooltip = 'Threshold used for the calculation of the Liquidity Levels & Voids\n\nPossible values [1-51]') / 100
- vpLev = input.int(27, 'Sensitivity' , minval = 10, maxval = 100, step = 1, group = grpLQ, tooltip = 'Adjusts the number of levels between two swing points, as a result, the height of a level is determined and then based on the above-given threshold the level is checked if it matches the liquidity level/void conditions\n\nPossible values [10-100]')
- liqFD = input.bool(false, 'Filled Liquidity Levels/Voids', inline = 'FL', group = grpLQ, tooltip = 'Toggles the visibility of the Filled Liquidity Levels/Voids and color customization option for Filled Liquidity Levels/Voids')
- liqFC = input.color(color.new(#787b86, 79), '', inline = 'FL', group = grpLQ)
- othGR = 'Other Features'
- ppLev = input.bool(false, 'Swing Highs/Lows', inline = 'ppLS', group = othGR, tooltip = 'Toggles the visibility of the Swing Levels, where tooltips present statistical information, such as price, price change, and cumulative volume between the two swing levels detected based on the detection length specified above\n\nColoring options to customize swing low and swing high label colors and Size option to adjust the size of the labels')
- ppLCB = input.color(color.new(#f23645, 0), '', inline = 'ppLS', group = othGR)
- ppLCS = input.color(color.new(#089981, 0), '', inline = 'ppLS', group = othGR)
- ppLS = input.string('Small', "", options=['Tiny', 'Small', 'Normal'], inline = 'ppLS', group = othGR)
- //-----------------------------------------------------------------------------}
- // User Defined Types
- //-----------------------------------------------------------------------------{
- // @type bar properties with their values
- //
- // @field h (float) high price of the bar
- // @field l (float) low price of the bar
- // @field v (float) volume of the bar
- // @field i (int) index of the bar
- type bar
- float h = high
- float l = low
- float v = volume
- int i = bar_index
- // @type store pivot high/low and index data
- //
- // @field x (int) last pivot bar index
- // @field x1 (int) previous pivot bar index
- // @field h (float) last pivot high
- // @field h1 (float) previous pivot high
- // @field l (float) last pivot low
- // @field l1 (float) previous pivot low
- type pivotPoint
- int x
- int x1
- float h
- float h1
- float l
- float l1
- // @type maintain liquidity data
- //
- // @field b (array<bool>) array maintains price levels where liquidity exists
- // @field bx (array<box>) array maintains visual object of price levels where liquidity exists
- type liquidity
- bool [] b
- box [] bx
- // @type maintain volume profile data
- //
- // @field vs (array<float>) array maintains tolal traded volume
- // @field vp (array<box>) array maintains visual object of each price level
- type volumeProfile
- float [] vs
- box [] vp
- //-----------------------------------------------------------------------------}
- // Variables
- //-----------------------------------------------------------------------------{
- bar b = bar.new()
- var pivotPoint pp = pivotPoint.new()
- var liquidity[] aLIQ = array.new<liquidity> (1, liquidity.new(array.new <bool> (vpLev, false), array.new <box> (na)))
- var liquidity[] dLIQ = array.new<liquidity> (1, liquidity.new(array.new <bool> (na) , array.new <box> (na)))
- volumeProfile aVP = volumeProfile.new(array.new <float> (vpLev + 1, 0.), array.new <box> (na))
- qBXs = 0
- //-----------------------------------------------------------------------------}
- // Functions/methods
- //-----------------------------------------------------------------------------{
- // @function calcuates highest price, lowest price and cumulative volume of the given range
- //
- // @param _l (int) length of the range
- // @param _c (bool) check
- // @param _o (int) offset
- //
- // @returns (float, float, float) highest, lowest and cumulative volume
- f_calcHLV(_l, _c, _o) =>
- if _c
- l = low [_o]
- h = high[_o]
- v = 0.
- for x = 0 to _l - 1
- l := math.min(low [_o + x], l)
- h := math.max(high[_o + x], h)
- v += volume[_o + x]
- l := math.min(low [_o + _l], l)
- h := math.max(high[_o + _l], h)
- [h, l, v]
- //-----------------------------------------------------------------------------}
- // Calculations
- //-----------------------------------------------------------------------------{
- per = mode == 'Present' ? last_bar_index - b.i <= back : true
- nzV = nz(b.v)
- ppS = switch ppLS
- 'Tiny' => size.tiny
- 'Small' => size.small
- 'Normal' => size.normal
- pp_h = ta.pivothigh(ppLen, ppLen)
- pp_l = ta.pivotlow (ppLen, ppLen)
- if not na(pp_h)
- pp.h1 := pp.h
- pp.h := pp_h
- if not na(pp_l)
- pp.l1 := pp.l
- pp.l := pp_l
- go = not na(pp_h) or not na(pp_l)
- if go
- pp.x1 := pp.x
- pp.x := b.i
- vpLen = pp.x - pp.x1
- [pHst, pLst, tV] = f_calcHLV(vpLen, go, ppLen)
- pStp = (pHst - pLst) / vpLev
- if go and nzV and pStp > 0 and b.i > vpLen and vpLen > 0 and per
- for bIt = vpLen to 1
- l = 0
- bI = bIt + ppLen
- for pLev = pLst to pHst by pStp
- if b.h[bI] >= pLev and b.l[bI] < pLev + pStp
- aVP.vs.set(l, aVP.vs.get(l) + nzV[bI] * ((b.h[bI] - b.l[bI]) == 0 ? 1 : pStp / (b.h[bI] - b.l[bI])))
- l += 1
- aLIQ.unshift(liquidity.new(array.new <bool> (vpLev, false), array.new <box> (na)))
- cLIQ = aLIQ.get(0)
- for l = vpLev - 1 to 0
- if aVP.vs.get(l) / aVP.vs.max() < liqT
- cLIQ.b.set(l, true)
- cLIQ.bx.unshift(box.new(b.i[ppLen], pLst + (l + 0.00) * pStp, b.i[ppLen], pLst + (l + 1.00) * pStp, border_color = color(na), bgcolor = liqUC ))
- else
- cLIQ.bx.unshift(box.new(na, na, na, na))
- cLIQ.b.set(l, false)
- for bIt = 0 to vpLen
- bI = bIt + ppLen
- int qBX = cLIQ.bx.size()
- for bx = 0 to (qBX > 0 ? qBX - 1 : na)
- if bx < cLIQ.bx.size()
- if cLIQ.b.get(bx)
- cBX = cLIQ.bx.get(bx)
- mBX = math.avg(cBX.get_bottom(), cBX.get_top())
- if math.sign(close[bI + 1] - mBX) != math.sign(low[bI] - mBX) or math.sign(close[bI + 1] - mBX) != math.sign(high[bI] - mBX) or math.sign(close[bI + 1] - mBX) != math.sign(close[bI] - mBX)
- cBX.set_left(b.i[bI])
- cLIQ.b.set(bx, false)
- for bI = ppLen to 0
- int qBX = cLIQ.bx.size()
- for bx = (qBX > 0 ? qBX - 1 : na) to 0
- if bx < cLIQ.bx.size()
- cBX = cLIQ.bx.get(bx)
- mBX = math.avg(box.get_bottom(cBX), box.get_top(cBX))
- if math.sign(close[bI + 1] - mBX) != math.sign(low[bI] - mBX) or math.sign(close[bI + 1] - mBX) != math.sign(high[bI] - mBX)
- if liqFD
- cBX.set_bgcolor(liqFC)
- else
- cBX.delete()
- cLIQ.bx.remove(bx)
- else
- cBX.set_right(b.i[bI])
- for i = aLIQ.size() - 1 to 0
- x = aLIQ.get(i)
- int qBX = x.bx.size()
- qBXs := qBXs + qBX
- if qBXs > 500
- aLIQ.pop()
- for bx = (qBX > 0 ? qBX - 1 : na) to 0
- if bx < x.bx.size()
- cBX = x.bx.get(bx)
- mBX = math.avg(box.get_bottom(cBX), box.get_top(cBX))
- if math.sign(close[1] - mBX) != math.sign(low - mBX) or math.sign(close[1] - mBX) != math.sign(high - mBX)
- //cBX.delete()
- if liqFD
- cBX.set_bgcolor(liqFC)
- else
- cBX.delete()
- x.bx.remove(bx)
- else
- cBX.set_right(b.i)
- if ppLev and (mode == 'Present' ? last_bar_index - b.i <= back * 1.318 : true)
- statTip = '\n -Traded Volume : ' + str.tostring(tV, format.volume) + ' (' + str.tostring(vpLen - 1) + ' bars)\n *Average Volume/Bar : ' + str.tostring(tV / (vpLen - 1), format.volume)
- if not na(pp_h)
- swH = pp.h > pp.h1 ? "HH" : pp.h < pp.h1 ? "LH" : na
- label.new(b.i[ppLen], pp.h, swH, xloc.bar_index, yloc.price, color(na), label.style_label_down, ppLCS, ppS, text.align_center, 'Swing High : ' + str.tostring(pp.h, format.mintick) + '\n -Price Change : %' + str.tostring((pp.h - pp.l) * 100 / pp.l, '#.##') + statTip)
- if not na(pp_l)
- swL = pp.l < pp.l1 ? "LL" : pp.l > pp.l1 ? "HL" : na
- label.new(b.i[ppLen], pp.l ,swL, xloc.bar_index, yloc.price, color(na), label.style_label_up , ppLCB, ppS, text.align_center, 'Swing Low : ' + str.tostring(pp.l, format.mintick) + '\n -Price Change : %' + str.tostring((pp.h - pp.l) * 100 / pp.h, '#.##') + statTip)
- vpLen := barstate.islast ? last_bar_index - pp.x + ppLen : 1
- pHst := ta.highest(b.h, vpLen > 0 ? vpLen + 1 : 1)
- pLst := ta.lowest (b.l, vpLen > 0 ? vpLen + 1 : 1)
- pStp := (pHst - pLst) / vpLev
- if barstate.islast and nzV and vpLen > 0 and pStp > 0
- tLIQ = dLIQ.shift()
- if tLIQ.bx.size() > 0
- for i = 0 to tLIQ.bx.size() - 1
- tLIQ.bx.shift().delete()
- tLIQ.b.shift()
- for bI = vpLen to 1 //1 to vpLen
- l = 0
- for pLev = pLst to pHst by pStp
- if b.h[bI] >= pLev and b.l[bI] < pLev + pStp
- aVP.vs.set(l, aVP.vs.get(l) + nzV[bI] * ((b.h[bI] - b.l[bI]) == 0 ? 1 : pStp / (b.h[bI] - b.l[bI])))
- l += 1
- dLIQ.unshift(liquidity.new(array.new <bool> (na), array.new <box> (na)))
- cLIQ = dLIQ.get(0)
- for l = 0 to vpLev - 1
- if aVP.vs.get(l) / aVP.vs.max() < liqT
- cLIQ.b.unshift(true)
- cLIQ.bx.unshift(box.new(b.i, pLst + (l + 0.00) * pStp, b.i, pLst + (l + 1.00) * pStp, border_color = color(na), bgcolor = liqUC))
- else
- cLIQ.bx.unshift(box.new(na, na, na, na))
- cLIQ.b.unshift(false)
- for bI = 0 to vpLen
- int qBX = cLIQ.bx.size()
- for bx = 0 to (qBX > 0 ? qBX - 1 : na)
- if bx < cLIQ.bx.size()
- if cLIQ.b.get(bx)
- cBX = cLIQ.bx.get(bx)
- mBX = math.avg(cBX.get_bottom(), cBX.get_top())
- if math.sign(close[bI + 1] - mBX) != math.sign(low[bI] - mBX) or math.sign(close[bI + 1] - mBX) != math.sign(high[bI] - mBX) or math.sign(close[bI + 1] - mBX) != math.sign(close[bI] - mBX)
- cBX.set_left(b.i[bI])
- cLIQ.b.set(bx, false)
- //-----------------------------------------------------------------------------}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement