Page 63 of 66

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Tue May 13, 2025 10:55 pm
by nwesterhuijs
Came across this one - https://www.tradingview.com/script/qZRH ... nd-Filter/ - and seemed interesting. Used an Mladen's version of Sylvain Vervoort Bollinger Band indie as a basis ("sve bollinger band_main_mtf + alerts.mq4").

It basically counts how many times the price crosses the upper channel or the lower channel. The original version resets when the price crosses the opposite channel. I did add the option to reset the counter to 0 when the middle band is crossed, which could potentially indicate a ranging market. First impression is that this option is better set to FALSE when using "SVE BB" channel.

BB Channel with and without reset when middle band is crossed:

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Wed May 21, 2025 12:39 am
by Starter02
Hi Mr.Tools

Please help.
Do you have an indicator that detects, retrieves, or automatically separates the last 20 candlesticks, excluding the current one?

Thanks in advance

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Wed May 21, 2025 5:07 am
by mrtools
Starter02 wrote: Wed May 21, 2025 12:39 am Hi Mr.Tools

Please help.
Do you have an indicator that detects, retrieves, or automatically separates the last 20 candlesticks, excluding the current one?

Thanks in advance

Hello, don't have one.

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Wed Jun 11, 2025 10:34 am
by iardavan
Hello and regards to everyone,

This LuxAlgo indicator is very useful — especially the way it automatically identifies peak points and draws support and resistance levels based on volume peaks and valleys.

I would really appreciate it if this indicator could be converted to MQL4 so that it can be used in MetaTrader 4.

Also, it would be great if the indicator could be anchored manually, similar to Anchored Volume Profile — allowing the user to move and resize the range between lines, rather than relying on a fixed number of bars.

If you need any additional information to help with the conversion, I am more than happy to provide it.

Thank you very much in advance for making this available for MT4!

https://www.luxalgo.com/library/indicat ... detection/

Main code:

Code: Select all

// 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<BAR> (1, BAR.new())

var barData barDataArray = barData.new(
     array.new <float> (na), 
     array.new <float> (na), 
     array.new <float> (na), 
     array.new <bool>  (na), 
     array.new <int>   (na)
 )

volumeData volumeDataArray = volumeData.new(
     array.new <float> (vp_profileNumberOfRows, 0.), 
     array.new <float> (vp_profileNumberOfRows, 0.), 
     array.new <float> (vp_profileNumberOfRows, 0.),
     array.new <int>   (vp_profileNumberOfRows, 0 ),
     array.new <bool>  (vp_profileNumberOfRows, 0.),
     array.new <bool>  (vp_profileNumberOfRows, 0.)
 )

