06 — Oscillators

Relative Strength Index#

Debated1 min read
◆ The PatternHow lopsided recent gains and losses are, squeezed into a 0–100 scale and read as stretched or calm

The Relative Strength Index — J. Welles Wilder's momentum oscillator (1978).

RS = Average Gain / Average Loss (over N periods)
RSI = 100 − 100 / (1 + RS)

Default period: 14. Scale: 0–100.

Zones:
• RSI > 70 → overbought (price may pull back)
• RSI < 30 → oversold (price may bounce)
Divergence: Price makes new high but RSI doesn't → bearish divergence (momentum weakening)
Worked example

Five-day RSI with Wilder’s smoothing (seeded here from the first change; the textbook seeds with a simple average, which only changes the first values). After the run-up it read 94.3 on day 5 — “overbought” — yet the price kept rising to 109 on day 7. On day 13, after four down days in five, it reached 29.9, below 30; by day 15 the close had climbed from 99 to 104.

The example data: 15 days, used in every indicator here
DayHighLowCloseVolume
11019910050,000
210310010262,000
310310010148,000
410510110470,000
510810410785,000
610810510660,000
711010610990,000
811010710872,000
910810410580,000
1010510210366,000
1110510210455,000
1210410110275,000
13102989995,000
141029910164,000
1510510110471,000
Where it misleads
  • Overbought is not a sell signal in a trend: in a strong rise RSI can stay above 70 for weeks while the price keeps climbing.
  • The 70 and 30 lines are convention, not calibration; the useful levels depend on the asset and the period.
  • Divergences (a new price high without a new RSI high) look clear in hindsight; in real time they are frequent, and most do not lead to a reversal.
In code
d = df['close'].diff()
gain = d.clip(lower=0).ewm(alpha=1/5, adjust=False).mean()    # Wilder smoothing
loss = (-d.clip(upper=0)).ewm(alpha=1/5, adjust=False).mean()
df['rsi5'] = 100 - 100 / (1 + gain / loss)
Python, in your browser — edit the code and run it again

Assumes import numpy as np, import pandas as pd, and a DataFrame df with columns high, low, close and volume — the 15 days above.

Sources
  • New Concepts in Technical Trading Systems, J. W. Wilder, Trend Research, 1978
  • C.-H. Park & S. H. Irwin, “What Do We Know About the Profitability of Technical Analysis?”, Journal of Economic Surveys 21(4), 2007 — a review of the evidence across studies

Last reviewed October 2026 · How this site checks its content · Report a mistake

Check yourself

1. RSI stays above 70 during a strong uptrend. What does that tell you?

Show answer

B. Fading every reading above 70 in a trend repeatedly sells too early.

2. What is RSI built from?

Show answer

C. RSI = 100 − 100 / (1 + average gain / average loss), with Wilder’s smoothing in the classic version.

3. How does a 5-period RSI differ from a 14-period one?

Show answer

C. A shorter window averages fewer moves, so the ratio swings more. Fixed thresholds mean different things at different lengths.

Pattern bridge: RSI normalizes momentum to [0,100] — a sigmoid-like activation bounding raw gains and losses. In statistics, it’s a ratio of mean gains to mean losses. In Python, pandas-ta computes it in one line.
How to validate this in practice
  1. Download OHLCV data and compute RSI using only historical closes.
  2. Define the trading rule before testing: threshold, hold period, stop, and transaction cost.
  3. Use walk-forward validation; tune thresholds on one period and test on the next.
  4. Compare against buy-and-hold and cash baselines.
  5. Report Sharpe, max drawdown, hit rate, turnover, and cost sensitivity.
Common pitfall — indicator worship: RSI is a deterministic formula, not proof of predictive edge. The trading claim is heuristic until validated out of sample.
Performance in practice
  • Tests of simple RSI rules give mixed results across markets and periods (Park & Irwin 2007); there is no reliable published figure for how often divergences lead to reversals
  • In strong trends, RSI can stay above 70 for weeks. Treating every "overbought" as a sell signal loses money in trending markets
  • Andrew Cardwell’s framework shifts the zones with the regime: roughly 40–80 in bull markets, 20–60 in bear markets
When to use this
✓ Use when: Ranging/sideways markets. Looking for divergences as confirmation. Setting stop-loss levels. Combining with trend filters (only buy oversold in uptrends).
✗ Skip when: Strong trending markets (RSI stays pinned). As a standalone entry signal. Low timeframes with high noise. News-driven moves where momentum is fundamentals-driven.
Use this pattern on real data
Pattern Portal Case: Market Strategy Backtest
Treat the indicator as a hypothesis. Validate with costs, walk-forward periods, and drawdown metrics.
Quick start — copy to notebook
pip install yfinance pandas pandas-ta
import yfinance as yf
import pandas_ta as ta

df = yf.download('SPY', start='2018-01-01', auto_adjust=True)
df['RSI_14'] = ta.rsi(df['Close'], length=14)
df['signal'] = (df['RSI_14'] < 30).astype(int).shift(1).fillna(0)
df['strategy'] = df['signal'] * df['Close'].pct_change()
print(df[['Close', 'RSI_14', 'signal', 'strategy']].tail())
Linked from 5
← Previous
VWAP
Open in the full reader, with the topic sidebar