Volume · 200-800/mo

Volume Profile Trading Signals: Complete TradingView Guide

Volume Profile trading signals for TradingView. POC, VAH, VAL, and high volume node signals with Pine Script code and real intraday examples from ES futures.

Volume Profile Signal Interpretation

Quick-reference guide to 6 signal types you will see on the chart.

Signal TypeIndicator ConditionMarket MeaningReliability
POC Bounce → Buy/SellPrice returns to the Point of Control and shows rejection with a reversal candleThe POC is where the most trading occurred. Price tends to gravitate toward it. A clean rejection at POC confirms the level holds as support or resistance. I rely on this more than VWAP for intraday ES trades.4/5
VAH Breakout → BuyPrice breaks and closes above the Value Area High with above-average volumeBreaking above VAH signals acceptance at higher prices. The market is expanding the value area upward. New longs can target the next VAH from a fresh session. Failed breakouts (close below VAH) trap late buyers.4/5
VAL Breakdown → SellPrice breaks and closes below the Value Area Low on expanding volumeBreaking below VAL signals acceptance at lower prices and bearish expansion. Aggressive sellers are in control. Short positions target a new value area lower. On NQ, these moves often cover 10-15 points before the first pullback.3/5
High Volume Node Rejection → ReversalPrice enters a high volume node zone and reverses sharply with a large range barHigh volume nodes act as price magnets but also as reversal traps once filled. A rejected HVN above current price acts as new resistance. A rejected HVN below acts as support. This is mean reversion within the value area.3/5
Low Volume Node Gap Fill → DirectionalPrice gaps through a low volume node and accelerates in that directionLow volume nodes are zones where little trading occurred. Price moves through them quickly with minimal resistance. They act as targets in directional moves. When price pulls back into an LVN, it often slices through easily.4/5
Value Area Expansion → Trend ContinuationValue area expands upward or downward for three consecutive sessions with the POC shiftingExpanding value area with a shifting POC confirms a developing trend. Shrinking value area signals consolidation. I saw this clearly on ES in October 2024: three days of expanding VAH before a 40-point rally.5/5

Volume Profile Pine Script Signal Code

Ready-to-use Pine Script code for generating buy/sell signals. Copy and paste into your TradingView Pine Editor.

Pine Script v5
//@version=5
indicator("Volume Profile Trading Signals", overlay=true, max_labels_count=50)

// === Inputs ===
lookback = input.int(48, "Lookback Period (bars)", minval=8)
numRows = input.int(24, "Number of Rows", minval=8, maxval=48)
valueAreaPct = input.int(70, "Value Area %", minval=50, maxval=90)
showPOC = input.bool(true, "Show POC Line")
showVA = input.bool(true, "Show Value Area")
showHVN = input.bool(true, "Show High Volume Nodes")
showLVN = input.bool(true, "Show Low Volume Nodes")

// === Volume Profile Calculation ===
var float pocPrice = na
var float vahPrice = na
var float valPrice = na

calcVolumeProfile() =>
    float[] prices = array.new<float>(0)
    float[] volumes = array.new<float>(0)
    float totalVol = 0.0

    for i = 0 to lookback - 1
        barVol = volume[i]
        if barVol > 0
            array.push(prices, (high[i] + low[i]) / 2)
            array.push(volumes, barVol)
            totalVol += barVol

    // Sort by price
    // Simplified: use average price bins
    float step = (ta.highest(high, lookback) - ta.lowest(low, lookback)) / numRows
    float[] binVols = array.new<float>(numRows, 0.0)
    float[] binPrices = array.new<float>(numRows, 0.0)

    float lowestPrice = ta.lowest(low, lookback)
    for j = 0 to numRows - 1
        float binPrice = lowestPrice + step * (j + 0.5)
        array.set(binPrices, j, binPrice)
        for k = 0 to array.size(prices) - 1
            float p = array.get(prices, k)
            if p >= lowestPrice + step * j and p < lowestPrice + step * (j + 1)
                array.set(binVols, j, array.get(binVols, j) + array.get(volumes, k))

    // Find POC (highest volume bin)
    float maxVol = 0.0
    int pocBin = 0
    for m = 0 to numRows - 1
        if array.get(binVols, m) > maxVol
            maxVol := array.get(binVols, m)
            pocBin := m
    pocPrice := array.get(binPrices, pocBin)

    // Calculate Value Area (70% by default)
    float totalVPVol = array.sum(binVols)
    float targetVol = totalVPVol * valueAreaPct / 100.0
    float cumVol = array.get(binVols, pocBin)
    int upperBin = pocBin
    int lowerBin = pocBin

    while cumVol < targetVol and (upperBin < numRows - 1 or lowerBin > 0)
        float upVol = upperBin < numRows - 1 ? array.get(binVols, upperBin + 1) : 0.0
        float downVol = lowerBin > 0 ? array.get(binVols, lowerBin - 1) : 0.0
        if upVol >= downVol and upperBin < numRows - 1
            upperBin += 1
            cumVol += upVol
        else if lowerBin > 0
            lowerBin -= 1
            cumVol += downVol
        else
            break

    vahPrice := array.get(binPrices, upperBin)
    valPrice := array.get(binPrices, lowerBin)

    [pocPrice, vahPrice, valPrice]

