PpoHistogram
PPO Histogram — the
ppo − signalbar of the Percentage Price Oscillator, a scale-free counterpart to the MACD histogram.
Quick reference
| Field | Value |
|---|---|
| Family | Price Oscillators |
| Input type | f64 (single close) |
| Output type | f64 (percentage points) |
| Output range | unbounded around zero |
| Default parameters | fast = 12, slow = 26, signal = 9 |
| Warmup period | slow + signal − 1 (exact) |
| Interpretation | Positive: PPO above its signal (rising momentum). Comparable across instruments. |
Formula
ppo = 100 · (EMA_fast − EMA_slow) / EMA_slow
signal = EMA(ppo, signal)
histogram = ppo − signalPpo itself emits only the percentage line; this indicator adds the classic signal EMA on top and reports the resulting zero-centered histogram. Because PPO divides the EMA gap by the slow EMA, the histogram is scale-free — a reading of 0.4 means the same relative momentum on any asset, unlike the price-unit MacdHistogram. Source: crates/wickra-core/src/indicators/ppo_histogram.rs.
Parameters
| Name | Type | Default | Constraint | Source |
|---|---|---|---|---|
fast | usize | 12 | > 0, < slow | PpoHistogram::new (ppo_histogram.rs:55) |
slow | usize | 26 | > 0 | PpoHistogram::new (ppo_histogram.rs:55) |
signal | usize | 9 | > 0 | PpoHistogram::new (ppo_histogram.rs:55) |
Any zero period returns [Error::PeriodZero]; fast >= slow returns [Error::InvalidPeriod] (test rejects_invalid_periods). Python defaults come from #[pyo3(signature = (fast=12, slow=26, signal=9))]; the Node constructor takes the three periods explicitly. The public class is PpoHistogram in both bindings.
Inputs / Outputs
use wickra::{Indicator, PpoHistogram};
// PpoHistogram: Input = f64, Output = f64
const _: fn(&mut PpoHistogram, f64) -> Option<f64> = <PpoHistogram as Indicator>::update;A single f64 close in, an Option<f64> out (percentage-point histogram). Python maps this to float | None / a float64 np.ndarray with NaN warmup; Node to number | null / Array<number> with NaN warmup.
Warmup
warmup_period() returns slow + signal − 1. The slow EMA seeds the PPO at input slow, then the signal EMA needs signal − 1 more PPO values. Pinned by warmup_emits_first_value_at_warmup_period (3, 6, 3: first value at input 8).
Edge cases
- Equals PPO minus a signal EMA. The output is bit-for-bit
Ppo(fast, slow)minus anEma(signal)of that line, composed by hand (testequals_ppo_minus_signal_ema). - Constant series. Both EMAs converge to the constant, PPO goes to
0, and the histogram converges to0(testconstant_series_converges_to_zero). - Steady linear trend. Unlike the MACD histogram, on a constant-slope ramp the PPO ratio keeps drifting (the gap is divided by the rising slow EMA), so the histogram stays nonzero (shown in the example below).
- Non-finite inputs. A
NaN/infinput returns the last value without advancing either the PPO stage or the signal EMA (testignores_non_finite_input). - Reset.
reset()clears the PPO, the signal EMA and the cached output (testreset_clears_state).
Examples
Rust
use wickra::{BatchExt, Indicator, PpoHistogram};
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Steady ramp 100, 102, 104, …: the PPO ratio keeps drifting, so the
// histogram is nonzero even on a straight line.
let prices: Vec<f64> = (0..20).map(|i| 100.0 + f64::from(i) * 2.0).collect();
let mut hist = PpoHistogram::new(3, 6, 3)?;
let out = hist.batch(&prices);
println!("warmup = {}", hist.warmup_period()); // 8
println!("first = {:.6}", out.into_iter().flatten().next().unwrap()); // -0.052098
Ok(())
}Output:
warmup = 8
first = -0.052098Python
import numpy as np
import wickra as ta
hist = ta.PpoHistogram(12, 26, 9)
out = hist.batch(np.array([...], dtype=float)) # percentage-point histogram, NaN warmupNode
const ta = require('wickra');
const hist = new ta.PpoHistogram(12, 26, 9);
const bar = hist.update(101.5); // null during warmup, else the histogram barStreaming
import wickra as ta
hist = ta.PpoHistogram(12, 26, 9)
for price in price_feed:
bar = hist.update(price) # None until slow + signal − 1 closes seen
if bar is not None and bar > 0:
rising_momentum() # PPO above its signalInterpretation
The PPO histogram is the MACD histogram made comparable across instruments:
- Zero crossings. A cross up through zero is PPO crossing above its signal (bullish); the reverse is bearish.
- Cross-asset screening. Because the reading is in percentage points, the same threshold (say
±0.5) is meaningful on every symbol — ideal for ranking a basket by momentum strength. - Slope = acceleration. As with MACD, rising bars mean momentum is building and shrinking bars mean it is fading.
For the PPO line on its own, use Ppo. For the price-unit version, see MacdHistogram.
Common pitfalls
- Expecting a flat reading on a straight trend. Dividing by the slow EMA keeps the ratio drifting, so a linear ramp gives a small nonzero histogram — unlike the MACD histogram, which collapses to zero there.
- Acting on the first nonzero bar. It appears only at
slow + signal − 1; earlierNone/NaNvalues are warmup.
References
- Gerald Appel, Technical Analysis: Power Tools for Active Investors, Financial Times Prentice Hall, 2005.
See also
- Ppo — the PPO line this histogram is built on.
- MacdHistogram — the price-unit counterpart.
- Apo — the absolute (price-unit) price oscillator.
- Indicators-Overview