OHLCV Volume Profile Analyzer

Volume Profile on TradingView: Compare & Estimate

Choose a TradingView profile by its range: fixed, visible or session. This free analyzer estimates POC and value area from your OHLCV CSV using uniform volume allocation within each candle. It does not reproduce native intrabar calculations.

Free CSV Analyzer
Pine Script v6 Code Included
POC & Value Area (70%) Calculation
CSV Calculation in Your Browser

Upload Price & Volume Data

Required CSV Format

date,open,high,low,close,volume
2026-01-01,99.0,100.5,98.5,100.2,1250000
2026-01-02,100.0,101.0,99.5,100.8,1450000

Required columns: date, high, low, close, volume

The initial sample is synthetic, not market data. Files are processed locally and saved in this browser. Use one instrument, interval and session definition. Prices remain in your input units.

Profile Settings

Which TradingView volume profile fits your task?

There is no single best volume profile indicator for every chart. Use the range definition that answers your question. Native calculations use lower-timeframe bars when available; the selected interval varies by tool and available history.

MethodRangeUseful forConstraint
Fixed Range (FRVP)Two selected anchorsComparing the same event or consolidationKeep anchors fixed; Extend Right changes the sample
Visible Range (VRVP)Bars visible on screenExploring the current chart windowPanning or zooming changes the profiled period
Session Volume ProfileDefined trading sessionsComparing separate sessionsMatch regular/extended hours and exchange timezone
This CSV analyzerAll imported rowsInspecting a documented OHLCV estimateNo intrabar requests, delta or native-profile replication
Pine example belowRolling chart-bar lookbackEditing the approximation on a chartLatest profile only; includes the developing bar

Check TradingView's current plan comparison for native feature access. A drawing tool, built-in indicator and community script can have different availability. This page does not require a TradingView subscription to calculate CSV estimates.

POC and value area in this analyzer

POC is displayed at the center of the largest estimated-volume bin. VAH is the top edge of the highest included bin; VAL is the bottom edge of the lowest. The percentage is a configurable volume target, not a standard deviation or confidence interval.

Each candle contributes volume × overlap / (high − low) to each intersecting bin. A zero-range candle contributes all its volume to its price bin. This assumes uniform volume across the candle range; OHLCV alone cannot recover the actual path or trades at each price.

The largest bin starts the value area. We add the larger adjacent bin until the target is met or exceeded, so the included percentage can overshoot. Equal POC volumes select the lower bin; equal adjacent volumes expand downward first. These explicit rules differ from TradingView's documented stopping and tie-breaking rules. Rows here are equal price intervals, without native tick-size rounding.

Compare on the same sample

  1. Select one symbol and data feed on standard candles. Record interval, timezone, session hours, start and end timestamps.
  2. Apply Fixed Range to those timestamps, with Extend Right disabled. Record row size and value-area percentage.
  3. Export exactly that OHLCV range to CSV. Match the analyzer bin count and value-area percentage; native tick rounding may still create different boundaries.
  4. Compare POC, VAH and VAL in price units and in bin widths. Repeat at a finer source interval and note whether conclusions change.

This is a comparison procedure, not a completed benchmark. No accuracy percentage or native-level agreement is claimed.

Delta volume profile is a separate calculation

TradingView's native profile delta subtracts down volume from up volume using its bar-direction classification. That is different from identifying trades executed at bid or ask. This analyzer and the Pine example show total estimated volume only.

Editable Pine Script v6 approximation

Paste this complete indicator into Pine Editor and add it to the chart. It draws a rolling profile on the last bar; it does not expose a historical signal series or alerts. The live bar can move the levels. No profile is drawn until the full lookback has volume data, positive total volume and a nonzero price range. Missing volume is never replaced with invented activity.

VolumeProfileApproximation.pinePine Script v6
//@version=6
indicator("Volume Profile Approximation [Pineify]", overlay = true, max_boxes_count = 500, max_lines_count = 500, max_bars_back = 300)