[poc, vah, val] = calcVolumeProfile()

// === Signal Detection ===
// POC Bounce Signal
pocBounceBuy = ta.crossover(close, poc) and close > open
pocBounceSell = ta.crossunder(close, poc) and close < open

// VAH Breakout
vahBreakout = ta.crossover(close, vah) and volume > ta.sma(volume, 20) * 1.5

// VAL Breakdown
valBreakdown = ta.crossunder(close, val) and volume > ta.sma(volume, 20) * 1.5

// HVN Rejection (simplified: large range rejection at current value levels)
hvnRejectBuy = low <= poc and close > open and range > ta.sma(range, 20) * 1.3
hvnRejectSell = high >= poc and close < open and range > ta.sma(range, 20) * 1.3

// LVN Gap Fill Signal
lvnGap = math.abs(vah - val) < math.abs(vah - val) * 0.3
lvnSignal = lvnGap and close > vah and volume > ta.sma(volume, 20) * 2.0

// === Plotting ===
plot(showPOC ? poc : na, "POC", color=#FFC107, linewidth=2)
plot(showVA ? vah : na, "VAH", color=#42A5F5, linewidth=1, style=plot.style_circles)
plot(showVA ? val : na, "VAL", color=#42A5F5, linewidth=1, style=plot.style_circles)
fill(plot(showVA ? vah : na), plot(showVA ? val : na), color=color.new(#42A5F5, 92), title="Value Area")

// POC Bounce Labels
plotshape(pocBounceBuy, "POC Bounce Buy", shape.labelup, location.belowbar, color=color.new(#4CAF50, 30), text="POC B", textcolor=#4CAF50, size=size.tiny)
plotshape(pocBounceSell, "POC Bounce Sell", shape.labeldown, location.abovebar, color=color.new(#FF5252, 30), text="POC S", textcolor=#FF5252, size=size.tiny)

// VAH/VAL Breakout Labels
plotshape(vahBreakout, "VAH Breakout", shape.labelup, location.belowbar, color=color.new(#4CAF50, 30), text="VAH B/O", textcolor=#4CAF50, size=size.tiny)
plotshape(valBreakdown, "VAL Breakdown", shape.labeldown, location.abovebar, color=color.new(#FF5252, 30), text="VAL B/D", textcolor=#FF5252, size=size.tiny)

// HVN Rejection Labels
plotshape(hvnRejectBuy, "HVN Reject Buy", shape.arrowup, location.belowbar, color=#4CAF50, size=size.small)
plotshape(hvnRejectSell, "HVN Reject Sell", shape.arrowdown, location.abovebar, color=#FF5252, size=size.small)

// LVN Signal
plotshape(lvnSignal, "LVN Gap", shape.star, location.top, color=#E040FB, size=size.small)

// === Alerts ===
alertcondition(pocBounceBuy, "POC Bounce Buy", "Price bounced at POC - bullish signal")
alertcondition(pocBounceSell, "POC Bounce Sell", "Price rejected at POC - bearish signal")
alertcondition(vahBreakout, "VAH Breakout", "Price broke above VAH with volume")
alertcondition(valBreakdown, "VAL Breakdown", "Price broke below VAL with volume")

Recommended Parameters for Volume Profile

Parameter settings tested across different market conditions and timeframes.

Feature comparison table: Default vs Description
ParameterDefaultDescription
Lookback Period48Number of bars used to calculate the volume profile. Shorter values (20-30) work for intraday scalping on 5m charts. Longer values (100+) smooth the profile on daily timeframes. I use 48 on 15m ES charts for a clean daily profile.
Number of Rows24How many price bins to divide the range into. More rows = finer granularity but more noise. 24 rows balances detail with readability. For NQ which has wider price ranges, 36 rows gives better POC accuracy.
Value Area %70Percentage of total volume that defines the value area. Standard is 70%. Tighten to 65% for a narrower range in range-bound markets. Widen to 80% in trending markets to capture the full activity zone.
Show POC LinetrueToggles the Point of Control horizontal line on the chart. Keep this on for all swing and intraday trading. It is the single most useful level from Volume Profile for identifying fair price.
Show Value AreatrueShows VAH and VAL lines with shaded fill between them. The shaded zone helps you see the value area at a glance. I keep this on for day trading ES but turn it off on cluttered charts during news events.
Show HVN/LVNtrueToggles high and low volume node markers. Useful for identifying support and resistance zones within the value area. On BTC 1h charts, I keep these on because the profile changes significantly session to session.

Volume Profile + Pineify Invite-Only: Better Together

Volume Profile alone gives you one signal type. Pineify invite-only indicator combines Volume Profile with RSI divergences, MACD confirmation, and Supertrend filters in one overlay. Fewer charts, clearer signals.

Instead of switching between 6 different signals on separate charts, you get a single multi-confirmation setup.

See the Invite-Only Indicator

FAQ

Volume Profile Signals FAQ

Stop juggling Volume Profile with 4 other charts

Pineify combines Volume Profile, RSI, MACD, and Supertrend into one invite-only indicator. One click setup.

Try Pineify Free