Mass Index indicator: formula, code, and reversal limits
The Mass Index adds ratios of a smoothed high-low range to its double-smoothed value. It tracks range expansion and contraction without identifying price direction. A reversal bulge can flag a condition for review; it cannot establish a buy or sell decision.
Use the worked calculation and Pine Script below to compare the intermediate values on each bar before adding trading rules.
Calculate the ratio before taking the sum
For the standard settings, let R = high − low, E1 = EMA(R, 9), and E2 = EMA(E1, 9). The Mass Index is the sum of E1 / E2 over the latest 25 bars. The second EMA smooths E1; it is not the separate DEMA indicator.
The formula uses a sum, not an average. With a constant positive range and matching EMA states, every ratio is 1 and MI is 25. Changing the sum length to 13 changes that reference value to 13. Keeping 27 and 26.5 after such a change does not preserve the same condition.
Formula cross-check: StockCharts Mass Index calculation. TradingView also describes the Mass Index range and bulge concept.
Worked example: the range doubles from 10 to 20
This is a hypothetical arithmetic example, not market data or a backtest. Assume at least 25 prior ranges of 10, both EMA states equal to 10, and 25 prior ratios of 1. A 9-period EMA uses alpha = 2 / (9 + 1) = 0.2.
| Bar | Range | E1 | E2 | Ratio | 25-bar MI |
|---|---|---|---|---|---|
| Before change | 10 | 10 | 10 | 1 | 25 |
| 1 | 20 | 12 | 10.4 | 1.153846 | 25.153846 |
| 2 | 20 | 13.6 | 11.04 | 1.231884 | 25.385730 |
| 3 | 20 | 14.88 | 11.808 | 1.260163 | 25.645893 |
On bar 1, E1 = 0.2 × 20 + 0.8 × 10 = 12. E2 = 0.2 × 12 + 0.8 × 10 = 10.4. Replace one old ratio of 1 with 12 / 10.4: MI = 24 + 1.153846 = 25.153846. The following bars each remove another old ratio of 1. Values in the table are rounded only for display.
No bulge is armed in these three bars because MI never exceeds 27. Reversing the price direction while keeping exactly the same high-low ranges would produce the same result. Multiplying all these ranges and initial EMA states by 10 would also leave MI unchanged.
Mass Index Pine Script v6
Paste this indicator into TradingView's Pine Editor and add it to the chart. The blue line is MI. Orange dots mark completed bulges on closed bars. Single EMA, Double EMA, and EMA ratio are available in the Data Window for numerical comparison.
//@version=6
indicator("Mass Index: range ratio and bulge", overlay=false)
emaLength = input.int(9, "EMA length", minval=1)
sumLength = input.int(25, "Sum length", minval=1)
span = high - low
singleEMA = ta.ema(span, emaLength)
doubleEMA = ta.ema(singleEMA, emaLength)
ratio = not na(span) and doubleEMA > 0 ? singleEMA / doubleEMA : na
// Require a complete window of valid consecutive chart bars.
invalidCount = math.sum(na(ratio) ? 1.0 : 0.0, sumLength)
ratioSum = math.sum(nz(ratio, 0.0), sumLength)
ready = bar_index >= sumLength - 1 and invalidCount == 0
mi = ready ? ratioSum : na
// Conventional levels are active only for the 9/25 configuration.
classic = emaLength == 9 and sumLength == 25
crossedBelow = ta.crossunder(mi, 26.5)
var bool armed = false
bool completed = false
if barstate.isconfirmed
if not ready or not classic
armed := false
else
if mi > 27
armed := true
if armed and crossedBelow
completed := true
armed := false
plot(mi, "Mass Index", color=color.blue, linewidth=2, style=plot.style_linebr)
hline(27, "Bulge level (9/25)", color=color.orange)
hline(26.5, "Return level (9/25)", color=color.gray)
plotshape(completed, title="Completed bulge", style=shape.circle,
location=location.bottom, color=color.orange, size=size.tiny)
plot(singleEMA, "Single EMA", display=display.data_window)
plot(doubleEMA, "Double EMA", display=display.data_window)
plot(ratio, "EMA ratio", display=display.data_window)
alertcondition(completed, "Mass Index bulge completed",
"Mass Index crossed below 26.5 after exceeding 27 on closed bars. Direction is unspecified.")The line updates while a bar is open. The event state updates only when barstate.isconfirmed is true. The script has no orders, exits, position sizing, or performance report. The 27 and 26.5 reference lines apply to the default configuration.
Inputs, initialization, and zero-range bars
| Input | Default | Effect and constraint |
|---|---|---|
| EMA length | 9 | Positive integer used for both smoothing stages. |
| Sum length | 25 | Positive integer; requires this many consecutive valid ratios. |
- The example delegates EMA initialization to
ta.ema. Its full-window check is a minimum data requirement, not proof that EMA seed effects have disappeared. Compare scripts with the same history and allow earlier data before the evaluation interval. - A zero current range does not automatically create a zero denominator. Previous positive ranges can keep E2 positive. The code invalidates the ratio only when the current range is missing or E2 is not positive.
- An all-zero history produces an undefined 0/0 ratio. MI stays missing until a full window of valid ratios is available. Zero substitution inside the rolling sum is paired with an invalid-count gate, so it cannot turn missing values into displayed data.
- On intraday charts, lengths count bars, not days. Different sessions, missing bars, and synthetic chart types can alter the input sequence. Match those settings before investigating a discrepancy.
A reversal bulge is an ordered condition
The conventional pattern first exceeds 27, then falls below 26.5. This implementation uses strict comparisons and requires a close for both stages. It holds the armed state until completion or invalid data; there is no time-based expiry.
| MI sequence | State or result |
|---|---|
| 26.8 → 26.4 | No event: 27 was never exceeded. |
| 27.2 → 26.8 → 26.5 | Armed, but no event at exactly 26.5. |
| 27.2 → 26.8 → 26.4 | One completed event at 26.4, then reset. |
| 27.2 → missing → 26.4 | Invalid data resets the state; no completion. |
A dot has no bullish or bearish label. Before converting the condition into a strategy, specify a separate directional rule, how long a setup remains eligible, entry timing, exits, and transaction costs. Range expansion can accompany continuation; a completed condition does not guarantee reversal.
Educational calculation and code example. No return estimate or trading recommendation is implied.
Check the calculation before adding a strategy
- Match the symbol, timeframe, session, chart type, and 9/25 inputs in both versions.
- Compare E1, E2, ratio, and MI on the same closed bars using the Data Window. Check ratio order before changing thresholds.
- Inspect an early-history segment and a zero-range segment. Confirm that missing data does not create a false completion.
- Check the state sequences in the table. Create a TradingView alert from the completion condition with Once Per Bar Close.
- Ask Pineify to adapt the verified script with explicit direction, entry, exit, and cost assumptions. Validate the revised values and signal timing again before evaluating a strategy.
The Pine Script AI Coding Agent can help write and revise the script from those requirements. Generated code still needs numerical checks; a successful syntax check alone does not validate a trading premise.
Adapt this Mass Index scriptMass Index questions
Sources and related references
Technical references checked October 17, 2026. The calculation and state tables are original hypothetical examples.