What it does
Stochastic RSI Zones applies a stochastic oscillator to RSI rather than to price, producing a fast 0–100 line (%K) and its average (%D). It shades an upper and a lower zone and marks signals when the oscillator turns from those zones.
Because Stochastic RSI is very sensitive, the script adds an optional trend filter on price: with it on, bullish signals only appear while price is above a long EMA and bearish signals only while price is below it.
How it works
- RSI is calculated on the source.
- Raw StochRSI = (RSI − lowest RSI over n) ÷ (highest RSI over n − lowest RSI over n) × 100.
- %K = SMA(raw, %K smoothing). %D = SMA(%K, %D smoothing).
- Signal type "K/D cross inside zone": bullish when %K crosses above %D while %K was at or below the lower zone on the previous bar; bearish when %K crosses below %D while it was at or above the upper zone.
- Signal type "K leaves zone": bullish when %K crosses up through the lower zone level; bearish when it crosses down through the upper level.
- Trend filter: bullish signals need close > EMA(trend length), bearish signals need close < EMA.
Signals are confirmed on bar close. A faint background shows which side of the trend EMA price is on when the filter is active.
How to use it
- Treat the indicator as a timing tool inside a trend you have already identified, which is exactly what the filter enforces. In an uptrend, a bullish signal from the lower zone marks a possible end of a pullback.
- "K leaves zone" signals are a little later but filter out some crosses that happen while the oscillator is still pinned at an extreme.
- Pair it with a price level: a bullish signal near support carries more context than one in open space.
- It is popular on M5–H4. Its speed makes it noisy on M1. For the RSI foundation, see the RSI lesson.
Settings
| Input | Default | What it changes |
|---|---|---|
| Source | close | Price used for RSI. |
| RSI length | 14 | Smoothness of the underlying RSI. |
| Stochastic length | 14 | Window for the stochastic of RSI. |
| %K / %D smoothing | 3 / 3 | Line smoothness. |
| Upper / Lower zone | 80 / 20 | Zone levels for signals. |
| Signal type | K/D cross inside zone | Which event triggers a signal. |
| Only signal with the trend | On | Applies the EMA trend filter. |
| Trend EMA length (on price) | 200 | Speed of the trend filter. |
Limitations
- Stochastic RSI swings between extremes very quickly and can stay pinned at 0 or 100 during strong moves, producing early counter-signals when the filter is off.
- The trend filter uses a lagging EMA; around trend changes it will block valid signals and allow late ones.
- Many signals occur in ranges where the trend filter itself flips often.
- It measures momentum of RSI, not price, so its swings can look dramatic while price barely moves. Always check the actual price distance against your spread and costs.
Pine Script source
In TradingView open Pine Editor, paste the code, click Save and then Add to chart.
FXP_StochRsiZones.pine
72 lines
// © FXPIPS ONLINE — https://fxpips.online/tradingview-indicators/stochastic-rsi-zones/
// License: MIT. Educational tool — not financial advice. Trading involves substantial risk.
//@version=6
// Stochastic RSI with overbought/oversold zone signals and an optional moving-average trend
// filter on price, so that only signals in the direction of the broader trend are shown.
indicator("FXP Stochastic RSI Zones", shorttitle = "FXP StochRSI", overlay = false)
// ── Inputs ─────────────────────────────────────────────────────────────
grpS = "Stochastic RSI"
src = input.source(close, "Source", group = grpS)
rsiLen = input.int(14, "RSI length", minval = 2, group = grpS)
stochLen = input.int(14, "Stochastic length", minval = 2, group = grpS)
kSmooth = input.int(3, "%K smoothing", minval = 1, group = grpS)
dSmooth = input.int(3, "%D smoothing", minval = 1, group = grpS)
obLevel = input.float(80.0, "Upper zone", minval = 50.0, maxval = 100.0, group = grpS)
osLevel = input.float(20.0, "Lower zone", minval = 0.0, maxval = 50.0, group = grpS)
grpSig = "Signals"
sigMode = input.string("K/D cross inside zone", "Signal type", options = ["K/D cross inside zone", "K leaves zone"], group = grpSig)
grpT = "Trend filter"
useTrend = input.bool(true, "Only signal with the trend", group = grpT)
trendLen = input.int(200, "Trend EMA length (on price)", minval = 2, group = grpT)
grpV = "Display"
colK = input.color(#2962ff, "%K line", group = grpV)
colD = input.color(#ff6d00, "%D line", group = grpV)
colBull = input.color(#26a69a, "Bullish signal", group = grpV)
colBear = input.color(#ef5350, "Bearish signal", group = grpV)
// ── Calculation ────────────────────────────────────────────────────────
rsiVal = ta.rsi(src, rsiLen)
k = ta.sma(ta.stoch(rsiVal, rsiVal, rsiVal, stochLen), kSmooth)
d = ta.sma(k, dSmooth)
trendMa = ta.ema(close, trendLen)
upTrend = close > trendMa
downTrend = close < trendMa
kdUp = ta.crossover(k, d)
kdDown = ta.crossunder(k, d)
exitOs = ta.crossover(k, osLevel)
exitOb = ta.crossunder(k, obLevel)
zoneUp = kdUp and k[1] <= osLevel
zoneDown = kdDown and k[1] >= obLevel
rawBull = sigMode == "K leaves zone" ? exitOs : zoneUp
rawBear = sigMode == "K leaves zone" ? exitOb : zoneDown
bullSignal = rawBull and (not useTrend or upTrend) and barstate.isconfirmed
bearSignal = rawBear and (not useTrend or downTrend) and barstate.isconfirmed
// ── Plots ──────────────────────────────────────────────────────────────
plot(k, "%K", color = colK, linewidth = 2)
plot(d, "%D", color = colD)
hUp = hline(obLevel, "Upper zone", color = color.new(colBear, 40), linestyle = hline.style_dashed)
hMid = hline(50, "Midline", color = color.new(color.gray, 60), linestyle = hline.style_dotted)
hLow = hline(osLevel, "Lower zone", color = color.new(colBull, 40), linestyle = hline.style_dashed)
hTop = hline(100, "Top", color = color.new(color.gray, 100))
hBot = hline(0, "Bottom", color = color.new(color.gray, 100))
fill(hUp, hTop, color = color.new(colBear, 90), title = "Upper zone fill")
fill(hLow, hBot, color = color.new(colBull, 90), title = "Lower zone fill")
plotshape(bullSignal, "Bullish zone signal", style = shape.triangleup, location = location.bottom, color = colBull, size = size.small)
plotshape(bearSignal, "Bearish zone signal", style = shape.triangledown, location = location.top, color = colBear, size = size.small)
bgcolor(useTrend ? (upTrend ? color.new(colBull, 96) : color.new(colBear, 96)) : na, title = "Trend filter background")
// ── Alerts ─────────────────────────────────────────────────────────────
alertcondition(bullSignal, "StochRSI bullish zone signal", "Stochastic RSI bullish signal from the lower zone on {{ticker}} ({{interval}})")
alertcondition(bearSignal, "StochRSI bearish zone signal", "Stochastic RSI bearish signal from the upper zone on {{ticker}} ({{interval}})")

