Advertisement
xmd79

Liquidity Levels/Voids (VP) [LuxAlgo]

Aug 18th, 2023
579
1
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.20 KB | None | 1 0
  1. // 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/
  2. // © LuxAlgo
  3.  
  4. //@version=5
  5.  
  6. 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
  7.  
  8. //------------------------------------------------------------------------------
  9. // Settings
  10. //-----------------------------------------------------------------------------{
  11.  
  12. 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]'
  13. mode = input.string('Present', title = 'Mode', options =['Present', 'Historical'], inline = 'MOD')
  14. back = input.int (360, ' # Bars', minval = 100, maxval = 5000, step = 10, inline = 'MOD', tooltip = mdTT)
  15.  
  16. grpLQ = 'Liquidity Levels / Voids'
  17. liqUC = input.color(color.new(#1848cc, 79), 'Liquidity Levels/Voids', inline = 'UFL', group = grpLQ, tooltip = 'Color customization option for Unfilled Liquidity Levels/Voids')
  18. ppLen = input.int(47, "Detection Length", minval = 1, group = grpLQ, tooltip = 'Lookback period used for the calculation of Swing Levels\n\nMinimum value [1]')
  19. 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
  20. 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]')
  21. 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')
  22. liqFC = input.color(color.new(#787b86, 79), '', inline = 'FL', group = grpLQ)
  23.  
  24. othGR = 'Other Features'
  25. 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')
  26. ppLCB = input.color(color.new(#f23645, 0), '', inline = 'ppLS', group = othGR)
  27. ppLCS = input.color(color.new(#089981, 0), '', inline = 'ppLS', group = othGR)
  28. ppLS = input.string('Small', "", options=['Tiny', 'Small', 'Normal'], inline = 'ppLS', group = othGR)
  29.  
  30. //-----------------------------------------------------------------------------}
  31. // User Defined Types
  32. //-----------------------------------------------------------------------------{
  33.  
  34. // @type bar properties with their values
  35. //
  36. // @field h (float) high price of the bar
  37. // @field l (float) low price of the bar
  38. // @field v (float) volume of the bar
  39. // @field i (int) index of the bar
  40.  
  41. type bar
  42. float h = high
  43. float l = low
  44. float v = volume
  45. int i = bar_index
  46.  
  47. // @type store pivot high/low and index data
  48. //
  49. // @field x (int) last pivot bar index
  50. // @field x1 (int) previous pivot bar index
  51. // @field h (float) last pivot high
  52. // @field h1 (float) previous pivot high
  53. // @field l (float) last pivot low
  54. // @field l1 (float) previous pivot low
  55.  
  56. type pivotPoint
  57. int x
  58. int x1
  59. float h
  60. float h1
  61. float l
  62. float l1
  63.  
  64. // @type maintain liquidity data
  65. //
  66. // @field b (array<bool>) array maintains price levels where liquidity exists
  67. // @field bx (array<box>) array maintains visual object of price levels where liquidity exists
  68.  
  69. type liquidity
  70. bool [] b
  71. box [] bx
  72.  
  73. // @type maintain volume profile data
  74. //
  75. // @field vs (array<float>) array maintains tolal traded volume
  76. // @field vp (array<box>) array maintains visual object of each price level
  77.  
  78. type volumeProfile
  79. float [] vs
  80. box [] vp
  81.  
  82. //-----------------------------------------------------------------------------}
  83. // Variables
  84. //-----------------------------------------------------------------------------{
  85.  
  86. bar b = bar.new()
  87.  
  88. var pivotPoint pp = pivotPoint.new()
  89.  
  90. var liquidity[] aLIQ = array.new<liquidity> (1, liquidity.new(array.new <bool> (vpLev, false), array.new <box> (na)))
  91. var liquidity[] dLIQ = array.new<liquidity> (1, liquidity.new(array.new <bool> (na) , array.new <box> (na)))
  92.  
  93. volumeProfile aVP = volumeProfile.new(array.new <float> (vpLev + 1, 0.), array.new <box> (na))
  94.  
  95. qBXs = 0
  96.  
  97. //-----------------------------------------------------------------------------}
  98. // Functions/methods
  99. //-----------------------------------------------------------------------------{
  100.  
  101. // @function calcuates highest price, lowest price and cumulative volume of the given range
  102. //
  103. // @param _l (int) length of the range
  104. // @param _c (bool) check
  105. // @param _o (int) offset
  106. //
  107. // @returns (float, float, float) highest, lowest and cumulative volume
  108.  
  109. f_calcHLV(_l, _c, _o) =>
  110. if _c
  111. l = low [_o]
  112. h = high[_o]
  113. v = 0.
  114.  
  115. for x = 0 to _l - 1
  116. l := math.min(low [_o + x], l)
  117. h := math.max(high[_o + x], h)
  118. v += volume[_o + x]
  119.  
  120. l := math.min(low [_o + _l], l)
  121. h := math.max(high[_o + _l], h)
  122.  
  123. [h, l, v]
  124.  
  125. //-----------------------------------------------------------------------------}
  126. // Calculations
  127. //-----------------------------------------------------------------------------{
  128.  
  129. per = mode == 'Present' ? last_bar_index - b.i <= back : true
  130. nzV = nz(b.v)
  131.  
  132. ppS = switch ppLS
  133. 'Tiny' => size.tiny
  134. 'Small' => size.small
  135. 'Normal' => size.normal
  136.  
  137. pp_h = ta.pivothigh(ppLen, ppLen)
  138. pp_l = ta.pivotlow (ppLen, ppLen)
  139.  
  140. if not na(pp_h)
  141. pp.h1 := pp.h
  142. pp.h := pp_h
  143.  
  144. if not na(pp_l)
  145. pp.l1 := pp.l
  146. pp.l := pp_l
  147.  
  148. go = not na(pp_h) or not na(pp_l)
  149.  
  150. if go
  151. pp.x1 := pp.x
  152. pp.x := b.i
  153.  
  154. vpLen = pp.x - pp.x1
  155.  
  156. [pHst, pLst, tV] = f_calcHLV(vpLen, go, ppLen)
  157. pStp = (pHst - pLst) / vpLev
  158.  
  159. if go and nzV and pStp > 0 and b.i > vpLen and vpLen > 0 and per
  160.  
  161. for bIt = vpLen to 1
  162. l = 0
  163. bI = bIt + ppLen
  164.  
  165. for pLev = pLst to pHst by pStp
  166. if b.h[bI] >= pLev and b.l[bI] < pLev + pStp
  167. 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])))
  168. l += 1
  169.  
  170. aLIQ.unshift(liquidity.new(array.new <bool> (vpLev, false), array.new <box> (na)))
  171. cLIQ = aLIQ.get(0)
  172.  
  173. for l = vpLev - 1 to 0
  174. if aVP.vs.get(l) / aVP.vs.max() < liqT
  175. cLIQ.b.set(l, true)
  176. 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 ))
  177. else
  178. cLIQ.bx.unshift(box.new(na, na, na, na))
  179. cLIQ.b.set(l, false)
  180.  
  181. for bIt = 0 to vpLen
  182. bI = bIt + ppLen
  183. int qBX = cLIQ.bx.size()
  184.  
  185. for bx = 0 to (qBX > 0 ? qBX - 1 : na)
  186. if bx < cLIQ.bx.size()
  187. if cLIQ.b.get(bx)
  188. cBX = cLIQ.bx.get(bx)
  189. mBX = math.avg(cBX.get_bottom(), cBX.get_top())
  190.  
  191. 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)
  192. cBX.set_left(b.i[bI])
  193. cLIQ.b.set(bx, false)
  194.  
  195. for bI = ppLen to 0
  196. int qBX = cLIQ.bx.size()
  197.  
  198. for bx = (qBX > 0 ? qBX - 1 : na) to 0
  199. if bx < cLIQ.bx.size()
  200. cBX = cLIQ.bx.get(bx)
  201. mBX = math.avg(box.get_bottom(cBX), box.get_top(cBX))
  202.  
  203. if math.sign(close[bI + 1] - mBX) != math.sign(low[bI] - mBX) or math.sign(close[bI + 1] - mBX) != math.sign(high[bI] - mBX)
  204. if liqFD
  205. cBX.set_bgcolor(liqFC)
  206. else
  207. cBX.delete()
  208.  
  209. cLIQ.bx.remove(bx)
  210. else
  211. cBX.set_right(b.i[bI])
  212.  
  213. for i = aLIQ.size() - 1 to 0
  214. x = aLIQ.get(i)
  215. int qBX = x.bx.size()
  216.  
  217. qBXs := qBXs + qBX
  218. if qBXs > 500
  219. aLIQ.pop()
  220.  
  221. for bx = (qBX > 0 ? qBX - 1 : na) to 0
  222. if bx < x.bx.size()
  223. cBX = x.bx.get(bx)
  224. mBX = math.avg(box.get_bottom(cBX), box.get_top(cBX))
  225.  
  226. if math.sign(close[1] - mBX) != math.sign(low - mBX) or math.sign(close[1] - mBX) != math.sign(high - mBX)
  227. //cBX.delete()
  228. if liqFD
  229. cBX.set_bgcolor(liqFC)
  230. else
  231. cBX.delete()
  232. x.bx.remove(bx)
  233. else
  234. cBX.set_right(b.i)
  235.  
  236.  
  237. if ppLev and (mode == 'Present' ? last_bar_index - b.i <= back * 1.318 : true)
  238. 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)
  239.  
  240. if not na(pp_h)
  241. swH = pp.h > pp.h1 ? "HH" : pp.h < pp.h1 ? "LH" : na
  242. 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)
  243. if not na(pp_l)
  244. swL = pp.l < pp.l1 ? "LL" : pp.l > pp.l1 ? "HL" : na
  245. 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)
  246.  
  247. vpLen := barstate.islast ? last_bar_index - pp.x + ppLen : 1
  248. pHst := ta.highest(b.h, vpLen > 0 ? vpLen + 1 : 1)
  249. pLst := ta.lowest (b.l, vpLen > 0 ? vpLen + 1 : 1)
  250. pStp := (pHst - pLst) / vpLev
  251.  
  252. if barstate.islast and nzV and vpLen > 0 and pStp > 0
  253.  
  254. tLIQ = dLIQ.shift()
  255. if tLIQ.bx.size() > 0
  256. for i = 0 to tLIQ.bx.size() - 1
  257. tLIQ.bx.shift().delete()
  258. tLIQ.b.shift()
  259.  
  260. for bI = vpLen to 1 //1 to vpLen
  261. l = 0
  262. for pLev = pLst to pHst by pStp
  263. if b.h[bI] >= pLev and b.l[bI] < pLev + pStp
  264. 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])))
  265. l += 1
  266.  
  267. dLIQ.unshift(liquidity.new(array.new <bool> (na), array.new <box> (na)))
  268. cLIQ = dLIQ.get(0)
  269.  
  270. for l = 0 to vpLev - 1
  271. if aVP.vs.get(l) / aVP.vs.max() < liqT
  272. cLIQ.b.unshift(true)
  273. 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))
  274. else
  275. cLIQ.bx.unshift(box.new(na, na, na, na))
  276. cLIQ.b.unshift(false)
  277.  
  278. for bI = 0 to vpLen
  279. int qBX = cLIQ.bx.size()
  280.  
  281. for bx = 0 to (qBX > 0 ? qBX - 1 : na)
  282. if bx < cLIQ.bx.size()
  283. if cLIQ.b.get(bx)
  284. cBX = cLIQ.bx.get(bx)
  285. mBX = math.avg(cBX.get_bottom(), cBX.get_top())
  286.  
  287. 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)
  288. cBX.set_left(b.i[bI])
  289. cLIQ.b.set(bx, false)
  290.  
  291. //-----------------------------------------------------------------------------}
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement