- The Percentage Volume Oscillator (PVO) measures the difference between two exponential moving averages of volume, expressed as a percentage of the ...
The Percentage Volume Oscillator (PVO) measures the difference between two exponential moving averages of volume, expressed as a percentage of the slower EMA. Essentially the MACD of volume, PVO identifies whether volume is expanding (accumulation) or contracting (distribution) relative to its recent history. This percentage normalization makes it comparable across instruments with vastly different volume profiles.
## Historical Context
PVO emerged as analysts sought to apply the successful MACD framework to volume analysis. While MACD identifies price momentum through the convergence and divergence of moving averages, PVO does the same for volume momentum. The percentage expression (rather than absolute difference) was a deliberate design choice—it allows meaningful comparison whether you're analyzing a penny stock averaging 50,000 shares daily or a mega-cap trading 50 million.
The indicator gained traction in the 1990s as electronic trading made volume data more accessible and reliable. Its three-component structure (PVO line, signal line, histogram) mirrors MACD, making it intuitive for traders already familiar with that framework.
## Architecture & Physics
### 1. Volume Input
PVO operates purely on volume data:
$$
V_t = Volume_t
$$
Non-finite values are replaced with the last valid volume; negative volumes are clamped to zero.
Warmup ends when `e_slowest < 1e-10`, ensuring all three EMAs have converged.
## Performance Profile
### Operation Count (Streaming Mode, Scalar)
| Operation | Count | Cost (cycles) | Subtotal |
| :--- | :---: | :---: | :---: |
| ADD/SUB | 8 | 1 | 8 |
| MUL | 6 | 3 | 18 |
| DIV | 2 | 15 | 30 |
| CMP | 3 | 1 | 3 |
| FMA | 3 | 4 | 12 |
| **Total** | **22** | — | **~71 cycles** |
### Batch Mode (SIMD Considerations)
PVO's recursive EMA structure limits SIMD parallelization. The span-based `Calculate` method processes sequentially with FMA optimization. For 512 bars:
| **TA-Lib** | N/A | Has PPO (price); no volume version |
| **Skender** | N/A | Has PPO (price); no volume version |
| **Tulip** | ✅ | Has pvo; formula should match |
| **Ooples** | ✅ | May have PVO; EMA warmup may differ |
## Common Pitfalls
1.**Warmup Period**: Requires `SlowPeriod` bars for meaningful values. The EMA compensator mathematically corrects early bias, but initial signals warrant caution.
2.**Period Constraint**: Fast period must be less than slow period (`FastPeriod < SlowPeriod`). The constructor throws `ArgumentException` if violated.
3.**Zero Volume Handling**: Markets with extended periods of zero volume (pre-market, halted stocks) will produce zero PVO values. Negative volumes are clamped to zero.
4.**Scale Interpretation**: Unlike price-based MACD, PVO values are percentages. A PVO of 10 means the fast EMA is 10% above the slow EMA—significant for volume but not comparable to MACD values.
5.**Signal Crossovers**: The signal line lags PVO, so crossovers occur after the underlying momentum shift. The histogram turning positive/negative precedes the crossover.
6.**Memory Footprint**: Per instance: ~160 bytes for state struct. Three output values (PVO, Signal, Histogram) per update.