• Profile picture of W

    W posted in the group AI Trading Lab

    2 June 2026 13:34

    PineScript for TradingView
    Volume Profile

    // 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(“Volume Profile with Node Detection [LuxAlgo]”, “LuxAlgo – Volume Profile with Node Detection”, overlay = true, max_boxes_count = 500, max_bars_back = 5000)

    //———————————————————————————————————————
    // Settings
    //———————————————————————————————————————{

    display = display.all – display.status_line

    vn_volumeNodesGroup = ‘Volume Nodes’

    vn_peakTTip = ‘A volume peak node is recognized when the volume profile nodes for the N preceding and N succeeding nodes are lower than that of the evaluated one, where N is determined by the \’Node Detection Percent %\’ option’
    vn_peaksShow = input.string(‘Peaks’, ‘Volume Peaks’, options = [‘Peaks’, ‘Clusters’, ‘None’], inline = ‘vnP’, tooltip = vn_peakTTip, group = vn_volumeNodesGroup, display = display)
    vn_peakVolumeColor = input.color(color.new(color.blue, 50), ”, inline = ‘vnP’, group = vn_volumeNodesGroup)
    vn_peaksNumberOfNodes = input.int(9, ‘  Node Detection Percent %’, minval = 0, maxval = 100, group = vn_volumeNodesGroup, display = display) / 100
    vn_peaksShow := vn_peaksNumberOfNodes == 0 ? ‘None’ : vn_peaksShow

    vn_troughsTTip = ‘A volume trough node is recognized when the volume profile nodes for the N preceding and N succeeding nodes exceed that of the evaluated one, where N is determined by the \’Node Detection Percent %\’ option’
    vn_troughsShow = input.string(‘None’, ‘Volume Troughs’, options = [‘Troughs’, ‘Clusters’, ‘None’], inline = ‘vnT’, tooltip = vn_troughsTTip, group = vn_volumeNodesGroup, display = display)
    vn_troughVolumeColor = input.color(color.new(color.gray, 50), ”, inline = ‘vnT’, group = vn_volumeNodesGroup)
    vn_troughsNumberOfNodes = input.int(7, ‘  Node Detection Percent %’, minval = 0, maxval = 100, group = vn_volumeNodesGroup, display = display) / 100
    vn_troughsShow := vn_troughsNumberOfNodes == 0 ? ‘None’ : vn_troughsShow

    vn_thresholdTTip = ‘A threshold value specified as a percentage is utilized to detect peak/trough volume nodes. If a value is set, the detection will disregard volume node values lower than the specified threshold.’
    vn_VolumeNodeThreshold = input.int(1, ‘Volume Node Threshold %’, minval = 0, maxval = 100, tooltip = vn_thresholdTTip, group = vn_volumeNodesGroup, display = display) / 100

    vn_highestNVolumeNodes = input.int(0, ‘Highest Volume Nodes’, minval = 0, maxval = 31, inline = ‘vnL’, group = vn_volumeNodesGroup, display = display)
    vn_highestVolumeColor = input.color(color.new(color.orange, 25), ”, inline = ‘vnL’, group = vn_volumeNodesGroup)

    vn_lowestNVolumeNodes = input.int(0, ‘Lowest Volume Nodes’, minval = 0, maxval = 31, inline = ‘vnH’, group = vn_volumeNodesGroup, display = display)
    vn_lowestVolumeColor = input.color(color.new(color.navy, 25), ”, inline = ‘vnH’, group = vn_volumeNodesGroup)

    vp_componentsGroup = ‘Volume Profile – Components’

    vp_profileShow = input.bool(true, ‘Volume Profile’, inline = ‘vp’, group = vp_componentsGroup)
    vp_profileGradientColors = input.string(‘Gradient Colors’, ”, options = [‘Gradient Colors’, ‘Classic Colors’ ], inline = ‘vp’, group = vp_componentsGroup)
    vp_valueAreaUpColor = input.color(color.new(#2962ff, 30), ‘  Value Area Up / Down’, inline = ‘VA’, group = vp_componentsGroup)
    vp_valueAreaDwonColor = input.color(color.new(#fbc02d, 30), ‘/’, inline = ‘VA’, group = vp_componentsGroup)
    vp_profileUpVolumeColor = input.color(color.new(#5d606b, 50), ‘  Profile Up / Down Volume’, inline = ‘VP’, group = vp_componentsGroup)
    vp_profileDownVolumeColor = input.color(color.new(#d1d4dc, 50), ‘/’, inline = ‘VP’, group = vp_componentsGroup)

    vp_pocShow = input.string(‘None’, ‘Point of Control’, options = [‘Developing’, ‘Regular’, ‘None’], inline = ‘poc’, group = vp_componentsGroup, display = display)
    vp_pocColor = input.color(#fbc02d, ”, inline = ‘poc’, group = vp_componentsGroup)
    vp_pocWidth = input.int(2, ‘Width’, inline = ‘poc’, group = vp_componentsGroup, display = display)

    vp_vahShow = input.bool(false, ‘Value Area High (VAH)’, inline = ‘vah’, group = vp_componentsGroup)
    vp_vahColor = input.color(#2962ff, ”, inline = ‘vah’, group = vp_componentsGroup)

    vp_valShow = input.bool(false, ‘Value Area Low (VAL)’, inline = ‘val’, group = vp_componentsGroup)
    vp_valColor = input.color(#2962ff, ”, inline = ‘val’, group = vp_componentsGroup)

    vp_profileLevels = input.string(‘Small’, “Profile Price Labels”, options=[‘Tiny’, ‘Small’, ‘Normal’, ‘None’], group = vp_componentsGroup, display = display)

    vp_displayGroup = ‘Volume Profile – Display Settings’

    vp_profileLength = input.int(360, ‘Profile Lookback Length’, minval = 10, maxval = 5000, step = 10, group = vp_displayGroup, display = display)
    vp_profileLength:= last_bar_index < vp_profileLength ? last_bar_index : vp_profileLength – 1

    vp_valueAreaThreshold = input.float(70, 'Value Area (%)', minval = 0, maxval = 100, group = vp_displayGroup, display = display) / 100

    vp_profilePlracment = input.string('Right', 'Profile Placement', options = ['Right', 'Left'], group = vp_displayGroup, display = display), profilePlacementRight = vp_profilePlracment == 'Right'
    vp_profileNumberOfRows = input.int(100, 'Profile Number of Rows' , minval = 30, maxval = 130 , step = 10, group = vp_displayGroup, display = display)
    vp_profileWidth = input.float(31, 'Profile Width', minval = 0, maxval = 250, group = vp_displayGroup, display = display) / 100
    vp_profileHorizontalOffset = input.int(13, 'Profile Horizontal Offset', maxval = 50, group = vp_displayGroup, display = display)

    vp_valueAreaBackground = input.bool(false, 'Value Area Background  ', inline = 'vBG', group = vp_displayGroup)
    vp_valueAreaBackgroundColor = input.color(color.new(#2962ff, 89), '', inline = 'vBG', group = vp_displayGroup)

    vp_profileBackground = input.bool(false, 'Profile Range Background ', inline = 'pBG', group = vp_displayGroup)
    vp_profileBackgroundColor = input.color(color.new(#2962ff, 95), '', inline = 'pBG', group = vp_displayGroup)

    //———————————————————————————————————————}
    // User Defined Types
    //———————————————————————————————————————{

    type BAR
    float open = open
    float high = high
    float low = low
    float close = close
    float volume = volume
    int index = bar_index

    type barData
    float [] barHigh
    float [] barLow
    float [] barVolume
    bool [] barPolarity
    int [] barCount

    type volumeData
    float [] totalVolume
    float [] bullishVolume
    float [] bearishVolume
    int [] endProfileIndex
    bool [] peakVolume
    bool [] troughVolume

    type volumeProfile
    box [] boxes
    chart.point [] pocPoints
    polyline pocPolyline
    int pocLevel
    int vahLevel
    int valLevel
    int startIndex

    //———————————————————————————————————————}
    // Variables
    //———————————————————————————————————————{

    BAR bar = BAR.new()
    BAR [] ltfBarData = array.new (1, BAR.new())

    var barData barDataArray = barData.new(
    array.new (na),
    array.new (na),
    array.new (na),
    array.new (na),
    array.new (na)
    )

    volumeData volumeDataArray = volumeData.new(
    array.new (vp_profileNumberOfRows, 0.),
    array.new (vp_profileNumberOfRows, 0.),
    array.new (vp_profileNumberOfRows, 0.),
    array.new (vp_profileNumberOfRows, 0 ),
    array.new (vp_profileNumberOfRows, 0.),
    array.new (vp_profileNumberOfRows, 0.)
    )

    var volumeProfile VP = volumeProfile.new(
    array.new (na),
    array.new (na),
    polyline.new (na), na, na, na, na
    )

    var float highestPrice = na
    var float lowestPrice = na

    //———————————————————————————————————————}
    // Functions / Methods
    //———————————————————————————————————————{

    renderLine(_x1, _y1, _x2, _y2, _xloc, _extend, _color, _style, _width) =>
    var id = line.new(_x1, _y1, _x2, _y2, _xloc, _extend, _color, _style, _width)
    line.set_xy1(id, _x1, _y1)
    line.set_xy2(id, _x2, _y2)
    line.set_color(id, _color)

    renderLabel(_x, _y, _text, _color, _style, _textcolor, _size, _tooltip) =>
    var lb = label.new(_x, _y, _text, xloc.bar_index, yloc.price, _color, _style, _textcolor, _size, text.align_left, _tooltip)
    lb.set_xy(_x, _y)
    lb.set_text(_text)
    lb.set_tooltip(_tooltip)
    lb.set_textcolor(_textcolor)

    requestBarData(_lowerTimeframe) => request.security_lower_tf(syminfo.tickerid, _lowerTimeframe, BAR.new(), ignore_invalid_timeframe = true)

    calculateTimeframe(_depth) =>
    int tfInMs = timeframe.in_seconds(timeframe.period)
    int mInMS = 60

    if _depth == 2
    switch
    tfInMs ‘1S’
    tfInMs ‘5S’

    tfInMs ‘1’
    tfInMs ‘5’
    tfInMs ’15’
    tfInMs ’60’
    => ‘D’

    else if _depth == 1
    switch
    tfInMs ‘1S’
    tfInMs ‘5S’
    tfInMs ’15S’

    tfInMs ‘1’
    tfInMs ‘5’
    tfInMs ’15’
    tfInMs ’60’
    tfInMs ‘240’
    => ‘D’

    getTextSize(_text) =>
    if _text != ‘None’
    switch _text
    ‘Tiny’ => size.tiny
    ‘Small’ => size.small
    ‘Normal’ => size.normal
    => size.auto

    //———————————————————————————————————————}
    // Calculations – Volume Profile
    //———————————————————————————————————————{

    profileLevesSize = getTextSize(vp_profileLevels)

    if bar.index == last_bar_index – vp_profileLength
    VP.startIndex := bar.index
    lowestPrice := bar.low
    highestPrice := bar.high
    else if bar.index > last_bar_index – vp_profileLength
    lowestPrice := math.min(bar.low, lowestPrice)
    highestPrice := math.max(bar.high, highestPrice)

    //if vp_profileLength <= 200
    // ltfBarData := requestBarData(calculateTimeframe(2))
    //else
    if vp_profileLength <= 700
    ltfBarData := requestBarData(calculateTimeframe(2))
    else
    ltfBarData := array.new (1, BAR.new(bar.open, bar.high, bar.low, bar.close, bar.volume))

    if barstate.ishistory and (bar.index >= last_bar_index – vp_profileLength) and bar.index 0

    log.info(“yaz_kizim {0} {1}”, ltfBarData.get(0).volume, na(nz(ltfBarData.get(0).volume)) )

    if ltfBarData.size() > 0 and not na(nz(ltfBarData.get(0).volume))
    for currentLtfBar = 0 to ltfBarData.size() – 1
    barDataArray.barHigh.push(ltfBarData.get(currentLtfBar).high)
    barDataArray.barLow.push(ltfBarData.get(currentLtfBar).low)
    barDataArray.barVolume.push(ltfBarData.get(currentLtfBar).volume)
    barDataArray.barPolarity.push(ltfBarData.get(currentLtfBar).close > ltfBarData.get(currentLtfBar).open)

    barDataArray.barCount.push(ltfBarData.size())

    priceStep = (highestPrice – lowestPrice) / vp_profileNumberOfRows

    if barstate.islast and ltfBarData.size() > 0 // barDataArray.barVolume.size() > 0 //

    if VP.boxes.size() > 0
    for boxIndex = 0 to VP.boxes.size() – 1
    box.delete(VP.boxes.shift())

    if barDataArray.barCount.size() > vp_profileLength
    barCount = barDataArray.barCount.shift()
    for barCountIndex = 0 to barCount – 1
    barDataArray.barHigh.shift()
    barDataArray.barLow.shift()
    barDataArray.barVolume.shift()
    barDataArray.barPolarity.shift()

    VP.pocPoints.clear()
    VP.pocPolyline.delete()

    if ltfBarData.size() > 0 and not na(nz(ltfBarData.get(0).volume))
    for currentLtfBar = 0 to ltfBarData.size() – 1
    barDataArray.

About Me

Futures Speculator in Slippers

Chief Financial Analyst at home, as never seen on Forbes or CNBC.

Discovered trading in 2006. Took me exactly 20 years of pain, market cycles, and weight gain to finally go full-time in 2026. And, well, normal jobs feel suspicious.

If I'm going to stay depressed inside my appartment, I might as well get paid for it.

Don't contact me unless it's about why NQ made that 100 point move without any reason.

Groups

Group logo of Review & Refine
Review & Refine
Public Group
Group logo of Trade
Trade
Public Group