CodeForFinance
Python

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 matplotlib

Step 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: 28

Next 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.

Browse All Tutorials →

Developer Essentials

As an Amazon Associate we may earn from qualifying purchases.