//@version=5 indicator("Custom Volume Profile Histogram", overlay=true, max_boxes_count=500) // ─── Inputs ─────────────────────────────────────────────── barsBack = input.int(100, "Bars Back", minval=10) rows = input.int(50, "Resolution (rows)", minval=10) showVAHVAL = input.bool(true, "Show VAH/VAL/POC") // ─── Calculate Range ────────────────────────────────────── highestPrice = ta.highest(high, barsBack) lowestPrice = ta.lowest(low, barsBack) range = highestPrice - lowestPrice step = range / rows // ─── Initialize Arrays ──────────────────────────────────── var volumeBins = array.new_float(rows, 0.0) // Reset bins each bar if barstate.islast array.fill(volumeBins, 0.0) for i = 0 to barsBack - 1 barHigh = high[i] barLow = low[i] barVol = volume[i] // Distribute volume across bins touched by bar for r = 0 to rows - 1 binLow = lowestPrice + r * step binHigh = binLow + step overlap = math.max(0, math.min(barHigh, binHigh) - math.max(barLow, binLow)) if overlap > 0 array.set(volumeBins, r, array.get(volumeBins, r) + barVol * (overlap / (barHigh - barLow + 1e-6))) // ─── Find POC, VAH, VAL ─────────────────────────────────── maxVol = 0.0 pocIndex = 0 for r = 0 to rows - 1 v = array.get(volumeBins, r) if v > maxVol maxVol := v pocIndex := r pocPrice = lowestPrice + pocIndex * step + step/2 // Value Area (70% of volume) totalVol = array.sum(volumeBins) targetVol = totalVol * 0.7 cumVol = 0.0 lowIndex = pocIndex highIndex = pocIndex while cumVol < targetVol and (lowIndex > 0 or highIndex < rows-1) leftVol = lowIndex > 0 ? array.get(volumeBins, lowIndex-1) : 0 rightVol = highIndex < rows-1 ? array.get(volumeBins, highIndex+1) : 0 if leftVol > rightVol lowIndex -= 1 cumVol += leftVol else highIndex += 1 cumVol += rightVol valPrice = lowestPrice + lowIndex * step vahPrice = lowestPrice + highIndex * step // ─── Plot Histogram Rectangles ──────────────────────────── if barstate.islast for r = 0 to rows - 1 binVol = array.get(volumeBins, r) binPriceLow = lowestPrice + r * step binPriceHigh = binPriceLow + step barWidth = math.round(1 + 40 * binVol / maxVol) // scale width box.new(bar_index - barsBack, binPriceLow, bar_index - barsBack + barWidth, binPriceHigh, xloc=xloc.bar_index, bgcolor=color.new(color.blue, 70), border_color=color.new(color.blue, 100)) // ─── Plot VAH/VAL/POC ───────────────────────────────────── if showVAHVAL and barstate.islast line.new(bar_index - barsBack, pocPrice, bar_index, pocPrice, xloc=xloc.bar_index, extend=extend.none, color=color.red, width=2) line.new(bar_index - barsBack, valPrice, bar_index, valPrice, xloc=xloc.bar_index, extend=extend.none, color=color.green, width=2) line.new(bar_index - barsBack, vahPrice, bar_index, vahPrice, xloc=xloc.bar_index, extend=extend.none, color=color.green, width=2)