Volume

Volume Profile Signals: POC, Value Area and Approximation Limits

Understand POC, VAH and VAL signals with a clearly labeled bar-midpoint approximation, Pine Script v6, volume checks and fixed-reference crossing rules.

Volume Profile Signal Interpretation

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

Signal TypeIndicator ConditionMarket MeaningInterpretation limits
Approximate POC recoveryThe previous close is above the current reference POC; the low touches it and the confirmed close finishes above it.Price tests the most heavily weighted midpoint bin from the prior bars.This approximation cannot identify the exact most-traded transaction price.
Approximate POC rejectionThe previous close is below the reference POC; the high touches it and the confirmed close finishes below it.Price tests the approximate POC from below.No named candle pattern or claim about buyer/seller identity is included.
Approximate VAH breakoutPrevious close is at or below the current reference VAH, current close is above it, and volume exceeds 1.5 times the prior 20-bar average.Price crosses the upper boundary of the selected contiguous bin area.Both closes are tested against the same reference level; a rolling-level jump alone is insufficient.
Approximate VAL breakdownPrevious close is at or above the reference VAL, current close is below it, and the same volume filter passes.Price crosses the lower boundary of the approximate area.Higher volume does not prove lasting acceptance, a completed trade or a return forecast.

Volume Profile Pine Script Signal Code

Educational indicator example. Review its exact rules and compare its plots with your intended calculation in TradingView. It does not place orders or establish profitability.

Pine Script v6
//@version=6
indicator("Bar-Midpoint Profile Approximation", overlay=true, max_bars_back=501)
lookback = input.int(48, "Prior Bars", minval=8, maxval=500)
rows = input.int(24, "Price Bins", minval=8, maxval=48)
valuePct = input.float(70, "Value Area Target Percent", minval=50, maxval=90)
showPOC = input.bool(true, "Show Approximate POC")
showVA = input.bool(true, "Show Approximate Value Area")

// Each completed chart bar's whole volume is assigned to its high-low midpoint.
// This is not transaction-level volume at price or TradingView's built-in profile.
profile(int n, int count, float pct) =>
    float lo = ta.lowest(low[1], n)
    float hi = ta.highest(high[1], n)
    float poc = na
    float vah = na
    float val = na
    bool valid = bar_index >= n and hi > lo
    float[] bins = array.new_float(count, 0.0)
    if valid
        float width = (hi - lo) / count
        for i = 1 to n
            if na(volume[i]) or volume[i] < 0 or na(high[i]) or na(low[i])
                valid := false
            else
                float midpoint = (high[i] + low[i]) / 2
                int bin = math.max(0, math.min(count - 1, int(math.floor((midpoint - lo) / width))))
                array.set(bins, bin, array.get(bins, bin) + volume[i])
        float total = array.sum(bins)
        if valid and total > 0
            // Equal POC volumes choose the lower bin.
            int peak = array.indexof(bins, array.max(bins))
            int upper = peak
            int lower = peak
            float included = array.get(bins, peak)
            float target = total * pct / 100
            // Adjacent-row expansion; ties choose above. May exceed the target.
            while included < target and (upper < count - 1 or lower > 0)
                float up = upper < count - 1 ? array.get(bins, upper + 1) : -1
                float down = lower > 0 ? array.get(bins, lower - 1) : -1
                if up >= down and upper < count - 1
                    upper += 1
                    included += up
                else
                    lower -= 1
                    included += down
            poc := lo + width * (peak + 0.5)
            vah := lo + width * (upper + 1)
            val := lo + width * lower
    [poc, vah, val]
[poc, vah, val] = profile(lookback, rows, valuePct)
priorAverageVolume = ta.sma(volume[1], 20)
priorValidVolume = math.sum(not na(volume[1]) and volume[1] >= 0 ? 1 : 0, 20) == 20
volumePass = priorValidVolume and priorAverageVolume > 0 and volume > 1.5 * priorAverageVolume
ready = barstate.isconfirmed and not na(poc)
recover = ready and close[1] > poc and low <= poc and close > poc
reject = ready and close[1] < poc and high >= poc and close < poc
breakUp = ready and close[1] <= vah and close > vah and volumePass
breakDown = ready and close[1] >= val and close < val and volumePass
plot(showPOC ? poc : na, "Approximate POC", color.orange, 2)
upperPlot = plot(showVA ? vah : na, "Approximate VAH", color.blue)
lowerPlot = plot(showVA ? val : na, "Approximate VAL", color.blue)
fill(upperPlot, lowerPlot, color.new(color.blue, 92))
plotshape(recover, "POC Recovery", shape.triangleup, location.belowbar, color.green, text="POC")
plotshape(reject, "POC Rejection", shape.triangledown, location.abovebar, color.red, text="POC")
plotshape(breakUp, "VAH Break", shape.labelup, location.belowbar, color.teal, text="VAH")
plotshape(breakDown, "VAL Break", shape.labeldown, location.abovebar, color.orange, text="VAL")
alertcondition(recover, "Approximate POC Recovery", "Price tested the midpoint-profile POC and closed above it")
alertcondition(reject, "Approximate POC Rejection", "Price tested the midpoint-profile POC and closed below it")
alertcondition(breakUp, "Approximate VAH Break", "Close crossed above reference VAH with the volume filter")
alertcondition(breakDown, "Approximate VAL Break", "Close crossed below reference VAL with the volume filter")

