TradingView indicator comparisons

True Strength Index vs MACD: Double-Smoothed Momentum vs MA Difference

True Strength Index (TSI) divides double-smoothed price changes by double-smoothed absolute changes. MACD subtracts a slow moving average from a fast one. TSI expresses directional momentum relative to recent movement; EMA-based MACD retains price units. In the matched example below, TSI crosses above its signal on bar 9 while MACD remains below its signal until bar 11, even though their underlying momentum is proportional. The changing TSI denominator explains the disagreement.

See the decision guide

What each formula measures

Let C be the close and d = C(t) - C(t-1). Define EMA_N(x) recursively as alpha × x(t) + (1 - alpha) × previous EMA, with alpha = 2 / (N + 1). TSI first smooths d and |d| with a long EMA, then smooths both results with a short EMA. Write P1 = EMA_long(d), P2 = EMA_short(P1), A1 = EMA_long(|d|) and A2 = EMA_short(A1). TSI = 100 × P2 / A2 when A2 > 0.

  • Take the absolute value before smoothing. EMA_short(EMA_long(|d|)) differs from |EMA_short(EMA_long(d))| when positive and negative changes mix. The latter would destroy the intended normalization.
  • TradingView documents the TSI sequence as 25 then 13. Two linear EMA filters commute with compatible initialization and uninterrupted data; merely reversing their written order does not inherently make TSI faster. Different seeds, missing-value policies or readiness gates can still produce different startup results.
  • The signal smooths the final ratio, not P2 and A2 separately again. Some implementations express TSI as a ratio on -1 to +1; this page uses 100 × that ratio. Check scaling before copying thresholds.
  • TradingView MACD allows EMA or SMA for both oscillator and signal calculations. This page selects EMA throughout. An SMA-based comparison is a different experiment.
ComponentTrue Strength IndexEMA-based MACD
Main line100 × P2 / A2EMA_fast(C) - EMA_slow(C)
SignalEMA of the completed TSI ratioEMA of the MACD line
Difference from signalTSI - TSI signal, in oscillator pointsMACD - MACD signal, in price units
Zero meansP2 = 0, provided A2 > 0Fast and slow averages are equal
ScaleBetween -100 and +100 with these compatible positive-weight EMA seedsNo universal fixed limits
Common reference lengthsFirst EMA 25, second EMA 13; specify the signal separatelyFast 12, slow 26, signal 9; specify both MA types

One close matrix, with every TSI stage exposed

The synthetic closes at bars 0 through 11 are 100, 100, 104, 108, 112, 108, 109, 110, 111, 112, 113, 114. Use TSI long 5 and short 3, MACD fast 3 and slow 5, and a 3-bar EMA for each signal. These small lengths make the arithmetic inspectable; they are not recommended settings. Assume a constant close of 100 before bar 0: both price EMAs start at 100 and all four change EMA states start at zero. The first change is explicitly zero. Both signal EMAs start together at bar 2, each seeded with its own first defined main-line value. Thus their length, seed rule and starting timestamp match.

  • At bar 2, P1 = 4/3 and P2 = 2/3. A1 and A2 are identical, so TSI = 100. At bar 5, the -4 change reduces P1 to 44/81 while |d| raises A1 to 260/81. P2 = 433/324, A2 = 865/324 and TSI = 100 × 433/865 = 50.057803.
  • Values are rounded only for display. Independent rational-number convolution and a separate floating-point recurrence agree across all stages to within 1.5 × 10^-14 on this fixture. No live-market or strategy-return claim follows from that arithmetic check.
  • The signal seed convention is deliberately controlled. It does not claim that an unmodified built-in MACD waits for a separate TSI indicator before starting its signal EMA.
BarClosedP1 (5)P2 (3)A1 (5)A2 (3)TSITSI signal (3)
010000.0000000.0000000.0000000.000000UndefinedUndefined
110000.0000000.0000000.0000000.000000UndefinedUndefined
210441.3333330.6666671.3333330.666667100.000000100.000000
310842.2222221.4444442.2222221.444444100.000000100.000000
411242.8148152.1296302.8148152.129630100.000000100.000000
5108-40.5432101.3364203.2098772.66975350.05780375.028902
610910.6954731.0159472.4732512.57150239.50790257.268402
711010.7969820.9064641.9821672.27683539.81247948.540440
811110.8646550.8855601.6547781.96580645.04815646.794298
911210.9097700.8976651.4365191.70116352.76771849.781008
1011310.9398470.9187561.2910131.49608861.41055155.595779
1111410.9598980.9393271.1940081.34504869.83592362.715851

The same bars produce a signal disagreement

