Overlaying the Relative Strength Index (RSI) on Multiple Stocks/Crypto in Python

Max Bade
Dev Genius
Published in
7 min readNov 16, 2020

--

Daily RSI for Several Crypto Coins in Plotly

I’ll Keep This Brief

If you’re like me, you could really care less about “how” you got there, as long as you’re there. I set out this weekend to find a way to overlay the RSI for multiple cryptocurrencies, and ended up with the above image (and one for stocks). It does exactly what I wanted.

Now, perhaps there’s a website out there that offers this feature for free, perhaps there’s not. Either way, if you’ve found this page, and were looking for a FUHHREEE way to see the RSI for multiple stocks/cryptos, this is all you need. The code is below. You’ll have to figure out how to run python on your own though.

*I only created the chart for the daily. I really need to add a parameter for pulling in stocks/cryptos at each period — (15m, 1h, 2h, 4h, 6h etc.) Maybe I’ll do that now… Maybe I won’t :) I’m sure it’s already a parameter in those libraries. It has to be.

What is the RSI?

Developed by J. Welles Wilder, the Relative Strength Index (RSI) is a momentum oscillator that measures the speed and change of price movements. RSI oscillates between zero and 100. According to Wilder, RSI is considered overbought when above 70 and oversold when below 30. Signals can also be generated by looking for divergences, failure swings and centerline crossovers. RSI can also be used to identify the general trend.

Wait.. what? The RSI is just a value that is calculated from some equation (not all to gnarly of an equation though), that tells you when you should buy/sell. It’s an indicator used on every trading platform, in pretty much every youtube video on “HOW TO MAKE A MILLION DOLLARS OMG!?!” and in most trading strategies, no matter what the goal; long term, short term HODL FOREVER #TOTHEMOON #OBVI

How To acutally View the RSI?

Ready? When the RSI line goes below the bottom line (30), BUY, when it goes above the top line (70), sell. That’s it. Walk away.

You will miss out on trades if you only apply this strategy on the daily period, but, it’s a damn good indicator. This is not trading advice, but you likely won’t go wrong if you only buy when the RSI dips below the 30 line. It will go up in the next few weeks. You’re welcome.

Trading View Image of The $COMP Coin on the Day

RSI Crypto Code

