TradingView indicators

Schaff Trend Cycle on TradingView

The Schaff Trend Cycle (STC) applies two stochastic normalizations and two smoothing steps to the MACD line. It expresses that transformed momentum on a 0 to 100 scale. A reading near an extreme does not prove that price will reverse.

To reproduce an STC indicator, match more than its lengths: smoothing, zero-range behavior, initial values and bar-close confirmation affect the result. The example uses cycle 10, EMA lengths 23 and 50, and smoothing factor 0.5.

The STC formula: normalize, smooth, repeat

Start with the MACD line M, the fast EMA minus the slow EMA. This is the EMA difference itself, not the MACD histogram or a signal-line crossover. Let L be the cycle length, and let lowest and highest include the current bar.

M = EMA(source, fast) − EMA(source, slow)

K1 = 100 × (M − lowest(M, L)) / (highest(M, L) − lowest(M, L))

D1 = previous D1 + 0.5 × (K1 − previous D1)

K2 = 100 × (D1 − lowest(D1, L)) / (highest(D1, L) − lowest(D1, L))

STC = previous STC + 0.5 × (K2 − previous STC)

The fractions apply only when their rolling range is positive. In this implementation a zero range carries the previous K value forward, or uses zero when none exists. The first D1 is seeded from K1; the first STC is seeded from K2. Both smoothers then move halfway toward their current input.

With finite inputs, valid normalization and seeds inside the range, the result stays between 0 and 100. The second normalization acts on D1, not directly on price. A value of 80 therefore does not mean an 80% chance of a rising market.

A nine-bar calculation you can reproduce

This is a synthetic arithmetic example, not market data or a backtest. Supply MACD values 0, 2, 1, 3, 2, 0, 1, 0, 2; use L = 3 and smoothing = 0.5. Start with zero K values and use the available window on the first two bars. The short cycle exposes each stage; it is not a recommended setting.

BarMACD MK1D1K2STC
100000
221005010050
31505010075
431007510087.5
525062.55068.75
60031.25034.375
715040.6253032.1875
80020.3125016.0938
9210060.156310058.0469

At bar 7, M is 1 within the three-bar range [0, 2], so K1 = 50. D1 = 31.25 + 0.5 × (50 − 31.25) = 40.625. The D1 window is [62.5, 31.25, 40.625], giving K2 = 100 × 9.375 / 31.25 = 30. Finally, STC = 34.375 + 0.5 × (30 − 34.375) = 32.1875.

Synthetic STC calculation over nine barsBlue STC values from the calculation table, with dotted levels at 25 and 75. STC crosses below 75 at bar 5 and above 25 at bar 9. No market returns are shown.0255075100123456789
Illustration: blue points match the table; dashed reference levels are 25 and 75. Bar 5 crosses down through 75, and bar 9 crosses up through 25. The chart omits the code example's warmup gate to expose the arithmetic.

The return of M to 2 at bar 9 does not reproduce its bar-2 STC value: the rolling windows and recursive state differ. This is why a single current MACD value cannot determine STC.

Settings, initialization and implementation variants

InputExample valueEffect or constraint
SourceCloseChanging the price input changes both EMAs. Keep it fixed when comparing scripts.
Cycle length10 barsUsed for both rolling ranges; the example requires at least 2 bars.
Fast / slow EMA23 / 50 barsFast must be shorter than slow. These are bar counts, not calendar durations.
Smoothing factor0.5, fixedBoth recursive smoothers move halfway toward each new input.
Lower / upper level25 / 75Editable reference levels; lower must be below upper.
Initial bars without signals100Suppresses markers and alerts for bar_index 0 through 99, while calculation continues.

The 100-bar gate is an example policy, not proof of convergence. EMAs and recursive smoothers depend on their starting state, and early rolling windows may be incomplete. Compare recent closed values after loading more history; if they still move materially, extend the comparison history before evaluating signals.

