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= (HH − LL) /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.
Option names — Blau's other study's names, deliberately
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
Which defaults — Blau's (13, 25, 2), and the short fork named
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.
Definition, verified
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.
Warm-up — per column
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.
Edges
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.
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:
Appends two columns, bounded −100 … +100 (
|M| ≤ Hon 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 whosecloseis missing is blanked in the denominator too; see the loop).+100is a close at the top of the range,−100at the bottom,0exactly 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%Kit replaces.The line is named
${prefix}, not${prefix}Line— the trix and trueStrengthIndex shape, with the signal keeping the suffix.Option names — Blau's other study's names, deliberately
longPeriod/shortPeriodare 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 asrands.periodis the range look-back he writesq. So the mapping to the vendor labels is:periodq%K LengthlongPeriodr%D Length/ first smoothingshortPeriodsEMA Length/ second smoothingsignalPeriodSignalWhich defaults — Blau's
(13, 25, 2), and the short fork namedTwo 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 / Hstraight is 126.95 away on the same input, measured. Dropping only the second stage is a much closer miss — 3.75 at Blau'sshortPeriod: 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.Definition, verified
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.Warm-up — per column
On gap-free input the range lands at bar
period − 1, the first EMAlongPeriod − 1bars later, the secondshortPeriod − 1after that, and the signalsignalPeriod − 1after 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.Edges
MandHare differences of prices, so a shift cancels in each; a scale multiplies both and cancels in the ratio. Pinned as property tests.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 defaultclosethe numerator is then zero too (|M| ≤ Hbar by bar) and the ratio would be a0/0; butclosecan 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 printsInfinityrather than a reading. Both are pinned by tests.highdoes not blank the extremes; an absentcloseblanks that bar'sM, and the EMAs step over it and carry on. Contrast the Wilder-smoothed studies, where an interior gap propagates to the end.