#import the libraries
from fastquant import get_stock_data
from fastquant import get_crypto_data
from pandas_datareader import data as pdr
import yfinance as yf
import plotly.graph_objects as go
import pandas as pd
from datetime import datetime
#set a date variable
now = datetime.today().strftime('%Y-%m-%d')
print('current date: ', now)
#pull in the data
BTC = get_crypto_data("BTC/USDT", "2020-01-01", now)
print('1/16 done')
LINK = get_crypto_data("LINK/USDT", "2020-01-01", now)
print('2/16 done')
ZRX = get_crypto_data("ZRX/USDT", "2020-01-01", now)
print('3/16 done')
YFI = get_crypto_data("YFI/USDT", "2020-01-01", now)
print('4/16 done')
UNI = get_crypto_data("UNI/USDT", "2020-01-01", now)
print('5/16 done')
OMG = get_crypto_data("OMG/USDT", "2020-01-01", now)
print('6/16 done')
ETH = get_crypto_data("ETH/USDT", "2020-01-01", now)
print('7/16 done')
XRP = get_crypto_data("XRP/USDT", "2020-01-01", now)
print('8/16 done')
LTC = get_crypto_data("LTC/USDT", "2020-01-01", now)
print('9/16 done')
COMP = get_crypto_data("COMP/USDT", "2020-01-01", now)
print('10/16 done')
BNB = get_crypto_data("BNB/USDT", "2020-01-01", now)
print('11/16 done')
SUSHI = get_crypto_data("SUSHI/USDT", "2020-01-01", now)
print('12/16 done')
TRX = get_crypto_data("TRX/USDT", "2020-01-01", now)
print('13/16 done')
BAND = get_crypto_data("BAND/USDT", "2020-01-01", now)
print('14/16 done')
EOS = get_crypto_data("EOS/USDT", "2020-01-01", now)
print('15/16 done')
ZEC = get_crypto_data("ZEC/USDT", "2020-01-01", now)
print('16/16 done')
#reset the indexes of each df
BTC = BTC.reset_index()
LINK = LINK.reset_index()
ZRX = ZRX.reset_index()
YFI = YFI.reset_index()
UNI = UNI.reset_index()
OMG = OMG.reset_index()
ETH = ETH.reset_index()
XRP = XRP.reset_index()
LTC = LTC.reset_index()
COMP = COMP.reset_index()
BNB = BNB.reset_index()
SUSHI = SUSHI.reset_index()
TRX = TRX.reset_index()
BAND = BAND.reset_index()
EOS = EOS.reset_index()
ZEC = ZEC.reset_index()
print('btc: ', BTC.head())#keep only the date and the close column
#rename the cloumns
BTC = BTC[['dt','close']].rename(columns={"dt": "Date", "close": "btc_close"})
LINK = LINK[['dt','close']].rename(columns={"dt": "Date", "close": "link_close"})
ZRX = ZRX[['dt','close']].rename(columns={"dt": "Date", "close": "zrx_close"})
YFI = YFI[['dt','close']].rename(columns={"dt": "Date", "close": "yfi_close"})
UNI = UNI[['dt','close']].rename(columns={"dt": "Date", "close": "uni_close"})
OMG = OMG[['dt','close']].rename(columns={"dt": "Date", "close": "omg_close"})
ETH = ETH[['dt','close']].rename(columns={"dt": "Date", "close": "eth_close"})
XRP = XRP[['dt','close']].rename(columns={"dt": "Date", "close": "xrp_close"})
LTC = LTC[['dt','close']].rename(columns={"dt": "Date", "close": "ltc_close"})
COMP = COMP[['dt','close']].rename(columns={"dt": "Date", "close": "comp_close"})
BNB = BNB[['dt','close']].rename(columns={"dt": "Date", "close": "bnb_close"})
SUSHI = SUSHI[['dt','close']].rename(columns={"dt": "Date", "close": "sushi_close"})
TRX = TRX[['dt','close']].rename(columns={"dt": "Date", "close": "trx_close"})
BAND = BAND[['dt','close']].rename(columns={"dt": "Date", "close": "band_close"})
EOS = EOS[['dt','close']].rename(columns={"dt": "Date", "close": "eos_close"})
ZEC = ZEC[['dt','close']].rename(columns={"dt": "Date", "close": "zec_close"})
print('btc: \n ', BTC.head())#merge all the dataframes
df = pd.merge(BTC, LINK, on='Date', how='left')
df = pd.merge(df, ZRX, on='Date', how='left')
df = pd.merge(df, YFI, on='Date', how='left')
df = pd.merge(df, UNI, on='Date', how='left')
df = pd.merge(df, OMG, on='Date', how='left')
df = pd.merge(df, ETH, on='Date', how='left')
df = pd.merge(df, XRP, on='Date', how='left')
df = pd.merge(df, LTC, on='Date', how='left')
df = pd.merge(df, COMP, on='Date', how='left')
df = pd.merge(df, BNB, on='Date', how='left')
df = pd.merge(df, SUSHI, on='Date', how='left')
df = pd.merge(df, TRX, on='Date', how='left')
df = pd.merge(df, BAND, on='Date', how='left')
df = pd.merge(df, EOS, on='Date', how='left')
df = pd.merge(df, ZEC, on='Date', how='left')
print(df.info)#rsi function
def computeRSI (data, time_window):
diff = data.diff(1).dropna() # diff in one field(one day)
#this preservers dimensions off diff values
up_chg = 0 * diff
down_chg = 0 * diff

# up change is equal to the positive difference, otherwise equal to zero
up_chg[diff > 0] = diff[ diff>0 ]

# down change is equal to negative deifference, otherwise equal to zero
down_chg[diff < 0] = diff[ diff < 0 ]

# check pandas documentation for ewm
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.ewm.html
# values are related to exponential decay
# we set com=time_window-1 so we get decay alpha=1/time_window
up_chg_avg = up_chg.ewm(com=time_window-1, min_periods=time_window).mean()
down_chg_avg = down_chg.ewm(com=time_window-1, min_periods=time_window).mean()

