Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Source Code

// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © KioseffTrading

//@version=6
indicator("Multi Timeframe Volume Profiles [TradingIQ]", overlay = true, max_boxes_count = 500, max_labels_count = 500, max_lines_count = 500, max_polylines_count = 100)

enum modelType 

    regVP   = "Traditional Volume Profile"
    deltaVP = "Delta Profile"

model          = input.enum(defval = modelType.regVP, title = "Model", options = [modelType.regVP, modelType.deltaVP])
rows           = input.int(defval = 20, minval = 5, maxval = 200, title = "Profile Rows")

htfUse1        = input.bool(defval = true, title = "", group = "HTF", inline = "1")
htf1           = input.timeframe("30", "HTF #1", group  = "HTF", inline = "1")
lowerTF1       = input.timeframe(defval = "1", title = "LTF Vol", group = "HTF", inline = "1")

htfUse2        = input.bool(defval = true, title = "", group = "HTF", inline = "2")
htf2           = input.timeframe("60", "HTF #2", group  = "HTF", inline = "2")
lowerTF2       = input.timeframe(defval = "1", title = "LTF Vol", group = "HTF", inline = "2")

htfUse3        = input.bool(defval = true, title = "", group = "HTF", inline = "3")
htf3           = input.timeframe("240", "HTF #3", group  = "HTF", inline = "3")
lowerTF3       = input.timeframe(defval = "1", title = "LTF Vol", group = "HTF", inline = "3")

htfUse4        = input.bool(defval = false, title = "", group = "HTF", inline = "4")
htf4           = input.timeframe("1D", "HTF #4", group  = "HTF", inline = "4")
lowerTF4       = input.timeframe(defval = "1", title = "LTF Vol", group = "HTF", inline = "4")

htfUse5        = input.bool(defval = false, title = "", group = "HTF", inline = "5")
htf5           = input.timeframe("1W", "HTF #5", group  = "HTF", inline = "5")
lowerTF5       = input.timeframe(defval = "1", title = "LTF Vol", group = "HTF", inline = "5")

