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

    Function stochasticMomentumIndex

    • Stochastic Momentum Index (William Blau, TASC January 1993) — where the close sits relative to the midpoint of the recent range rather than to its bottom, double-smoothed above and below the line:

      HH, LL          = highest high / lowest low over `period` bars
      M = close − (HH + LL) / 2 the distance from the midpoint
      H = (HHLL) / 2 half the range
      ${prefix} = 100 · EMA(EMA(M, longPeriod), shortPeriod)
      / EMA(EMA(H, longPeriod), shortPeriod)
      ${prefix}Signal = EMA(${prefix}, signalPeriod)

      Appends two columns, bounded −100 … +100 (|M| ≤ H on every bar, and an EMA has non-negative weights, so the smoothed numerator cannot exceed the smoothed denominator — provided the two EMAs step over the same bars, which is why a bar whose close is missing is blanked in the denominator too; see the loop). +100 is a close at the top of the range, −100 at the bottom, 0 exactly at the midpoint — where the classic stochastic reads 50. The midpoint reference is the point of the study: it makes the reading signed, so the zero line means something, and double smoothing makes it far quieter than the raw %K it replaces.

      The line is named ${prefix}, not ${prefix}Line — the trix and trueStrengthIndex shape, with the signal keeping the suffix.

      longPeriod / shortPeriod are the same two knobs trueStrengthIndex exposes, in the same order (the longer span applied first), because they are the same construction by the same author: Blau writes both as r and s. period is the range look-back he writes q. So the mapping to the vendor labels is:

      this study Blau typical chart label
      period q %K Length
      longPeriod r %D Length / first smoothing
      shortPeriod s EMA Length / second smoothing
      signalPeriod Signal

      Two parameterisations circulate. Blau's own — a 13-bar range, a 25-bar first smoothing and a 2-bar finish — ships here, with a 3-bar signal EMA. The other is a much shorter fork (a 5-bar range with both smoothings at 3) that charting packages ship as a "fast" SMI; it is reachable as { period: 5, longPeriod: 3, shortPeriod: 3 } and is a genuinely different reading rather than a rounding of this one — measured on the package's oracle input, the two lines sit 108.99 apart on a reading that spans −48.19 … 74.33 (scripts/oracle/generate.py). Naming both and shipping one is the F-AMBIG discipline; the number is what makes it a choice rather than a coin flip.

      The smoothing is not a parameter either, and it is what separates this from a rescaled stochastic: dropping both EMAs and reading 100 · M / H straight is 126.95 away on the same input, measured. Dropping only the second stage is a much closer miss — 3.75 at Blau's shortPeriod: 2, where the finishing EMA is light, and 23.28 at the oracle's second shape (8, 10, 4, 5). All three separations are asserted by the generator, the small one included: it is the honest size of that particular mistake rather than a number chosen to look large.

      No TA-Lib function, so the oracle is a pandas replication built on the same ewm(adjust=False) recursion the package's own EMA runs, with the analytic first-valid bar asserted and the three separations above measured.

      On gap-free input the range lands at bar period − 1, the first EMA longPeriod − 1 bars later, the second shortPeriod − 1 after that, and the signal signalPeriod − 1 after that: bars 37 and 39 at the defaults. Both EMAs go through the K2 engine's array door, which counts finite values rather than rows, so each stage steps over the previous one's warm-up instead of averaging it.

      • Scale-invariant and shift-invariant. Both M and H are differences of prices, so a shift cancels in each; a scale multiplies both and cancels in the ratio. Pinned as property tests.
      • A flat range for the whole smoothed history → undefined. The denominator is an EMA of an EMA, so it reaches exactly zero only when every bar it has seen was flat. With the default close the numerator is then zero too (|M| ≤ H bar by bar) and the ratio would be a 0/0; but close can be redirected at a column the range does not bound, and then the numerator is not forced to zero and the division is a real number over zero. The guard is live for that reason — and for a second: after a long flat run following live data the denominator underflows to zero while the numerator is still a denormal, which without the guard prints Infinity rather than a reading. Both are pinned by tests.
      • The range skips a missing bar (highestLowestValues composes on core's reducers), so an absent high does not blank the extremes; an absent close blanks that bar's M, and the EMAs step over it and carry on. Contrast the Wilder-smoothed studies, where an interior gap propagates to the end.

      Type Parameters

      • S extends SeriesSchema
      • const Prefix extends string = "smi"

      Parameters

      Returns TimeSeries<
          readonly [
              S[0],
              ValueColumnsForSchema<
                  readonly [
                      S[0],
                      ValueColumnsForSchema<S>,
                      OptionalNumberColumn<`${Prefix}`>,
                  ],
              >,
              OptionalNumberColumn<`${Prefix}Signal`>,
          ],
      >