MACD uses the same closes and the same baseline. At bar 2, its fast EMA is 102 and its slow EMA is 101.333333, so MACD is 0.666667. Both signals start on that bar, then use alpha = 0.5. Compare a strict above-signal state; an upward crossing additionally requires the previous paired valid bar to be at or below its signal. Do not count the first defined observation as a crossing.

  • At bar 8, TSI is 45.048156, below its 46.794298 signal. At bar 9, TSI is 52.767718, above 49.781008: an upward crossing. MACD is 0.897665, below 0.938559, so its histogram remains -0.040894. Bar 10 still disagrees; MACD crosses above its signal at bar 11.
  • From bar 8 to bar 9, P2 increases only about 1.37%, while A2 falls about 13.46%. Earlier four-unit moves are losing weight while the new closes advance by one unit. The ratio rises more strongly than the unnormalized momentum. This denominator effect explains the TSI crossing without a generic claim that TSI is faster.
  • For exactly these compatible seeds and lengths, MACD(3,5) equals P2 on every bar. More generally, EMA_fast(C) - EMA_slow(C) = (slow - fast)/2 × EMA_fast(EMA_slow(d)) under constant-price prehistory. Consequently their zero-line signs agree whenever A2 > 0 and slow > fast. The identity does not cover arbitrary seeds or unmatched length pairs, including TSI 25/13 versus MACD 12/26.
  • The table has no negative main-line momentum after bar 2, yet its MACD histogram is negative on bars 5 through 10. Requiring a positive main line, a positive histogram or a fresh signal crossing selects different observations.
BarCloseFast EMA (3)Slow EMA (5)MACDSignal (3)Histogram
0100100.000000100.0000000.000000UndefinedUndefined
1100100.000000100.0000000.000000UndefinedUndefined
2104102.000000101.3333330.6666670.6666670.000000
3108105.000000103.5555561.4444441.0555560.388889
4112108.500000106.3703702.1296301.5925930.537037
5108108.250000106.9135801.3364201.464506-0.128086
6109108.625000107.6090531.0159471.240226-0.224280
7110109.312500108.4060360.9064641.073345-0.166881
8111110.156250109.2706900.8855600.979452-0.093893
9112111.078125110.1804600.8976650.938559-0.040894
10113112.039062111.1203070.9187560.928657-0.009901
11114113.019531112.0802050.9393270.9339920.005335

Price scaling changes MACD units, not its sign

Repeat the entire experiment with every close multiplied by 10, including the initial price EMA seeds. P1, P2, A1 and A2 all multiply by 10, so the TSI ratio and its signal are unchanged. Both price EMAs also multiply by 10, leaving a MACD difference that is 10 times larger. Its signal and histogram scale with it. These statements require a positive constant multiplier and the same initialization rules.

  • A MACD > 1 price-unit filter fails on the original bar 9 and passes after scaling. A MACD > signal filter keeps the same result. Crossings of zero and of a consistently scaled signal are invariant to this positive scaling.
  • An affine shift of all closes by a constant also cancels out of both indicators when the seeds shift consistently. A stock split applied to only part of an unadjusted history is not a uniform rescaling and can create an artificial change.
  • TSI normalization permits a common numerical scale, but it does not make two symbols equally volatile or give their TSI levels the same outcome probabilities. MACD magnitudes across differently priced assets need a separately defined normalization.
Bar 9 outputOriginal pricesPrices × 10
TSI52.76771852.767718
TSI signal49.78100849.781008
MACD0.8976658.976647
MACD signal0.9385599.385586
MACD histogram-0.040894-0.408939

Initialization, flat prices and missing observations

A recursive EMA has a first output before its initialization influence has vanished. This fixture uses a known constant prehistory. A first-nonmissing-change seed or an SMA seed changes early TSI values and can break the proportionality used above. Loading more chart history can change the starting state. Match seeds and available history before interpreting early differences as indicator behavior.

  • A flat series from initialization has P2 = A2 = 0. TSI is undefined, not zero, while MACD is zero. This comparison leaves both signal lines undefined until TSI exists so their initialization remains paired.
  • A flat interval after earlier movement is different: both smoothed change states retain history and decay. TSI need not immediately fall to zero; its denominator can become very small. Do not force a denominator of zero to one or convert an undefined TSI into a neutral reading.
  • The script resets every stage on an na close and starts a new constant-price baseline at the next valid close. It discards the change across the gap, leaves TSI undefined on that restart bar, and requires a valid prior comparison before marking a crossing. This is an explicit experimental policy, not a claim about every built-in function.
  • Chart mode gates signal markers until more than ten times the largest selected EMA length of consecutive closes. That is a chosen warm-up screen, not a numerical error bound. Increase it and compare loaded-history variants; double smoothing and a small denominator can prolong sensitivity.
  • Both indicators can whipsaw. A crossover is a condition on past and current prices, not an order, forecast or proof of an edge. Test any complete rule with fixed costs, execution timing and a reserved evaluation period.

Reproduce the fixture in Pine Script v6

Paste the full indicator into Pine Editor. Fixture mode places the 12 closes on the first 12 dataset bars, so scroll to the beginning and inspect Fixture bar in the Data Window. Later fixture observations are intentionally missing. The chart supplies only a bar clock. Switch Pane units to inspect one scale at a time; all stages remain in the Data Window. T marks the TSI upward crossing at bar 9 and M marks the MACD upward crossing at bar 11. Every displayed value and marker waits for the chart bar to close.

  • With default lengths, the embedded reference check compares both main lines and both signal lines with the independent calculation; it also accounts for the Price multiplier. A mismatch raises a runtime error. Set Price multiplier to 10 to reproduce the scaling table.
  • Flat mode holds 100 throughout. Missing mode replaces fixture bar 6 with na and restarts on bar 7 at 110. Chart close mode reads actual chart closes. The plotted values remain inspectable during warm-up; only crossing markers are gated.
  • The published tables come from independent computation, not captured TradingView runtime output. The successful syntax check does not execute the fixture assertions. Inspect the Data Window and markers after adding the script to verify its runtime behavior in your chart environment.
  • No higher-timeframe request or negative plot offset is used. Standard chart candles, source, session and history must match for a market comparison. Confirmed chart bars would not by themselves confirm data from an unfinished higher timeframe.
Original Pine v6 comparison with explicit EMA states, a common signal seed policy, flat/missing modes and independent fixture reference values. Syntax checked with Pineify; no orders are placed.pine
//@version=6
indicator("True Strength Index vs MACD: same closes", overlay = false, precision = 6)
mode = input.string("Fixture", "Data", options = ["Fixture", "Flat", "Missing", "Chart close"])
scale = input.float(1.0, "Price multiplier", minval = 0.001)
longLen = input.int(5, "TSI first EMA length", minval = 1)
shortLen = input.int(3, "TSI second EMA length", minval = 1)
fastLen = input.int(3, "MACD fast EMA length", minval = 1)
slowLen = input.int(5, "MACD slow EMA length", minval = 1)
signalLen = input.int(3, "Shared signal EMA length", minval = 1)
pane = input.string("TSI", "Pane units", options = ["TSI", "MACD"])

// Each written call owns a separate state. Missing data resets all stages.
emaStep(float x, int length, bool reset, float seed) =>
    var float state = na
    float alpha = 2.0 / (length + 1)
    state := reset ? seed : alpha * x + (1.0 - alpha) * state
    state

var prices = array.from(100.0, 100.0, 104.0, 108.0, 112.0, 108.0, 109.0, 110.0, 111.0, 112.0, 113.0, 114.0)
// Anchor the 12 samples at the dataset's first bar; later fixture bars are na.
float raw = mode == "Chart close" ? close : bar_index < 12 ? array.get(prices, bar_index) : na
if mode == "Flat"
    raw := 100.0
if mode == "Missing" and bar_index == 6
    raw := na
c = raw * scale
reset = na(c) or na(c[1])
delta = na(c) ? na : na(c[1]) ? 0.0 : c - c[1]
zeroSeed = na(c) ? na : 0.0
p1 = emaStep(delta, longLen, reset, zeroSeed)
p2 = emaStep(p1, shortLen, reset, zeroSeed)
a1 = emaStep(math.abs(delta), longLen, reset, zeroSeed)
a2 = emaStep(a1, shortLen, reset, zeroSeed)
tsi = a2 > 0 ? 100.0 * p2 / a2 : na
fast = emaStep(c, fastLen, reset, c)
slow = emaStep(c, slowLen, reset, c)
macd = fast - slow
// Start both signal EMAs on the first jointly defined observation.
signalReset = na(tsi) or na(tsi[1])
tsiSignal = emaStep(tsi, signalLen, signalReset, tsi)
macdSignal = emaStep(macd, signalLen, signalReset, na(tsi) ? na : macd)
hist = macd - macdSignal
var int consecutive = 0
consecutive := na(c) ? 0 : consecutive + 1
warmup = mode == "Chart close" ? 10 * math.max(math.max(longLen, shortLen), math.max(math.max(fastLen, slowLen), signalLen)) : 0
ready = consecutive > warmup and not na(tsiSignal) and not na(macdSignal)
confirmed = barstate.isconfirmed
upTsi = ready and ready[1] and tsi > tsiSignal and tsi[1] <= tsiSignal[1]
upMacd = ready and ready[1] and hist > 0 and hist[1] <= 0

plot(confirmed and pane == "TSI" ? tsi : na, "TSI pane", color.blue)
plot(confirmed and pane == "TSI" ? tsiSignal : na, "TSI signal pane", color.orange)
plot(confirmed and pane == "MACD" ? macd : na, "MACD pane", color.blue)
plot(confirmed and pane == "MACD" ? macdSignal : na, "MACD signal pane", color.orange)
plot(confirmed ? bar_index : na, "Fixture bar", display = display.data_window)
plot(confirmed ? c : na, "Close", display = display.data_window)
plot(confirmed ? delta : na, "Change", display = display.data_window)
plot(confirmed ? p1 : na, "Change EMA 1", display = display.data_window)
plot(confirmed ? p2 : na, "Change EMA 2", display = display.data_window)
plot(confirmed ? a1 : na, "Abs change EMA 1", display = display.data_window)
plot(confirmed ? a2 : na, "Abs change EMA 2", display = display.data_window)
plot(confirmed ? tsi : na, "TSI", display = display.data_window)
plot(confirmed ? tsiSignal : na, "TSI signal", display = display.data_window)
plot(confirmed ? fast : na, "Fast EMA", display = display.data_window)
plot(confirmed ? slow : na, "Slow EMA", display = display.data_window)
plot(confirmed ? macd : na, "MACD", display = display.data_window)
plot(confirmed ? macdSignal : na, "MACD signal", display = display.data_window)
plot(confirmed ? hist : na, "MACD histogram", display = display.data_window)
plot(confirmed and ready ? (tsi > tsiSignal ? 1 : 0) : na, "TSI above signal", display = display.data_window)
plot(confirmed and ready ? (hist > 0 ? 1 : 0) : na, "MACD above signal", display = display.data_window)
plotchar(confirmed and upTsi, "TSI crosses above signal", "T", location.top, color.blue)
plotchar(confirmed and upMacd, "MACD crosses above signal", "M", location.bottom, color.orange)

// Independent rational calculation references; checked when fixture defaults run.
var expectedTsi = array.from(float(na), float(na), 100, 100, 100, 50.057803468208, 39.507901580316, 39.812478819144, 45.048155551517, 52.767717775522, 61.410550930297, 69.835923089941)
var expectedTsiSignal = array.from(float(na), float(na), 100, 100, 100, 75.028901734104, 57.26840165721, 48.540440238177, 46.794297894847, 49.781007835185, 55.595779382741, 62.715851236341)
var expectedMacd = array.from(0, 0, 0.66666666666667, 1.4444444444444, 2.1296296296296, 1.3364197530864, 1.0159465020576, 0.90646433470508, 0.88555955647005, 0.89766470431337, 0.91875563620891, 0.93932667413927)
var expectedMacdSignal = array.from(float(na), float(na), 0.66666666666667, 1.0555555555556, 1.5925925925926, 1.4645061728395, 1.2402263374486, 1.0733453360768, 0.97945244627343, 0.9385585752934, 0.92865710575116, 0.93399188994521)
same(float actual, float expected) =>
    na(expected) ? na(actual) : not na(actual) and math.abs(actual - expected) < 0.00000001 * math.max(1.0, math.abs(expected))
isFixture = mode == "Fixture" and longLen == 5 and shortLen == 3 and fastLen == 3 and slowLen == 5 and signalLen == 3
if isFixture and bar_index < 12
    ok = same(tsi, array.get(expectedTsi, bar_index)) and same(tsiSignal, array.get(expectedTsiSignal, bar_index)) and same(macd, scale * array.get(expectedMacd, bar_index)) and same(macdSignal, scale * array.get(expectedMacdSignal, bar_index))
    if not ok
        runtime.error("Fixture differs from independent reference")

Turn the chosen measurement into an explicit rule

Use TSI when the rule asks about signed momentum relative to recent absolute movement. Use EMA-based MACD when it asks about the separation of two price averages. Specify the signal definition separately. In this fixture, the zero-line states agree but the signal-line states do not, so requiring both signals creates another filter rather than independent confirmation.

  • The Pine Script AI Coding Agent can draft and edit a comparison from these specifications. Give it the close source, four lengths, shared signal length, seed policy, missing-data policy and confirmed-bar condition; explicitly request a syntax check.
  • Keep the fixture and inspect returned values before adding entries, exits or alerts. Then compare selected timestamps on the same market data. Code generation and a syntax pass do not establish a profitable strategy.
Where Pineify fits

Pine Script AI Coding Agent

Draft or edit a TSI and MACD comparison with explicit seeds, signal rules and confirmation. Request syntax checking and compare the output with the fixture.

Draft a matched comparison

Frequently asked questions

Educational comparison, not investment advice. The close matrix is synthetic arithmetic evidence, not market history or backtested performance. Indicator agreement, generated code and syntax checks do not guarantee profitable trades.

Sources and verification

Published by Pineify. Pineify is an independent product and is not affiliated with TradingView. Sources support the technical explanations; examples do not establish trading performance.