Appends two columns, bounded −100 … +100. The numerator is net
movement and the denominator is total movement over the same smoothing,
so the ratio is "what fraction of the recent motion went one way" — +100
is an unbroken run of up bars, 0 is as much down as up. Double smoothing
is what makes it readable: a single-smoothed version of the same ratio is
as noisy as the price.
The line is named ${prefix}, not ${prefix}Line — the trix
shape, with the signal keeping the family suffix.
Option names — longPeriod / shortPeriod, not long / short
Blau writes them r and s, and most vendors label them "Long" and
"Short". Both bare words are position vocabulary in a financial
package — "long" reads as a side, not a length — and every other period
option here ends in Period (kPeriod, atrPeriod, signalPeriod,
wmaPeriod, …). So they carry the suffix, matching coppock's
longPeriod / shortPeriod.
The order is not symmetric: longPeriod is applied first, to the
raw change, and shortPeriod to its output. Swapping them is a real
change, not a relabelling — measured on the oracle input, the swapped
build sits 15.93 away on a line that spans −28.7 … +80.7 — and the
generator asserts that separation.
Definition, verified
No TA-Lib TSI, so the oracle is a pandas replication. What it can
borrow from TA-Lib is the smoothing itself: the generator rebuilds an EMA
stage on TA-Lib's own SMA seed over the change array and requires it to
match talib.EMA bit-exactly (5.6e-16, measured), so the stage
arithmetic is vendor-checked even though the assembly is not. The EMAs
themselves are pond's — first-sample seed, α = 2/(span+1) — as
everywhere else in the package (the macd precedent).
Some write-ups smooth the percentage change rather than the price
change. That is a different indicator; this is Blau's, on the raw
difference, which is what makes the ratio scale-invariant without a
division per bar.
Warm-up
The change costs bar 0; each EMA stage then steps over the previous
stage's warm-up rather than seeding on it, so on gap-free input the line
first lands on bar longPeriod + shortPeriod − 1 (37 at the defaults)
and the signal signalPeriod − 1 later (43). Each column is emitted
where it is defined rather than both waiting for the slower.
Edges
A zero denominator → undefined, and there is deliberately NO guard
for it. The denominator is an EMA of absolute values, so it is zero
only when every change the recursion has consumed is exactly zero — a
perfectly flat column. Apply the test rather than the precedent: the
numerator is then forced to zero too (|numerator| ≤ denominator by
construction), so the division is a literal 0/0, which is already
NaN and already a missing cell. An if (den === 0) branch here would
change no output — measured, mutating it away fails no test — so it is
not written. (Contrast disparityIndex, whose numerator is not
forced to zero: there a zero denominator gives ±∞ and the guard is
load-bearing.) A test on a constant series pins that both columns come
back empty.
Bounded −100 … +100 by the same inequality, on any input.
Scale-invariant AND shift-invariant: both legs are built from
differences, so k·price + c leaves both columns unchanged (pinned).
This is the opposite of kst and priceMomentumOscillator,
whose rates of change read levels and therefore move under a shift.
A leading gap shifts the start; an interior gap costs the bar and
the bar after it (the difference reads a predecessor), and the EMAs then
skip and carry on — the ema family's rule, so nothing propagates to
the end.
True Strength Index (William Blau) — the bar-over-bar change, smoothed twice, divided by its own magnitude smoothed the same way:
Appends two columns, bounded
−100 … +100. The numerator is net movement and the denominator is total movement over the same smoothing, so the ratio is "what fraction of the recent motion went one way" —+100is an unbroken run of up bars,0is as much down as up. Double smoothing is what makes it readable: a single-smoothed version of the same ratio is as noisy as the price.The line is named
${prefix}, not${prefix}Line— the trix shape, with the signal keeping the family suffix.Option names —
longPeriod/shortPeriod, notlong/shortBlau writes them
rands, and most vendors label them "Long" and "Short". Both bare words are position vocabulary in a financial package — "long" reads as a side, not a length — and every other period option here ends inPeriod(kPeriod,atrPeriod,signalPeriod,wmaPeriod, …). So they carry the suffix, matching coppock'slongPeriod/shortPeriod.The order is not symmetric:
longPeriodis applied first, to the raw change, andshortPeriodto its output. Swapping them is a real change, not a relabelling — measured on the oracle input, the swapped build sits 15.93 away on a line that spans −28.7 … +80.7 — and the generator asserts that separation.Definition, verified
No TA-Lib
TSI, so the oracle is a pandas replication. What it can borrow from TA-Lib is the smoothing itself: the generator rebuilds an EMA stage on TA-Lib's own SMA seed over the change array and requires it to matchtalib.EMAbit-exactly (5.6e-16, measured), so the stage arithmetic is vendor-checked even though the assembly is not. The EMAs themselves are pond's — first-sample seed,α = 2/(span+1)— as everywhere else in the package (the macd precedent).Some write-ups smooth the percentage change rather than the price change. That is a different indicator; this is Blau's, on the raw difference, which is what makes the ratio scale-invariant without a division per bar.
Warm-up
The change costs bar 0; each EMA stage then steps over the previous stage's warm-up rather than seeding on it, so on gap-free input the line first lands on bar
longPeriod + shortPeriod − 1(37 at the defaults) and the signalsignalPeriod − 1later (43). Each column is emitted where it is defined rather than both waiting for the slower.Edges
undefined, and there is deliberately NO guard for it. The denominator is an EMA of absolute values, so it is zero only when every change the recursion has consumed is exactly zero — a perfectly flat column. Apply the test rather than the precedent: the numerator is then forced to zero too (|numerator| ≤ denominatorby construction), so the division is a literal0/0, which is alreadyNaNand already a missing cell. Anif (den === 0)branch here would change no output — measured, mutating it away fails no test — so it is not written. (Contrast disparityIndex, whose numerator is not forced to zero: there a zero denominator gives±∞and the guard is load-bearing.) A test on a constant series pins that both columns come back empty.−100 … +100by the same inequality, on any input.k·price + cleaves both columns unchanged (pinned). This is the opposite of kst and priceMomentumOscillator, whose rates of change read levels and therefore move under a shift.emafamily's rule, so nothing propagates to the end.