TradingView indicator guides

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.

MI = sum(E1 / E2, 25)

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.

Mass Index after three ranges of 20, starting from steady ranges of 10
BarRangeE1E2Ratio25-bar MI
Before change101010125
1201210.41.15384625.153846
22013.611.041.23188425.385730
32014.8811.8081.26016325.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.

Pine Script v6

//@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

InputDefaultEffect and constraint
EMA length9Positive integer used for both smoothing stages.
Sum length25Positive 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.

Hypothetical closed-bar sequence, with state initially unarmed
MI sequenceState or result
26.8 → 26.4No event: 27 was never exceeded.
27.2 → 26.8 → 26.5Armed, but no event at exactly 26.5.
27.2 → 26.8 → 26.4One completed event at 26.4, then reset.
27.2 → missing → 26.4Invalid 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

  1. Match the symbol, timeframe, session, chart type, and 9/25 inputs in both versions.
  2. Compare E1, E2, ratio, and MI on the same closed bars using the Data Window. Check ratio order before changing thresholds.
  3. Inspect an early-history segment and a zero-range segment. Confirm that missing data does not create a false completion.
  4. Check the state sequences in the table. Create a TradingView alert from the completion condition with Once Per Bar Close.
  5. 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 script

Mass Index questions

Mass Index measures changes in the high-low range through a ratio of two smoothed range series. The standard configuration adds 25 ratios calculated with two successive 9-period EMAs. A completed reversal bulge is a condition to investigate, not proof that price will reverse. Neither close-to-close price direction nor volume enters this calculation.

For each bar, calculate E1 = EMA(high - low, 9), then E2 = EMA(E1, 9). Divide E1 by E2 and sum the most recent 25 ratios. The numerator is the single EMA. The denominator is the EMA of that EMA. Summing the reciprocal or dividing two window sums produces a different indicator.

Neither direction follows from Mass Index alone. The conventional sequence first exceeds 27 and later crosses below 26.5. The example script remembers the first condition, emits one event on the closing bar of the second, and resets. A cross below 26.5 without an armed bulge does not produce an event.

One high-equals-low bar can still produce a valid ratio because earlier positive ranges remain in both EMA states. If the double EMA is zero, the ratio is undefined. This script marks it missing and waits for a complete valid window before displaying MI. It does not turn an undefined 0/0 ratio into a neutral reading.

Wider price ranges alone do not justify higher thresholds. Multiplying every range by a positive constant multiplies both EMAs by that constant, leaving their ratio unchanged. Different sequences of range expansion can produce different readings. Test thresholds on the intended symbol, timeframe, and evaluation period without assuming one asset class needs a fixed adjustment.

Yes, both lengths are inputs in the example. Changing the sum length also changes the scale: a stable positive range produces MI equal to the sum length once the window is full. The script therefore disables conventional bulge events outside the 9/25 configuration. The reference lines remain labeled for 9/25; alternate settings need separately defined thresholds.

Yes. The current high and low can change on an open bar, so the plotted line can move. In this example only closed bars update the bulge state or emit the completion event. Configure an alert from the script condition and select Once Per Bar Close. This confirmation policy does not guarantee that a subsequent price reversal will occur.

Mass Index is a dimensionless sum of smoothed high-low range ratios. ATR averages true range, which also compares the current high and low with the previous close, and is expressed in price units. A gap can therefore affect the two inputs differently. Use the indicator that matches the quantity you need to inspect; an MI value is not a stop distance.