var volumeProfile VP = volumeProfile.new(
     array.new<box>         (na),
     array.new<chart.point> (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 <                 30  =>  '1S'
            tfInMs <          1 * mInMS  =>  '5S'

            tfInMs <=        15 * mInMS  =>   '1'
            tfInMs <=        60 * mInMS  =>   '5'
            tfInMs <=       240 * mInMS  =>  '15'
            tfInMs <=      1440 * mInMS  =>  '60'
            => 'D'

    else if _depth == 1
        switch
            tfInMs <                 15  =>  '1S'
            tfInMs <                 30  =>  '5S'
            tfInMs <          1 * mInMS  => '15S'

            tfInMs <=         5 * mInMS  =>   '1'
            tfInMs <=        15 * mInMS  =>   '5'
            tfInMs <=        60 * mInMS  =>  '15'
            tfInMs <=       240 * mInMS  =>  '60'
            tfInMs <=      1440 * mInMS  => '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<BAR> (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 < last_bar_index and ltfBarData.size() > 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.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())

    barIndex = vp_profileLength
    numberOfBars = 0
    arraySize = barDataArray.barVolume.size()

    for arrayIndex = 0 to arraySize - 1

        levelHigh = barDataArray.barHigh.get(arrayIndex)
        levelLow = barDataArray.barLow.get(arrayIndex)
        levelVolume = barDataArray.barVolume.get(arrayIndex)

        // Shoutout to @tkarolak for contributing to the code's optimization! Much appreciated.
        
        int startSlotIndex = math.max(math.floor((levelLow - lowestPrice) / priceStep), 0)
        int endSlotIndex = math.min(math.floor((levelHigh - lowestPrice) / priceStep), vp_profileNumberOfRows - 1)
        
        for priceLevelIndex = startSlotIndex to endSlotIndex

            float priceLevel = lowestPrice + priceLevelIndex * priceStep

            volumeProportion = switch
                levelLow >= priceLevel and levelHigh > priceLevel + priceStep => (priceLevel + priceStep - levelLow) / (levelHigh - levelLow)
                levelHigh <= priceLevel + priceStep and levelLow < priceLevel => (levelHigh - priceLevel) / (levelHigh - levelLow)
                levelLow >= priceLevel and levelHigh <= priceLevel + priceStep => 1
                => priceStep / (levelHigh - levelLow)

            volumeDataArray.totalVolume.set(priceLevelIndex, volumeDataArray.totalVolume.get(priceLevelIndex) + levelVolume * volumeProportion)

            if barDataArray.barPolarity.get(arrayIndex)
                volumeDataArray.bullishVolume.set(priceLevelIndex, volumeDataArray.bullishVolume.get(priceLevelIndex) + levelVolume * volumeProportion)

//        priceLevelIndex = 0
//        for priceLevel = lowestPrice to highestPrice - priceStep by priceStep
//
//            if levelHigh >= priceLevel and levelLow < priceLevel + priceStep
//
//                volumeProportion = if levelLow >= priceLevel and levelHigh > priceLevel + priceStep
//                    (priceLevel + priceStep - levelLow) / (levelHigh - levelLow)
//                else if levelHigh <= priceLevel + priceStep and levelLow < priceLevel
//                    (levelHigh - priceLevel) / (levelHigh - levelLow)
//                else if levelLow >= priceLevel and levelHigh <= priceLevel + priceStep
//                    1
//                else
//                    priceStep / (levelHigh - levelLow)
//
//                volumeDataArray.totalVolume.set(priceLevelIndex, volumeDataArray.totalVolume.get(priceLevelIndex) + levelVolume * volumeProportion)
//
//                if barDataArray.barPolarity.get(arrayIndex)
//                    volumeDataArray.bullishVolume.set(priceLevelIndex, volumeDataArray.bullishVolume.get(priceLevelIndex) + levelVolume * volumeProportion)
//            priceLevelIndex += 1

        if vp_pocShow == 'Developing'
            if arrayIndex == barDataArray.barCount.get(vp_profileLength - barIndex)
                VP.pocPoints.push(chart.point.from_index(bar.index[barIndex], math.avg(bar.high[barIndex], bar.low[barIndex])))
                VP.pocPoints.push(chart.point.from_index(bar.index[barIndex] + 1, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max()) + .5) * priceStep))
                numberOfBars += barDataArray.barCount.get(vp_profileLength - barIndex)
                barIndex  -= 1
            else if arrayIndex == (numberOfBars + barDataArray.barCount.get(vp_profileLength - barIndex)) and numberOfBars != 0
                VP.pocPoints.push(chart.point.from_index(bar.index[barIndex] + 1, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max()) + .5) * priceStep))
                numberOfBars += barDataArray.barCount.get(vp_profileLength - barIndex)
                barIndex  -= 1
            else if barIndex == 0
                VP.pocPoints.push(chart.point.from_index(bar.index[barIndex] + 1, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max()) + .5) * priceStep))
                numberOfBars += barDataArray.barCount.get(vp_profileLength - barIndex)

    VP.pocPolyline := polyline.new(VP.pocPoints, false, false, xloc.bar_index, vp_pocColor, color(na), line.style_solid, vp_pocWidth)

    for volumeIndex = 0 to vp_profileNumberOfRows - 1
        bearishVolume = 2 * volumeDataArray.bullishVolume.get(volumeIndex) - volumeDataArray.totalVolume.get(volumeIndex)
        volumeDataArray.bearishVolume.set(volumeIndex, volumeDataArray.bearishVolume.get(volumeIndex) + bearishVolume * (bearishVolume > 0 ? 1 : -1) )

    VP.pocLevel := volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max())
    totalTradedVolume = volumeDataArray.totalVolume.sum() * vp_valueAreaThreshold
    valueAreaVolume = VP.pocLevel != -1 ? volumeDataArray.totalVolume.get(VP.pocLevel) : 0
    VP.vahLevel := VP.pocLevel
    VP.valLevel := VP.pocLevel
    
    while valueAreaVolume < totalTradedVolume
        if VP.valLevel == 0 and VP.vahLevel == vp_profileNumberOfRows - 1
            break

        volumeAbovePOC = 0.
        if VP.vahLevel < vp_profileNumberOfRows - 1 
            volumeAbovePOC := volumeDataArray.totalVolume.get(VP.vahLevel + 1)

        volumeBelowPOC = 0.
        if VP.valLevel > 0
            volumeBelowPOC := volumeDataArray.totalVolume.get(VP.valLevel - 1)
        
        if volumeBelowPOC == 0 and volumeAbovePOC == 0
            break

        if volumeAbovePOC >= volumeBelowPOC
            valueAreaVolume  += volumeAbovePOC
            VP.vahLevel += 1
        else
            valueAreaVolume  += volumeBelowPOC
            VP.valLevel -= 1

    vahPrice = lowestPrice + (VP.vahLevel + 1.) * priceStep
    pocPrice = lowestPrice + (VP.pocLevel + .5) * priceStep
    valPrice = lowestPrice + (VP.valLevel + .0) * priceStep

    profilePlottingLength = vp_profileLength > 360 ? 360 : vp_profileLength
    profileWidth = profilePlottingLength * vp_profileWidth
    profileHorizontalOffset = int(profileWidth + vp_profileHorizontalOffset)

    if vp_profileShow and profilePlacementRight and vp_pocShow == 'Developing'
        renderLine(last_bar_index, pocPrice, profileHorizontalOffset + int(last_bar_index - profileWidth + 1), pocPrice, xloc.bar_index, extend.none, vp_pocColor, line.style_solid, vp_pocWidth)

    if vp_vahShow
        renderLine(VP.startIndex, vahPrice, 
                   profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                   vahPrice, xloc.bar_index, extend.none, vp_vahColor, line.style_solid, 1)
    
    if vp_pocShow == 'Regular'
        renderLine(VP.startIndex, pocPrice, profilePlacementRight ? vp_profileShow ? profileHorizontalOffset + int(last_bar_index - profileWidth + 1) : last_bar_index : last_bar_index, pocPrice, xloc.bar_index, extend.none, vp_pocColor, line.style_solid, vp_pocWidth)

    if vp_valShow
        renderLine(VP.startIndex, valPrice, 
                   profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                   valPrice, xloc.bar_index, extend.none, vp_valColor, line.style_solid, 1)

    if vp_valueAreaBackground
        VP.boxes.push(box.new(VP.startIndex, valPrice, last_bar_index, vahPrice, vp_valueAreaBackgroundColor, 1, line.style_dotted, bgcolor = vp_valueAreaBackgroundColor))

    if vp_profileBackground
        VP.boxes.push(box.new(VP.startIndex, lowestPrice, last_bar_index, highestPrice, vp_profileBackgroundColor, 1, line.style_dotted, bgcolor = vp_profileBackgroundColor))

    if vp_profileLevels != 'None' and VP.pocLevel != -1 
        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : vp_profileShow ? VP.startIndex : last_bar_index, 
                     highestPrice, str.tostring(highestPrice, format.mintick), color.new(chart.fg_color, 89), label.style_label_down, chart.fg_color, profileLevesSize, 'Profile High')

        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                     vahPrice, str.tostring(vahPrice, format.mintick), color.new(vp_vahColor, 89), label.style_label_left, vp_vahColor, profileLevesSize, 'Value Area High')

        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                     pocPrice, str.tostring(pocPrice, format.mintick), color.new(vp_pocColor, 89), label.style_label_left, vp_pocColor, profileLevesSize, 'Point of Control')
 
        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                     valPrice, str.tostring(valPrice, format.mintick), color.new(vp_valColor, 89), label.style_label_left, vp_valColor, profileLevesSize, 'Value Area Low')

        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : vp_profileShow ? VP.startIndex : last_bar_index, 
                     lowestPrice, str.tostring(lowestPrice, format.mintick), color.new(chart.fg_color, 89), label.style_label_up, chart.fg_color, profileLevesSize, 'Profile Low')

    for volumeNodeLevel = 0 to vp_profileNumberOfRows - 1
    
        if vp_profileShow
            if vp_profileGradientColors == 'Gradient Colors'
                vp_valueAreaUpColor       := color.from_gradient(volumeDataArray.totalVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max(), 0, 1, color.new(vp_valueAreaUpColor      , 95), color.new(vp_valueAreaUpColor      , 0))  
                vp_valueAreaDwonColor     := color.from_gradient(volumeDataArray.totalVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max(), 0, 1, color.new(vp_valueAreaDwonColor    , 95), color.new(vp_valueAreaDwonColor    , 0))  
                vp_profileUpVolumeColor   := color.from_gradient(volumeDataArray.totalVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max(), 0, 1, color.new(vp_profileUpVolumeColor  , 95), color.new(vp_profileUpVolumeColor  , 0))  
                vp_profileDownVolumeColor := color.from_gradient(volumeDataArray.totalVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max(), 0, 1, color.new(vp_profileDownVolumeColor, 95), color.new(vp_profileDownVolumeColor, 0))  
 
            startProfileIndex = profilePlacementRight ? 
                                 profileHorizontalOffset + int(last_bar_index - volumeDataArray.bullishVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max() * profileWidth) :
                                 VP.startIndex
            endProfileIndex   = profilePlacementRight ? 
                                 profileHorizontalOffset + last_bar_index : 
                                 int(startProfileIndex + volumeDataArray.bullishVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max() * profileWidth)

            VP.boxes.push(box.new(startProfileIndex, lowestPrice + (volumeNodeLevel + .1) * priceStep, endProfileIndex, lowestPrice + (volumeNodeLevel + .9) * priceStep, 
                                   color(na), bgcolor = volumeNodeLevel >= VP.valLevel and volumeNodeLevel <= VP.vahLevel ? vp_valueAreaUpColor : vp_profileUpVolumeColor))

            startProfileIndex := profilePlacementRight ? startProfileIndex : endProfileIndex
            endProfileIndex   := profilePlacementRight ? 
                                 startProfileIndex - int( (volumeDataArray.totalVolume.get(volumeNodeLevel) - volumeDataArray.bullishVolume.get(volumeNodeLevel)) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                 startProfileIndex + int( (volumeDataArray.totalVolume.get(volumeNodeLevel) - volumeDataArray.bullishVolume.get(volumeNodeLevel)) / volumeDataArray.totalVolume.max() * profileWidth)

            VP.boxes.push(box.new(startProfileIndex, lowestPrice + (volumeNodeLevel + .1) * priceStep, endProfileIndex, lowestPrice + (volumeNodeLevel + .9) * priceStep, 
                                   color(na), bgcolor = volumeNodeLevel >= VP.valLevel and volumeNodeLevel <= VP.vahLevel ? vp_valueAreaDwonColor : vp_profileDownVolumeColor))
            volumeDataArray.endProfileIndex.set(volumeNodeLevel, endProfileIndex)

    if  vn_peaksShow != 'None' or  vn_troughsShow != 'None'
        var int startVolumeNodeIndex = na, var int endVolumeNodeIndex = na
        var bool peakUpperNth = na, var bool peakLowerNth = na

        peaksNumberOfNodes = int(vp_profileNumberOfRows * vn_peaksNumberOfNodes)

        tempPeakTotalVolume = volumeDataArray.totalVolume.copy()

        for index = 1 to peaksNumberOfNodes
            tempPeakTotalVolume.unshift(0.)
            tempPeakTotalVolume.push(0.)

        for volumeNodeLevel = 0 to vp_profileNumberOfRows - 1 + 2 * peaksNumberOfNodes 

            if vn_peaksShow != 'None' and volumeNodeLevel >= 2 * peaksNumberOfNodes 

                for currentVolumeNode = volumeNodeLevel - 2 * peaksNumberOfNodes to volumeNodeLevel - peaksNumberOfNodes - 1
                    if tempPeakTotalVolume.get(volumeNodeLevel - peaksNumberOfNodes) <= tempPeakTotalVolume.get(currentVolumeNode)
                        peakUpperNth := false
                        break
                    else
                        peakUpperNth := true

                for currentVolumeNode = volumeNodeLevel - peaksNumberOfNodes + 1 to volumeNodeLevel
                    if tempPeakTotalVolume.get(volumeNodeLevel - peaksNumberOfNodes) <= tempPeakTotalVolume.get(currentVolumeNode)
                        peakLowerNth := false
                        break
                    else
                        peakLowerNth := true

                if peakUpperNth and peakLowerNth and tempPeakTotalVolume.get(volumeNodeLevel - peaksNumberOfNodes) / tempPeakTotalVolume.max() > vn_VolumeNodeThreshold

                    startVolumeNodeIndex := vp_profileShow ? profilePlacementRight ? 
                                                             VP.startIndex : 
                                                             volumeDataArray.endProfileIndex.get(volumeNodeLevel - 2 * peaksNumberOfNodes) : //VP.startIndex + int(volumeDataArray.totalVolume.get(volumeNodeLevel - vn_peaksNumberOfNodes) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                             VP.startIndex

                    endVolumeNodeIndex   := vp_profileShow ? profilePlacementRight ? 
                                                             volumeDataArray.endProfileIndex.get(volumeNodeLevel - 2 * peaksNumberOfNodes) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(volumeNodeLevel - vn_peaksNumberOfNodes) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                             last_bar_index : 
                                             last_bar_index

                    vn_peakVolumeColor := vn_peaksShow == 'Peaks' ? vn_peakVolumeColor : color.from_gradient(tempPeakTotalVolume.get(volumeNodeLevel - peaksNumberOfNodes) / tempPeakTotalVolume.max(), 0, 1, color.new(vn_peakVolumeColor, 95), color.new(vn_peakVolumeColor, 65))  

                    VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (volumeNodeLevel - 2 * peaksNumberOfNodes + .1) * priceStep, endVolumeNodeIndex, lowestPrice + (volumeNodeLevel - 2 * peaksNumberOfNodes + .9) * priceStep, 
                                          color(na), bgcolor = vn_peakVolumeColor))

                    if vn_peaksShow == 'Clusters'

                        for currentVolumeNode = volumeNodeLevel - 2 * peaksNumberOfNodes to volumeNodeLevel

                            if currentVolumeNode >= peaksNumberOfNodes and currentVolumeNode <= vp_profileNumberOfRows - 1 + peaksNumberOfNodes
                                if not volumeDataArray.peakVolume.get(currentVolumeNode - peaksNumberOfNodes)

                                    startVolumeNodeIndex := vp_profileShow ? profilePlacementRight ? 
                                                                         VP.startIndex : 
                                                                         volumeDataArray.endProfileIndex.get(currentVolumeNode - peaksNumberOfNodes) : //VP.startIndex + int(volumeDataArray.totalVolume.get(currentVolumeNode) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                         VP.startIndex

                                    endVolumeNodeIndex   := vp_profileShow ? profilePlacementRight ? 
                                                                         volumeDataArray.endProfileIndex.get(currentVolumeNode - peaksNumberOfNodes) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(currentVolumeNode) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                                         last_bar_index : 
                                                         last_bar_index

                                    VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (currentVolumeNode - peaksNumberOfNodes + .0) * priceStep, endVolumeNodeIndex, lowestPrice + (currentVolumeNode - peaksNumberOfNodes + 1.) * priceStep, 
                                                       color(na), bgcolor = vn_peakVolumeColor))
                                    volumeDataArray.peakVolume.set(currentVolumeNode - peaksNumberOfNodes, true)

        tempPeakTotalVolume.clear()

        var bool troughUpperNth = na, var bool troughLowerNth = na
        troughsNumberOfNodes = int(vp_profileNumberOfRows * vn_troughsNumberOfNodes)

        tempTroughTotalVolume = volumeDataArray.totalVolume.copy()

        for index = 1 to troughsNumberOfNodes
            tempTroughTotalVolume.unshift(volumeDataArray.totalVolume.max())
            tempTroughTotalVolume.push(volumeDataArray.totalVolume.max())
            
        for volumeNodeLevel = 0 to vp_profileNumberOfRows - 1 + 2 * troughsNumberOfNodes 

            if vn_troughsShow != 'None' and volumeNodeLevel >= 2 * troughsNumberOfNodes 

                for currentVolumeNode = volumeNodeLevel - 2 * troughsNumberOfNodes to volumeNodeLevel - troughsNumberOfNodes - 1
                    if tempTroughTotalVolume.get(volumeNodeLevel - troughsNumberOfNodes) >= tempTroughTotalVolume.get(currentVolumeNode)
                        troughUpperNth := false
                        break
                    else
                        troughUpperNth := true

                for currentVolumeNode = volumeNodeLevel - troughsNumberOfNodes + 1 to volumeNodeLevel
                    if tempTroughTotalVolume.get(volumeNodeLevel - troughsNumberOfNodes) >= tempTroughTotalVolume.get(currentVolumeNode)
                        troughLowerNth := false
                        break
                    else
                        troughLowerNth := true

                if troughUpperNth and troughLowerNth and tempTroughTotalVolume.get(volumeNodeLevel - troughsNumberOfNodes) / tempTroughTotalVolume.max() > vn_VolumeNodeThreshold

                    startVolumeNodeIndex := vp_profileShow ? profilePlacementRight ? 
                                                             VP.startIndex : 
                                                             volumeDataArray.endProfileIndex.get(volumeNodeLevel - 2 * troughsNumberOfNodes) : //VP.startIndex + int(volumeDataArray.totalVolume.get(volumeNodeLevel - vn_troughsNumberOfNodes) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                             VP.startIndex

                    endVolumeNodeIndex   := vp_profileShow ? profilePlacementRight ? 
                                                             volumeDataArray.endProfileIndex.get(volumeNodeLevel - 2 * troughsNumberOfNodes) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(volumeNodeLevel - vn_troughsNumberOfNodes) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                             last_bar_index : 
                                             last_bar_index

                    vn_troughVolumeColor := vn_troughsShow == 'Troughs' ? vn_troughVolumeColor : color.from_gradient(tempTroughTotalVolume.get(volumeNodeLevel - troughsNumberOfNodes) / tempTroughTotalVolume.max(), 0, 1, color.new(vn_troughVolumeColor, 95), color.new(vn_troughVolumeColor, 31))  

                    VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (volumeNodeLevel - 2 * troughsNumberOfNodes + .1) * priceStep, endVolumeNodeIndex, lowestPrice + (volumeNodeLevel - 2 * troughsNumberOfNodes + .9) * priceStep, 
                                          color(na), bgcolor = vn_troughVolumeColor))


                    if vn_troughsShow == 'Clusters'

                        for currentVolumeNode = volumeNodeLevel - 2 * troughsNumberOfNodes to volumeNodeLevel

                            if currentVolumeNode >= troughsNumberOfNodes and currentVolumeNode <= vp_profileNumberOfRows - 1 + troughsNumberOfNodes

                                if not volumeDataArray.troughVolume.get(currentVolumeNode - troughsNumberOfNodes)
                                    startVolumeNodeIndex := vp_profileShow ? profilePlacementRight ? 
                                                                         VP.startIndex : 
                                                                         volumeDataArray.endProfileIndex.get(currentVolumeNode - troughsNumberOfNodes) : //VP.startIndex + int(volumeDataArray.totalVolume.get(currentVolumeNode) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                         VP.startIndex

                                    endVolumeNodeIndex   := vp_profileShow ? profilePlacementRight ? 
                                                                         volumeDataArray.endProfileIndex.get(currentVolumeNode - troughsNumberOfNodes) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(currentVolumeNode) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                                         last_bar_index : 
                                                         last_bar_index

                                    VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (currentVolumeNode - troughsNumberOfNodes + .0) * priceStep, endVolumeNodeIndex, lowestPrice + (currentVolumeNode - troughsNumberOfNodes + 1.) * priceStep, 
                                                      color(na), bgcolor = vn_troughVolumeColor))
                                    volumeDataArray.troughVolume.set(currentVolumeNode - troughsNumberOfNodes, true)

        tempTroughTotalVolume.clear()

    if vn_highestNVolumeNodes > 0
        for highestNode = 0 to vn_highestNVolumeNodes - 1
            startVolumeNodeIndex = vp_profileShow ? profilePlacementRight ? 
                                                     VP.startIndex : 
                                                     volumeDataArray.endProfileIndex.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode))) : //VP.startIndex + int(volumeDataArray.totalVolume.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode))) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                     VP.startIndex
            endVolumeNodeIndex   = vp_profileShow ? profilePlacementRight ? 
                                                     volumeDataArray.endProfileIndex.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode))) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode))) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                     last_bar_index : 
                                     last_bar_index

            VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode)) + .1) * priceStep, endVolumeNodeIndex, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode)) + .9) * priceStep, color(na), bgcolor = vn_highestVolumeColor))

    if vn_lowestNVolumeNodes > 0

        lowestNVolumeNodeCount = 0
        lowestNVolumeNodeIndex = 0//volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min())
        lowestNVolumeNodeValue = 0.

        while lowestNVolumeNodeCount < vn_lowestNVolumeNodes

            if lowestNVolumeNodeIndex == vp_profileNumberOfRows
                break

            if volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex) != lowestNVolumeNodeValue
                lowestNVolumeNodeValue := volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex)

                startVolumeNodeIndex = vp_profileShow ? profilePlacementRight ? 
                                                     VP.startIndex : 
                                                     volumeDataArray.endProfileIndex.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex))) : //VP.startIndex + int(volumeDataArray.totalVolume.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex))) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                     VP.startIndex

                endVolumeNodeIndex   = vp_profileShow ? profilePlacementRight ? 
                                                     volumeDataArray.endProfileIndex.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex))) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex))) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                     last_bar_index : 
                                     last_bar_index

                VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex)) + .1) * priceStep, endVolumeNodeIndex, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex)) + .9) * priceStep, color(na), bgcolor = vn_lowestVolumeColor))
                lowestNVolumeNodeCount += 1
            lowestNVolumeNodeIndex += 1

    log.info("yaz_kizim {0} {1}", VP.boxes.size(), volumeDataArray.totalVolume.size() )

