Technical Analysis | Mastering RSI Calculation in Python

Enhance your algorithmic trading strategies by incorporating RSI calculation in Python, and make informed investment decisions

In this blog, we will cover the below topics

  • What is Relative Strength Index?
  • How to access the data from yahoo finance?
  • How to build RSI and visualize it in Python?
  • How to interpret RSI and its limitation?

What is Relative Strength Index (RSI)?

The Relative Strength Index is one of the popular indexes that track the momentum of the price as it measures both the speed and the rate of change in the price movement. It was developed by J.Welles Wilder Jr in 1978. The RSI values range between 0 and 100, the RSI value below 30 indicates oversold, and greater than 70 indicates overbought. Although some traders and analysts change the range to 40–60, it is very subjective and comes with experience. The RSI is plotted just below the asset price trend on the same timeline.

A typical stock price trend and RSI indicator look as below. We will develop a similar plot using Python in the subsequent sections of this blog.

Source: Investopedia

How to access the data from yahoo finance?

We will use yahoo finance to load the stock data and use standard Python libraries like pandas and numpy.

import pandas as pd
import numpy as np
import yfinance as yf
import yahoo_fin.stock_info as si
import matplotlib.pyplot as plt
from plotly.subplots import make_subplots
import plotly.graph_objects as go

Accessing data from Yahoo Finance

We will set the stock name and period for data extraction.

ticker = 'AAPL'
start_date = '2022-01-01'
end_date = '2023-01-01'
window_length = 14

df = yf.Ticker(ticker).history(start= start_date,
end=end_date)[['Open', 'High', 'Low', 'Close', 'Volume']]
           Open         High       Low       Close     Volume
2021-12-31 176.548233 177.678363 175.725416 176.032745 64062300
2022-01-03 176.290475 181.296759 176.171519 180.434280 104487900
2022-01-04 181.048914 181.356227 177.569291 178.144272 99310400
2022-01-05 178.055086 178.610235 173.128111 173.405685 94537600
2022-01-06 171.204893 173.782390 170.154072 170.510956 96904000
... ... ... ... ... ...
2022-12-23 130.539854 132.035499 129.263572 131.477127 63814900
2022-12-27 130.998521 131.028433 128.346242 129.652435 69007800
2022-12-28 129.293476 130.649527 125.504514 125.674019 85438400
2022-12-29 127.618359 130.101127 127.359119 129.233658 75703700
2022-12-30 128.037144 129.572665 127.059986 129.552719 77034200

Let’s take a look at the Close price trend over the period.

fig, ax = plt.subplots(figsize=(16,8))
plt.title(f'Stock Trend Over The Years - {ticker}')
plt.ylabel('Price in USD')
ax.plot(df['Close'], label = 'Close Price', alpha = 0.9, color = 'red')
Source: Author | Fig 1

As we have Open, Close, High, Low, and Volume data available with us, it will be good to have a candlestick chart below that shows a similar trend to the above chart but with additional information.

Source: Author | Fig 2

Calculating the RSI

There are two components to RSI i.e. Relative Strength and the Relative Strength Index.

Relative Strength: It is a technique used to compare the stock to another stock or benchmark and is a ratio of avg gain to avg loss.

Relative Strength Index: It is an oscillator that provides information on oversold and overbought stocks and it ranges between o and 100.

With that understanding, let’s calculate the RS first and then the RSI as below.

df['diff'] = df['Close'].diff(1)
df['gain'] = df['diff'].clip(lower=0).round(2)
df['loss'] = df['diff'].clip(upper=0).abs().round(2)

Open High Low Close Volume diff gain loss
2015-12-31 38.47 38.47 37.92 37.94 30018000 NaN NaN NaN
2016-01-04 37.15 37.20 36.56 37.09 65456000 -0.85 0.00 0.85
2016-01-05 37.32 37.60 36.93 37.13 39014000 0.04 0.04 0.00
2016-01-06 36.50 37.36 36.45 37.18 38940000 0.05 0.05 0.00
2016-01-07 36.52 36.92 35.95 36.32 59274000 -0.86 0.00 0.86

