本页源码来自 TradingView 公开发布的开源脚本,版权归原作者所有, 请遵循其原始许可(Pine 脚本常见 CC BY-NC-SA / MPL-2.0 / MIT)。 本项目仅用于研究检索与许可范围内的移植。
// 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_lines_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
var string lowerTimeframe = 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
lowerTimeframe := calculateTimeframe(2)
ltfBarData := requestBarData(calculateTimeframe(2))
else if vp_profileLength <= 700
lowerTimeframe := calculateTimeframe(1)
ltfBarData := requestBarData(calculateTimeframe(1))
else
lowerTimeframe := 'Chart'
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
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
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
priceLevelIndex = 0
for priceLevel = lowestPrice to highestPrice - priceStep by priceStep
levelHigh = barDataArray.barHigh.get(arrayIndex)
levelLow = barDataArray.barLow.get(arrayIndex)
levelVolume = barDataArray.barVolume.get(arrayIndex)
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 = volumeDataArray.totalVolume.get(VP.pocLevel)
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 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'
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)
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)
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
//---------------------------------------------------------------------------------------------------------------------}