6.7 KiB
PPO: Percentage Price Oscillator
MACD told you the spread in dollars. PPO tells you the spread in percent. One of those actually works across instruments.
| Property | Value |
|---|---|
| Category | Momentum |
| Inputs | Source (close) |
| Parameters | fastPeriod (default 12), slowPeriod (default 26), signalPeriod (default 9) |
| Outputs | Multiple series (Signal, Histogram) |
| Output range | Varies (see docs) |
| Warmup | slowPeriod + signalPeriod bars (35 default) |
| PineScript | ppo.pine |
- PPO (Percentage Price Oscillator) measures the percentage difference between a fast EMA and a slow EMA.
- Similar: MACD, APO | Complementary: Volume oscillator | Trading note: Percentage Price Oscillator; MACD expressed as percentage. Comparable across instruments.
- Validated against TA-Lib, Skender, and Tulip reference implementations where available.
PPO (Percentage Price Oscillator) measures the percentage difference between a fast EMA and a slow EMA. It is functionally equivalent to MACD normalized by the slow EMA, producing values that are comparable across instruments with different price levels. The implementation outputs three components: the PPO line, a signal line (EMA of PPO), and a histogram (PPO minus Signal).
Historical Context
PPO emerged as a direct answer to MACD's most significant architectural limitation: scale dependency. Gerald Appel's MACD (1979) reports the absolute spread between two EMAs, meaning a MACD value of 2.0 on a $200 stock represents a 1% divergence, while the same value on a $20 stock represents 10%. PPO normalizes this by dividing by the slow EMA, producing a percentage that is directly comparable across any price level.
The formula appears in most technical analysis textbooks as the "normalized MACD" or "percentage MACD." StockCharts.com popularized the PPO terminology. The default parameters (12, 26, 9) mirror MACD's defaults, making PPO a drop-in replacement for cross-instrument analysis.
This implementation uses compensated EMAs internally (via the Ema class) for improved warmup accuracy, and applies FMA where applicable for performance.
Architecture & Physics
1. Dual EMA Pipeline
Two independent EMA instances process the same input:
\text{FastEMA}_t = \text{EMA}(P_t, \text{fastPeriod})
\text{SlowEMA}_t = \text{EMA}(P_t, \text{slowPeriod})
2. Percentage Normalization
\text{PPO}_t = 100 \times \frac{\text{FastEMA}_t - \text{SlowEMA}_t}{\text{SlowEMA}_t}
Division by SlowEMA normalizes the result to a percentage. When SlowEMA is zero (startup edge case), the result defaults to 0.0.
3. Signal and Histogram
\text{Signal}_t = \text{EMA}(\text{PPO}_t, \text{signalPeriod})
\text{Histogram}_t = \text{PPO}_t - \text{Signal}_t
4. State Management
The indicator delegates state management to three internal Ema instances. The _state / _p_state pattern handles only the LastValid value for NaN sanitization.
Mathematical Foundation
Core Formulas
\text{PPO}_t = 100 \times \frac{\text{EMA}(P, f)_t - \text{EMA}(P, s)_t}{\text{EMA}(P, s)_t}
where f = fast period, s = slow period.
Relationship to MACD
| Property | MACD | PPO |
|---|---|---|
| Formula | \text{Fast} - \text{Slow} |
100 \times \frac{\text{Fast} - \text{Slow}}{\text{Slow}} |
| Units | Price units | Percentage |
| Cross-instrument | No | Yes |
| Zero crossover | Identical timing | Identical timing |
| Signal crossover | Same concept | Same concept |
Conversion
\text{PPO} = \frac{\text{MACD}}{\text{SlowEMA}} \times 100
Default Parameters
| Parameter | Default | Purpose |
|---|---|---|
| fastPeriod | 12 | Fast EMA period |
| slowPeriod | 26 | Slow EMA period |
| signalPeriod | 9 | Signal line EMA period |
Constraints
fastPeriod >= 1slowPeriod >= 1fastPeriod < slowPeriod(enforced in constructor)signalPeriod >= 1
Warmup
\text{WarmupPeriod} = \text{slowPeriod} + \text{signalPeriod}
Performance Profile
Operation Count (Streaming Mode)
| Operation | Count | Notes |
|---|---|---|
| EMA updates | 3 | fast + slow + signal |
| SUB | 2 | fast-slow, ppo-signal |
| DIV | 1 | normalization by slow |
| MUL | 1 | scale to percentage |
| Total | ~7 ops | Plus internal EMA ops |
Batch Mode (Span-based)
| Operation | Complexity | Notes |
|---|---|---|
| Per-element | O(1) | Fixed operations per bar |
| Total | O(n) | Linear scan |
| Memory | O(1) | Internal EMA state only |
Quality Metrics
| Metric | Score | Notes |
|---|---|---|
| Accuracy | 9/10 | Compensated EMA for warmup precision |
| Timeliness | 6/10 | EMA lag from both smoothing stages |
| Smoothness | 7/10 | Dual EMA provides good noise rejection |
| Simplicity | 7/10 | Straightforward composition of EMAs |
Validation
| Library | Status | Notes |
|---|---|---|
| Skender | ✅ | Matches within 1e-9 tolerance |
| TA-Lib | ✅ | PPO function matches |
| Tulip | ✅ | PPO matches exactly |
| Ooples | ✅ | Matches within 1e-6 tolerance |
Common Pitfalls
-
Period ordering:
fastPeriodmust be strictly less thanslowPeriod. The constructor enforces this with anArgumentException. -
Division by zero: When SlowEMA is zero (only during initial startup), the PPO defaults to 0.0. This is a transient condition that resolves after the first few bars.
-
Signal vs PPO: The histogram (
PPO - Signal) is the derivative of momentum. Histogram shrinking toward zero indicates momentum deceleration, not necessarily a reversal. -
Warmup asymmetry: The fast EMA becomes hot before the slow EMA.
IsHotrequires both EMAs to be warmed up, which depends on the slow period. -
Three outputs: PPO exposes
Last(PPO line),Signal, andHistogramas separateTValueproperties. Consumers must access the appropriate property for their use case. -
MACD equivalence: PPO zero crossovers occur at exactly the same points as MACD zero crossovers. The only difference is the vertical scale.
References
- Appel, G. (2005). "Technical Analysis: Power Tools for Active Investors." FT Press.
- Murphy, J. J. (1999). "Technical Analysis of the Financial Markets." New York Institute of Finance.
- StockCharts.com: "Percentage Price Oscillator (PPO)" Technical Analysis documentation.
- TA-Lib documentation: PPO function reference.