The RSI or Relative Strength Index is one of the main momentum indicators I personally use when making decisions about an options or stock trade, not as a main indicator, but a good tool to confirm the current momentum of a stock. All tools I’ve found or used so far ( broker custom paid tools included ) gives this indicator visually, so you can check where the RSI is on a certain period or a certain stock over a graphic, however you don’t have access to the data directly, just to its visual representation over a chart. This is inconvenient in many ways if you plan to actively work with this data, specially if you have a long watchlist of stocks.
The Relative Strength Index (RSI) is a measurement used by traders to assess the price momentum of a stock or other security. The basic idea behind the RSI is to measure how quickly traders are bidding the price of the security up or down. The RSI plots this result on a scale of 0 to 100. Readings below 30 generally indicate that the stock is oversold, while readings above 70 indicate that it is overbought. Traders will often place this RSI chart below the price chart for the security, so they can compare its recent momentum against its market price.https://www.investopedia.com/terms/r/rsi.asp
What problem is this tool trying to solve?
By checking the RSI momentum indicator we can see if a stock is in the overbought or oversold territory, this tells us that the probability of bouncing back increases, and that momentum of going in the opposite direction increases as the RSI touches its maximum values. The main problem with existing tools is that while you can see this visually, you don’t have access to the numbers, so you cannot automate this unless you’re scrapping a website, which is finicky at best. You must go through all your watchlist of stocks and confirm visually where is in RSI values are at that moment, this is even more taxing if you’re both investing and trading, as the period to check varies its values.
With this script I’m pulling the data for all stocks on my list and then making the calculation on my own, skipping the final visual representation step, this way I can work directly with the data and for example set alerts whenever a stock reach a designated value or range.
As follows you’ll see the technical implementation of the whole thing and how has been coded, if you don’t care about this ( which is perfectly fine ) you can just get and use the script from GitHub here.
Installing and importing the modules
While I was writing this RSI implementation using the Interactive Brokers API I discovered that it may not be necessary, and we can just get the historic daily data from Yahoo Finance, so this is the approach I took at the moment:
import pandas_datareader as pdr import datetime as dt import sys
We’ll use the pandas_datareader module and the get_data_yahoo() method to get our initial data source, this will gives us a starting point of data to work with, specially the closing prices of a given stock by the end of the day, which is what we’re looking for in order to build our RSI indicator.
ticker = pdr.get_data_yahoo("AMD", dt.datetime(2021,1,1), dt.datetime.now())
The output will be something like this:
# High Low Open Close Volume Adj Close # Date # 2020-12-31 92.300003 90.870003 92.099998 91.709999 24930700 91.709999 # 2021-01-04 96.059998 90.919998 92.110001 92.300003 51802600 92.300003 # 2021-01-05 93.209999 91.410004 92.099998 92.769997 34208000 92.769997 # 2021-01-06 92.279999 89.459999 91.620003 90.330002 51911700 90.330002 # 2021-01-07 95.510002 91.199997 91.330002 95.160004 42897200 95.160004 # ... ... ... ... ... ... ... # 2021-06-11 82.330002 80.699997 81.610001 81.309998 24290800 81.309998 # 2021-06-14 81.550003 80.199997 81.510002 81.550003 27830200 81.550003 # 2021-06-15 81.680000 80.220001 81.589996 80.470001 26194300 80.470001 # 2021-06-16 81.449997 78.959999 80.750000 80.110001 29560900 80.110001 # 2021-06-17 85.370003 80.570000 80.769997 84.559998 77822823 84.559998 # [116 rows x 6 columns]
We do not need all that data for this tool, but just the date and closing price, or better said, we’re looking for the difference between sessions in closing price. We’ll get both just to be sure we’re checking the correct data:
delta = ticker['Close']
It will return something like:
# Date # 2020-12-31 91.709999 # 2021-01-04 92.300003 # 2021-01-05 92.769997 # 2021-01-06 90.330002 # 2021-01-07 95.160004 # ... # 2021-06-11 81.309998 # 2021-06-14 81.550003 # 2021-06-15 80.470001 # 2021-06-16 80.110001 # 2021-06-17 84.559998 # Name: Close, Length: 116, dtype: float64
At this point we need to calculate
[Closing_price(n-1) - Closing_price(n)] in order to get the difference between the closing prices of consecutive market sessions, for this we can just go ahead and use Panda’s
.diff() method, so we don’t need to calculate these “manually”:
delta = ticker['Close'].diff()
The resulting column will be:
# Date # 2020-12-31 NaN # 2021-01-04 0.590004 # 2021-01-05 0.469994 # 2021-01-06 -2.439995 # 2021-01-07 4.830002 # ... # 2021-06-11 -0.250000 # 2021-06-14 0.240005 # 2021-06-15 -1.080002 # 2021-06-16 -0.360001 # 2021-06-17 4.449997 # Name: Close, Length: 116, dtype: float64
Note that the first value is NaN ( Not a Number ) because there’s no data for (n-1) we can use when calculating the difference between sessions, that’s perfectly fine. We also double check manually that other values are correctly just to be sure to be in the right track, for example:
If we take the data from
delta = ticker['Close'] we can calculate the difference between the 1st and the 2nd value ( 2020-11-04 and 2020-12-31 ) , these are closing prices of
92.300003 - 91.709999 = 0.590004, so we’re correctly calculating the difference here via diff() . Later on we can add unit tests at this point to be sure that is not breaking on future iterations, but for now let’s move on:
Transforming initial data into the RSI indicator
At this point we need to transform the data we got into input to feed the RSI indicator. For this momentum indicator we basically need 2 inputs:
- Up = Average of all up-moves in the last N price bars.
- Down = Average of all down-moves in the last N price bars.
up = delta.clip(lower=0)
# Date # 2020-12-31 NaN # 2021-01-04 0.590004 # 2021-01-05 0.469994 # 2021-01-06 0.000000 ### Clipped from -2.43 to 0 # 2021-01-07 4.830002 # ... # 2021-06-11 0.000000 ### Clipped from -0.25 to 0 # 2021-06-14 0.240005 # 2021-06-15 0.000000 ### Clipped from -1.08 to 0 # 2021-06-16 0.000000 ### Clipped from -0.36 to 0 # 2021-06-17 4.449997 # Name: Close, Length: 116, dtype: float64
down = -1*delta.clip(upper=0)
# Date # 2020-12-31 NaN # 2021-01-04 -0.000000 # 2021-01-05 -0.000000 # 2021-01-06 2.439995 # 2021-01-07 -0.000000 # ... # 2021-06-11 0.250000 # 2021-06-14 -0.000000 # 2021-06-15 1.080002 # 2021-06-16 0.360001 # 2021-06-17 -0.000000 # Name: Close, Length: 116, dtype: float64
Creating the exponential weighted (EW) functions.
At this point we have all the data ready to feed our Relative Strength calculation, so we’ll dig into the meat and potatoes:
Pandas comes with a handy method called ewm(), this will provide us with exponential weighted calculations from the data we feed into it. This is important because we’re giving more importante, or weight, to recent price data. We’ll also be applying the mean() method which gives us the statistical mean (average) of a given DataFrame.
We’ll adjust the ewm() parameters to calculate this recurrently and with no decay, as there’s no imbalance in prices as could be in other uses of this function.
Once we get the Relative Strength calculation we only need to convert this into an index by calculating its value over 0-100, and then we add this Relative Strength Index values as a new column to our initial Dataframe:
ema_up = up.ewm(com=13, adjust=False).mean() ema_down = down.ewm(com=13, adjust=False).mean() rs = ema_up/ema_down ticker['RSI'] = 100 - ( 100 / (1+rs) ) print(ticker)
This will be the output, notice the last column showing the RSI for a given day. You can also change the last print statement for
print(ticker['RSI']) in order to get only this column and ignore the rest ( which is what I’m doing ).
# Example output # High Low Open ... Volume Adj Close RSI # Date ... # 2020-01-02 32.500000 31.959999 32.310001 ... 10721100 32.299999 NaN # 2020-01-03 32.099998 31.260000 31.709999 ... 14429500 31.520000 0.000000 # 2020-01-06 31.709999 31.160000 31.230000 ... 12582500 31.639999 1.169582 # 2020-01-07 32.700001 31.719999 31.799999 ... 13712900 32.540001 9.699977 # 2020-01-08 33.400002 32.349998 32.349998 ... 14632400 33.049999 14.218360 # ... ... ... ... ... ... ... ... # 2020-08-11 39.000000 36.709999 37.590000 ... 20486000 37.279999 58.645030 # 2020-08-12 38.000000 36.820000 37.500000 ... 11013300 37.439999 59.532873 # 2020-08-13 38.270000 37.369999 37.430000 ... 13259400 37.820000 61.639293 # 2020-08-14 37.959999 37.279999 37.740002 ... 10377300 37.900002 62.086731 # 2020-08-17 38.090000 37.270000 37.950001 ... 10186900 37.970001 62.498897
Analyzing multiple stocks and showing only the RSI signals
We’ll do a quick refactor in order to:
- Being able to analyze a list of multiple stocks
- Display only the RSI signal ( Neutral, Buy, or Sell ).
We create a new list for all our tickers, for example:
tickerList = ['MU','INTC', 'BP', 'TROX', 'MMM', 'UL', 'SFTBY', 'CRSR']
We create a new method named
calc_rsi(t) that accepts one parameter, the list of tickers. Then we create a loop and iterate through all elements of the list:
def calc_rsi(t): # RSI code implementation goes here for t in tickerList: calc_rsi(t)
Finally we need to output this data in a meaningful way, I’ve chosen to just display a different message depending if the stock is neutral, overbought, or oversold:
length_of_dataframe = len(ticker) latest_rsi_value = ticker['RSI'][length_of_dataframe-1] oversold = 30 overbought = 70 if latest_rsi_value.astype(int) > overbought: print(t + ' RSI: ' + str(latest_rsi_value) + ' - SELL signal') elif latest_rsi_value.astype(int) < oversold: print(t + ' RSI: ' + str(latest_rsi_value) + ' - BUY signal') else: print(t + ' RSI: ' + str(latest_rsi_value) + ' - Neutral signal')
What about plotting the results graphically?
Not even thinking about it, the core problem this script is trying to solve is precisely to move from a visual solution offered by multiple brokers, to a pure numerical one, which is not offered. If you’d like to see the visual representation of the RSI for a given stock, any broker ( or tools like Yahoo Finance or Google Finance ) will give you that data.
At this point the tool is 100% complete and functional, however there’s always improvements to be made, for example:
- Create an UI
- Multiple timeframes available – Now daily only
- Set up the script on a cloud server with a recurring job.
- Unit Tests
- Alerts via Telegram, email, OSX notifications, or other apps.
Have more ideas, feedback, or recommendations? Please drop a comment below or submit directly to the project GitHub repository here:
If you enjoyed the article and feel inclined to donate, you can do so here: