TradingView indicator comparisons

Stochastic vs Stochastic RSI: Price Range or RSI Range

Stochastic locates the close inside a recent price high-low range. Stochastic RSI locates RSI inside its own recent range after first calculating RSI from close changes. Choose the first for price range position and the second for relative changes in RSI. In the controlled example below, price Stochastic is 51 while Stochastic RSI is 100: RSI has reached a local maximum even though price is near the middle of its range. Neither reading proves a reversal.

See the decision guide

Two range formulas, different input series

Let P be the price range length, R the RSI length, S the RSI range length, K the first SMA length and D the second SMA length. Include the current bar in each window. The formulas below use a 0 to 100 display for both oscillators; divide every output and threshold by 100 for a 0 to 1 display.

  • RSI here is 100 × average gain / (average gain + average loss). Seed both averages with R consecutive close changes; then update each as ((R - 1) × previous average + current gain or loss) / R.
  • K = 1 leaves the raw oscillator unchanged. D only smooths K. Neither smoothing step recalculates the range. A smoothed K of 100 requires its entire valid averaging window to be 100.
  • TradingView describes the raw Stoch RSI ratio on 0 to 1 and exposes separate RSI Length, Stochastic Length, K and D controls. This script explicitly multiplies the ratio by 100, so its reference levels are 20 and 80.
StagePrice StochasticStochastic RSI
InputCurrent close, P-bar highest high and lowest lowRSI of close, then S-bar highest and lowest RSI
Raw value100 × (close - lowest low) / (highest high - lowest low)100 × (RSI - lowest RSI) / (highest RSI - lowest RSI)
K and DK line = SMA(raw, K); D line = SMA(K line, D)The same two SMA steps after RSI normalization
What 100 means before smoothingClose equals the window highRSI equals the window RSI maximum
Source of memoryP bars for the raw rangeWilder RSI retains decaying history; S only bounds the range step

Parameter alignment and double warm-up

Use the following controlled settings, not a shorthand such as 14,3,3. Fixture bars are numbered from zero. With no gaps or undefined denominators, price raw values first exist at P - 1; RSI first exists at R; raw Stoch RSI first exists at R + S - 1. K smoothing adds K - 1 bars and D adds D - 1 more. These are earliest possible valid bars, not guarantees.

  • With P = R = S = 14 and K = D = 3, price K/D can first appear at bars 15/17, while Stoch RSI K/D can first appear at bars 29/31. Flat ranges can delay these further.
  • Earlier price K values must not enter a head-to-head event count before Stoch RSI is ready. The first common valid observation is a state, not a crossing, because a common valid predecessor is absent.
  • The first valid RSI is only an initialization point. More earlier history can change Wilder averages and later Stoch RSI values. Use the same history start and a fixed evaluation segment; the window count alone does not prove seed effects have vanished.
ControlFixture baselineSecond run / chart comparison
DataSame 16 synthetic OHLC bars for bothSame symbol, exchange feed, session and standard candles
Range lengthsP = 3; R = 3; S = 3Record P, R and S separately; change only one per experiment
SmoothingK = 1; D = 3 for bothK = 3; D = 3 for both
First K / D, baselinePrice: bars 2 / 4; Stoch RSI: bars 5 / 7With K = 3: price 4 / 6; Stoch RSI 7 / 9
EvaluationCompleted bars with both K values validFor D or crosses, require both D values too
Threshold ruleK > 80, strict upper stateUpper entry: previous K <= 80 and current K > 80

A small price move can saturate the RSI range

This synthetic fixture has open = close, high = 110 and low = 90 on every bar. Closes at bars 0 to 15 are 100, 99, 100, 99, 100, 100.2, 100.4, 100.3, 100.5, 100.6, 100.4, 100.7, 100.6, 100.8, 100.7, 101. Use P = R = S = 3, K = 1 and D = 3. Opens do not enter either formula. All table values are rounded only for display.

  • At bar 3, changes are -1, +1, -1: gain = 1/3 and loss = 2/3, so RSI = 100/3. At bar 4 the averages become 5/9 and 4/9. At bar 5 they become 59/135 and 40/135, giving RSI = 5900/99 = 59.595960.
  • The three RSI readings at bar 5 span 33.333333 to 59.595960. The newest is the maximum, so Stoch RSI = 100. Price K is only 100 × (100.2 - 90) / 20 = 51.
  • At bar 7 the close drops by 0.1 and RSI is below the preceding two RSI values, making Stoch RSI zero. Price K moves from 52 to 51.5. This range rescaling explains the abrupt swing without implying a proportionately large price move.
  • From bars 5 to 15 the common-valid upper-entry rule fires for Stoch RSI at 8, 11, 13 and 15, and never for price K. Bar 5 is excluded as an entry. These are event counts, not trade outcomes.
