Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
- // © Leviathan
- // Some Volume Profile elements are inspired by @LonesomeTheBlue's volume profile script
- //@version=5
- indicator("Market sessions and Volume profile - By Leviathan", overlay=true, max_boxes_count=500, max_bars_back=1000)
- //==========================
- //Inputs
- //==========================
- sessionType = input.string('Daily', 'Session Type', options=['Tokyo','London','New York','Daily','Weekly', 'Monthly', 'Quarterly', 'Yearly'])
- showProf = input.bool(true, 'Show Volume Profile', group='Display')
- showSbox = input.bool(true, 'Show Session Box', group='Display')
- showPoc = input.bool(true, 'Show POC', group='Display')
- showVA = input.bool(true, 'Show VAH and VAL', group='Display')
- showVAb = input.bool(false, 'Show Value Area Box', group='Display')
- showCur = input.bool(true, 'Show Live Zone', group='Display')
- showLabels = input.bool(true, 'Show Session Lables', group='Display')
- showFx = input.bool(false, 'Show Forex Sessions (no profile)', group='Display')
- 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')
- VAwid = input.int(70, 'Value Area Volume %', minval=1, maxval=100, group='Volume Profile Settings')
- dispMode = input.string('Mode 2', 'Bar Mode', ['Mode 1', 'Mode 2', 'Mode 3'], group='Volume Profile Settings')
- volType = input.string('Volume', 'Profile Data Type', options=['Volume', 'Open Interest'], group='Volume Profile Settings')
- 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')
- dataTf = ''
- bullCol = input.color(color.rgb(76, 175, 79, 50), 'Up Volume', group='Appearance')
- bearCol = input.color(color.rgb(255, 82, 82, 50), 'Down Volume', group='Appearance')
- VAbCol = input.color(color.rgb(107, 159, 255, 90), 'Value Area Box', group='Appearance' )
- pocCol = input.color(color.red, 'POC', inline='p', group='Appearance')
- pocWid = input.int(1, 'Thickness', inline='p', group='Appearance')
- vahCol = input.color(color.aqua, 'VAH', inline='h', group='Appearance')
- vahWid = input.int(1, 'Thickness', inline='h', group='Appearance')
- valCol = input.color(color.aqua, 'VAL', inline='l', group='Appearance')
- valWid = input.int(1, 'Thickness', inline='l', group='Appearance')
- boxBg = input.color(color.rgb(255, 153, 0, 100), 'Box', inline='m', group='Appearance')
- boxWid = input.int(1, 'Thickness', inline='m', group='Appearance')
- //==========================
- //Constants / Variable Declaration
- //==========================
- var int zoneStart = 0
- var int tokyoStart = 0
- var int londonStart = 0
- var int nyStart = 0
- int lookback = bar_index - zoneStart
- var activeZone = false
- // Defining arrays that store the information
- var vpGreen = array.new_float(resolution, 0) // Sum of volume on long bars
- var vpRed = array.new_float(resolution, 0) // Same thing but with red bars
- var zoneBounds = array.new_float(resolution, 0) // array that stores the highest value that can be in a zone
- //Values to store current intra bar data
- var float[] ltfOpen = array.new_float(0)
- var float[] ltfClose = array.new_float(0)
- var float[] ltfHigh = array.new_float(0)
- var float[] ltfLow = array.new_float(0)
- var float[] ltfVolume = array.new_float(0)
- //Getting OI Data
- string userSymbol = syminfo.prefix + ":" + syminfo.ticker
- string openInterestTicker = str.format("{0}_OI", userSymbol)
- string timeframe = syminfo.type == "futures" and timeframe.isintraday ? "1D" : timeframe.period
- deltaOi = request.security(openInterestTicker, timeframe, close-close[1], ignore_invalid_symbol = true)
- //Selecting what vol type to use
- vol() =>
- out = smoothVol ? ta.ema(volume, 5) : volume
- if volType == 'Open Interest'
- out := deltaOi
- out
- //Getting intrabar intial data
- [dO, dC, dH, dL, dV] = request.security_lower_tf(syminfo.tickerid, dataTf, [open, close, high, low, vol()])
- //==========================
- //Functions
- //==========================
- resetProfile(enable) =>
- if enable
- array.fill(vpGreen, 0)
- array.fill(vpRed, 0)
- array.clear(ltfOpen)
- array.clear(ltfHigh)
- array.clear(ltfLow)
- array.clear(ltfClose)
- array.clear(ltfVolume)
- profHigh = ta.highest(high, lookback+1)[1]
- profLow = ta.lowest(low, lookback+1)[1]
- tr = ta.atr(1)
- atr = ta.atr(14)
- get_vol(y11, y12, y21, y22, height, vol) =>
- 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)
- profileAdd(o, h, l, c, v, g, w) =>
- //Array to store how much to distribute in each zone, on scale of 1 for full gap size to 0
- zoneDist = array.new_float(resolution, 0)
- distSum = 0.0
- // Going over each zone
- for i = 0 to array.size(vpGreen) - 1
- // Checking to see if cur bar is in zone
- zoneTop = array.get(zoneBounds, i)
- zoneBot = zoneTop - g
- body_top = math.max(c, o)
- body_bot = math.min(c, o)
- itsgreen = c >= o
- topwick = h - body_top
- bottomwick = body_bot - l
- body = body_top - body_bot
- bodyvol = body * v / (2 * topwick + 2 * bottomwick + body)
- topwickvol = 2 * topwick * v / (2 * topwick + 2 * bottomwick + body)
- bottomwickvol = 2 * bottomwick * v / (2 * topwick + 2 * bottomwick + body)
- if volType == 'Volume'
- 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)
- 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)
- else if volType == 'Open Interest'
- if v > 0
- array.set(vpGreen, i, array.get(vpGreen, i) + get_vol(zoneBot, zoneTop, body_bot, body_top, body, v))// + get_vol(zoneBot, zoneTop, body_top, h, topwick, topwickvol) / 2 + get_vol(zoneBot, zoneTop, body_bot, l, bottomwick, bottomwickvol) / 2)
- if v < 0
- array.set(vpRed, i, array.get(vpRed, i) + get_vol(zoneBot, zoneTop, body_bot, body_top, body, -v))// + get_vol(zoneBot, zoneTop, body_top, h, topwick, topwickvol) / 2 + get_vol(zoneBot, zoneTop, body_bot, l, bottomwick, bottomwickvol) / 2)
- calcSession(update) =>
- array.fill(vpGreen, 0)
- array.fill(vpRed, 0)
- if bar_index > lookback and update
- gap = (profHigh - profLow) / resolution
- // Defining profile bounds
- for i = 0 to resolution - 1
- array.set(zoneBounds, i, profHigh - gap * i)
- // Putting each bar inside zone into the volume profile array
- if array.size(ltfOpen) > 0
- for j = 0 to array.size(ltfOpen) - 1
- profileAdd(array.get(ltfOpen, j), array.get(ltfHigh, j), array.get(ltfLow, j), array.get(ltfClose, j), array.get(ltfVolume, j), gap, 1)
- pocLevel() =>
- float maxVol = 0
- int levelInd = 0
- for i = 0 to array.size(vpRed) - 1
- if array.get(vpRed, i) + array.get(vpGreen, i) > maxVol
- maxVol := array.get(vpRed, i) + array.get(vpGreen, i)
- levelInd := i
- float outLevel = na
- if levelInd != array.size(vpRed) - 1
- outLevel := array.get(zoneBounds, levelInd) - (array.get(zoneBounds, levelInd) - array.get(zoneBounds, levelInd+1)) / 2
- outLevel
- valueLevels(poc) =>
- float gap = (profHigh - profLow) / resolution
- float volSum = array.sum(vpRed) + array.sum(vpGreen)
- float volCnt = 0
- float vah = profHigh
- float val = profLow
- //Finding poc index
- int pocInd = 0
- for i = 0 to array.size(zoneBounds)-2
- if array.get(zoneBounds, i) >= poc and array.get(zoneBounds, i + 1) < poc
- pocInd := i
- volCnt += (array.get(vpRed, pocInd) + array.get(vpGreen, pocInd))
- for i = 1 to array.size(vpRed)
- if pocInd + i >= 0 and pocInd + i < array.size(vpRed)
- volCnt += (array.get(vpRed, pocInd + i) + array.get(vpGreen, pocInd + i))
- if volCnt >= volSum * (VAwid/100)
- break
- else
- val := array.get(zoneBounds, pocInd + i) - gap
- if pocInd - i >= 0 and pocInd - i < array.size(vpRed)
- volCnt += (array.get(vpRed, pocInd - i) + array.get(vpGreen, pocInd - i))
- if volCnt >= volSum * (VAwid/100)
- break
- else
- vah := array.get(zoneBounds, pocInd - i)
- [val, vah]
- drawNewZone(update) =>
- if bar_index > lookback and update and array.sum(vpGreen) + array.sum(vpRed) > 0
- gap = (profHigh - profLow) / resolution
- float leftMax = bar_index[lookback]
- float rightMax = bar_index[int(lookback / 1.4)]
- float rightMaxVol = array.max(vpGreen)+array.max(vpRed)
- float buffer = gap / 10
- if showLabels
- label.new((bar_index - 1 + int(leftMax))/2, profHigh, sessionType, color=color.rgb(0,0,0,100), textcolor=chart.fg_color)
- if showProf
- for i = 0 to array.size(vpRed) - 1
- greenEnd = int(leftMax + (rightMax - leftMax) * (array.get(vpGreen, i) / rightMaxVol))
- redEnd = int(greenEnd + (rightMax - leftMax) * (array.get(vpRed, i) / rightMaxVol))
- if dispMode == 'Mode 2'
- box.new(int(leftMax), array.get(zoneBounds, i) - buffer, greenEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bullCol, border_width=0)
- box.new(greenEnd, array.get(zoneBounds, i) - buffer, redEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bearCol, border_width=0)
- else if dispMode == 'Mode 1'
- box.new(int(leftMax), array.get(zoneBounds, i) - buffer, greenEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bullCol, border_width=0)
- else
- box.new(int(leftMax), array.get(zoneBounds, i) - buffer, greenEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bullCol, border_width=0)
- box.new(int(leftMax)-redEnd+greenEnd, array.get(zoneBounds, i) - buffer, int(leftMax), array.get(zoneBounds, i) - gap + buffer, bgcolor=bearCol, border_width=0)
- box.new(int(leftMax), profHigh, bar_index-1, profLow, chart.fg_color, boxWid, line.style_dashed, bgcolor=boxBg)
- poc = pocLevel()
- [val, vah] = valueLevels(poc)
- if showPoc
- line.new(int(leftMax), poc, bar_index-1, poc, color=pocCol, width=pocWid)
- if showVA
- line.new(int(leftMax), vah, bar_index-1, vah, color=vahCol, width=vahWid)
- line.new(int(leftMax), val, bar_index-1, val, color=valCol, width=valWid)
- if showVAb
- box.new(int(leftMax), vah, bar_index-1, val, border_color=color.rgb(54, 58, 69, 100), bgcolor=VAbCol)
- //if update
- // resetProfile(true)
- drawCurZone(update, delete) =>
- var line pocLine = na
- var line vahLine = na
- var line valLine = na
- var box outBox = na
- var label sessionLab = na
- var redBoxes = array.new_box(array.size(vpRed), na)
- var greenBoxes = array.new_box(array.size(vpRed), na)
- if bar_index > lookback and update and array.sum(vpGreen) + array.sum(vpRed) > 0
- //Clearing the previous boxes and array
- if not na(pocLine)
- line.delete(pocLine)
- if not na(vahLine)
- line.delete(vahLine)
- if not na(valLine)
- line.delete(valLine)
- if not na(outBox)
- box.delete(outBox)
- if not na(sessionLab)
- label.delete(sessionLab)
- for i = 0 to array.size(redBoxes) - 1
- if not na(array.get(redBoxes, i))
- box.delete(array.get(redBoxes, i))
- box.delete(array.get(greenBoxes, i))
- gap = (profHigh - profLow) / resolution
- float leftMax = bar_index[lookback]
- float rightMax = bar_index[int(lookback / 1.4)]
- float rightMaxVol = array.max(vpGreen)+array.max(vpRed)
- float buffer = gap / 10
- if showLabels
- sessionLab := label.new((bar_index - 1 + int(leftMax))/2, profHigh, sessionType, color=color.rgb(0,0,0,100), textcolor=chart.fg_color)
- if showProf
- for i = 0 to array.size(vpRed) - 1
- greenEnd = int(leftMax + (rightMax - leftMax) * (array.get(vpGreen, i) / rightMaxVol))
- redEnd = int(greenEnd + (rightMax - leftMax) * (array.get(vpRed, i) / rightMaxVol))
- if dispMode == 'Mode 2'
- 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))
- array.set(redBoxes, i, box.new(greenEnd, array.get(zoneBounds, i) - buffer, redEnd, array.get(zoneBounds, i) - gap + buffer, bgcolor=bearCol, border_width=0))
- else if dispMode == 'Mode 1'
- 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))
- else
- 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))
- array.set(redBoxes, i, box.new(int(leftMax)-redEnd+greenEnd, array.get(zoneBounds, i) - buffer, int(leftMax), array.get(zoneBounds, i) - gap + buffer, bgcolor=bearCol, border_width=0))
- outBox := box.new(int(leftMax), profHigh, bar_index-1, profLow, chart.fg_color, boxWid, line.style_dashed, bgcolor=boxBg)
- poc = pocLevel()
- [val, vah] = valueLevels(poc)
- if showPoc
- line.delete(pocLine)
- pocLine := line.new(int(leftMax), poc, bar_index-1, poc, color=pocCol, width=pocWid)
- if showVA
- line.delete(vahLine)
- line.delete(valLine)
- vahLine := line.new(int(leftMax), vah, bar_index-1, vah, color=vahCol, width=vahWid)
- valLine := line.new(int(leftMax), val, bar_index-1, val, color=valCol, width=valWid)
- if showVAb
- box.new(int(leftMax), vah, bar_index-1, val, border_color=color.rgb(54, 58, 69, 100), bgcolor=VAbCol)
- if delete
- box.delete(outBox)
- line.delete(pocLine)
- line.delete(vahLine)
- line.delete(valLine)
- for i = 0 to array.size(greenBoxes)-1
- box.delete(array.get(greenBoxes, i))
- for i = 0 to array.size(redBoxes)-1
- box.delete(array.get(redBoxes, i))
- drawForexBox(startBar, title, top, bottom) =>
- box.new(int(startBar), top, bar_index-1, bottom, chart.fg_color, boxWid, line.style_dashed, bgcolor=boxBg)
- if showLabels
- label.new((bar_index - 1 + int(startBar))/2, top, title, color=color.rgb(0,0,0,100), textcolor=chart.fg_color)
- combArray(arr1, arr2) =>
- out = array.copy(arr1)
- if array.size(arr2) > 0
- for i = 0 to array.size(arr2) - 1
- array.push(out, array.get(arr2, i))
- out
- updateIntra(o, h, l, c, v) =>
- if array.size(o) > 0
- for i = 0 to array.size(o) - 1
- array.push(ltfOpen, array.get(o, i))
- array.push(ltfHigh,array.get(h, i))
- array.push(ltfLow,array.get(l, i))
- array.push(ltfClose,array.get(c, i))
- array.push(ltfVolume,array.get(v, i))
- //==========================
- //Calculations
- //==========================
- //Detecting different start dates
- newDaily = dayofweek != dayofweek[1]
- newWeekly = weekofyear != weekofyear[1]
- newMonthly = (dayofmonth != dayofmonth[1] + 1) and (dayofmonth != dayofmonth[1])
- newYearly = year != year[1]
- newQuarterly = month != month[1] and (month - 1) % 3 == 0
- utcHour = hour(time(timeframe.period, '0000-2400', 'GMT'), 'GMT')
- newTokyo = utcHour != utcHour[1] + 1 and utcHour != utcHour[1]
- endTokyo = utcHour >= 9 and utcHour[1] < 9
- newLondon = utcHour >= 7 and utcHour[1] < 7
- endLondon = utcHour >= 16 and utcHour[1] < 16
- newNewYork = utcHour >= 13 and utcHour[1] < 13
- endNewYork = utcHour >= 22 and utcHour[1] < 22
- newSession = switch sessionType
- 'Tokyo' => newTokyo
- 'London' => newLondon
- 'New York' => newNewYork
- 'Daily' => newDaily
- 'Weekly' => newWeekly
- 'Monthly' => newMonthly
- 'Yearly' => newYearly
- 'Quarterly' => newQuarterly
- => newDaily
- zoneEnd = switch sessionType
- 'Tokyo' => endTokyo
- 'London' => endLondon
- 'New York' => endNewYork
- 'Daily' => newDaily
- 'Weekly' => newWeekly
- 'Monthly' => newMonthly
- 'Yearly' => newYearly
- 'Quarterly' => newQuarterly
- => newDaily
- isForex = showFx
- //Re calculating and drawing zones
- calcSession(zoneEnd or (barstate.islast and showCur))
- drawNewZone(zoneEnd)
- drawCurZone(barstate.islast and not zoneEnd and showCur and activeZone, zoneEnd)
- //Reseting profie at start of new zone
- resetProfile(newSession)
- //Updating data arrays
- updateIntra(dO, dH, dL, dC, dV)
- //Reseting zone start value
- if zoneEnd
- activeZone := false
- if newSession
- zoneStart := bar_index
- activeZone := true
- if newLondon
- londonStart := bar_index
- if newTokyo
- tokyoStart := bar_index
- if newNewYork
- nyStart := bar_index
- londonHigh = ta.highest(high, bar_index-londonStart+1)
- tokyoHigh = ta.highest(high, bar_index-tokyoStart+1)
- nyHigh = ta.highest(high, bar_index-nyStart+1)
- londonLow = ta.lowest(low, bar_index-londonStart+1)
- tokyoLow = ta.lowest(low, bar_index-tokyoStart+1)
- nyLow = ta.lowest(low, bar_index-nyStart+1)
- if endLondon and isForex
- drawForexBox(londonStart, 'London', londonHigh, londonLow)
- if endNewYork and isForex
- drawForexBox(nyStart, 'New York', nyHigh, nyLow)
- if endTokyo and isForex
- drawForexBox(tokyoStart, 'Tokyo', tokyoHigh, tokyoLow)
Advertisement
Add Comment
Please, Sign In to add comment