// ---------------------------------------------------------
// Inputs & Configuration
// ---------------------------------------------------------
int lookbackBars     = input.int(60, "Lookback Period (Bars)", minval = 10, maxval = 300, tooltip = "Number of historical bars to aggregate volume distribution")
int numBins          = input.int(24, "Price Levels (Bins)", minval = 10, maxval = 50, tooltip = "Number of horizontal price buckets")
int vaPercent        = input.int(70, "Value Area Volume (%)", minval = 50, maxval = 90, tooltip = "Standard percentage of total volume to include in Value Area (70% standard)")
color pocColor       = input.color(color.new(#f59e0b, 0), "Point of Control (POC) Color")
color vaColor        = input.color(color.new(#3b82f6, 65), "Value Area Box Color")
color nonVaColor     = input.color(color.new(#94a3b8, 80), "Outside VA Box Color")
int profileWidthBars = input.int(25, "Histogram Width (Bars)", minval = 5, maxval = 80, tooltip = "Maximum bar length of the horizontal histogram")

// ---------------------------------------------------------
// Drawing Object Storage
// ---------------------------------------------------------
var box[] profileBoxes = array.new_box()
var line pocLine = na
var line vahLine = na
var line valLine = na

// ---------------------------------------------------------
// Volume-at-Price Calculation (Executed on Last Bar Only)
// ---------------------------------------------------------
float highestPrice = ta.highest(high, lookbackBars)
float lowestPrice = ta.lowest(low, lookbackBars)
float windowVolume = math.sum(nz(volume, 0), lookbackBars)
float missingVolumeBars = math.sum(na(volume) ? 1.0 : 0.0, lookbackBars)

if barstate.islast
    // Clean up historical drawing objects to preserve memory
    if array.size(profileBoxes) > 0
        for i = 0 to array.size(profileBoxes) - 1
            box.delete(array.get(profileBoxes, i))
        array.clear(profileBoxes)

    line.delete(pocLine)
    line.delete(vahLine)
    line.delete(valLine)

    // Find extreme high and low across the lookback period
    float priceRange   = highestPrice - lowestPrice

    if bar_index >= lookbackBars - 1 and priceRange > 0 and windowVolume > 0 and missingVolumeBars == 0
        float binSize = priceRange / numBins
        array<float> binVolumes = array.new_float(numBins, 0.0)
        float totalVol = 0.0

        // Distribute volume across price bins proportionally
        for b = 0 to lookbackBars - 1
            float bHigh = high[b]
            float bLow  = low[b]
            float bVol  = volume[b]
            float bSpan = bHigh - bLow

            if bSpan > 0
                int startBin = math.max(0, math.floor((bLow - lowestPrice) / binSize))
                int endBin   = math.min(numBins - 1, math.floor((bHigh - lowestPrice) / binSize))

                for bin = startBin to endBin
                    float binFloor = lowestPrice + bin * binSize
                    float binCeil  = binFloor + binSize
                    float overlap  = math.max(0.0, math.min(bHigh, binCeil) - math.max(bLow, binFloor))
                    float allocatedVol = bVol * (overlap / bSpan)
                    array.set(binVolumes, bin, array.get(binVolumes, bin) + allocatedVol)
            else
                int targetBin = math.min(numBins - 1, math.max(0, math.floor((close[b] - lowestPrice) / binSize)))
                array.set(binVolumes, targetBin, array.get(binVolumes, targetBin) + bVol)

            totalVol += bVol

        // Find Point of Control (POC: bin with highest estimated volume)
        float maxVol = 0.0
        int pocIndex = 0
        for i = 0 to numBins - 1
            float v = array.get(binVolumes, i)
            if v > maxVol
                maxVol := v
                pocIndex := i

        // Calculate Value Area (70% total volume around POC)
        float targetVaVol = totalVol * (vaPercent / 100.0)
        float currentVaVol = array.get(binVolumes, pocIndex)
        int vaLowIdx = pocIndex
        int vaHighIdx = pocIndex

        while currentVaVol < targetVaVol and (vaLowIdx > 0 or vaHighIdx < numBins - 1)
            float lowerVol = vaLowIdx > 0 ? array.get(binVolumes, vaLowIdx - 1) : 0.0
            float upperVol = vaHighIdx < numBins - 1 ? array.get(binVolumes, vaHighIdx + 1) : 0.0

            if lowerVol >= upperVol and vaLowIdx > 0
                vaLowIdx -= 1
                currentVaVol += lowerVol
            else if vaHighIdx < numBins - 1
                vaHighIdx += 1
                currentVaVol += upperVol
            else if vaLowIdx > 0
                vaLowIdx -= 1
                currentVaVol += lowerVol

        // Render horizontal histogram boxes
        int startBarIdx = bar_index
        for i = 0 to numBins - 1
            float bVolume = array.get(binVolumes, i)
            float ratio = maxVol > 0 ? (bVolume / maxVol) : 0.0
            int barLen = math.round(ratio * profileWidthBars)

            if barLen > 0
                float bBottom = lowestPrice + i * binSize
                float bTop    = bBottom + binSize
                bool isInVA   = (i >= vaLowIdx and i <= vaHighIdx)
                color bColor  = (i == pocIndex) ? color.new(pocColor, 30) : (isInVA ? vaColor : nonVaColor)
                color bBorder = (i == pocIndex) ? pocColor : color.new(bColor, 40)

                box bx = box.new(left = startBarIdx, top = bTop, right = startBarIdx + barLen, bottom = bBottom,
                                 border_color = bBorder, border_width = 1, bgcolor = bColor)
                array.push(profileBoxes, bx)

        // Draw POC, VAH, and VAL horizontal reference lines
        float pocPrice = lowestPrice + (pocIndex + 0.5) * binSize
        float vahPrice = lowestPrice + (vaHighIdx + 1) * binSize
        float valPrice = lowestPrice + vaLowIdx * binSize

        pocLine := line.new(x1 = bar_index - lookbackBars + 1, y1 = pocPrice, x2 = bar_index + profileWidthBars, y2 = pocPrice,
                            color = pocColor, width = 2, style = line.style_solid)
        vahLine := line.new(x1 = bar_index - lookbackBars + 1, y1 = vahPrice, x2 = bar_index + profileWidthBars, y2 = vahPrice,
                            color = color.green, width = 1, style = line.style_dashed)
        valLine := line.new(x1 = bar_index - lookbackBars + 1, y1 = valPrice, x2 = bar_index + profileWidthBars, y2 = valPrice,
                            color = color.red, width = 1, style = line.style_dashed)

CSV inputs and limits

Use the upload button with an unquoted CSV containing date (or time), high, low, close and volume. Open is optional and unused. Keep prices and volume units consistent. The tool accepts 2 to 50,000 rows and files up to 10 MB. It rejects missing or non-finite values, negative volume, closes outside the high-low range, zero total volume and a flat full range. Duplicate timestamps are counted as separate rows; remove duplicates before importing.

For further chart methods, see TradingView indicators or the fixed range volume profile guide. For custom calculation and alert logic, use Pine Script AI Agent and review the generated source before use.

Sources and interpretation

Profiles describe activity within a selected historical sample. A POC or value-area boundary does not establish fair value, institutional intent or a profitable entry. Educational information only, not investment advice.

Frequently Asked Questions

Which TradingView volume profile indicator is best?

Choose Fixed Range for a repeatable start and end time, Visible Range for the bars currently on screen, and Session Volume Profile for session comparisons. This analyzer and its Pine example use an OHLCV approximation. They do not reproduce native calculations.

Is this volume profile analyzer free?

Yes. Upload a CSV or load the synthetic sample without signing in. Calculation happens in your browser. TradingView subscriptions and market-data access are separate; check the current plan comparison for native feature availability.

What do POC, VAH and VAL mean here?

POC is the center of the bin with the most estimated volume. VAH and VAL are the upper and lower edges of the contiguous value area. The area starts at POC and expands until it meets or exceeds the selected volume percentage. These are descriptive levels, not trade signals.

Does this calculate delta volume profile?

No. This tool allocates total bar volume only. OHLCV bars do not identify aggressor buying and selling at each price. TradingView native profile delta is the difference between its up and down volume classifications; it should not be confused with exchange bid/ask trade classification.

Why can results differ from TradingView?

The data feed, session, interval, row boundaries and value-area rules can all differ. This analyzer spreads each candle volume uniformly across its high-low range. No agreement rate with native profiles has been measured.

Can the Pine example generate alerts?

The supplied example draws the latest rolling profile only. It does not create alerts or a backtestable historical POC series. Those require additional calculations and explicit rules for confirmed bars.

Customize Your Volume Profile Calculation

Use Pineify to draft and revise Pine Script calculations. Specify the data source, range, value-area rules and confirmed-bar behavior, then inspect and test the generated script.