//---------------------------------------------------------------------------------------------------------------------}

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Thu Jun 19, 2025 4:52 am
by RodrigoRT7
iardavan wrote: Wed Jun 11, 2025 10:34 am Hello and regards to everyone,

This LuxAlgo indicator is very useful — especially the way it automatically identifies peak points and draws support and resistance levels based on volume peaks and valleys.

I would really appreciate it if this indicator could be converted to MQL4 so that it can be used in MetaTrader 4.

Also, it would be great if the indicator could be anchored manually, similar to Anchored Volume Profile — allowing the user to move and resize the range between lines, rather than relying on a fixed number of bars.

If you need any additional information to help with the conversion, I am more than happy to provide it.

Thank you very much in advance for making this available for MT4!

https://www.luxalgo.com/library/indicat ... detection/

Main code:

Code: Select all

// 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<BAR> (1, BAR.new())

var barData barDataArray = barData.new(
     array.new <float> (na), 
     array.new <float> (na), 
     array.new <float> (na), 
     array.new <bool>  (na), 
     array.new <int>   (na)
 )

volumeData volumeDataArray = volumeData.new(
     array.new <float> (vp_profileNumberOfRows, 0.), 
     array.new <float> (vp_profileNumberOfRows, 0.), 
     array.new <float> (vp_profileNumberOfRows, 0.),
     array.new <int>   (vp_profileNumberOfRows, 0 ),
     array.new <bool>  (vp_profileNumberOfRows, 0.),
     array.new <bool>  (vp_profileNumberOfRows, 0.)
 )