For timeframe comparisons, 50 hourly bars represent 50 chart hours, whereas 50 daily bars represent 50 daily observations. Shorter lengths respond to a shorter history but may produce more alternating threshold events. Choose a test interval, hold the other rules fixed and compare nearby settings. There is no supported universal schedule of best settings for scalping, swing or position trading.

The LEAN STC implementation uses two smoothing stages and defaults to cycle 10, fast 23 and slow 50. Its zero-range stochastic result is zero, while this page preserves the previous K value. Its initialization also follows its own indicator classes. A 0.5 recurrence has the coefficient of an EMA of length 3, but matching that coefficient alone does not match seeding or zero-range behavior.

Threshold states are different from confirmed crossings

ConditionWhat it recordsWhat it does not establish
STC below 25A low oscillator state that can persist over many bars.A price bottom or an immediate entry.
Previous STC ≤ 25; current STC > 25A crossing above the lower level.That price will keep rising.
STC above 75A high oscillator state.An automatic short signal.
Previous STC ≥ 75; current STC < 75A crossing below the upper level.That the broader trend has reversed.

Range normalization discards the absolute size of MACD. A small fluctuation inside a narrow range can travel from one oscillator extreme to the other. Sideways price can therefore cause repeated crossings; a persistent trend can hold STC near an extreme. Neither failure is fixed by calling the levels overbought or oversold.

On an open chart bar, the changing source price can move STC across a level and back again. The code evaluates crossings on every bar, then requires barstate.isconfirmed for markers and alerts. TradingView documents this as the closing update of a realtime bar. The plotted line still moves before close. This rule is for the chart timeframe; it is not a solution for confirmation inside request.security().

Before converting a crossing into a strategy, specify order timing, position sizing, exits, loss limits, fees and slippage. If adding a price trend filter, test its extra rule separately. These indicator events provide no evidence of expected profitability.

Pine Script v6: STC with confirmed threshold events

Paste this complete script into the TradingView Pine Editor and add it to a chart. It retains Pineify's STC calculation and puts the oscillator in a separate pane. Blue lower-level markers and orange upper-level markers appear only after the warmup gate and bar close.

Pine Script v6
// This source code is subject to the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Pineify
//@version=6
indicator("Schaff Trend Cycle: confirmed thresholds", overlay=false)

source = input.source(close, "Source")
length = input.int(10, "Cycle length", minval=2)
fastLength = input.int(23, "Fast EMA length", minval=1)
slowLength = input.int(50, "Slow EMA length", minval=2)
lowerLevel = input.float(25.0, "Lower level", minval=0, maxval=100)
upperLevel = input.float(75.0, "Upper level", minval=0, maxval=100)
warmupBars = input.int(100, "Initial bars without signals", minval=0)

if fastLength >= slowLength
    runtime.error("Fast EMA length must be smaller than slow EMA length.")
if lowerLevel >= upperLevel
    runtime.error("Lower level must be smaller than upper level.")

p_ta_stc(series float source, simple int length, simple int fast_length, simple int slow_length) =>
    factor = 0.5
    m = ta.ema(source, fast_length) - ta.ema(source, slow_length)
    v1 = ta.lowest(m, length)
    v2 = ta.highest(m, length) - v1
    float f1 = 0
    f1 := v2 > 0 ? ((m - v1) / v2) * 100 : nz(f1[1])
    float pf = 0
    pf := na(pf[1]) ? f1 : pf[1] + (factor * (f1 - pf[1]))
    v3 = ta.lowest(pf, length)
    v4 = ta.highest(pf, length) - v3
    float f2 = 0
    f2 := v4 > 0 ? ((pf - v3) / v4) * 100 : nz(f2[1])
    float pff = 0
    pff := na(pff[1]) ? f2 : pff[1] + (factor * (f2 - pff[1]))
    pff

stc = p_ta_stc(source, length, fastLength, slowLength)
plot(stc, "STC", color=color.blue, linewidth=2)
hline(lowerLevel, "Lower level", color=color.gray)
hline(upperLevel, "Upper level", color=color.gray)

