Calculate RSI (Relative Strength Index) in Python
The Relative Strength Index (RSI) is a momentum oscillator that measures the speed and magnitude of recent price changes. An RSI above 70 suggests a stock may be overbought, while below 30 suggests oversold. In this tutorial, we build RSI from scratch in Python, then use it to generate trading signals.
Prerequisites
- ✓Basic Python and pandas
- ✓Understanding of price momentum
Step 1.Install and import
Standard finance stack for data and visualisation.
pip install yfinance pandas matplotlibStep 2.Download price data
We use Tesla as it is volatile enough to produce interesting RSI signals.
import yfinance as yf
import pandas as pd
import matplotlib.pyplot as plt
df = yf.download("TSLA", start="2022-01-01", end="2024-06-01")
df = df[["Close"]].copy()
df.columns = ["close"]
print(f"{len(df)} days of data")Step 3.Calculate RSI from scratch
RSI = 100 - 100/(1+RS) where RS is the ratio of average gains to average losses over the lookback period.
def calculate_rsi(prices: pd.Series, period: int = 14) -> pd.Series:
delta = prices.diff()
gain = delta.where(delta > 0, 0.0)
loss = -delta.where(delta < 0, 0.0)
avg_gain = gain.rolling(window=period).mean()
avg_loss = loss.rolling(window=period).mean()
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
df["rsi"] = calculate_rsi(df["close"])
print(df[["close", "rsi"]].tail(10))Step 4.Identify overbought and oversold
Traditional RSI thresholds are 70 for overbought and 30 for oversold.
df["overbought"] = df["rsi"] > 70
df["oversold"] = df["rsi"] < 30
ob_days = df["overbought"].sum()
os_days = df["oversold"].sum()
print(f"Overbought days: {ob_days}")
print(f"Oversold days: {os_days}")Step 5.Visualise RSI with price
Price on top, RSI on the bottom with overbought/oversold zones shaded.
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 8),
gridspec_kw={"height_ratios": [3, 1]})
ax1.plot(df.index, df["close"], color="white", linewidth=1)
ax1.set_title("TSLA Price")
ax2.plot(df.index, df["rsi"], color="cyan", linewidth=1)
ax2.axhline(70, color="red", linestyle="--", alpha=0.5)
ax2.axhline(30, color="green", linestyle="--", alpha=0.5)
ax2.fill_between(df.index, 70, 100, alpha=0.1, color="red")
ax2.fill_between(df.index, 0, 30, alpha=0.1, color="green")
ax2.set_title("RSI (14)")
ax2.set_ylim(0, 100)
plt.tight_layout()
plt.savefig("rsi.png", dpi=150)
plt.show()Expected Output
612 days of data
Overbought days: 34
Oversold days: 28Next Steps
- →Combine RSI with moving averages for stronger signals
- →Test different RSI periods (7, 21, 28)
- →Build an RSI divergence detector
Recommended Reading
Technical Analysis Explained →As an Amazon Associate we may earn from qualifying purchases.