Bar / closeRSIPrice KStoch RSI KStoch RSI D
3 / 9933.33333345Not readyNot ready
4 / 10055.55555650Not readyNot ready
5 / 100.259.59596051100Not ready
6 / 100.464.44444452100Not ready
7 / 100.359.12334451.5066.666667
8 / 100.567.23856252.510066.666667
9 / 100.671.4844445310066.666667
10 / 100.451.47209452066.666667

Run controls that isolate the denominator

The fixture selector keeps both calculations on the same inputs within each run. These cases distinguish price wicks, a constant RSI, and missing observations. The explicit policies below are research choices; do not assume every built-in or third-party script uses the same missing-data conventions.

  • A flat close after a nonzero RSI seed is different from an entirely flat seed: both averages decay proportionally, so RSI can stay constant. Once all RSI readings in S are equal, its denominator is zero even if price highs and lows still span a wide range.
  • Zero range returns na, never a fabricated 0, 50 or carried value. Missing closes restart RSI initialization; normalization and smoothing require consecutive valid observations. The script does not skip gaps to fill a window.
  • In real data, small positive RSI ranges can amplify small numerical changes. This implementation uses an exact positive-range test and retains full precision. If a tolerance is introduced, declare its units and test it as a new rule.
Fixture variantExpected price behaviorExpected RSI-range behavior
BaseBar 5 K = 51Bar 5 K = 100
Wider wicks: H = 120, L = 80Bar 5 K = 50.5Identical to Base: close sequence is unchanged
Flat: O = H = L = C = 100Zero price range: raw and smoothed values are naGain + loss = 0: RSI is na; normalization is na
Rising closes: C = 100 + 0.1 × barPositive price range; valid K after bar 2RSI = 100 from bar 3; its range is zero, so Stoch RSI is na
Missing close at bar 6Three-bar window invalid at 6 to 8; raw resumes at 9RSI resets, resumes at 10; three valid RSI readings allow raw to resume at 12

Reproduce both lines in Pine Script v6

Paste the complete indicator into TradingView Pine Editor. Leave the fixture enabled for the table above: it occupies the first 16 loaded bars, and the on-chart table retains those rows even when they are offscreen. At least 16 loaded bars are needed. Change the fixture selector for the controls, or turn the fixture off for chart OHLC. The default is a calculation demonstration, not an order-generating strategy.

  • The four plotted K/D lines and upper-entry events are shown only for confirmed bars. Underlying RSI and close remain in the Data Window for diagnostics; current-bar diagnostic values can change before close.
  • The helper functions implement the stated seed, strict windows and missing-value policy rather than silently inheriting a library gap convention. Use the fixture table to check your own implementation before comparing chart readings.
  • For a K/D crossover study, require current and previous K and D for both indicators. The included upper-entry event uses K only. A different event requires a separate, named comparison.
Complete Pine v6 indicator. Syntax checked on 2026-10-18. Explicit window and gap policies make the fixture reproducible; syntax checking does not verify market performance.pine
//@version=6
indicator("Stochastic vs Stoch RSI controlled comparison", overlay=false, max_bars_back=1000)
fixture = input.bool(true, "Use the 16-bar fixture")
variant = input.string("Base", "Fixture variant", options=["Base", "Wider wicks", "Flat", "Missing close", "Rising closes"])
priceLen = input.int(3, "Price range length", minval=1, maxval=100)
rsiLen = input.int(3, "RSI length", minval=1, maxval=100)
rsiRangeLen = input.int(3, "RSI range length", minval=1, maxval=100)
kLen = input.int(1, "K SMA length (both)", minval=1, maxval=100)
dLen = input.int(3, "D SMA length (both)", minval=1, maxval=100)

// Every observation in the window must exist. No partial windows.
fullRange(float src, float hi, float lo, int n) =>
    float top = na
    float bottom = na
    bool valid = true
    for j = 0 to n - 1
        valid := valid and not na(src[j]) and not na(hi[j]) and not na(lo[j])
        valid := valid and hi[j] >= src[j] and src[j] >= lo[j]
        top := na(top) ? hi[j] : math.max(top, hi[j])
        bottom := na(bottom) ? lo[j] : math.min(bottom, lo[j])
    valid and top > bottom ? 100 * (src - bottom) / (top - bottom) : na

fullSma(float src, int n) =>
    float total = 0.0
    bool valid = true
    for j = 0 to n - 1
        valid := valid and not na(src[j])
        total += src[j]
    valid ? total / n : na

// Seed with n consecutive changes. A missing change restarts the seed.
strictRsi(float src, int n) =>
    float delta = src - src[1]
    var float gains = 0.0
    var float losses = 0.0
    var int count = 0
    if na(delta)
        gains := 0.0
        losses := 0.0
        count := 0
    else if count < n
        gains += math.max(delta, 0)
        losses += math.max(-delta, 0)
        count += 1
        if count == n
            gains /= n
            losses /= n
    else
        gains := ((n - 1) * gains + math.max(delta, 0)) / n
        losses := ((n - 1) * losses + math.max(-delta, 0)) / n
    count == n and gains + losses > 0 ? (losses == 0 ? 100.0 : gains == 0 ? 0.0 : 100 * gains / (gains + losses)) : na

