@pond-ts/financial API Reference
    Preparing search index...

    Function massIndex

    • Mass Index (Donald Dorsey) — a running sum of how much a smoothed bar range exceeds its own smoothing, which is a measure of range expansion that ignores direction entirely:

      E1 = EMA(high − low, emaPeriod)
      E2 = EMA(E1, emaPeriod)
      ${output} = Σ over sumPeriod of (E1 / E2)

      Appends one column. Reads high and low, each named by an option defaulting to its DEFAULT_OHLCV name (the atr precedent).

      Dorsey's reading: the ratio sits near 1 while ranges are steady and climbs while they widen, so a 25-bar sum of it near 25 is a quiet market. The signal he named is the reversal bulge — the index rising through 27 and then falling back below 26.5 — which warns that a trend is about to turn without saying which way. The index has no direction of its own, which is the whole point of it: pair it with a trend study.

      TA-Lib has no Mass Index, so the oracle is a pandas replication with the analytic first-valid bar asserted and a measured separation from the plausible wrong turn (using the ratio's mean rather than its sum, which is the same shape divided by 25 and would sit inside a chart's noise if it were not asserted apart).

      • 9 and 25 are Dorsey's own. Both are options because the two lengths do different jobs, but the defaults are the published pair and the 27 / 26.5 bulge thresholds only mean anything at sumPeriod: 25 — a caller who changes it has to rescale the thresholds too, which is said here rather than left to be discovered.
      • A sum, not an average. The index is Σ ratio, so it lives on a ~sumPeriod scale (about 25 for a steady market). Dividing by sumPeriod would be a perfectly good indicator and is not this one; every published threshold is on the sum.
      • The EMAs are pond's, first-sample seed, α = 2/(emaPeriod + 1) — the macd / trix convention, so the chain cannot disagree with ema() inside the package.
      • Plain range, not true range (barRangeValues) — Dorsey's, and the same fork chaikinVolatility takes. atr and choppinessIndex take the other one.

      Stage 2 steps over stage 1's warm-up rather than poisoning its seed with it (that is the K2 array door's rule for derived inputs), so on gap-free input E1 lands on bar emaPeriod − 1, E2 on 2·emaPeriod − 2, and the summation sumPeriod − 1 bars after that: 2·emaPeriod + sumPeriod − 3, which is bar 40 at the defaults. Length-preserving.

      • Scale-invariant, and shift-invariant. The ratio of two linear filters of the same non-negative array is unchanged by scaling every price, and the range is a difference, so it is unchanged by shifting them too. Both are pinned by property tests.
      • A steady market reads ≈ sumPeriod, never 0. The natural comparison level is 25, not zero — this is not an oscillator around a zero line.
      • A zero denominator reports undefined, and needs no guard at all. Two ways to reach one, and the summation absorbs both. On real bars E2 = 0 requires every range from the series' start to that bar to be exactly zero (a first-sample-seeded EMA of non-negative values is zero only if all of them are), which forces E1 = 0 too, so the ratio is 0/0 — already NaN. On crossing columns (high and low redirected at two fields that swap order) the range changes sign, E2 can land exactly on zero with E1 non-zero beside it, and the ratio is ±Infinity — which rollingMeanValues counts as a missing cell, exactly as it counts a NaN, so the summation over it is undefined and nothing non-finite ever reaches withColumn. An explicit d === 0 guard was written here first and mutation testing deleted it: no input can tell it is there. That is the commodityChannelIndex finding arriving by a new route — not "the numerator is forced to zero" but "a rolling kernel downstream of the division masks non-finite values" — and it is why choppinessIndex's guards, which sit at its output, are live while this one was not. A unit test reaches the crossing case and pins the undefined.
      • A leading gap shifts the start; an interior gap blanks that bar in both EMA stages and then every summation window holding it, after which the study recovers — the ema family skips, so the hole does not run to the end of the series the way atr's does.

      Type Parameters

      • S extends SeriesSchema
      • const Output extends string = "mass"

      Parameters

      Returns TimeSeries<
          readonly [S[0], ValueColumnsForSchema<S>, OptionalNumberColumn<Output>],
      >