Negative Volume Index on TradingView: formula and limits
The Negative Volume Index indicator updates only when a bar has less volume than the preceding bar. On those bars, it compounds the closing-price return into its previous value. Equal or higher volume leaves NVI unchanged. This page shows the arithmetic and a Pine Script implementation you can inspect.
For valid prices and a known volume pair:
NVI[t] = NVI[t−1] × close[t] / close[t−1] if volume falls.
NVI[t] = NVI[t−1] otherwise.
Volume selects the bar; its magnitude does not multiply the return.
An eight-bar calculation you can reproduce
These are invented prices and volumes, with a starting NVI of 1,000. They demonstrate arithmetic, not market performance. The custom script explicitly holds its internal value when either volume observation is missing; the script below hides the affected plot points.
| Bar | Close | Volume | Rule and calculation | NVI |
|---|---|---|---|---|
| 0 | 100 | 1,000 | Seed | 1,000 |
| 1 | 110 | 800 | Lower: 1,000 × 1.10 | 1,100 |
| 2 | 99 | 800 | Equal: hold | 1,100 |
| 3 | 100 | 1,200 | Higher: hold | 1,100 |
| 4 | 90 | 900 | Lower: 1,100 × 0.90 | 990 |
| 5 | 99 | Missing | No valid volume pair: hold | 990 |
| 6 | 100 | 700 | Previous volume missing: hold | 990 |
| 7 | 110 | 500 | Lower: 990 × 1.10 | 1,089 |
Bar 2 loses 10% in price but contributes nothing because volume is equal. Bar 4 loses 10% on declining volume, so NVI falls from 1,100 to 990. Bar 6 cannot compare its 700 volume with a missing observation. Bar 7 resumes with its own 10% return; it does not recover the skipped price moves.
The final result is 1,000 × 1.10 × 0.90 × 1.10 = 1,089. Summing the selected returns would produce 1,100, which is a different calculation. A lower-volume bar with an unchanged close would also leave NVI flat.
Initial values and adjusted prices
TradingView documents a 1,000 seed for its NVI chart indicator; the Pine reference implementation of ta.nvi starts at 1. This page implements the recurrence directly with an editable initial value and explicit input checks. For the same positive-price history, starting the worked example at 1 gives a final 1.089 instead of 1,089.
Scaling a series and its EMA by the same positive constant preserves their crossover bars. An absolute threshold such as 1,050 must scale too. Starting on another date can change the accumulated level: compare the same feed, session, timeframe and history before attributing a difference to the formula.
Price adjustments can change a selected return. Consider a hypothetical 2-for-1 split: an unadjusted close moves from 100 to 50. If volume declines, the raw input would halve NVI. With both closes expressed on the same post-split basis, 50 to 50 contributes zero. The split itself did not create that loss. TradingView documents historical split adjustments and a separate dividend-adjustment option.
A constant positive multiplier applied to every close cancels in the return ratio. Adjustments across a corporate-action boundary need not cancel. Record the adjustment setting when comparing calculations, and keep the price and volume histories consistent.
Copy the NVI Pine Script v6 indicator
Paste this into a new indicator in TradingView's Pine Editor, save it and add it to the chart. The blue line is the custom NVI; amber is its EMA. The two alert conditions describe confirmed crossovers. This indicator places no orders.
// 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("Negative Volume Index audit", overlay=false)
scale = input.float(1000.0, "Initial scale", minval=0.01)
emaLength = input.int(255, "EMA length", minval=1)
// Explicit policy: hold the index when either input pair is invalid.
validVolumePair = not na(volume) and not na(volume[1]) and volume >= 0 and volume[1] >= 0
validPricePair = not na(close) and not na(close[1]) and close > 0 and close[1] > 0
validInputs = validVolumePair and validPricePair
var float nvi = scale
if validInputs and volume < volume[1]
nvi := nvi * close / close[1]
signal = ta.ema(nvi, emaLength)
// Plot gaps expose skipped bars without resetting the cumulative value.
plot(validInputs ? nvi : na, "NVI", color=#2962ff, linewidth=2, style=plot.style_linebr)
plot(validInputs ? signal : na, "NVI EMA", color=#f59e0b, style=plot.style_linebr)
plotchar(not validInputs, "Invalid input pair", "?", location.top, color=color.orange)
// Evaluate crosses on every bar; publish alert conditions only at close.
crossUp = ta.crossover(nvi, signal)
crossDown = ta.crossunder(nvi, signal)
alertcondition(barstate.isconfirmed and validInputs and crossUp, "NVI above EMA", "NVI crossed above its EMA at bar close.")
alertcondition(barstate.isconfirmed and validInputs and crossDown, "NVI below EMA", "NVI crossed below its EMA at bar close.")- Initial scale: 1,000
- Changes the index units. It does not change the volume comparison.
- EMA length: 255 bars
- Smooths NVI on every bar, including held values. It is not an NVI lookback window.
- Question mark and plot gap
- Either input pair is missing, volume is negative, or a close is zero or negative. The first dataset bar also has no previous observation. These gaps expose skipped comparisons; they do not repair the feed.
This custom recurrence requires two positive closes and two nonnegative volumes. It holds NVI when that condition fails and never substitutes zero for missing volume. It does not attempt to reproduce every exceptional-data branch of ta.nvi. After a skipped pair, it resumes with the next valid adjacent pair rather than bridging the gap.
Check the data before interpreting the line
- A reported zero volume is a number, not missing data. Zero after positive volume satisfies the decline rule. A feed filled with zeros produces a flat series after initialization; that cannot establish a lack of buying or selling.
- Volume comes from the selected symbol and feed. Confirm its units and coverage. A venue-specific crypto feed and a market-wide series are different inputs, even when they quote the same asset.
- Live volume accumulates before close. A bar can initially qualify for an NVI update and later fail the decline condition. The plotted line can change during that bar; the example alerts require
barstate.isconfirmed. - The EMA can move during flat NVI segments. A crossover describes the relation between two calculated series. It does not reveal institutional positions or establish an entry edge.
NVI is a conditional price-return index. Price Volume Trend instead adds volume-weighted returns. OBV adds or subtracts bar volume according to the direction of the close change. Their values have different meanings and cannot share arbitrary thresholds.
Specify the NVI behavior in Pineify
Use the Pine Script AI Coding Agent to adapt the script to an explicit calculation contract. Include the initial scale, chart timeframe, missing-volume treatment and bar-close requirement in the request.
Build an NVI indicator with a 1,000 initial scale and a 255-bar EMA. Explicitly hold NVI when either volume is missing, either volume is negative, or either close is missing or nonpositive. Mark skipped bars. Confirm crossover alerts at bar close. Verify the eight-bar example ends at 1,089 before adding trading rules.
Check individual bars in TradingView's Data Window after reviewing the generated source. A trading strategy also needs exits, position sizing, commissions and slippage. Syntax validation checks compilation, not predictive value. This guide is educational and is not investment advice.
Adapt NVI with PineifyNegative Volume Index questions
Sources and related guides
Official documentation checked October 17, 2026. The worked example uses hypothetical data.