Skip to content

Instantly share code, notes, and snippets.

@4skinSkywalker
Last active January 28, 2026 23:51
Show Gist options
  • Select an option

  • Save 4skinSkywalker/d5e42f46851decf69054e0d0287ab6f5 to your computer and use it in GitHub Desktop.

Select an option

Save 4skinSkywalker/d5e42f46851decf69054e0d0287ab6f5 to your computer and use it in GitHub Desktop.
Volume Profile Visible Range in Pine Script
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Fr3d0C0rl30n3
//@version=6
indicator("Fr3d0's Volume Profile Visible Range", "VPVR", overlay=true, max_boxes_count=500)
LINE_COLOR = color.new(color.black, 15)
POC_LINE_COLOR = color.new(color.red, 30)
VALUE_AREA_LINE_COLOR = color.new(color.yellow, 30)
BORDER_COLOR = color.new(color.black, 70)
BUY_COLOR = color.new(color.green, 15)
SELL_COLOR = color.new(color.red, 15)
TIME_UNIT = time - time[1]
MAX_BARS = 365
topBottomLineColor = input.color(LINE_COLOR, "Top/bottom Line Color")
pocLineColor = input.color(POC_LINE_COLOR, "PoC Line Color")
valueAreaLineColor = input.color(VALUE_AREA_LINE_COLOR, "Value Area Line Color")
buyBarColor = input.color(BUY_COLOR, "Buy Bar Color")
sellBarColor = input.color(SELL_COLOR, "Sell Bar Color")
numOfBars = input.int(90, 'Number of bars', minval=14, maxval=MAX_BARS)
distLastCandle = input.int(2, 'Distance from last candle', minval=2, maxval=20)
rangeHigh = ta.highest(high, numOfBars)
rangeLow = ta.lowest(low, numOfBars)
rangeHeight = rangeHigh - rangeLow
firstBar = bar_index - numOfBars + 1
lastBar = bar_index
visibleBars = lastBar - firstBar + 1
numOfHistograms = input.int(50, 'Number of histograms', minval=10, maxval=200)
widestHistogramWidth = visibleBars
histogramHeight = rangeHeight / numOfHistograms
histogramLowList = array.new_float(numOfHistograms, na)
histogramHighList = array.new_float(numOfHistograms, na)
histogramPriceList = array.new_float(numOfHistograms, 0.0)
histogramBuyVolumeList = array.new_float(numOfHistograms, 0.0)
histogramSellVolumeList = array.new_float(numOfHistograms, 0.0)
histogramVolumePercentageList = array.new_float(numOfHistograms, 0.0)
// Clean up drawings every tick
var buyBars = array.new_box(MAX_BARS, na)
for i=0 to MAX_BARS-1
box.delete(array.get(buyBars, i))
var sellBars = array.new_box(MAX_BARS, na)
for i=0 to MAX_BARS-1
box.delete(array.get(sellBars, i))
var line topLine = na
line.delete(topLine)
var line bottomLine = na
line.delete(bottomLine)
var line pocLine = na
line.delete(pocLine)
var line vahLine = na
line.delete(vahLine)
var line valLine = na
line.delete(valLine)
var totalVolume = 0.0
var float volumeAccum = 0
var float VAH = na
var float VAL = na
if barstate.islast
// Define lows and highs of the histograms
for i = 0 to numOfHistograms - 1
histogramLow = rangeLow + histogramHeight * i
histogramHigh = rangeLow + histogramHeight * (i + 1)
array.set(histogramLowList, i, histogramLow)
array.set(histogramHighList, i, histogramHigh)
array.set(histogramPriceList, i, (histogramLow + histogramHigh) / 2)
// Assign bar's volumes to histograms
for i = 0 to numOfBars - 1
totalVolume := totalVolume + volume[i]
currentBarHeight = high[i] - low[i]
currentBuyVolume = (high[i] == low[i]) ? 0 : volume[i] * (close[i] - low[i]) / currentBarHeight
currentSellVolume = (high[i] == low[i]) ? 0 : volume[i] * (high[i] - close[i]) / currentBarHeight
// Define the percentages of the current volume to give to histograms
for j = 0 to numOfHistograms - 1
histogramLow = array.get(histogramLowList, j)
histogramHigh = array.get(histogramHighList, j)
target = (math.max(histogramHigh, high[i]) - math.min(histogramLow, low[i]))
- (math.max(histogramHigh, high[i]) - math.min(histogramHigh, high[i]))
- (math.max(histogramLow, low[i]) - math.min(histogramLow, low[i]))
histogramVolumePercentage = target / currentBarHeight
histogramBuyVolume = array.get(histogramBuyVolumeList, j)
histogramSellVolume = array.get(histogramSellVolumeList, j)
// If there is at least one histogram affected
// then divide the current volume by the number of histograms affected
if histogramVolumePercentage > 0
array.set(histogramBuyVolumeList, j, histogramBuyVolume + currentBuyVolume * histogramVolumePercentage)
array.set(histogramSellVolumeList, j, histogramSellVolume + currentSellVolume * histogramVolumePercentage)
// Find the histogram with the highest volume
highestHistogramVolume = 0.0
pocIndex = 0
for i = 0 to numOfHistograms - 1
histogramBuyVolume = array.get(histogramBuyVolumeList, i)
histogramSellVolume = array.get(histogramSellVolumeList, i)
histogramVolume = histogramBuyVolume + histogramSellVolume
if histogramVolume > highestHistogramVolume
highestHistogramVolume := histogramVolume
pocIndex := i
timeUnit = time - time[1]
// Draw top and bottom of the range considered
topLine := line.new(time[numOfBars], rangeHigh, time_close + distLastCandle * timeUnit, rangeHigh, xloc=xloc.bar_time, color=topBottomLineColor, width = 2)
bottomLine := line.new(time[numOfBars], rangeLow, time_close + distLastCandle * timeUnit, rangeLow, xloc=xloc.bar_time, color=topBottomLineColor, width = 2)
// Draw Point of Control
histLow = array.get(histogramLowList, pocIndex)
histHigh = array.get(histogramHighList, pocIndex)
pocPos = histLow + (histHigh - histLow) / 2
pocLine := line.new(time[numOfBars], pocPos, time_close + distLastCandle * timeUnit, pocPos, xloc=xloc.bar_time, color=pocLineColor, width = 2)
// Value area calculation
valueAreaVolume = totalVolume * 0.7
volumeAccum := array.get(histogramBuyVolumeList, pocIndex) + array.get(histogramSellVolumeList, pocIndex)
VAH := pocPos
VAL := pocPos
upIndex = pocIndex + 1
downIndex = pocIndex - 1
for _ = 0 to numOfHistograms - 1
volUp = (upIndex < numOfHistograms) ? (array.get(histogramBuyVolumeList, upIndex) + array.get(histogramSellVolumeList, upIndex)) : 0.0
volDown = (downIndex >= 0) ? (array.get(histogramBuyVolumeList, downIndex) + array.get(histogramSellVolumeList, downIndex)) : 0.0
if volumeAccum >= valueAreaVolume
break
if volUp >= volDown and upIndex < numOfHistograms
VAH := array.get(histogramPriceList, upIndex)
volumeAccum += volUp
upIndex += 1
else if downIndex >= 0
VAL := array.get(histogramPriceList, downIndex)
volumeAccum += volDown
downIndex -= 1
vahLine := line.new(firstBar, VAH, lastBar, VAH, color=valueAreaLineColor, width=2, style=line.style_dashed) // VA High
valLine := line.new(firstBar, VAL, lastBar, VAL, color=valueAreaLineColor, width=2, style=line.style_dashed)
// Draw histograms and highlight the Point of Control
for i = 0 to numOfHistograms - 1
histogramLow = array.get(histogramLowList, i)
histogramHigh = array.get(histogramHighList, i)
histogramBuyVolume = array.get(histogramBuyVolumeList, i)
histogramSellVolume = array.get(histogramSellVolumeList, i)
histogramVolume = histogramBuyVolume + histogramSellVolume
histogramWidth = widestHistogramWidth * histogramVolume / highestHistogramVolume
histogramBuyWidth = math.floor(histogramWidth * histogramBuyVolume / histogramVolume)
histogramSellWidth = math.floor(histogramWidth * histogramSellVolume / histogramVolume)
// Draw buy and sell histograms
array.set(buyBars, i, box.new(
left=firstBar,
top=histogramHigh,
right=firstBar + histogramBuyWidth,
bottom=histogramLow,
bgcolor=buyBarColor,
border_color=BORDER_COLOR))
array.set(sellBars, i, box.new(
left=firstBar + histogramBuyWidth,
top=histogramHigh,
right=firstBar + histogramBuyWidth + histogramSellWidth,
bottom=histogramLow,
bgcolor=sellBarColor,
border_color=BORDER_COLOR))
@BreakNews
Copy link

Anyway can I get this script compatible with Webull? It seems like Webull uses a mix of Pine script and easy language but I'm no coder so idk

@vovkapoc
Copy link

vovkapoc commented Mar 7, 2024

Thank you very much! Great work!🤟🏻

@rickinglob
Copy link

thanks Great Work

@Noob-dummy
Copy link

How do you even begin to know how to do this? This is amazing.

@4skinSkywalker
Copy link
Author

Thanks! 💕
There is the article that explains how I did it: Fredo's VPVR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment