Skip to content

Positive Volume Index (PVI)

Mirror of Nvi: cumulative index that only updates when volume expands. Fosback's interpretation is that the crowd ("uninformed money") trades on volume spikes, so PVI tracks the crowd-driven leg of price action. When today's volume is at or below yesterday's, PVI is left unchanged.

Quick reference

ItemValue
FamilyVolume
Input typeCandle (uses close, volume)
Output typef64
Output rangeunbounded (anchored at 1000.0)
Default parametersnone
Warmup period2
Interpretation"Crowd money" trend on noisy days

Formula

Seed: PVI = 1000.0

PVI_t = PVI_{t-1} · (1 + (close_t - close_{t-1}) / close_{t-1})
        if volume_t > volume_{t-1}

PVI_t = PVI_{t-1}    otherwise

See crates/wickra-core/src/indicators/pvi.rs.

Parameters

None.

Inputs / Outputs

Indicator<Input = Candle, Output = f64>. Same as Nvi.

Warmup

warmup_period() == 2.

Edge cases

  • Volume equal. No update.
  • Long quiet stretches. PVI doesn't move; long noisy stretches actively move it.
  • Reset. Resets to 1000 and clears the prior bar.

Examples

Rust

rust
use wickra::{BatchExt, Candle, Indicator, Pvi};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let candles: Vec<Candle> = (0..20).map(|i| {
        let b = 100.0 + f64::from(i);
        let v = if i % 3 == 0 { 200.0 } else { 500.0 };  // alternating
        Candle::new(b, b + 0.5, b - 0.5, b, v, i as i64).unwrap()
    }).collect();
    let mut pvi = Pvi::new();
    println!("last = {:?}", pvi.batch(&candles).last());
    Ok(())
}

Python

python
import numpy as np
import wickra as ta

close = 100 + np.arange(20, dtype=float)
vol = np.where(np.arange(20) % 3 == 0, 200.0, 500.0)
pvi = ta.Pvi()
print(pvi.batch(close, vol)[-1])

Node

javascript
const wickra = require('wickra');
const pvi = new wickra.Pvi();

Streaming

rust
use wickra::{Candle, Indicator, Pvi};

let mut pvi = Pvi::new();
let candle_stream: Vec<wickra::Candle> = Vec::new(); // your live OHLCV candle feed
for bar in candle_stream {
    if let Some(v) = pvi.update(bar) {
        // v is the crowd-money cumulative index
    }
}

Interpretation

  • PVI trend. Rising PVI = price advances on busy days = the crowd is buying. Falling PVI = retail selling.
  • PVI vs 255-day EMA. Fosback's classic rule: PVI above its 255-day EMA = "crowd is bullish, bull market 79% likely per Fosback's stats" (lower than NVI's reliability — crowd is often wrong).
  • NVI + PVI together. When both rise, broad-based bull. When NVI rises and PVI falls, smart money is buying while retail sells — historically a strong contrarian signal.

Common pitfalls

  • Treating PVI in isolation. PVI alone has lower predictive value than NVI per Fosback's data. The combined NVI/PVI signal is the canonical use.
  • Frequency dependence. Same caveat as NVI — intraday vs daily behave differently.

References

  • Paul L. Dysart, 1936 — original PVI.
  • Norman G. Fosback, Stock Market Logic (1976) — popularised NVI / PVI together.

See also

  • Nvi — smart-money cousin.
  • Obv — simpler all-day cumulative volume.
  • Adl — alternative cumulative flow.
  • Indicators-Overview — full taxonomy.