df['avg_gain'] = df['gain'].rolling(window_length).mean()
df['avg_loss'] = df['loss'].rolling(window_length).mean()

# Calculate RS Values
df['rs'] = df['avg_gain'] / df['avg_loss']

# Calculate RSI
df['rsi'] = 100 - (100 / (1 + df['rs']))

rs rsi
2015-12-31 NaN NaN
2016-01-04 NaN NaN
2016-01-05 NaN NaN
2016-01-06 NaN NaN
2016-01-07 NaN NaN
... ... ...
2023-04-24 1.23 55.07
2023-04-25 0.95 48.75
2023-04-26 0.95 48.77
2023-04-27 0.95 48.70
2023-04-28 1.15 53.42

Visualizing the RSI indicator

We have set the RSI range between 30 and 70. Let’s update the previous chart with the range, oversold and overbought lines as below.

fig.add_hline(y=0, col=1, row=2, line_color="red", line_width=1)
fig.add_hline(y=100, col=1, row=2, line_color="red", line_width=1)

fig.add_hline(y=30, col=1, row=2, line_color='gray', line_width=1,
fig.add_hline(y=70, col=1, row=2, line_color='gray', line_width=1,
Source: Author | Fig 3

Let’s also add volume in the background to the price trend chart Fig 2.

x= df.index,
y= df['Volume'],
), row=1, col=1,secondary_y=False,
Source: Author | Fig 4

Let’s bring all the above plots together into a single plot. We will do this by defining a function by the name RSI.

def RSI(df):

# Create Figure
fig = make_subplots(
rows=2, cols=1, shared_xaxes=False, row_width=[100, 250],
specs=[[{"secondary_y": True}],[{"secondary_y": False}]]

# Create Candlestick chart for price data
),row=1, col=1, secondary_y=True,
x= df.index,
y= df['Volume'],
), row=1, col=1,secondary_y=False,
line=dict(color='blue', width=1),
), row=2, col=1
# Add upper/lower bounds
fig.update_yaxes(range=[-10, 110], row=2, col=1)
fig.add_hline(y=0, col=1, row=2, line_color="red", line_width=1)
fig.add_hline(y=100, col=1, row=2, line_color="red", line_width=1)

# Add overbought/oversold
fig.add_hline(y=30, col=1, row=2, line_color='gray', line_width=1, line_dash='dot')
fig.add_hline(y=70, col=1, row=2, line_color='gray', line_width=1, line_dash='dot')



The result is a consolidated chart with price trends, volume, and an RSI chart at the bottom. This is very similar to the one we had seen in the earlier section from a trading platform. The complete code can be accessed from the GitHub

Source: Author | Fig 5

How to use the RSI indicator?

  • It helps in validating the trends and reversals
  • It can be used to analyze the price behaviors of the asset as in positive divergence or negative divergence
  • It can be used as a signal for buy/sell in the short term
  • It can be clubbed with other indicators to build a trading strategy
  • It indicates the directionality and points to oversold and overbought securities
  • The RSI levels are flexible and can be set to fit the trend

Key points to remember — limitations

  • Like most other technical indicators, RSI should not be used in isolation to make a trade. It is always better to validate the indications alongside other indicators.
  • The RSI is a momentum indicator and is not considered to be reliable in a trending market. It can be misleading with strong positive or negative trends.

We have covered a lot so far and successfully implemented the RSI plot. As a final validation, let’s try with different stock may be Microsoft (MSFT), and see how the trends pan out.

Source: Author | Fig 6

Closing thoughts

The purpose of all technical indicators is to give analysts and traders a direction on the price trend either positive or negative. It is one of the easiest ways to analyze and predict near-future patterns. The RSI is the most popular technical indicator to measure the degree of price movement of an asset to infer the oversold and overbought conditions. It also lets investors know the entry and exit positions to make a profit.

All the trading platforms have interfaces that readily generate the indicator required. The objective of this blog was to replicate it using Python and generate similar visuals. This would be a good foundation for anyone interested in algo trading.

I hope you liked the article and found it helpful.

You can connect with me — on Linkedin and Github


The blog is only educational and should not be used as professional advice for real-world financial decisions.



Leave a Reply

Your email address will not be published. Required fields are marked *