TradingView indicator guides

Positive Volume Index on TradingView: formula and limits

The Positive Volume Index indicator compounds the closing-price return only when volume exceeds the preceding bar's volume. Equal or lower volume leaves the index unchanged. A larger volume increase does not give the return more weight. Use the formula, worked example and Pine Script to check which bars contribute.

For valid prices and a known volume pair:

PVI[t] = PVI[t-1] × (1 + (close[t] - close[t-1]) / close[t-1]) when volume[t] > volume[t-1].

Otherwise, keep PVI[t-1]. The update also equals PVI[t-1] × close[t] / close[t-1].

A +2% eligible return multiplies PVI by 1.02; it does not add 2 index points.

A twelve-bar PVI audit

This synthetic sequence starts both calculations at 1,000. The blue-series column follows the official ta.pvi reference rules after scaling; the custom column requires two known volume observations and two positive closes. Values are calculated from the displayed inputs, not from market history or a backtest.

Hypothetical PVI values comparing built-in and skip-missing policies
BarCloseVolumeDecisionBuilt-in × 1,000Skip-missing
01001,000Seed1,0001,000
11101,500Higher volume; +10%1,1001,100
21211,500Equal volume; hold1,1001,100
3108.91,200Lower volume; hold1,1001,100
4119.790Zero current volume; hold1,1001,100
5131.769200200 > 0; +10%1,2101,210
6144.9459MissingMissing current volume; hold1,2101,210
7159.44049300Previous volume missing; policies differ1,3311,210
8175.384539400Higher volume; +10% for both1,464.11,331
90500Zero current close; skip1,464.11,331
10100600Zero previous close; skip1,464.11,331
11110700Valid pair; +10% for both1,610.511,464.1

Bar 5 uses the immediately preceding close: 1,100 × 131.769 / 119.79 = 1,210. It does not catch up with returns ignored on bars 2 to 4. At bar 7, the built-in comparison treats the previous missing volume as zero, while the custom calculation skips the bar. Both later compound from their different totals.

With a seed of 1 instead of 1,000, the built-in column ends at 1.61051. A positive rescaling also rescales its EMA, so a same-series crossover does not change solely because the seed changed. Fixed numeric thresholds must use the chosen units.

Copy a Pine Script v6 comparison

Paste this indicator into TradingView's Pine Editor and add it to the chart. Blue is the scaled built-in PVI, amber is its EMA, and purple circles show the custom skip-missing series. A question mark flags an input pair rejected by the custom policy. This is an indicator with alert conditions; it places no strategy orders.

Pine Script v6

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify
//@version=6
indicator("Positive Volume Index audit", overlay=false)

scale = input.float(1000.0, "Initial scale", minval=0.01)
emaLength = input.int(255, "Built-in PVI EMA length", minval=1)
showStrict = input.bool(true, "Show skip-missing comparison")
confirmedOnly = input.bool(true, "Plot only confirmed bars")

// Built-in series starts at 1; scale changes its display units.
builtinPvi = ta.pvi * scale
signal = ta.ema(builtinPvi, emaLength)

// Custom policy: require known, nonnegative volume and positive prices.
validVolumes = not na(volume) and not na(volume[1]) and volume >= 0 and volume[1] >= 0
validPrices = not na(close) and not na(close[1]) and close > 0 and close[1] > 0
validInputs = validVolumes and validPrices
var float strictPvi = scale
strictPvi := nz(strictPvi[1], scale)
if validInputs and volume > volume[1]
    strictPvi := strictPvi * close / close[1]