var volumeProfile VP = volumeProfile.new(
     array.new<box>         (na),
     array.new<chart.point> (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 <                 30  =>  '1S'
            tfInMs <          1 * mInMS  =>  '5S'

            tfInMs <=        15 * mInMS  =>   '1'
            tfInMs <=        60 * mInMS  =>   '5'
            tfInMs <=       240 * mInMS  =>  '15'
            tfInMs <=      1440 * mInMS  =>  '60'
            => 'D'

    else if _depth == 1
        switch
            tfInMs <                 15  =>  '1S'
            tfInMs <                 30  =>  '5S'
            tfInMs <          1 * mInMS  => '15S'

            tfInMs <=         5 * mInMS  =>   '1'
            tfInMs <=        15 * mInMS  =>   '5'
            tfInMs <=        60 * mInMS  =>  '15'
            tfInMs <=       240 * mInMS  =>  '60'
            tfInMs <=      1440 * mInMS  => '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<BAR> (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 < last_bar_index and ltfBarData.size() > 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.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())

    barIndex = vp_profileLength
    numberOfBars = 0
    arraySize = barDataArray.barVolume.size()

    for arrayIndex = 0 to arraySize - 1

        levelHigh = barDataArray.barHigh.get(arrayIndex)
        levelLow = barDataArray.barLow.get(arrayIndex)
        levelVolume = barDataArray.barVolume.get(arrayIndex)

        // Shoutout to @tkarolak for contributing to the code's optimization! Much appreciated.
        
        int startSlotIndex = math.max(math.floor((levelLow - lowestPrice) / priceStep), 0)
        int endSlotIndex = math.min(math.floor((levelHigh - lowestPrice) / priceStep), vp_profileNumberOfRows - 1)
        
        for priceLevelIndex = startSlotIndex to endSlotIndex

            float priceLevel = lowestPrice + priceLevelIndex * priceStep

            volumeProportion = switch
                levelLow >= priceLevel and levelHigh > priceLevel + priceStep => (priceLevel + priceStep - levelLow) / (levelHigh - levelLow)
                levelHigh <= priceLevel + priceStep and levelLow < priceLevel => (levelHigh - priceLevel) / (levelHigh - levelLow)
                levelLow >= priceLevel and levelHigh <= priceLevel + priceStep => 1
                => priceStep / (levelHigh - levelLow)

            volumeDataArray.totalVolume.set(priceLevelIndex, volumeDataArray.totalVolume.get(priceLevelIndex) + levelVolume * volumeProportion)

            if barDataArray.barPolarity.get(arrayIndex)
                volumeDataArray.bullishVolume.set(priceLevelIndex, volumeDataArray.bullishVolume.get(priceLevelIndex) + levelVolume * volumeProportion)

//        priceLevelIndex = 0
//        for priceLevel = lowestPrice to highestPrice - priceStep by priceStep
//
//            if levelHigh >= priceLevel and levelLow < priceLevel + priceStep
//
//                volumeProportion = if levelLow >= priceLevel and levelHigh > priceLevel + priceStep
//                    (priceLevel + priceStep - levelLow) / (levelHigh - levelLow)
//                else if levelHigh <= priceLevel + priceStep and levelLow < priceLevel
//                    (levelHigh - priceLevel) / (levelHigh - levelLow)
//                else if levelLow >= priceLevel and levelHigh <= priceLevel + priceStep
//                    1
//                else
//                    priceStep / (levelHigh - levelLow)
//
//                volumeDataArray.totalVolume.set(priceLevelIndex, volumeDataArray.totalVolume.get(priceLevelIndex) + levelVolume * volumeProportion)
//
//                if barDataArray.barPolarity.get(arrayIndex)
//                    volumeDataArray.bullishVolume.set(priceLevelIndex, volumeDataArray.bullishVolume.get(priceLevelIndex) + levelVolume * volumeProportion)
//            priceLevelIndex += 1

        if vp_pocShow == 'Developing'
            if arrayIndex == barDataArray.barCount.get(vp_profileLength - barIndex)
                VP.pocPoints.push(chart.point.from_index(bar.index[barIndex], math.avg(bar.high[barIndex], bar.low[barIndex])))
                VP.pocPoints.push(chart.point.from_index(bar.index[barIndex] + 1, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max()) + .5) * priceStep))
                numberOfBars += barDataArray.barCount.get(vp_profileLength - barIndex)
                barIndex  -= 1
            else if arrayIndex == (numberOfBars + barDataArray.barCount.get(vp_profileLength - barIndex)) and numberOfBars != 0
                VP.pocPoints.push(chart.point.from_index(bar.index[barIndex] + 1, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max()) + .5) * priceStep))
                numberOfBars += barDataArray.barCount.get(vp_profileLength - barIndex)
                barIndex  -= 1
            else if barIndex == 0
                VP.pocPoints.push(chart.point.from_index(bar.index[barIndex] + 1, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max()) + .5) * priceStep))
                numberOfBars += barDataArray.barCount.get(vp_profileLength - barIndex)

    VP.pocPolyline := polyline.new(VP.pocPoints, false, false, xloc.bar_index, vp_pocColor, color(na), line.style_solid, vp_pocWidth)

    for volumeIndex = 0 to vp_profileNumberOfRows - 1
        bearishVolume = 2 * volumeDataArray.bullishVolume.get(volumeIndex) - volumeDataArray.totalVolume.get(volumeIndex)
        volumeDataArray.bearishVolume.set(volumeIndex, volumeDataArray.bearishVolume.get(volumeIndex) + bearishVolume * (bearishVolume > 0 ? 1 : -1) )

    VP.pocLevel := volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max())
    totalTradedVolume = volumeDataArray.totalVolume.sum() * vp_valueAreaThreshold
    valueAreaVolume = VP.pocLevel != -1 ? volumeDataArray.totalVolume.get(VP.pocLevel) : 0
    VP.vahLevel := VP.pocLevel
    VP.valLevel := VP.pocLevel
    
    while valueAreaVolume < totalTradedVolume
        if VP.valLevel == 0 and VP.vahLevel == vp_profileNumberOfRows - 1
            break

        volumeAbovePOC = 0.
        if VP.vahLevel < vp_profileNumberOfRows - 1 
            volumeAbovePOC := volumeDataArray.totalVolume.get(VP.vahLevel + 1)

        volumeBelowPOC = 0.
        if VP.valLevel > 0
            volumeBelowPOC := volumeDataArray.totalVolume.get(VP.valLevel - 1)
        
        if volumeBelowPOC == 0 and volumeAbovePOC == 0
            break

        if volumeAbovePOC >= volumeBelowPOC
            valueAreaVolume  += volumeAbovePOC
            VP.vahLevel += 1
        else
            valueAreaVolume  += volumeBelowPOC
            VP.valLevel -= 1

    vahPrice = lowestPrice + (VP.vahLevel + 1.) * priceStep
    pocPrice = lowestPrice + (VP.pocLevel + .5) * priceStep
    valPrice = lowestPrice + (VP.valLevel + .0) * priceStep

    profilePlottingLength = vp_profileLength > 360 ? 360 : vp_profileLength
    profileWidth = profilePlottingLength * vp_profileWidth
    profileHorizontalOffset = int(profileWidth + vp_profileHorizontalOffset)

    if vp_profileShow and profilePlacementRight and vp_pocShow == 'Developing'
        renderLine(last_bar_index, pocPrice, profileHorizontalOffset + int(last_bar_index - profileWidth + 1), pocPrice, xloc.bar_index, extend.none, vp_pocColor, line.style_solid, vp_pocWidth)

    if vp_vahShow
        renderLine(VP.startIndex, vahPrice, 
                   profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                   vahPrice, xloc.bar_index, extend.none, vp_vahColor, line.style_solid, 1)
    
    if vp_pocShow == 'Regular'
        renderLine(VP.startIndex, pocPrice, profilePlacementRight ? vp_profileShow ? profileHorizontalOffset + int(last_bar_index - profileWidth + 1) : last_bar_index : last_bar_index, pocPrice, xloc.bar_index, extend.none, vp_pocColor, line.style_solid, vp_pocWidth)

    if vp_valShow
        renderLine(VP.startIndex, valPrice, 
                   profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                   valPrice, xloc.bar_index, extend.none, vp_valColor, line.style_solid, 1)

    if vp_valueAreaBackground
        VP.boxes.push(box.new(VP.startIndex, valPrice, last_bar_index, vahPrice, vp_valueAreaBackgroundColor, 1, line.style_dotted, bgcolor = vp_valueAreaBackgroundColor))

    if vp_profileBackground
        VP.boxes.push(box.new(VP.startIndex, lowestPrice, last_bar_index, highestPrice, vp_profileBackgroundColor, 1, line.style_dotted, bgcolor = vp_profileBackgroundColor))

    if vp_profileLevels != 'None' and VP.pocLevel != -1 
        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : vp_profileShow ? VP.startIndex : last_bar_index, 
                     highestPrice, str.tostring(highestPrice, format.mintick), color.new(chart.fg_color, 89), label.style_label_down, chart.fg_color, profileLevesSize, 'Profile High')

        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                     vahPrice, str.tostring(vahPrice, format.mintick), color.new(vp_vahColor, 89), label.style_label_left, vp_vahColor, profileLevesSize, 'Value Area High')

        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                     pocPrice, str.tostring(pocPrice, format.mintick), color.new(vp_pocColor, 89), label.style_label_left, vp_pocColor, profileLevesSize, 'Point of Control')
 
        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : last_bar_index, 
                     valPrice, str.tostring(valPrice, format.mintick), color.new(vp_valColor, 89), label.style_label_left, vp_valColor, profileLevesSize, 'Value Area Low')

        renderLabel(profilePlacementRight ? (vp_profileShow ? profileHorizontalOffset : 0) + last_bar_index : vp_profileShow ? VP.startIndex : last_bar_index, 
                     lowestPrice, str.tostring(lowestPrice, format.mintick), color.new(chart.fg_color, 89), label.style_label_up, chart.fg_color, profileLevesSize, 'Profile Low')

    for volumeNodeLevel = 0 to vp_profileNumberOfRows - 1
    
        if vp_profileShow
            if vp_profileGradientColors == 'Gradient Colors'
                vp_valueAreaUpColor       := color.from_gradient(volumeDataArray.totalVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max(), 0, 1, color.new(vp_valueAreaUpColor      , 95), color.new(vp_valueAreaUpColor      , 0))  
                vp_valueAreaDwonColor     := color.from_gradient(volumeDataArray.totalVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max(), 0, 1, color.new(vp_valueAreaDwonColor    , 95), color.new(vp_valueAreaDwonColor    , 0))  
                vp_profileUpVolumeColor   := color.from_gradient(volumeDataArray.totalVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max(), 0, 1, color.new(vp_profileUpVolumeColor  , 95), color.new(vp_profileUpVolumeColor  , 0))  
                vp_profileDownVolumeColor := color.from_gradient(volumeDataArray.totalVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max(), 0, 1, color.new(vp_profileDownVolumeColor, 95), color.new(vp_profileDownVolumeColor, 0))  
 
            startProfileIndex = profilePlacementRight ? 
                                 profileHorizontalOffset + int(last_bar_index - volumeDataArray.bullishVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max() * profileWidth) :
                                 VP.startIndex
            endProfileIndex   = profilePlacementRight ? 
                                 profileHorizontalOffset + last_bar_index : 
                                 int(startProfileIndex + volumeDataArray.bullishVolume.get(volumeNodeLevel) / volumeDataArray.totalVolume.max() * profileWidth)

            VP.boxes.push(box.new(startProfileIndex, lowestPrice + (volumeNodeLevel + .1) * priceStep, endProfileIndex, lowestPrice + (volumeNodeLevel + .9) * priceStep, 
                                   color(na), bgcolor = volumeNodeLevel >= VP.valLevel and volumeNodeLevel <= VP.vahLevel ? vp_valueAreaUpColor : vp_profileUpVolumeColor))

            startProfileIndex := profilePlacementRight ? startProfileIndex : endProfileIndex
            endProfileIndex   := profilePlacementRight ? 
                                 startProfileIndex - int( (volumeDataArray.totalVolume.get(volumeNodeLevel) - volumeDataArray.bullishVolume.get(volumeNodeLevel)) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                 startProfileIndex + int( (volumeDataArray.totalVolume.get(volumeNodeLevel) - volumeDataArray.bullishVolume.get(volumeNodeLevel)) / volumeDataArray.totalVolume.max() * profileWidth)

            VP.boxes.push(box.new(startProfileIndex, lowestPrice + (volumeNodeLevel + .1) * priceStep, endProfileIndex, lowestPrice + (volumeNodeLevel + .9) * priceStep, 
                                   color(na), bgcolor = volumeNodeLevel >= VP.valLevel and volumeNodeLevel <= VP.vahLevel ? vp_valueAreaDwonColor : vp_profileDownVolumeColor))
            volumeDataArray.endProfileIndex.set(volumeNodeLevel, endProfileIndex)

    if  vn_peaksShow != 'None' or  vn_troughsShow != 'None'
        var int startVolumeNodeIndex = na, var int endVolumeNodeIndex = na
        var bool peakUpperNth = na, var bool peakLowerNth = na

        peaksNumberOfNodes = int(vp_profileNumberOfRows * vn_peaksNumberOfNodes)

        tempPeakTotalVolume = volumeDataArray.totalVolume.copy()

        for index = 1 to peaksNumberOfNodes
            tempPeakTotalVolume.unshift(0.)
            tempPeakTotalVolume.push(0.)

        for volumeNodeLevel = 0 to vp_profileNumberOfRows - 1 + 2 * peaksNumberOfNodes 

            if vn_peaksShow != 'None' and volumeNodeLevel >= 2 * peaksNumberOfNodes 

                for currentVolumeNode = volumeNodeLevel - 2 * peaksNumberOfNodes to volumeNodeLevel - peaksNumberOfNodes - 1
                    if tempPeakTotalVolume.get(volumeNodeLevel - peaksNumberOfNodes) <= tempPeakTotalVolume.get(currentVolumeNode)
                        peakUpperNth := false
                        break
                    else
                        peakUpperNth := true

                for currentVolumeNode = volumeNodeLevel - peaksNumberOfNodes + 1 to volumeNodeLevel
                    if tempPeakTotalVolume.get(volumeNodeLevel - peaksNumberOfNodes) <= tempPeakTotalVolume.get(currentVolumeNode)
                        peakLowerNth := false
                        break
                    else
                        peakLowerNth := true

                if peakUpperNth and peakLowerNth and tempPeakTotalVolume.get(volumeNodeLevel - peaksNumberOfNodes) / tempPeakTotalVolume.max() > vn_VolumeNodeThreshold

                    startVolumeNodeIndex := vp_profileShow ? profilePlacementRight ? 
                                                             VP.startIndex : 
                                                             volumeDataArray.endProfileIndex.get(volumeNodeLevel - 2 * peaksNumberOfNodes) : //VP.startIndex + int(volumeDataArray.totalVolume.get(volumeNodeLevel - vn_peaksNumberOfNodes) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                             VP.startIndex

                    endVolumeNodeIndex   := vp_profileShow ? profilePlacementRight ? 
                                                             volumeDataArray.endProfileIndex.get(volumeNodeLevel - 2 * peaksNumberOfNodes) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(volumeNodeLevel - vn_peaksNumberOfNodes) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                             last_bar_index : 
                                             last_bar_index

                    vn_peakVolumeColor := vn_peaksShow == 'Peaks' ? vn_peakVolumeColor : color.from_gradient(tempPeakTotalVolume.get(volumeNodeLevel - peaksNumberOfNodes) / tempPeakTotalVolume.max(), 0, 1, color.new(vn_peakVolumeColor, 95), color.new(vn_peakVolumeColor, 65))  

                    VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (volumeNodeLevel - 2 * peaksNumberOfNodes + .1) * priceStep, endVolumeNodeIndex, lowestPrice + (volumeNodeLevel - 2 * peaksNumberOfNodes + .9) * priceStep, 
                                          color(na), bgcolor = vn_peakVolumeColor))

                    if vn_peaksShow == 'Clusters'

                        for currentVolumeNode = volumeNodeLevel - 2 * peaksNumberOfNodes to volumeNodeLevel

                            if currentVolumeNode >= peaksNumberOfNodes and currentVolumeNode <= vp_profileNumberOfRows - 1 + peaksNumberOfNodes
                                if not volumeDataArray.peakVolume.get(currentVolumeNode - peaksNumberOfNodes)

                                    startVolumeNodeIndex := vp_profileShow ? profilePlacementRight ? 
                                                                         VP.startIndex : 
                                                                         volumeDataArray.endProfileIndex.get(currentVolumeNode - peaksNumberOfNodes) : //VP.startIndex + int(volumeDataArray.totalVolume.get(currentVolumeNode) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                         VP.startIndex

                                    endVolumeNodeIndex   := vp_profileShow ? profilePlacementRight ? 
                                                                         volumeDataArray.endProfileIndex.get(currentVolumeNode - peaksNumberOfNodes) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(currentVolumeNode) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                                         last_bar_index : 
                                                         last_bar_index

                                    VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (currentVolumeNode - peaksNumberOfNodes + .0) * priceStep, endVolumeNodeIndex, lowestPrice + (currentVolumeNode - peaksNumberOfNodes + 1.) * priceStep, 
                                                       color(na), bgcolor = vn_peakVolumeColor))
                                    volumeDataArray.peakVolume.set(currentVolumeNode - peaksNumberOfNodes, true)

        tempPeakTotalVolume.clear()

        var bool troughUpperNth = na, var bool troughLowerNth = na
        troughsNumberOfNodes = int(vp_profileNumberOfRows * vn_troughsNumberOfNodes)

        tempTroughTotalVolume = volumeDataArray.totalVolume.copy()

        for index = 1 to troughsNumberOfNodes
            tempTroughTotalVolume.unshift(volumeDataArray.totalVolume.max())
            tempTroughTotalVolume.push(volumeDataArray.totalVolume.max())
            
        for volumeNodeLevel = 0 to vp_profileNumberOfRows - 1 + 2 * troughsNumberOfNodes 

            if vn_troughsShow != 'None' and volumeNodeLevel >= 2 * troughsNumberOfNodes 

                for currentVolumeNode = volumeNodeLevel - 2 * troughsNumberOfNodes to volumeNodeLevel - troughsNumberOfNodes - 1
                    if tempTroughTotalVolume.get(volumeNodeLevel - troughsNumberOfNodes) >= tempTroughTotalVolume.get(currentVolumeNode)
                        troughUpperNth := false
                        break
                    else
                        troughUpperNth := true

                for currentVolumeNode = volumeNodeLevel - troughsNumberOfNodes + 1 to volumeNodeLevel
                    if tempTroughTotalVolume.get(volumeNodeLevel - troughsNumberOfNodes) >= tempTroughTotalVolume.get(currentVolumeNode)
                        troughLowerNth := false
                        break
                    else
                        troughLowerNth := true

                if troughUpperNth and troughLowerNth and tempTroughTotalVolume.get(volumeNodeLevel - troughsNumberOfNodes) / tempTroughTotalVolume.max() > vn_VolumeNodeThreshold

                    startVolumeNodeIndex := vp_profileShow ? profilePlacementRight ? 
                                                             VP.startIndex : 
                                                             volumeDataArray.endProfileIndex.get(volumeNodeLevel - 2 * troughsNumberOfNodes) : //VP.startIndex + int(volumeDataArray.totalVolume.get(volumeNodeLevel - vn_troughsNumberOfNodes) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                             VP.startIndex

                    endVolumeNodeIndex   := vp_profileShow ? profilePlacementRight ? 
                                                             volumeDataArray.endProfileIndex.get(volumeNodeLevel - 2 * troughsNumberOfNodes) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(volumeNodeLevel - vn_troughsNumberOfNodes) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                             last_bar_index : 
                                             last_bar_index

                    vn_troughVolumeColor := vn_troughsShow == 'Troughs' ? vn_troughVolumeColor : color.from_gradient(tempTroughTotalVolume.get(volumeNodeLevel - troughsNumberOfNodes) / tempTroughTotalVolume.max(), 0, 1, color.new(vn_troughVolumeColor, 95), color.new(vn_troughVolumeColor, 31))  

                    VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (volumeNodeLevel - 2 * troughsNumberOfNodes + .1) * priceStep, endVolumeNodeIndex, lowestPrice + (volumeNodeLevel - 2 * troughsNumberOfNodes + .9) * priceStep, 
                                          color(na), bgcolor = vn_troughVolumeColor))


                    if vn_troughsShow == 'Clusters'

                        for currentVolumeNode = volumeNodeLevel - 2 * troughsNumberOfNodes to volumeNodeLevel

                            if currentVolumeNode >= troughsNumberOfNodes and currentVolumeNode <= vp_profileNumberOfRows - 1 + troughsNumberOfNodes

                                if not volumeDataArray.troughVolume.get(currentVolumeNode - troughsNumberOfNodes)
                                    startVolumeNodeIndex := vp_profileShow ? profilePlacementRight ? 
                                                                         VP.startIndex : 
                                                                         volumeDataArray.endProfileIndex.get(currentVolumeNode - troughsNumberOfNodes) : //VP.startIndex + int(volumeDataArray.totalVolume.get(currentVolumeNode) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                         VP.startIndex

                                    endVolumeNodeIndex   := vp_profileShow ? profilePlacementRight ? 
                                                                         volumeDataArray.endProfileIndex.get(currentVolumeNode - troughsNumberOfNodes) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(currentVolumeNode) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                                         last_bar_index : 
                                                         last_bar_index

                                    VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (currentVolumeNode - troughsNumberOfNodes + .0) * priceStep, endVolumeNodeIndex, lowestPrice + (currentVolumeNode - troughsNumberOfNodes + 1.) * priceStep, 
                                                      color(na), bgcolor = vn_troughVolumeColor))
                                    volumeDataArray.troughVolume.set(currentVolumeNode - troughsNumberOfNodes, true)

        tempTroughTotalVolume.clear()

    if vn_highestNVolumeNodes > 0
        for highestNode = 0 to vn_highestNVolumeNodes - 1
            startVolumeNodeIndex = vp_profileShow ? profilePlacementRight ? 
                                                     VP.startIndex : 
                                                     volumeDataArray.endProfileIndex.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode))) : //VP.startIndex + int(volumeDataArray.totalVolume.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode))) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                     VP.startIndex
            endVolumeNodeIndex   = vp_profileShow ? profilePlacementRight ? 
                                                     volumeDataArray.endProfileIndex.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode))) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode))) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                     last_bar_index : 
                                     last_bar_index

            VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode)) + .1) * priceStep, endVolumeNodeIndex, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.max(highestNode)) + .9) * priceStep, color(na), bgcolor = vn_highestVolumeColor))

    if vn_lowestNVolumeNodes > 0

        lowestNVolumeNodeCount = 0
        lowestNVolumeNodeIndex = 0//volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min())
        lowestNVolumeNodeValue = 0.

        while lowestNVolumeNodeCount < vn_lowestNVolumeNodes

            if lowestNVolumeNodeIndex == vp_profileNumberOfRows
                break

            if volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex) != lowestNVolumeNodeValue
                lowestNVolumeNodeValue := volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex)

                startVolumeNodeIndex = vp_profileShow ? profilePlacementRight ? 
                                                     VP.startIndex : 
                                                     volumeDataArray.endProfileIndex.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex))) : //VP.startIndex + int(volumeDataArray.totalVolume.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex))) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                     VP.startIndex

                endVolumeNodeIndex   = vp_profileShow ? profilePlacementRight ? 
                                                     volumeDataArray.endProfileIndex.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex))) : //profileHorizontalOffset + int(last_bar_index - volumeDataArray.totalVolume.get(volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex))) / volumeDataArray.totalVolume.max() * profileWidth) : 
                                                     last_bar_index : 
                                     last_bar_index

                VP.boxes.push(box.new(startVolumeNodeIndex, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex)) + .1) * priceStep, endVolumeNodeIndex, lowestPrice + (volumeDataArray.totalVolume.indexof(volumeDataArray.totalVolume.min(lowestNVolumeNodeIndex)) + .9) * priceStep, color(na), bgcolor = vn_lowestVolumeColor))
                lowestNVolumeNodeCount += 1
            lowestNVolumeNodeIndex += 1

    log.info("yaz_kizim {0} {1}", VP.boxes.size(), volumeDataArray.totalVolume.size() )