showVals       = input.bool(defval = false, title = "Show Total Vol/Delta Labels")
upCol          = input.color(defval = #55ffda, title = "Up Color")
dnCol          = input.color(defval = color.rgb(255, 116, 116), title = "Down Color")
pocVaLab       = input.bool(defval = true, title = "POC + VA Labels")
type htfDraw 

    array<box>  body 
    array<line> wick
    array<label> keyPointsLabel 
    array<box>   valsBoxes

type dataStoreLTF

    float H 
    float L 
    float V

type profile 

    array<float>        levels
    array<float>        delta 
    array<float>        buyVol 
    array<float>        sellVol 
    array<float>        totalVol 
    array<dataStoreLTF> LTFvals
    array<chart.point>  normedValsBuy 
    array<chart.point>  normedValsSell
    array<polyline>     regVP 

direction(lowerTF) => 

    if lowerTF == "1T" 

        switch 

            close == bid => -1 
            close == ask =>  1
            =>               math.sign(close - close[1]) 
        
    else 

        math.sign(close - close[1])

method setVals(profile htfProfile, int dnLev, int upLev, float div) => 

    for x = dnLev to upLev

        absDiv = math.abs(div)

        htfProfile.delta   .set(x, htfProfile.delta.get(x) + div)
        htfProfile.totalVol.set(x, htfProfile.totalVol.get(x) + absDiv)

        switch math.sign(div) 

            1  => htfProfile.buyVol .set(x, htfProfile.buyVol .get(x) + absDiv)
            -1 => htfProfile.sellVol.set(x, htfProfile.sellVol.get(x) + absDiv)

method addPoints(array<chart.point> id, bool isBuy, int left, int normBuyEnd, int normSell, float level, float Range, float signedDelta, int right, int normSellEnd) => 

    tempPoints = 
    
     if model == modelType.regVP 

        switch isBuy

            true =>  array.from(
                                chart.point.from_index(left, level), 
                                chart.point.from_index(normBuyEnd, level), 
                                chart.point.from_index(normBuyEnd, level + Range), 
                                chart.point.from_index(left, level + Range)
                                )

            =>       array.from(
                                chart.point.from_index(normBuyEnd, level),
                                chart.point.from_index(normBuyEnd - normSell, level),
                                chart.point.from_index(normBuyEnd - normSell, level + Range),
                                chart.point.from_index(normBuyEnd, level + Range)
                                )

    else if model == modelType.deltaVP 

        switch 

            signedDelta == 1 and isBuy => array.from(

                     chart.point.from_index(left, level), 
                     chart.point.from_index(normBuyEnd, level), 
                     chart.point.from_index(normBuyEnd, level + Range), 
                     chart.point.from_index(left, level + Range)
                    
             )

            signedDelta == -1 and not isBuy => array.from(

                     chart.point.from_index(right, level), 
                     chart.point.from_index(normSellEnd, level), 
                     chart.point.from_index(normSellEnd, level + Range), 
                     chart.point.from_index(right, level + Range)
                    
             )      

            isBuy => array.from(chart.point.from_index(left, level), chart.point.from_index(left, level + Range))
            =>       array.from(chart.point.from_index(right, level), chart.point.from_index(right, level + Range))

    id.concat(tempPoints)

formTF(string HTF) =>

    isH   = str.contains(HTF, "D") or str.contains(HTF, "W") or str.contains(HTF, "M")
    tfNum = str.tonumber(HTF)

    switch 

        isH   => HTF 
        tfNum >= 60 and tfNum % 60 == 0 => str.tostring(tfNum / 60) + "h"
        =>       HTF

getHTFvals(HTF, left, right, lowerTF, use) => 

    if use

        var htfO = open, var htfH = high, var htfL = low, var htfT = time  

        var htfProfile  = profile.new(array.new<float>(rows), array.new<float>(rows, 0), array.new<float>(rows, 0), array.new<float>(rows, 0), array.new<float>(rows, 0), array.new<dataStoreLTF>(), array.new<chart.point>(), array.new<chart.point>(), array.new<polyline>(2))
        var miniProfile = profile.new(array.new<float>(20), array.new<float>(20, 0), array.new<float>(20, 0), array.new<float>(20, 0), array.new<float>(20, 0), array.new<dataStoreLTF>(), array.new<chart.point>(), array.new<chart.point>(), array.new<polyline>(2))

        [ltfH, ltfL, ltfV] = request.security_lower_tf("", lowerTF, [high, low, volume * direction(lowerTF)]) 


        if timeframe.change(HTF)

            htfO := open 
            htfH := high 
            htfL := low 
            htfT := time

        lastTime = last_bar_time - htfT <= timeframe.in_seconds(HTF) * 1000

        htfH := math.max(high, htfH)
        htfL := math.min(low, htfL)

        if lastTime

            Range   = (htfH - htfL) / rows
            Range20 = (htfH - htfL) / 20

            if timeframe.change(HTF)

                htfProfile.LTFvals.clear()

            if ltfH.size() > 0 

                for [i, data] in ltfV
    
                    htfProfile.LTFvals.push(dataStoreLTF.new(ltfH.get(i), ltfL.get(i), data))
            
            var dLevels = htfDraw.new(array.new<box>(), array.new<line>(), array.new<label>(), array.new<box>())

            if barstate.islast 

                bodySize = dLevels.body.size(), wickSize = dLevels.wick.size(), labelSize = dLevels.keyPointsLabel.size()
                valsBoxSize = dLevels.valsBoxes.size()

                if bodySize > 0 
                    for i = 0 to bodySize - 1
                        dLevels.body.shift().delete()

                if wickSize > 0 
                    for i = 0 to wickSize - 1
                        dLevels.wick.shift().delete()

                if labelSize > 0 
                    for i = 0 to labelSize - 1
                        dLevels.keyPointsLabel.shift().delete()

                if valsBoxSize > 0 
                    for i = 0 to valsBoxSize - 1 
                        dLevels.valsBoxes.shift().delete()

                for polys in htfProfile.regVP
                    polys.delete()

                htfProfile.normedValsBuy .clear()
                htfProfile.normedValsSell.clear()

                for i = 0 to rows - 1

                    htfProfile.levels.set(i, htfL + Range * i)

                for data in htfProfile.LTFvals

                    upLev = htfProfile.levels.binary_search_leftmost(data.H)
                    dnLev = htfProfile.levels.binary_search_leftmost(data.L)

                    div = data.V / (math.abs(upLev - dnLev) + 1)

                    htfProfile.setVals(dnLev, upLev, div)

                deltaAbs = htfProfile.delta.abs()

                [min, max] = switch model 

                    modelType.regVP   => [math.min(htfProfile.buyVol.min(), htfProfile.sellVol.min()), math.max(htfProfile.buyVol.max(), htfProfile.sellVol.max())]
                    modelType.deltaVP => [deltaAbs.min(), deltaAbs.max()]

                if model == modelType.regVP
                    htfProfile.normedValsSell.push(chart.point.from_index(left, htfL))

                for i = 0 to rows - 1

                    getBuy = htfProfile.buyVol.get(i), getSell  = htfProfile.sellVol.get(i)
                    level  = htfProfile.levels.get(i), getDelta = htfProfile.delta.get(i)

                    normBuy   = math.round(1 + (((getBuy - min) / (max - min)) * 15))
                    normSell  = math.round(1 + (((getSell - min) / (max - min)) * 15))
                    normDelta = math.round(1 + (((deltaAbs.get(i) - min) / (max - min)) * 15))

                    normBuyEnd  = switch model 

                        modelType.regVP   => left - normBuy
                        modelType.deltaVP => left - normDelta

                    normSellEnd = right + 2 + normDelta 
                    signedDelta = math.sign(getDelta)

                    htfProfile.normedValsBuy .addPoints(true, left, normBuyEnd, normSell, level, Range, signedDelta, right + 2, normSellEnd)
                    htfProfile.normedValsSell.addPoints(false, left, normBuyEnd, normSell, level, Range, signedDelta, right + 2, normSellEnd)

                    getTotalVol = htfProfile.totalVol.get(i)

                    if close >= level and close <= level + Range 

                        xPoint = switch model 
                            
                            modelType.regVP => normBuyEnd - normSell - 2
                            =>                 normBuyEnd - 2

                        dLevels.keyPointsLabel.push(label.new(xPoint, math.avg(level, level + Range), style = label.style_label_center, color = #00000000, textcolor = chart.fg_color, size = size.tiny, text = "●"))


                if showVals

                    for i = 0 to 19

                        miniProfile.levels.set(i, htfL + Range * i)

                    for data in htfProfile.LTFvals

                        upLev = miniProfile.levels.binary_search_leftmost(data.H)
                        dnLev = miniProfile.levels.binary_search_leftmost(data.L)

                        divMini = data.V / (math.abs(upLev - dnLev) + 1)

                        miniProfile.setVals(dnLev, upLev, divMini)

                    deltaAbsMini = miniProfile.delta.abs()

                    [minMini, maxMini] = switch model 

                        modelType.regVP   => [math.min(miniProfile.buyVol.min(), miniProfile.sellVol.min()), math.max(miniProfile.buyVol.max(), miniProfile.sellVol.max())]
                        modelType.deltaVP => [deltaAbsMini.min(), deltaAbsMini.max()]

                    if model == modelType.regVP
                        miniProfile.normedValsSell.push(chart.point.from_index(left, htfL))

                    for i = 0 to rows - 1

                        level  = miniProfile.levels.get(i)

                        seriesShow = switch model

                            modelType.regVP   => miniProfile.totalVol.get(i)
                            modelType.deltaVP => miniProfile.delta.get(i)

                        colNow = switch 

                            model == modelType.regVP =>                                  chart.fg_color
                            model == modelType.deltaVP and math.sign(seriesShow) == 1 => upCol
                            =>                                                           dnCol

                        dLevels.valsBoxes.push(box.new(left + 1, level, right + 1, level + Range20, text = str.tostring(seriesShow, format.volume), bgcolor = #00000000, text_color = colNow, border_color = #00000000))


                if model == modelType.regVP
                    htfProfile.normedValsSell.push(chart.point.from_index(left, htfH))

                htfProfile.regVP.set(1, polyline.new(htfProfile.normedValsSell, line_color = chart.bg_color, line_width = 1, fill_color = color.new(dnCol, 20)))
                htfProfile.regVP.set(0, polyline.new(htfProfile.normedValsBuy , line_color = chart.bg_color, line_width = 1, fill_color = color.new(upCol, 20)))

                pocIndex    = htfProfile.totalVol.indexof(htfProfile.totalVol.max())
                getPOClevel = htfProfile.levels.get(pocIndex)

                indexUp = pocIndex, indexDn = pocIndex, sum = 0.0

                target  = htfProfile.totalVol.sum() * 0.7

                loopSize = htfProfile.levels.size() 

                for i = 0 to loopSize - 1

                    if indexUp == indexDn 

                        sum     += htfProfile.totalVol.get(indexUp)
                        indexUp += 1
                        indexDn -= 1
                        if sum >= target 
                            break 

                    else 

                        if indexUp < loopSize 

                            sum     += htfProfile.totalVol.get(indexUp)
                            indexUp += 1
                            if sum >= target 
                                break

                        if indexDn > -1 

                            sum     += htfProfile.totalVol.get(indexDn) 
                            indexDn -= 1
                            if sum >= target 
                                break 
                            
                getVAHlevel = htfProfile.levels.get(math.min(indexUp, loopSize - 1))
                getVALlevel = htfProfile.levels.get(math.max(indexDn, 0))

                if pocVaLab

                    dLevels.keyPointsLabel.push(label.new(right + 8, math.avg(getPOClevel, getPOClevel + Range), style = label.style_label_center, color = #00000000, text = "◂ POC", textcolor = chart.fg_color, size = size.small))
                    dLevels.keyPointsLabel.push(label.new(right + 8, math.avg(getVAHlevel, getVAHlevel + Range), style = label.style_label_center, color = #00000000, text = "◂ VAH", textcolor = chart.fg_color, size = size.small))
                    dLevels.keyPointsLabel.push(label.new(right + 8, math.avg(getVALlevel, getVALlevel + Range), style = label.style_label_center, color = #00000000, text = "◂ VAL", textcolor = chart.fg_color, size = size.small))

                bodyCol = switch

                    close >= htfO => upCol
                    =>               dnCol

                mid = math.round(math.avg(left + 1, right + 1))

                dLevels.body.push(box.new(left + 1, math.max(close, htfO), right + 1, math.min(close, htfO), border_color = chart.bg_color, border_width = 1,bgcolor = color.new(bodyCol, showVals ? 80 : 0)))
                dLevels.wick.push(line.new(mid, htfH, mid, math.max(htfO, close) , color = color.gray, width = 2))
                dLevels.wick.push(line.new(mid, htfL, mid, math.min(htfO, close), color = color.gray, width = 2))
                dLevels.keyPointsLabel.push(label.new(mid, htfProfile.levels.last() + Range, style = label.style_label_down, color = #00000000, text = formTF(HTF), textcolor = chart.fg_color, size = size.small))

                y1 = math.max(close, htfO)
                y2 = math.min(close, htfO)

                pointArr = array.from(
                    chart.point.from_index(left  + 1, y1), 
                    chart.point.from_index(right + 1, y1), 
                    chart.point.from_index(left  + 1, y2), 
                    chart.point.from_index(right + 1, y2)  
                )



getHTFvals(htf1, bar_index + 49, bar_index + 59, lowerTF1, htfUse1)
getHTFvals(htf2, bar_index + 109, bar_index + 119, lowerTF2, htfUse2)
getHTFvals(htf3, bar_index + 169, bar_index + 179, lowerTF3, htfUse3)
getHTFvals(htf4, bar_index + 229, bar_index + 239, lowerTF4, htfUse4)
getHTFvals(htf5, bar_index + 289, bar_index + 299, lowerTF5, htfUse5)