Example settings for Volume Profile

Record the inputs used by the example. These settings are not an optimized trading recommendation.

Feature comparison table: Default vs Description
ParameterDefaultDescription
Prior Bars48Uses the previous 48 completed chart bars and excludes the current bar. It is a rolling window, not an exchange session. Forty-eight 15-minute bars represent 12 hours of bars.
Price Bins24Divides the prior high-low range into equal widths. More bins change discretization; they cannot recover transaction prices missing from chart-bar data.
Value Area Target Percent70Starts at the largest bin and adds the larger adjacent bin until at least the target volume is included. Full bins can take the result above the target.
Show Approximate POC / Show Approximate Value Areatrue / trueDisplay controls for three lines and the area fill. No HVN/LVN detector or session comparison is provided.

Turn the condition into an explicit rule

Specify the calculation, confirmation time, entry and exit rules before asking Pineify to draft or revise Pine Script. Review the code and simulate realistic costs before evaluating a strategy.

Explore Pine Script coding

Related Resources

Volume Profile Pine Script ReferenceVolume Profile Alert Setup

FAQ

Volume Profile Signals FAQ

Is this a full volume-at-price calculation?
No. It assigns each prior chart bar entire volume to the bin containing its high-low midpoint. A bar with high 110, low 100 and volume 1,000 places all 1,000 units at the bin containing 105, even though actual trades may have occurred throughout that range. TradingView built-in profiles use lower-timeframe data. This approximation is for inspecting binning and signal logic, not reproducing those profiles.
How are POC, VAH and VAL selected here?
POC is the center of the largest bin, choosing the lower bin on a tie. The value area expands contiguously from that bin toward the larger adjacent volume; equal candidates choose above. Expansion stops once the included total reaches or exceeds the target. VAH and VAL are the outer edges of the included bins. This overshoot rule and tie handling are explicit example choices, not a claim of matching TradingView.
Can I check the value-area algorithm with a small example?
Suppose five adjacent bins have hypothetical volumes 10, 20, 40, 25 and 5. Total volume is 100 and a 70% target is 70. The third bin is POC. Adding the fourth brings the sum to 65; the next comparison selects the second bin, reaching 85. The area includes bins two through four. It contains at least 70%, not exactly 70%.
Why are signals based on a prior-bar profile?
Excluding the current bar prevents its volume from moving the reference during that bar. For each event, both the previous close and current close are compared with that same current reference level. The window still rolls between bars, so these lines are not a frozen prior-session profile. Confirmed markers wait for the current bar to close.
What happens when volume is unavailable?
The profile returns no levels if any bar in the required window has missing or negative volume, if total volume is zero, if history is insufficient, or if the price range has zero width. The breakout filter separately requires valid prior 20-bar volume. A data feed may supply trade volume, tick counts or another volume convention; the script cannot convert one into another.
Do high and low volume nodes reveal standing orders?
No. A profile summarizes historical reported activity. It does not show current resting orders, trader identities or institutional intent. This example calculates only the highest bin and a contiguous value area; it does not detect other high-volume peaks or low-volume valleys. Neither kind of node guarantees a bounce or fast traversal.
How does Volume Profile differ from Market Profile?
Volume Profile groups activity by price using reported volume. Time Price Opportunity profiles group the periods during which prices were visited. They can produce different points of control. Neither is automatically more precise for forecasting a trade, and this midpoint example is not a TPO implementation.
Is a volume-filtered breakout a verified trading strategy?
No. The example has no orders, exits, position sizing or measured performance. A close beyond the approximate area with volume over the specified average is a defined event. Its later path and executable trade return require separate evaluation, including costs and session choices.