//---------------------------------------------------------------------------------------------------------------------}
Very interesting to mark points of interest of the market profile :D

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Mon Jul 07, 2025 8:41 am
by mo2mengaber22
thank you

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Mon Jul 14, 2025 4:13 am
by kvak
//Sup TV. This script is inspired by (and dedicated to) closure of sales (today, Oct 20 '21) of the famous Jurik Research.
...
Jurik Research, the real people who been doing real things by using the real instruments, while many others been reading books "How to become a billionaire in 2 days", watching 5687 hours videos of how to use RSI, and studying+applying machine learning to everything cuz suddenly it became trendy xD
...
This is my remake of the original Jurik Moving Average (JMA) based on all the info I managed to get my hands on, some stuff is dated back to 2008 or smth.
The whole point of this filter, the point missed by other attempts of its remakes even posted there on TV, is that it takes into account volatility and adjusts its speed based on it.
Think about it as an EMA, where the alpha parameter is dynamic.
Now, by all means I'm not claiming that's this is the perfect replica of the original algo. I've tested it a lot, looks like it's working legit...

Code: Select all

//@version=4
study('Jurik Moving Average', 'JMA', true, resolution='')

//pine_jma[
pine_jma(_src, _length, _phase) =>
    lower_band = _src
    upper_band = _src
    del2       = abs(_src - lower_band[1])
    del1       = abs(_src - upper_band[1])
    
    vola = del1 == del2 ? 0 : max(del1, del2)
    
    vola_sum  = 0.0
    vola_sum := nz(vola_sum[1]) + 0.1 * (vola - vola[10])
    
    avg_len    = 65
    // avg_len = _length * 10
    
    y = bar_index + 1
    
    avg_vola  = 0.0
    avg_vola := if y <= avg_len + 1
        nz(avg_vola[1]) + 2.0 * (vola_sum - nz(avg_vola[1]))/(avg_len + 1)
    else
        sma(vola_sum, avg_len)    
    
    len  = 0.5 * (_length - 1)
    len1 = max(log(sqrt(len)) / log(2) + 2, 0)
    pow1 = max(len1 - 2, 0.5)
    
    r_vola  = avg_vola > 0 ? vola / avg_vola : 0
    r_vola := if r_vola > pow(len1, 1 / pow1)
        pow(len1, 1 / pow1)
    else      if r_vola < 1
        1
    else
        r_vola
    
    pow2 = pow(r_vola, pow1)
    len2 = sqrt(len) * len1
    bet  = len2/(len2 + 1)
    kv   = pow(bet, sqrt(pow2))
    
    lower_band := y == 1 ? _src : del2 < 0 ? _src : _src - kv * del2
    upper_band := y == 1 ? _src : del1 < 0 ? _src : _src + kv * del1
    
    beta  = 0.45 * (len - 1) / (0.45 * (len - 1) + 2)
    pr    = _phase < -100 ? 0.5 : _phase > 100 ? 2.5 : _phase / 100 + 1.5
    alpha = pow(beta, pow2)
    
    ma1  = 0.0
    det0 = 0.0
    jma  = 0.0
    det1 = 0.0
    
    ma1  := (1 - alpha) * _src + alpha * nz(ma1[1])
    det0 := (_src - ma1) * (1 - beta) + beta * nz(det0[1])
    ma2   = ma1 + pr * det0
    det1 := (ma2 - nz(jma[1])) * pow((1 - alpha), 2) + pow(alpha, 2) * nz(det1[1])
    jma  := nz(jma[1]) + det1
//]

source = input(close, 'Source',        inline='1')
log    = input(false, 'Log transform', inline='1')
length = input(13,                     inline='2')
phase  = input(0,                      inline='2')
offset = input(0)

src       = log ? log(source)  : source
pre_jma   = pine_jma(src, length, phase)
jma       = log ? exp(pre_jma) : pre_jma
jma_color = jma > jma[1] ? color.blue : color.red

plot(jma, 'JMA', jma_color, 2, offset=offset)

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Thu Jul 24, 2025 6:40 am
by MeNiazi
mrtools wrote: Fri May 09, 2025 12:55 am Hello, added arrows for the buffers and the averages with the price options.
Wow seems great addition.....
Pls convert into Mt5....
Loving ur works.....

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Thu Jul 24, 2025 8:26 pm
by mrtools
MeNiazi wrote: Thu Jul 24, 2025 6:40 am Wow seems great addition.....
Pls convert into Mt5....
Loving ur works.....
Try here

Re: Already Converted TradingView Indicators to MT4 Indicators

Posted: Thu Jul 31, 2025 5:14 pm
by MeNiazi
mrtools wrote: Fri May 09, 2025 12:55 am Hello, added arrows for the buffers and the averages with the price options.
Hello.....is it available in MTF??