MassIndex
Mass Index — Donald Dorsey's range-expansion indicator: it watches the high–low range widen and contract to anticipate reversals.
Quick reference
| Field | Value |
|---|---|
| Family | Trend & Directional |
| Input type | Candle (uses high, low) |
| Output type | f64 |
| Output range | > 0, oscillates around sum_period |
| Default parameters | (ema_period = 9, sum_period = 25) (Python) |
| Warmup period | 2·ema_period + sum_period − 2 |
| Interpretation | A rise above 27 then fall below 26.5 flags a reversal. |
Formula
range_t = high_t − low_t
single_t = EMA(range, ema_period)_t
double_t = EMA(single, ema_period)_t
ratio_t = single_t / double_t
MassIndex = Σ ratio over sum_periodThe Mass Index ignores direction entirely — it tracks volatility shape. When the high–low range widens, the single EMA pulls ahead of the double EMA, the ratio climbs above 1, and the windowed sum rises. Dorsey's "reversal bulge" is the classic pattern: the Mass Index rising above 27 and then falling back below 26.5 warns that a range expansion is about to resolve — often into a trend reversal.
Parameters
| Name | Type | Default | Valid range | Description |
|---|---|---|---|---|
ema_period | usize | 9 (Python) | >= 1 | Period of both EMAs in the cascade. 0 errors with Error::PeriodZero. |
sum_period | usize | 25 (Python) | >= 1 | Length of the summation window. |
The Python binding defaults the pair to (9, 25). The periods property returns (ema_period, sum_period).
Inputs / Outputs
From crates/wickra-core/src/indicators/mass_index.rs:
use wickra::{Indicator, MassIndex, Candle};
// MassIndex: Input = Candle, Output = f64
const _: fn(&mut MassIndex, Candle) -> Option<f64> = <MassIndex as Indicator>::update;MassIndex is a candle-input indicator: it reads high and low. In Python the streaming update accepts a 6-tuple or a dict; the batch helper takes high and low numpy arrays. Node and WASM expose update(high, low) and batch(high, low).
Warmup
warmup_period() == 2·ema_period + sum_period − 2. The first EMA seeds at input ema_period; the second EMA, stacked on it, seeds at 2·ema_period − 1; the summation window then needs sum_period ratios. For the default (9, 25) that is 41 bars.
Edge cases
- Constant range. When every bar has the same high–low range, both EMAs converge to the same value, every ratio is
1, and the Mass Index equalssum_period(constant_range_sums_to_sum_periodpins this). - Zero-range market. A flat market (
high == low) drives both EMAs to0; the0 / 0is guarded with the neutral ratio1, so the Mass Index again equalssum_period(zero_range_market_sums_to_sum_periodpins this). - Candle validation.
Candle::newrejects invalid bars upstream. - Reset.
mi.reset()clears both EMAs, the window and the sum.
Examples
Rust
use wickra::{BatchExt, Candle, Indicator, MassIndex};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut mi = MassIndex::new(3, 4)?;
// Constant high-low range of 2.0; the Mass Index settles at sum_period.
let candles: Vec<Candle> = (0..40)
.map(|i| {
let mid = 100.0 + f64::from(i);
Candle::new(mid, mid + 1.0, mid - 1.0, mid, 1.0, i64::from(i)).unwrap()
})
.collect();
let out = mi.batch(&candles);
println!("warmup_period = {}", mi.warmup_period());
println!("last = {:?}", out.last().unwrap());
Ok(())
}Output:
warmup_period = 8
last = Some(4.0)A constant range makes every ratio 1, so the sum equals sum_period (4). This matches the constant_range_sums_to_sum_period test in crates/wickra-core/src/indicators/mass_index.rs.
Python
import numpy as np
import wickra as ta
mi = ta.MassIndex() # (ema_period=9, sum_period=25)
mid = np.arange(100.0, 160.0)
high = mid + 1.0
low = mid - 1.0
print(mi.batch(high, low)[-1]) # constant range -> 25Output:
25.0Node
const ta = require('wickra');
const mi = new ta.MassIndex(9, 25);
const mid = Array.from({ length: 60 }, (_, i) => 100 + i);
const high = mid.map((m) => m + 1);
const low = mid.map((m) => m - 1);
console.log(mi.batch(high, low).at(-1)); // 25Interpretation
MassIndex is a reversal-warning tool, not a direction tool — it never tells you which way price will go, only that a turn is likely. The textbook use is the "reversal bulge" on the default (9, 25) settings: watch for the index to push above 27, then act when it drops back under 26.5, using a directional indicator (a moving average, ADX) to pick the side.
Common pitfalls
- Expecting a direction. The Mass Index is direction-blind; always pair it with a trend indicator.
- Feeding it scalar prices. It needs
high/low; it takes aCandle, not anf64.
References
Donald Dorsey, "The Mass Index", Technical Analysis of Stocks & Commodities (1992). The double-EMA-of-range construction and the (9, 25) defaults follow Dorsey's original.
See also
- Indicator-Atr — directional-free volatility in price units.
- Indicator-BollingerBands — another range-expansion lens.
- Indicators-Overview — the full taxonomy.