// Compute crosses every bar. The gate suppresses signals, not calculations.
crossUp = ta.crossover(stc, lowerLevel)
crossDown = ta.crossunder(stc, upperLevel)
ready = bar_index >= warmupBars
confirmedUp = ready and crossUp and barstate.isconfirmed
confirmedDown = ready and crossDown and barstate.isconfirmed
plotshape(confirmedUp, "Confirmed lower-level cross", shape.triangleup, location.bottom, color.blue, size=size.tiny)
plotshape(confirmedDown, "Confirmed upper-level cross", shape.triangledown, location.top, color.orange, size=size.tiny)
alertcondition(confirmedUp, "STC crossed above lower level", "STC crossed above its lower level on a closed bar.")
alertcondition(confirmedDown, "STC crossed below upper level", "STC crossed below its upper level on a closed bar.")

To receive notifications, create an alert from the relevant script condition and select Once Per Bar Close. The script is an indicator, so its markers do not place or backtest orders. Historical markers represent calculation events, not simulated fills.

The formula runs on every bar, including the first 100 bars. Gating the function call itself would interrupt its history. The function name p_ta_stc is defined in this example; it is not a Pine built-in that works without its definition.

Check a custom STC script with Pineify

Give the Pine Script AI Coding Agent the complete calculation contract: source, EMA lengths, both cycle windows, smoothing factor, zero-range fallback, seed behavior and confirmation timing. Request a review of the generated code before adding trading rules.

  1. Start with the 10/23/50 example and state that both smoothers use 0.5.
  2. Preserve the previous K value on a zero range and seed the smoothers from their first K values.
  3. Specify that the warmup gate suppresses signals while calculations continue.
  4. Compare the same completed bars on the same symbol, feed, timeframe and session. Inspect intermediate M, K1, D1 and K2 values if the outputs disagree.
  5. Define exits and costs separately before requesting a strategy. Syntax validation does not validate a trading hypothesis.
Build or review STC code with Pineify

Schaff Trend Cycle questions

STC is an oscillator built from the difference between fast and slow moving averages. The implementation here normalizes that MACD line over a rolling cycle window, smooths it, normalizes the smoothed series again, and applies a final smoothing step. Its 0 to 100 scale describes relative momentum position, not a probability of a trend or reversal.

There is no verified universal best setting. This example starts with cycle 10, fast EMA 23, slow EMA 50, and fixed smoothing factor 0.5. These are bar counts: 50 hourly bars and 50 daily bars cover different time spans. Compare nearby settings with the same data, confirmation rule, costs and exits, then check a separate period.

Paste the complete example into the Pine Editor, save it, and add it to the chart. STC appears in a separate pane. Use the settings to choose lengths, thresholds and a signal warmup gate. The triangles mark confirmed crossings; they do not place orders. To receive notifications, create an alert using one of the script conditions and choose Once Per Bar Close.

No. Above 75 is a level state, not proof that price has peaked. STC can stay near its upper bound while a trend continues. The example distinguishes that state from a crossing back below 75 on a completed bar. Even the crossing can fail as a trade signal, so any strategy needs separate entry, exit and loss rules.

Match the price source, moving average type and lengths, both stochastic windows, smoothing factors, zero-range handling and initialization. The same 10/23/50 label does not describe every choice. The example carries the previous raw stochastic value through a zero range; QuantConnect LEAN returns zero. Compare the same closed bars and include enough shared history.

The current line value changes while its price input is changing on an open bar. This example marks crossings only on the closing update. It uses no higher-timeframe request or future pivot placement. Feed revisions, different chart history and parameter changes can still alter results; close confirmation is not a promise that all STC scripts behave identically.

STC and MACD measure different things. MACD is an EMA difference in price units; STC transforms that difference into a rolling range position. Their threshold and signal-line events can occur on different bars. A claim that STC always leads by a fixed number of bars requires specified events and data; this example makes no such claim.