xmd79

Market sessions and Volume profile - By Leviathan

Nov 4th, 2022
466
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.49 KB | None | 0 0
  1. // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
  2. // © Leviathan
  3.  
  4. // Some Volume Profile elements are inspired by @LonesomeTheBlue's volume profile script
  5.  
  6. //@version=5
  7. indicator("Market sessions and Volume profile - By Leviathan", overlay=true, max_boxes_count=500)
  8.  
  9. //==========================
  10. //Inputs
  11. //==========================
  12. sessionType = input.string('Daily', 'Session Type', options=['Tokyo','London','New York','Daily','Weekly', 'Monthly'])
  13.  
  14. showProf = input.bool(true, 'Show Volume Profile', group='Display')
  15. showPoc = input.bool(true, 'Show POC', group='Display')
  16. showVA = input.bool(true, 'Show Value Area', group='Display')
  17. showCur = input.bool(true, 'Show Live Zone', group='Display')
  18. resolution = input.int(30, 'Resolution', minval=5, tooltip='The higher the value, the more refined of a profile, but less profiles shown on chart', group='Volume Profile Settings')
  19. smoothVol = input.bool(false, 'Smooth Volume Data', tooltip='Useful for assets that have very large spikes in volume over large bars, helps create better profiles', group='Volume Profile Settings')
  20. dataTf = ''
  21.  
  22. bullCol = input.color(color.rgb(76, 175, 79, 50), 'Up Volume', group='Appearance')
  23. bearCol = input.color(color.rgb(255, 82, 82, 50), 'Down Volume', group='Appearance')
  24. pocCol = input.color(color.red, 'POC', inline='p', group='Appearance')
  25. pocWid = input.int(1, 'Thickness', inline='p', group='Appearance')
  26. vahCol = input.color(color.aqua, 'VAH', inline='h', group='Appearance')
  27. vahWid = input.int(1, 'Thickness', inline='h', group='Appearance')
  28. valCol = input.color(color.aqua, 'VAL', inline='l', group='Appearance')
  29. valWid = input.int(1, 'Thickness', inline='l', group='Appearance')
  30. boxWid = input.int(1, 'Session Box Thickness', group='Appearance')
  31.  
  32. //==========================
  33. //Constants / Variable Declaration
  34. //==========================
  35. var int zoneStart = 0
  36. int lookback = bar_index - zoneStart
  37. var activeZone = false
  38.  
  39. // Defining arrays that store the information
  40. var vpGreen = array.new_float(resolution, 0) // Sum of volume on long bars
  41. var vpRed = array.new_float(resolution, 0) // Same thing but with red bars
  42. var zoneBounds = array.new_float(resolution, 0) // array that stores the highest value that can be in a zone
  43.  
  44. //Values to store current intra bar data
  45. var float[] ltfOpen = array.new_float(0)
  46. var float[] ltfClose = array.new_float(0)
  47. var float[] ltfHigh = array.new_float(0)
  48. var float[] ltfLow = array.new_float(0)
  49. var float[] ltfVolume = array.new_float(0)
  50.  
  51.  
  52. vol() =>
  53. smoothVol ? ta.ema(volume, 5) : volume
  54.  
  55. //Getting intrabar intial data
  56. [dO, dC, dH, dL, dV] = request.security_lower_tf(syminfo.tickerid, dataTf, [open, close, high, low, vol()])
  57.  
  58. //==========================
  59. //Functions
  60. //==========================
  61. resetProfile(enable) =>
  62. if enable
  63. array.fill(vpGreen, 0)
  64. array.fill(vpRed, 0)
  65. array.clear(ltfOpen)
  66. array.clear(ltfHigh)
  67. array.clear(ltfLow)
  68. array.clear(ltfClose)
  69. array.clear(ltfVolume)
  70.  
  71. profHigh = ta.highest(high, lookback+1)[1]
  72. profLow = ta.lowest(low, lookback+1)[1]
  73.  
  74. tr = ta.atr(1)
  75. atr = ta.atr(14)
  76.  
  77. get_vol(y11, y12, y21, y22, height, vol) =>
  78. nz(math.max(math.min(math.max(y11, y12), math.max(y21, y22)) - math.max(math.min(y11, y12), math.min(y21, y22)), 0) * vol / height)
  79.  
  80. profileAdd(o, h, l, c, v, g, w) =>
  81. //Array to store how much to distribute in each zone, on scale of 1 for full gap size to 0
  82. zoneDist = array.new_float(resolution, 0)
  83. distSum = 0.0
  84. // Going over each zone
  85. for i = 0 to array.size(vpGreen) - 1
  86. // Checking to see if cur bar is in zone
  87. zoneTop = array.get(zoneBounds, i)
  88. zoneBot = zoneTop - g
  89.  
  90. body_top = math.max(c, o)
  91. body_bot = math.min(c, o)
  92. itsgreen = c >= o
  93.  
  94. topwick = h - body_top
  95. bottomwick = body_bot - l
  96. body = body_top - body_bot
  97.  
  98. bodyvol = body * v / (2 * topwick + 2 * bottomwick + body)
  99. topwickvol = 2 * topwick * v / (2 * topwick + 2 * bottomwick + body)
  100. bottomwickvol = 2 * bottomwick * v / (2 * topwick + 2 * bottomwick + body)
  101.  
  102. array.set(vpGreen, i, array.get(vpGreen, i) + (itsgreen ? get_vol(zoneBot, zoneTop, body_bot, body_top, body, bodyvol) : 0) + get_vol(zoneBot, zoneTop, body_top, h, topwick, topwickvol) / 2 + get_vol(zoneBot, zoneTop, body_bot, l, bottomwick, bottomwickvol) / 2)
  103. array.set(vpRed, i, array.get(vpRed, i) + (itsgreen ? 0 : get_vol(zoneBot, zoneTop, body_bot, body_top, body, bodyvol)) + get_vol(zoneBot, zoneTop, body_top, h, topwick, topwickvol) / 2 + get_vol(zoneBot, zoneTop, body_bot, l, bottomwick, bottomwickvol) / 2)
  104.  
  105. calcSession(update) =>
  106. array.fill(vpGreen, 0)
  107. array.fill(vpRed, 0)
  108. if bar_index > lookback and update
  109. gap = (profHigh - profLow) / resolution
  110.  
  111. // Defining profile bounds
  112. for i = 0 to resolution - 1
  113. array.set(zoneBounds, i, profHigh - gap * i)
  114.  
  115. // Putting each bar inside zone into the volume profile array
  116. if array.size(ltfOpen) > 0
  117. for j = 0 to array.size(ltfOpen) - 1
  118. profileAdd(array.get(ltfOpen, j), array.get(ltfHigh, j), array.get(ltfLow, j), array.get(ltfClose, j), array.get(ltfVolume, j), gap, 1)
  119.  
  120. pocLevel() =>
  121. float maxVol = 0
  122. int levelInd = 0
  123. for i = 0 to array.size(vpRed) - 1
  124. if array.get(vpRed, i) + array.get(vpGreen, i) > maxVol
  125. maxVol := array.get(vpRed, i) + array.get(vpGreen, i)
  126. levelInd := i
  127.  
  128. float outLevel = na
  129. if levelInd != array.size(vpRed) - 1
  130. outLevel := array.get(zoneBounds, levelInd) - (array.get(zoneBounds, levelInd) - array.get(zoneBounds, levelInd+1)) / 2
  131. outLevel
  132.  
  133. valueLevels(poc) =>
  134. float gap = (profHigh - profLow) / resolution
  135. float volSum = array.sum(vpRed) + array.sum(vpGreen)
  136. float volCnt = 0
  137.  
  138. float vah = profHigh
  139. float val = profLow
  140.  
  141. //Finding poc index
  142. int pocInd = 0
  143. for i = 0 to array.size(zoneBounds)-2
  144. if array.get(zoneBounds, i) >= poc and array.get(zoneBounds, i + 1) < poc
  145. pocInd := i
  146.  
  147. volCnt += (array.get(vpRed, pocInd) + array.get(vpGreen, pocInd))
  148. for i = 1 to array.size(vpRed)
  149. if pocInd + i >= 0 and pocInd + i < array.size(vpRed)
  150. volCnt += (array.get(vpRed, pocInd + i) + array.get(vpGreen, pocInd + i))
  151. if volCnt >= volSum * 0.7
  152. break
  153. else
  154. val := array.get(zoneBounds, pocInd + i) - gap
  155. if pocInd - i >= 0 and pocInd - i < array.size(vpRed)
  156. volCnt += (array.get(vpRed, pocInd - i) + array.get(vpGreen, pocInd - i))
  157. if volCnt >= volSum * 0.7
  158. break
  159. else
  160. vah := array.get(zoneBounds, pocInd - i)
  161.  
  162. [val, vah]
  163.  
  164. drawNewZone(update) =>
  165. if bar_index > lookback and update and array.sum(vpGreen) + array.sum(vpRed) > 0
  166. gap = (profHigh - profLow) / resolution
  167. float leftMax = bar_index[lookback]
  168. float rightMax = bar_index[int(lookback / 1.4)]
  169. float rightMaxVol = array.max(vpGreen)+array.max(vpRed)
  170. float buffer = gap / 10
  171. if showProf
  172. for i = 0 to array.size(vpRed) - 1
  173. greenEnd = int(leftMax + (rightMax - leftMax) * (array.get(vpGreen, i) / rightMaxVol))
  174. redEnd = int(greenEnd + (rightMax - leftMax) * (array.get(vpRed, i) / rightMaxVol))
  175. box.new(int(leftMax), array.get(zoneBounds, i) - buffer, greenEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bullCol, border_width=0)
  176. box.new(greenEnd, array.get(zoneBounds, i) - buffer, redEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bearCol, border_width=0)
  177. box.new(int(leftMax), profHigh, bar_index-1, profLow, chart.fg_color, boxWid, line.style_dashed, bgcolor=color.rgb(0,0,0,100))
  178. poc = pocLevel()
  179. [val, vah] = valueLevels(poc)
  180. if showPoc
  181. line.new(int(leftMax), poc, bar_index-1, poc, color=pocCol, width=pocWid)
  182. if showVA
  183. line.new(int(leftMax), vah, bar_index-1, vah, color=vahCol, width=vahWid)
  184. line.new(int(leftMax), val, bar_index-1, val, color=valCol, width=valWid)
  185.  
  186.  
  187. //if update
  188. // resetProfile(true)
  189.  
  190. drawCurZone(update) =>
  191. var line pocLine = na
  192. var line vahLine = na
  193. var line valLine = na
  194. var box outBox = na
  195.  
  196. var redBoxes = array.new_box(array.size(vpRed), na)
  197. var greenBoxes = array.new_box(array.size(vpRed), na)
  198.  
  199. if bar_index > lookback and update and array.sum(vpGreen) + array.sum(vpRed) > 0
  200. //Clearing the previous boxes and array
  201. if not na(pocLine)
  202. line.delete(pocLine)
  203. if not na(vahLine)
  204. line.delete(vahLine)
  205. if not na(valLine)
  206. line.delete(valLine)
  207. if not na(outBox)
  208. box.delete(outBox)
  209.  
  210. for i = 0 to array.size(redBoxes) - 1
  211. if not na(array.get(redBoxes, i))
  212. box.delete(array.get(redBoxes, i))
  213. box.delete(array.get(greenBoxes, i))
  214.  
  215.  
  216. gap = (profHigh - profLow) / resolution
  217. float leftMax = bar_index[lookback]
  218. float rightMax = bar_index[int(lookback / 1.4)]
  219. float rightMaxVol = array.max(vpGreen)+array.max(vpRed)
  220. float buffer = gap / 10
  221. if showProf
  222. for i = 0 to array.size(vpRed) - 1
  223. greenEnd = int(leftMax + (rightMax - leftMax) * (array.get(vpGreen, i) / rightMaxVol))
  224. redEnd = int(greenEnd + (rightMax - leftMax) * (array.get(vpRed, i) / rightMaxVol))
  225. array.set(greenBoxes, i, box.new(int(leftMax), array.get(zoneBounds, i) - buffer, greenEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bullCol, border_width=0))
  226. array.set(redBoxes, i, box.new(greenEnd, array.get(zoneBounds, i) - buffer, redEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bearCol, border_width=0))
  227. outBox := box.new(int(leftMax), profHigh, bar_index-1, profLow, chart.fg_color, boxWid, line.style_dashed, bgcolor=color.rgb(0,0,0,100))
  228.  
  229. poc = pocLevel()
  230. [val, vah] = valueLevels(poc)
  231. if showPoc
  232. line.new(int(leftMax), poc, bar_index-1, poc, color=pocCol, width=pocWid)
  233. if showVA
  234. line.new(int(leftMax), vah, bar_index-1, vah, color=vahCol, width=vahWid)
  235. line.new(int(leftMax), val, bar_index-1, val, color=valCol, width=valWid)
  236.  
  237. combArray(arr1, arr2) =>
  238. out = array.copy(arr1)
  239. if array.size(arr2) > 0
  240. for i = 0 to array.size(arr2) - 1
  241. array.push(out, array.get(arr2, i))
  242. out
  243.  
  244. updateIntra(o, h, l, c, v) =>
  245. if array.size(o) > 0
  246. for i = 0 to array.size(o) - 1
  247. array.push(ltfOpen, array.get(o, i))
  248. array.push(ltfHigh,array.get(h, i))
  249. array.push(ltfLow,array.get(l, i))
  250. array.push(ltfClose,array.get(c, i))
  251. array.push(ltfVolume,array.get(v, i))
  252.  
  253.  
  254. //==========================
  255. //Calculations
  256. //==========================
  257. //Detecting different start dates
  258. newDaily = dayofweek != dayofweek[1]
  259. newWeekly = (dayofweek != dayofweek[1] + 1) and (dayofweek != dayofweek[1])
  260. newMonthly = (dayofmonth != dayofmonth[1] + 1) and (dayofmonth != dayofmonth[1])
  261.  
  262. utcHour = hour(time(timeframe.period, '0000-2400', 'GMT'), 'GMT')
  263.  
  264. newTokyo = utcHour != utcHour[1] + 1 and utcHour != utcHour[1]
  265. endTokyo = utcHour >= 9 and utcHour[1] < 9
  266.  
  267. newLondon = utcHour >= 7 and utcHour[1] < 7
  268. endLondon = utcHour >= 16 and utcHour[1] < 16
  269.  
  270. newNewYork = utcHour >= 13 and utcHour[1] < 13
  271. endNewYork = utcHour >= 22 and utcHour[1] < 22
  272.  
  273. newSession = switch sessionType
  274. 'Tokyo' => newTokyo
  275. 'London' => newLondon
  276. 'New York' => newNewYork
  277. 'Daily' => newDaily
  278. 'Weekly' => newWeekly
  279. 'Monthly' => newMonthly
  280.  
  281. zoneEnd = switch sessionType
  282. 'Tokyo' => endTokyo
  283. 'London' => endLondon
  284. 'New York' => endNewYork
  285. 'Daily' => newDaily
  286. 'Weekly' => newWeekly
  287. 'Monthly' => newMonthly
  288.  
  289. //Re calculating and drawing zones
  290. calcSession(zoneEnd or (barstate.islast and showCur))
  291. drawNewZone(zoneEnd)
  292. drawCurZone(barstate.islast and not zoneEnd and showCur and activeZone)
  293.  
  294. //Reseting profie at start of new zone
  295. resetProfile(newSession)
  296.  
  297. //Updating data arrays
  298. updateIntra(dO, dH, dL, dC, dV)
  299.  
  300. //Reseting zone start value
  301. if zoneEnd
  302. activeZone := false
  303.  
  304. if newSession
  305. zoneStart := bar_index
  306. activeZone := true
  307.  
Advertisement
Add Comment
Please, Sign In to add comment