canPlot = not confirmedOnly or barstate.isconfirmed
plot(canPlot ? builtinPvi : na, "Built-in PVI", color=#2962ff, linewidth=2)
plot(canPlot ? signal : na, "Built-in PVI EMA", color=#f59e0b)
plot(canPlot and showStrict ? strictPvi : na, "Skip-missing PVI", color=#a855f7, style=plot.style_circles)
plotchar(canPlot and showStrict and bar_index > 0 and not validInputs, "Custom calculation skipped", "?", location.top, color=color.orange)

crossUp = ta.crossover(builtinPvi, signal)
crossDown = ta.crossunder(builtinPvi, signal)
alertcondition(barstate.isconfirmed and validInputs and crossUp, "PVI above EMA", "Built-in PVI crossed above its EMA at bar close.")
alertcondition(barstate.isconfirmed and validInputs and crossDown, "PVI below EMA", "Built-in PVI crossed below its EMA at bar close.")
InputDefaultEffect
Initial scale1,000Sets display units and the custom seed.
Built-in PVI EMA length255 barsSmooths the built-in series, including held values.
Show skip-missing comparisonOnDisplays the custom series and rejected-input markers.
Plot only confirmed barsOnHides open-bar plots. Alerts always require confirmation and valid inputs.

The script uses the current chart timeframe and does not request another timeframe. On a live bar, volume can cross above the previous total and price can change before the close. Create an alert from the named crossover condition after adding the indicator; the code evaluates crosses each bar and gates publication with barstate.isconfirmed.

What changes the result?

Missing volume is different from zero

A reported zero is a numeric observation. Missing volume is unavailable data. The custom calculation holds its total when either volume is missing and also skips the next bar if its previous volume is missing. It resumes using the next valid adjacent pair, without filling the omitted return. The built-in uses its own reference rules; compare policies explicitly.

Zero prices need a defined policy

The general return formula cannot divide by a previous close of zero. The official reference holds when either close is zero or missing. The custom comparison also rejects negative prices. It is intended for positive-price data; its treatment of negative-price instruments is a deliberate difference from the built-in.

History and adjustments are inputs

A cumulative index carries earlier selected returns, so compare the same starting history, session and feed. Dividend adjustment changes the price series. By the formula, a changed return on an eligible bar changes PVI and its later values. A uniform price rescaling leaves the close ratio unchanged; an adjustment across an event can change that ratio. Keep the adjustment setting fixed when auditing results.

Higher volume does not mean a positive return

If volume rises from 1,000 to 1,500 and price falls from 100 to 98, PVI falls from 1,000 to 980. If volume instead rises to 15,000, PVI still becomes 980. The qualifying return has the same weight. A flat PVI can also mean an unchanged price or skipped data, so inspect the input pair before interpreting it.

PVI describes selected price returns. It does not identify retail traders, institutional investors, accumulation or distribution by a named group. The worked values and alert conditions are educational calculations, not investment advice or evidence of profitability.

PVI and NVI select different bars

Volume relationshipPVINVI
Current > previousApply price returnHold
Current < previousHoldApply price return
Current = previousHoldHold

This comparison assumes valid inputs and matched seeds. Neither index weights the return by the size of volume. For the lower-volume calculation, see the Negative Volume Index guide. For a volume-weighted cumulative contribution, compare Price Volume Trend.

Specify the calculation before a trading rule

Pineify's Pine Script AI Coding Agent can help turn a written calculation into code. Provide the input policy and expected bar values so the output can be checked.

  1. Choose the built-in PVI or the custom skip-missing policy and state the initial scale.
  2. Specify the chart timeframe, EMA length and whether plots may show an unfinished bar.
  3. Supply the worked rows and ask for values that can be compared in TradingView's Data Window.
  4. Check equal volume, zero volume, missing data and zero prices before adding strategy logic.
  5. Define entries, exits, position sizing, commissions and slippage separately before running a backtest.

Positive Volume Index questions

PVI compounds closing-price returns on bars with more volume than the preceding bar. Volume selects which returns count; it is not a multiplier. A qualifying bar can lower PVI when its close falls. The indicator does not identify the traders behind the volume.

ta.pvi is a built-in series float variable in Pine Script v6. Use ta.pvi without parentheses. A moving average is a separate calculation, such as ta.ema applied to that series.

The Pine reference implementation starts at 1; the TradingView PVI indicator uses 1000. A positive scale factor changes the units. Compare the same history, session, data feed and missing-data policy before assuming every difference is just scale.

Equal volume does not satisfy the strict greater-than condition. With nonnegative volume, a current zero cannot qualify, but positive volume following zero can. An unchanged close still leaves PVI unchanged even on a qualifying bar.

No. The official ta.pvi reference replaces missing previous volume with zero. A positive current volume can then qualify if prices pass its checks. The custom comparison on this page instead skips a bar whenever either volume observation is missing, so it can produce a different cumulative series.

255 is the TradingView indicator default and represents about a trading year on daily equities. It counts bars, so it is not a year on weekly or hourly charts. An EMA crossover is a defined comparison, not evidence that a trading rule will be profitable.

PVI selects higher-volume bars; NVI selects lower-volume bars. Equal-volume bars qualify for neither. Those complementary conditions do not reveal retail or institutional identity, and the two index levels should not be interpreted as amounts of money invested.

Yes. The live closing price and accumulated volume can change during the bar. This script hides open-bar plots by default and publishes crossover alert conditions only on confirmed bars with valid inputs. Disabling the plot option exposes live values; alerts remain close-only.

Sources and calculation notes

Official documentation reviewed October 17, 2026. The worked sequence is hypothetical; the skip-missing comparison is a custom policy.

Browse TradingView indicator guides