rs = abs(up_chg_avg/down_chg_avg)
rsi = 100 - 100/(1+rs)
return rsi
#run the function for each column
df['btc_close'] = computeRSI(df['btc_close'], 14)
df['link_close'] = computeRSI(df['link_close'], 14)
df['zrx_close'] = computeRSI(df['zrx_close'], 14)
df['yfi_close'] = computeRSI(df['yfi_close'], 14)
df['uni_close'] = computeRSI(df['uni_close'], 14)
df['omg_close'] = computeRSI(df['omg_close'], 14)
df['eth_close'] = computeRSI(df['eth_close'], 14)
df['xrp_close'] = computeRSI(df['xrp_close'], 14)
df['ltc_close'] = computeRSI(df['ltc_close'], 14)
df['comp_close'] = computeRSI(df['comp_close'], 14)
df['bnb_close'] = computeRSI(df['bnb_close'], 14)
df['sushi_close'] = computeRSI(df['sushi_close'], 14)
df['trx_close'] = computeRSI(df['trx_close'], 14)
df['band_close'] = computeRSI(df['band_close'], 14)
df['eos_close'] = computeRSI(df['eos_close'], 14)
df['zec_close'] = computeRSI(df['zec_close'], 14)
#set the high and low lines (as columns)
df['low'] = 30
df['high'] = 70
df.head(20)
#plot it!
fig = go.Figure()
#create lines/traces
fig.add_trace(go.Scatter(x=df['Date'], y=df['btc_close'],
mode='lines',
name='BTC',
line=dict(color="Silver", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['link_close'],
mode='lines',
name='LINK',
line=dict(color="orange", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['zrx_close'],
mode='lines',
name='ZRX',
line=dict(color="royalblue", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['yfi_close'],
mode='lines',
name='YFI',
line=dict(color="LightGreen", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['uni_close'],
mode='lines',
name='UNI',
line=dict(color="MediumPurple", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['omg_close'],
mode='lines',
name='OMG',
line=dict(color="Red", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['eth_close'],
mode='lines',
name='ETH',
line=dict(color="Aqua", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['xrp_close'],
mode='lines',
name='XRP',
line=dict(color="Gold", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['ltc_close'],
mode='lines',
name='LTC',
line=dict(color="Yellow", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['comp_close'],
mode='lines',
name='COMP',
line=dict(color="lightseagreen", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['bnb_close'],
mode='lines',
name='BNB',
line=dict(color="darkturquoise", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['sushi_close'],
mode='lines',
name='SUSHI',
line=dict(color="slateblue", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['trx_close'],
mode='lines',
name='TRX',
line=dict(color="firebrick", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['band_close'],
mode='lines',
name='BAND',
line=dict(color="turquoise", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['eos_close'],
mode='lines',
name='EOS',
line=dict(color="olivedrab", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['zec_close'],
mode='lines',
name='ZEC',
line=dict(color="maroon", width=1),))
fig.add_trace(go.Scatter(x=df['Date'], y=df['high'],
fill=None,
mode='lines',
line=dict(width=0.5, color='rgb(222, 196, 255)', dash='dash')))
fig.add_trace(go.Scatter(x=df['Date'],y=df['low'],
fill='tonexty', # fill area between trace0 and trace1
mode='lines',
line=dict(width=0.5, color='rgb(222, 196, 255)', dash='dash')))
#update axis ticks
fig.update_yaxes(nticks=30,showgrid=True)
fig.update_xaxes(nticks=12,showgrid=True)
#update layout
fig.update_layout(title="<b>Daily RSI</b>"
, height = 700
, xaxis_title='Date'
, yaxis_title='Relative Strength Index'
, template = "plotly" #['ggplot2', 'seaborn', 'simple_white', 'plotly', 'plotly_white', 'plotly_dark']
)
#update legend
fig.update_layout(legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1
))
#show the figure
fig.show()

RSI Stocks Code

from pandas_datareader import data as pdr
import yfinance as yf
import plotly.graph_objects as go
yf.pdr_override()# download dataframe
data = pdr.get_data_yahoo(tickers = "AMZN AAPL MSFT GOOGL NFLX FB BIGC"
, start="2019-01-01"
, end="2020-11-14")
print(data.head())#rsi function
def computeRSI (data, time_window):
diff = data.diff(1).dropna() # diff in one field(one day)
#this preservers dimensions off diff values
up_chg = 0 * diff
down_chg = 0 * diff

# up change is equal to the positive difference, otherwise equal to zero
up_chg[diff > 0] = diff[ diff>0 ]

# down change is equal to negative deifference, otherwise equal to zero
down_chg[diff < 0] = diff[ diff < 0 ]

# check pandas documentation for ewm
# https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.ewm.html
# values are related to exponential decay
# we set com=time_window-1 so we get decay alpha=1/time_window
up_chg_avg = up_chg.ewm(com=time_window-1, min_periods=time_window).mean()
down_chg_avg = down_chg.ewm(com=time_window-1, min_periods=time_window).mean()

rs = abs(up_chg_avg/down_chg_avg)
rsi = 100 - 100/(1+rs)
return rsi
#create rsi columns
close = data['Adj Close']
close = close.reset_index()
close['RSI_APPLE'] = computeRSI(close['AAPL'],14)
close['RSI_AMZN'] = computeRSI(close['AMZN'],14)
close['RSI_FB'] = computeRSI(close['FB'],14)
close['RSI_GOOGL'] = computeRSI(close['GOOGL'],14)
close['RSI_MSFT'] = computeRSI(close['MSFT'],14)
close['RSI_NFLX'] = computeRSI(close['NFLX'],14)
close['RSI_BIGC'] = computeRSI(close['BIGC'],14)
close['low'] = 30
close['high'] = 70
close.head(20)
#plot
fig = go.Figure()
#create lines/traces
fig.add_trace(go.Scatter(x=close['Date'], y=close['RSI_APPLE'],
mode='lines',
name='Apple',
line=dict(color="Silver", width=1),))
fig.add_trace(go.Scatter(x=close['Date'], y=close['RSI_AMZN'],
mode='lines',
name='Amazon',
line=dict(color="orange", width=1),))
fig.add_trace(go.Scatter(x=close['Date'], y=close['RSI_FB'],
mode='lines',
name='FaceBook',
line=dict(color="royalblue", width=1),))
fig.add_trace(go.Scatter(x=close['Date'], y=close['RSI_GOOGL'],
mode='lines',
name='Google',
line=dict(color="LightGreen", width=1),))
fig.add_trace(go.Scatter(x=close['Date'], y=close['RSI_MSFT'],
mode='lines',
name='Microsoft',
line=dict(color="MediumPurple", width=1),))
fig.add_trace(go.Scatter(x=close['Date'], y=close['RSI_NFLX'],
mode='lines',
name='Netflix',
line=dict(color="Red", width=1),))
fig.add_trace(go.Scatter(x=close['Date'], y=close['RSI_BIGC'],
mode='lines',
name='BIGC',
line=dict(color="Teal", width=1),))
fig.add_trace(go.Scatter(x=close['Date'], y=close['high'],
fill=None,
mode='lines',
line=dict(width=0.5, color='rgb(222, 196, 255)', dash='dash')))
fig.add_trace(go.Scatter(x=close['Date'],y=close['low'],
fill='tonexty', # fill area between trace0 and trace1
mode='lines',
line=dict(width=0.5, color='rgb(222, 196, 255)', dash='dash')))
#update axis ticks
fig.update_yaxes(nticks=30,showgrid=True)
fig.update_xaxes(nticks=12,showgrid=True)
#update layout
fig.update_layout(title="<b>Daily RSI</b>"
, height = 700
, xaxis_title='Date'
, yaxis_title='Relative Strength Index'
, template = "plotly" #['ggplot2', 'seaborn', 'simple_white', 'plotly', 'plotly_white', 'plotly_dark']
)
#update legend
fig.update_layout(legend=dict(
orientation="h",
yanchor="bottom",
y=1.02,
xanchor="right",
x=1
))
fig.show()
Daily RSI for Several Stocks

Some Details

For Crypto, if you’re interested, there are two libraries that made this possible. Not saying there are only two libraries to get this job done, there are many I'm sure. I didn’t want to go the route of creating an account and generating api keys. That’s annoying. So I found fastquant an awesome python library that allows you to pull in historic data as well as backtest different indicators. Shout out to them!

And for stocks, I used the yfinance libarary, which is great as well. Here’s an overly decent post that shows you more details on how to use it to generate the RSI. Mine is just prettier ;) Here is also a great stackoverflow post on generating the RSI.

To Wrap Up

And by wrap up, I mean I baselessly make the slightly too OCD crowd sit through and read the rest of my post because let’s face it, they have to read the full article, right?

--

--