Implementing RSI Calculation in Python for Financial Time Series
RSI Formulations
The Relative Strength Index (RSI) measures momentum by comparing the magnitude of recent upward price movements against recent downward movements. Two common calculasion variants are widely used.
Summation Approach (SMA-based)
For a chosen look-back period N:
G= Sum of all positive daily price changes over the intervalL= Sum of absolute values of all negative daily changes over the interval- RSI =
G / (G + L) × 100
Smoothed Aproach (Wilder's Method)
- Average Gain = Smoothed moving average of positive changes using smoothing factor
α = 1 / N - Average Loss = Smoothed moving average of absolute negative changes using
α = 1 / N - Relative Strength (RS) = Average Gain / Average Loss
- RSI =
100 − 100 / (1 + RS)
The smoothed approach is the convention used by most professional charting platforms.
Dataset Prerequisites
The implementations below expect a DataFrame containing at minimum a close column of numeric prices. A date column is optional but recommended for chronological alignment. The default look-back is 14 periods, though horizons such as 6 or 21 are also common.
Implementation: Summation Method
import pandas as pd
def rsi_sma_variant(prices: pd.DataFrame, period: int = 14) -> pd.DataFrame:
frame = prices.copy()
if "date" in frame.columns:
frame["date"] = pd.to_datetime(frame["date"])
frame = frame.sort_values("date").reset_index(drop=True)
# Daily price deltas
delta = frame["close"].diff()
# Isolate upward and downward moves
advances = delta.clip(lower=0)
declines = (-delta).clip(lower=0)
# Aggregate over the look-back window
sum_gains = advances.rolling(window=period, min_periods=period).sum()
sum_losses = declines.rolling(window=period, min_periods=period).sum()
# Compute oscillator
frame["rsi"] = 100.0 * sum_gains / (sum_gains + sum_losses)
return frame
Implementation: Smoothed Average Method
import pandas as pd
import numpy as np
def rsi_smoothed_variant(prices: pd.DataFrame, period: int = 14) -> pd.DataFrame:
frame = prices.copy()
if "date" in frame.columns:
frame["date"] = pd.to_datetime(frame["date"])
frame = frame.sort_values("date").reset_index(drop=True)
# Compute daily returns
delta = frame["close"].diff()
up = delta.clip(lower=0)
down = (-delta).clip(lower=0)
# Wilder's exponential smoothing: alpha = 1 / period
avg_up = up.ewm(alpha=1 / period, min_periods=period, adjust=False).mean()
avg_down = down.ewm(alpha=1 / period, min_periods=period, adjust=False).mean()
# Guard against zero average loss
rs = avg_up / avg_down.replace(0, np.nan)
# Apply RSI formula
frame["rsi"] = 100.0 - (100.0 / (1.0 + rs))
# When no losses exist, RSI is defined as 100
frame.loc[avg_down == 0, "rsi"] = 100.0
return frame
Consistency Notes
The smoothed implementation generally reproduces values displayed in retail and institutional trading terminals because standard RSI defaults to Wilder's exponential smoothing rather than a simple rolling summation. Where average loss equals zero, the formula naturally converges to the upper bound of 100.