What it does
The True Strength Index (TSI), introduced by William Blau, is a momentum oscillator that double-smooths price changes. The result is a line between −100 and +100 that is much smoother than raw momentum yet still responsive.
This version adds a signal line, a histogram of the gap between TSI and its signal line, reference levels at ±25, and markers plus alerts for signal-line crosses and zero-line crosses.
How it works
- Price change = source − previous source.
- Double-smoothed change = EMA(EMA(price change, long), short).
- Double-smoothed absolute change = EMA(EMA(|price change|, long), short).
- TSI = 100 × double-smoothed change ÷ double-smoothed absolute change.
- Signal = EMA(TSI, signal length). Histogram = TSI − signal.
Because the numerator can never be larger than the denominator in size, TSI stays within ±100. A reading of +40 means recent smoothed movement has been strongly one-directional upward.
Signals, all confirmed on bar close:
- TSI crosses above or below its signal line (small triangles).
- TSI crosses above or below zero (circles).
How to use it
- The zero line divides bullish from bearish momentum. A zero-line cross is slower and more meaningful for trend direction than a signal-line cross.
- Signal-line crosses work better as timing tools in the direction of the zero-line reading: in positive territory, a cross back above the signal after a dip can mark the end of a pullback.
- Readings beyond ±25 show stretched momentum. They are not automatic reversal signals but can warn that a move is extended.
- Divergence between price and TSI can also be read manually, in the same way as with other oscillators. TSI's smoothness makes H1 to W1 charts a natural fit. Combine it with technical analysis basics such as structure and levels.
Settings
| Input | Default | What it changes |
|---|---|---|
| Source | close | Price used for momentum. |
| Long smoothing | 25 | First smoothing pass. Longer = slower. |
| Short smoothing | 13 | Second smoothing pass. |
| Signal length | 13 | Speed of the signal line. |
| Upper / lower reference level | 25 / −25 | Positions of the dashed reference lines. |
| Show signal-line crosses | On | Triangle markers. |
| Show zero-line crosses | On | Circle markers. |
| Show TSI − signal histogram | On | Gap histogram. |
Limitations
- Double smoothing means real lag: zero-line crosses usually come well after a turn in price.
- Signal-line crosses are frequent in ranges and often reverse within a few bars.
- The ±25 levels are conventions; how far TSI typically stretches varies by market and timeframe.
- TSI measures momentum only and ignores volatility, levels and costs. Use it alongside a clear risk plan.
Pine Script source
In TradingView open Pine Editor, paste the code, click Save and then Add to chart.
FXP_TrueStrengthIndex.pine
61 lines
// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/true-strength-index/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
// True Strength Index: double-smoothed price momentum divided by double-smoothed absolute
// momentum, scaled to ±100, with a signal line, signal-line crosses and zero-line crosses.
indicator("FXP True Strength Index", shorttitle = "FXP TSI", overlay = false)
// ── Inputs ─────────────────────────────────────────────────────────────
grpT = "TSI"
src = input.source(close, "Source", group = grpT)
longLen = input.int(25, "Long smoothing", minval = 1, group = grpT)
shortLen = input.int(13, "Short smoothing", minval = 1, group = grpT)
sigLen = input.int(13, "Signal length", minval = 1, group = grpT)
grpL = "Levels"
upLevel = input.float(25.0, "Upper reference level", minval = 0.0, maxval = 100.0, group = grpL)
downLevel = input.float(-25.0, "Lower reference level", minval = -100.0, maxval = 0.0, group = grpL)
grpS = "Signals"
showSigX = input.bool(true, "Show signal-line crosses", group = grpS)
showZeroX = input.bool(true, "Show zero-line crosses", group = grpS)
showHist = input.bool(true, "Show TSI − signal histogram", group = grpS)
grpV = "Display"
colTsi = input.color(#2962ff, "TSI line", group = grpV)
colSig = input.color(#ff6d00, "Signal line", group = grpV)
colBull = input.color(#26a69a, "Bullish", group = grpV)
colBear = input.color(#ef5350, "Bearish", group = grpV)
// ── Calculation ────────────────────────────────────────────────────────
pc = nz(ta.change(src))
dblPc = ta.ema(ta.ema(pc, longLen), shortLen)
dblAbsPc = ta.ema(ta.ema(math.abs(pc), longLen), shortLen)
tsi = dblAbsPc != 0 ? 100.0 * dblPc / dblAbsPc : 0.0
signal = ta.ema(tsi, sigLen)
diff = tsi - signal
sigUp = ta.crossover(tsi, signal) and barstate.isconfirmed
sigDown = ta.crossunder(tsi, signal) and barstate.isconfirmed
zeroUp = ta.crossover(tsi, 0) and barstate.isconfirmed
zeroDn = ta.crossunder(tsi, 0) and barstate.isconfirmed
// ── Plots ──────────────────────────────────────────────────────────────
plot(showHist ? diff : na, "TSI − signal", color = diff >= 0 ? color.new(colBull, 60) : color.new(colBear, 60), style = plot.style_columns)
plot(tsi, "TSI", color = colTsi, linewidth = 2)
plot(signal, "Signal", color = colSig)
hline(0, "Zero line", color = color.new(color.gray, 40))
hline(upLevel, "Upper level", color = color.new(color.gray, 60), linestyle = hline.style_dashed)
hline(downLevel, "Lower level", color = color.new(color.gray, 60), linestyle = hline.style_dashed)
plotshape(showSigX and sigUp, "TSI crosses above signal", style = shape.triangleup, location = location.bottom, color = colBull, size = size.tiny)
plotshape(showSigX and sigDown, "TSI crosses below signal", style = shape.triangledown, location = location.top, color = colBear, size = size.tiny)
plotshape(showZeroX and zeroUp, "TSI crosses above zero", style = shape.circle, location = location.bottom, color = colBull, size = size.small)
plotshape(showZeroX and zeroDn, "TSI crosses below zero", style = shape.circle, location = location.top, color = colBear, size = size.small)
// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(sigUp, "TSI bullish signal cross", "TSI crossed above its signal line on {{ticker}} ({{interval}})")
alertcondition(sigDown, "TSI bearish signal cross", "TSI crossed below its signal line on {{ticker}} ({{interval}})")
alertcondition(zeroUp, "TSI above zero", "TSI crossed above zero on {{ticker}} ({{interval}})")
alertcondition(zeroDn, "TSI below zero", "TSI crossed below zero on {{ticker}} ({{interval}})")