var values = array.from(100.0, 99.0, 100.0, 99.0, 100.0, 100.2, 100.4, 100.3, 100.5, 100.6, 100.4, 100.7, 100.6, 100.8, 100.7, 101.0)
float fixtureClose = bar_index < array.size(values) ? array.get(values, bar_index) : na
if variant == "Flat"
    fixtureClose := bar_index < 16 ? 100.0 : na
if variant == "Rising closes"
    fixtureClose := bar_index < 16 ? 100.0 + bar_index * 0.1 : na
if variant == "Missing close" and bar_index == 6
    fixtureClose := na
float c = fixture ? fixtureClose : close
float h = fixture ? (variant == "Flat" ? 100.0 : variant == "Wider wicks" ? 120.0 : 110.0) : high
float l = fixture ? (variant == "Flat" ? 100.0 : variant == "Wider wicks" ? 80.0 : 90.0) : low
float r = strictRsi(c, rsiLen)
float priceRaw = fullRange(c, h, l, priceLen)
float rsiRaw = fullRange(r, r, r, rsiRangeLen)
float priceK = fullSma(priceRaw, kLen)
float rsiK = fullSma(rsiRaw, kLen)
float priceD = fullSma(priceK, dLen)
float rsiD = fullSma(rsiK, dLen)
bool ready = not na(priceK) and not na(rsiK)
bool pair = ready and ready[1] and barstate.isconfirmed
bool priceUpperEntry = pair and priceK > 80 and priceK[1] <= 80
bool rsiUpperEntry = pair and rsiK > 80 and rsiK[1] <= 80
plot(barstate.isconfirmed ? priceK : na, "Price K", color=color.blue)
plot(barstate.isconfirmed ? rsiK : na, "Stoch RSI K", color=color.orange)
plot(barstate.isconfirmed ? priceD : na, "Price D", color=color.new(color.blue, 60))
plot(barstate.isconfirmed ? rsiD : na, "Stoch RSI D", color=color.new(color.orange, 60))
plot(r, "Underlying RSI", display=display.data_window)
plot(c, "Source close", display=display.data_window)
plot(bar_index, "Fixture bar index", display=display.data_window)
plot(priceUpperEntry ? 1 : 0, "Price upper entry", display=display.data_window)
plot(rsiUpperEntry ? 1 : 0, "RSI range upper entry", display=display.data_window)
hline(80, "Upper")
hline(20, "Lower")
f(float x) =>
    na(x) ? "na" : str.tostring(x, "#.####")
var table results = table.new(position.top_right, 7, 17, border_width=1)
if barstate.isfirst and fixture
    table.cell(results, 0, 0, "Bar")
    table.cell(results, 1, 0, "Close")
    table.cell(results, 2, 0, "RSI")
    table.cell(results, 3, 0, "Price K")
    table.cell(results, 4, 0, "RSI K")
    table.cell(results, 5, 0, "Price D")
    table.cell(results, 6, 0, "RSI D")
if fixture and bar_index < 16 and barstate.isconfirmed
    table.cell(results, 0, bar_index + 1, str.tostring(bar_index))
    table.cell(results, 1, bar_index + 1, f(c))
    table.cell(results, 2, bar_index + 1, f(r))
    table.cell(results, 3, bar_index + 1, f(priceK))
    table.cell(results, 4, bar_index + 1, f(rsiK))
    table.cell(results, 5, bar_index + 1, f(priceD))
    table.cell(results, 6, bar_index + 1, f(rsiD))

Move from arithmetic checks to a trading experiment

After the fixture agrees, select one standard-candle chart and freeze feed, session, timeframe, history start and test dates. Compare both indicators at the same completed bar. Record upper and lower state duration, threshold-entry timestamps and the fraction of bars excluded by warm-up or missing values. This tests what the formulas select before any claim about returns.

  • A 0/100 plateau is a relative range condition. It is neither a probability nor a command to fade the trend. Sensitivity can create repeated triggers whose usefulness depends on an independently specified entry and exit rule.
  • If orders are added, hold order timing, sizing, exits, commission and slippage constant. Compare a separate period that was not used to choose R, S or smoothing; changing parameters after seeing the result changes the hypothesis.
  • The Pine Script AI Coding Agent can draft or revise a script from these named controls and fixture expectations. Request values and event logs first, review the source, and then define any strategy orders. The published script is the reference calculation, not a claim that generated code needs no review.
Where Pineify fits

Pine Script AI Coding Agent

Use the fixture and parameter matrix as a specification for a comparison script. Ask the agent to draft or revise Pine Script, then inspect the source and verify the values before adding orders.

Draft a controlled oscillator comparison

Frequently asked questions

Educational calculations, not investment advice. The fixture is synthetic and contains no backtested or live returns. Syntax validation does not establish trading correctness or profitability. Review generated code and test complete rules independently.

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.