Developed by J. Welles Wilder Jr., the Relative Strength Index (RSI) is a widely respected momentum oscillator that measures the speed and change of price movements. It operates by comparing the magnitude of recent gains to recent losses over a specified time period, typically 14 periods, to evaluate whether an asset is overbought or oversold. The RSI oscillates on a scale from 0 to 100.
The calculation of RSI involves a few steps:
Calculate Average Gain and Average Loss: Over a chosen period (N), typically 14 days:
Average Gain = [(Previous Average Gain) * (N-1) + Current Gain] / N
Average Loss = [(Previous Average Loss) * (N-1) + Current Loss] / N
Calculate Relative Strength (RS): The RS is the
ratio of the Average Gain to the Average Loss. Note: Wilder initially smoothed RS using a
Wilder’s smoothing method, which is similar to an exponential moving
average.
Calculate RSI: The RSI is then normalized to
oscillate between 0 and 100 using the following formula: If the
Average Loss is zero (meaning prices only went up or stayed flat for N
periods), RS becomes infinite, and RSI is 100. Conversely, if the
Average Gain is zero, RS is zero, and RSI is 0.
Usage: The RSI provides several ways to interpret market momentum:
TA-Lib Function: The Technical Analysis Library (TA-Lib) provides a convenient function for calculating RSI:
talib.RSI(close_prices, timeperiod=14)
close_prices
: An array or series of closing
prices.timeperiod
: The lookback period for RSI calculation,
with 14 being the default and most common value.Code Example (Calculation & Plot with yfinance Data):
The following Python code demonstrates how to fetch stock data using
yfinance
, calculate the RSI, and then plot the price along
with the RSI indicator, including the typical overbought, oversold, and
midpoint lines.
import yfinance as yf
import talib
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
# --- 1. Data Fetching using yfinance ---
= "AAPL" # Example: Apple Inc.
ticker_symbol = yf.download(ticker_symbol, start="2023-01-01", end="2024-05-01", auto_adjust=False, progress=False)
data
if data.empty:
print(f"No data found for {ticker_symbol}. Exiting.")
else:
# Complying with user preference for droplevel
if isinstance(data.columns, pd.MultiIndex):
= data.columns.droplevel(level=1)
data.columns
= data['Close'].dropna()
close_prices = close_prices.index
date_index
# --- 2. RSI Calculation ---
= 14 # Standard period for RSI
time_period_rsi
# RSI needs N periods + initial data for smoothing.
# TA-Lib handles the initial NaN values.
if len(close_prices) > time_period_rsi:
= talib.RSI(close_prices, timeperiod=time_period_rsi)
rsi_values = f"RSI({time_period_rsi})"
indicator_name print(f"\n--- {indicator_name} - Relative Strength Index for {ticker_symbol} ---")
= rsi_values[~np.isnan(rsi_values)]
valid_rsi if len(valid_rsi) >= 5:
print(f"Output {indicator_name} (last 5 valid): {valid_rsi[-5:].round(2)}")
elif len(valid_rsi) > 0:
print(f"Output {indicator_name} (all valid): {valid_rsi.round(2)}")
else:
print(f"Output {indicator_name}: No valid RSI values calculated.")
# --- 3. Plotting ---
= plt.subplots(2, 1, figsize=(14, 10), sharex=True,
fig, axes ={'height_ratios': [3, 1]}) # Price chart 3x taller
gridspec_kw
# Plot Price
0].plot(date_index, close_prices, label='Close Price', color='blue')
axes[0].set_title(f'{ticker_symbol} Price and {indicator_name}')
axes[0].set_ylabel('Price')
axes[0].legend(loc='upper left')
axes[0].grid(True)
axes[
# Plot RSI
1].plot(date_index, rsi_values, label=indicator_name, color='purple')
axes[1].axhline(70, color='red', linestyle='--', linewidth=0.8, label='Overbought (70)')
axes[1].axhline(50, color='gray', linestyle='--', linewidth=0.8, label='Midpoint (50)')
axes[1].axhline(30, color='green', linestyle='--', linewidth=0.8, label='Oversold (30)')
axes[1].set_ylim(0, 100) # RSI oscillates between 0 and 100
axes[1].set_ylabel('RSI Value')
axes[1].set_xlabel('Date')
axes[1].legend(loc='upper left')
axes[1].grid(True)
axes[
# Adjust layout to prevent overlap
plt.tight_layout()
plt.show()
else:
print(f"\nSkipping RSI plot for {ticker_symbol}: Insufficient data (need > {time_period_rsi} points).")
if not data.empty:
print(f"Data available from {data.index.min().date()} to {data.index.max().date()}.")
Explanation of the Code:
yfinance
, talib
, numpy
,
matplotlib.pyplot
, and pandas
are
imported.yf.download()
fetches historical data for the specified
ticker_symbol
(e.g., “AAPL”). User preferences
auto_adjust=False
and droplevel
are
applied.close_prices
are extracted.time_period_rsi
is set (typically 14).talib.RSI()
calculates the RSI values. TA-Lib will
output NaN
for the initial periods where there’s not enough
data.plt.subplots(2, 1, ...)
creates a figure with two
subplots stacked vertically (2
rows, 1
column). sharex=True
links the x-axes.
gridspec_kw
allows resizing the subplots.axhline()
is used to draw horizontal lines at the 70
(overbought), 50 (midpoint), and 30 (oversold) levels, making it easier
to identify these key thresholds.set_ylim(0, 100)
ensures the y-axis for RSI is scaled
correctly.plt.tight_layout()
adjusts subplot parameters for a
tight layout.plt.show()
displays the chart.The RSI is a versatile indicator that, when used correctly and often in conjunction with other analytical tools, can provide valuable insights into market psychology and potential price movements.