Algorithmic Trading with Relative Strength Index in Python

Learn to make better trades with the most popular trading indicator in python



While there are other momentum oscillators like the Stochastic Oscillator and the Awesome Oscillator, the one we will discuss today is considered the most popular among traders and a great one for beginner traders. It is none other than the Relative Strength Index, shortly known as RSI. In this article, we will build some basic intuitions about RSI and its calculation, then, we will be creating this indicator from scratch and build a trading strategy based on it in python. Without further ado, let’s dive into the article.


Relative Strength Index (RSI)


Before moving on, let’s first gain an understanding of what an Oscillator means in the stock trading space. An oscillator is a technical tool that constructs a trend-based indicator whose values are bound between a high and low band. Traders use these bands along with the constructed trend-based indicator to identify the market state and make potential buy and sell trades. Also, oscillators are widely used for short-term trading purposes but there are no restrictions in using them for long-term investments.


Founded and developed by J. Welles Wilder in 1978, the Relative Strength Index is a momentum oscillator that is used by traders to identify whether the market is in the state of overbought or oversold. Before moving on, let’s explore what overbought and oversold is. A market is considered to be in the state of overbought when an asset is constantly bought by traders moving it to an extremely bullish trend and bound to consolidate. Similarly, a market is considered to be in the state of oversold when an asset is constantly sold by traders moving it to a bearish trend and tends to bounce back.


Being an oscillator, the values of RSI bound between 0 to 100. The traditional way to evaluate a market state using the Relative Strength Index is that an RSI reading of 70 or above reveals a state of overbought, and similarly, an RSI reading of 30 or below represents the market is in the state of oversold. These overbought and oversold can also be tuned concerning which stock or asset you choose. For example, some assets might have constant RSI readings of 80 and 20. So in that case, you can set the overbought and oversold levels to be 80 and 20 respectively. The standard setting of RSI is 14 as the lookback period.


RSI might sound more similar to Stochastic Oscillator in terms of value interpretation but the way it's being calculated is quite different. There are three steps involved in the calculation of RSI.


  • Calculating the Exponential Moving Average (EMA) of the gain and loss of an asset: A word on Exponential Moving Average. EMA is a type of Moving Average (MA) that automatically allocates greater weighting (nothing but importance) to the most recent data point and lesser weighting to data points in the distant past. In this step, we will first calculate the returns of the asset and separate the gains from losses. Using these separated values, the two EMAs for a specified number of periods are calculated.


  • Calculating the Relative Strength of an asset: The Relative Strength of an asset is determined by dividing the Exponential Moving Average of the gain of an asset from the Exponential Moving Average of the loss of an asset for a specified number of periods. It can be mathematically represented as follows:



RS = GAIN EMA / LOSS EMA
where,
RS = Relative Strength
GAIN EMA = Exponential Moving Average of the gains
LOSS EMA = Exponential Moving Average of the losses


  • Calculating the RSI values: In this step, we will calculate the RSI itself by making use of the Relative Strength values we calculated in the previous step. To calculate the values of RSI of a given asset for a specified number of periods, there is a formula that we need to follow:



RSI = 100.0 - (100.0 / (1.0 + RS))
where,
RSI = Relative Strength Index
RS = Relative Strength


Now that we have some basic understanding of what Relative Strength Index is and how it is being calculated. Let’s build some intuitions on our RSI trading strategy.


About our RSI Trading Strategy: In this article, we are going to implement a simple crossover strategy with the traditional setting of 30 and 70 as the oversold and overbought levels respectively, and with 14 as the lookback period. Our strategy reveals a buy signal whenever the previous RSI value is above the oversold level and the current RSI value crosses below the oversold level. Likewise, the strategy reveals a sell signal whenever the previous RSI value is below the oversold level and the current RSI value crosses above the oversold level. Our trading strategy can be represented as follows:



IF PREVIOUS RSI > 30 AND CURRENT RSI < 30 ==> BUY SIGNAL
IF PREVIOUS RSI < 70 AND CURRENT RSI > 70 ==> SELL SIGNAL


This concludes our theory part on the Relative Strength Index and our trading strategy. Let’s now code our trading strategy in python to see some exciting results. Before moving on, a note on disclaimer: This article’s sole purpose is to educate people and must be considered as an information piece but not as investment advice or so.


Implementation in Python


The coding part is classified into various steps as follows:



1. Importing Packages
2. Extracting Data from Alpha Vantage
3. RSI calculation
4. RSI Plot
5. Creating the Trading Strategy
6. Plotting the Trading Lists
7. Creating our Position
8. Backtesting


We will be following the order mentioned in the above list and buckle up your seat belts to follow every upcoming coding part.


Step-1: Importing Packages


Importing the required packages into the python environment is a non-skippable step. The primary packages are going to be Pandas to work with data, NumPy to work with arrays and for complex functions, Matplotlib for plotting purposes, and Requests to make API calls. The secondary packages are going to be Math for mathematical functions and Termcolor for font customization (optional).


Python Implementation:



import pandas as pd 
import matplotlib.pyplot as plt
import requests
import numpy as np
from math import floor
from termcolor import colored as cl 

plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (20, 10)


Now that we have imported all the essential packages into our python environment. Let’s proceed with pulling the historical data of IBM with Alpha Vantage’s powerful stock API.


Step-2: Extracting Data from Alpha Vantage


In this step, we are going to pull the historical data of IBM using an API endpoint provided by Alpha Vantage. Before that, a note on Alpha Vantage: Alpha Vantage provides free stock APIs through which users can access a wide range of data like real-time updates, and historical data on equities, currencies, and cryptocurrencies. Make sure that you have an account on Alpha Vantage, only then, you will be able to access your secret API key (a crucial element for pulling data using an API).


Python Implementation:



def get_historical_data(symbol, start_date = None):
    api_key = open(r'api_key.txt')
    api_url = f'https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol={symbol}&apikey={api_key}&outputsize=full'
    raw_df = requests.get(api_url).json()
    df = pd.DataFrame(raw_df[f'Time Series (Daily)']).T
    df = df.rename(columns = {'1. open': 'open', '2. high': 'high', '3. low': 'low', '4. close': 'close', '5. adjusted close': 'adj close', '6. volume': 'volume'})
    for i in df.columns:
        df[i] = df[i].astype(float)
    df.index = pd.to_datetime(df.index)
    df = df.iloc[::-1].drop(['7. dividend amount', '8. split coefficient'], axis = 1)
    if start_date:
        df = df[df.index >= start_date]
    return df

ibm = get_historical_data('IBM', '2020-01-01')
ibm


Output:



Code Explanation: The first thing we did is to define a function named ‘get_historical_data’ that takes the stock’s symbol (‘symbol’) as a required parameter and the starting date of the historical data (‘start_date’) as an optional parameter. Inside the function, we are defining the API key and the URL and stored them into their respective variable. Next, we are extracting the historical data in JSON format using the ‘get’ function and stored it into the ‘raw_df’ variable. After doing some processes to clean and format the raw JSON data, we are returning it in the form of a clean Pandas dataframe. Finally, we are calling the created function to pull the historic data of IBM from the starting of 2020 and stored it into the ‘ibm’ variable.


Step-3: RSI Calculation


In this step, we are going to calculate the values of RSI with 14 as the lookback period using the RSI formula we discussed before.


Python Implementation:



def get_rsi(close, lookback):
    ret = close.diff()
    up = []
    down = []
    for i in range(len(ret)):
        if ret[i] < 0:
            up.append(0)
            down.append(ret[i])
        else:
            up.append(ret[i])
            down.append(0)
    up_series = pd.Series(up)
    down_series = pd.Series(down).abs()
    up_ewm = up_series.ewm(com = lookback - 1, adjust = False).mean()
    down_ewm = down_series.ewm(com = lookback - 1, adjust = False).mean()
    rs = up_ewm/down_ewm
    rsi = 100 - (100 / (1 + rs))
    rsi_df = pd.DataFrame(rsi).rename(columns = {0:'rsi'}).set_index(close.index)
    rsi_df = rsi_df.dropna()
    return rsi_df[3:]

ibm['rsi_14'] = get_rsi(ibm['close'], 14)
ibm = ibm.dropna()
ibm


Output:



Code Explanation: Firstly, we are defining a function named ‘get_rsi’ that takes the closing price of a stock (‘close’) and the lookback period (‘lookback’) as parameters. Inside the function, we are first calculating the returns of the stock using the ‘diff’ function provided by the Pandas package and stored it into the ‘ret’ variable. This function basically subtracts the current value from the previous value. Next, we are passing a for-loop on the ‘ret’ variable to distinguish gains from losses and append those values to the concerning variable (‘up’ or ‘down’).


Then, we are calculating the Exponential Moving Averages for both the ‘up’ and ‘down’ using the ‘ewm’ function provided by the Pandas package and stored them into the ‘up_ewm’ and ‘down_ewm’ variable respectively. Using these calculated EMAs, we are determining the Relative Strength by following the formula we discussed before and stored it into the ‘rs’ variable.


By making use of the calculated Relative Strength values, we are calculating the RSI values by following its formula. After doing some data processing and manipulations, we are returning the calculated Relative Strength Index values in the form of a Pandas dataframe. Finally, we are calling the created function to store the RSI values of IBM with 14 as the lookback period.


Step-4: RSI Plot


In this step, we are going to plot the calculated Relative Strength Index values of IBM to make more sense out of it. The main aim of this part is not on the coding section but instead to observe the plot to gain a solid understanding of RSI.


Python Implementation:



ax1 = plt.subplot2grid((10,1), (0,0), rowspan = 4, colspan = 1)
ax2 = plt.subplot2grid((10,1), (5,0), rowspan = 4, colspan = 1)
ax1.plot(ibm['close'], linewidth = 2.5)
ax1.set_title('IBM CLOSE PRICE')
ax2.plot(ibm['rsi_14'], color = 'orange', linewidth = 2.5)
ax2.axhline(30, linestyle = '--', linewidth = 1.5, color = 'grey')
ax2.axhline(70, linestyle = '--', linewidth = 1.5, color = 'grey')
ax2.set_title('IBM RELATIVE STRENGTH INDEX')
plt.show()


Output:


The above chart is separated into two panels: The above panel with the closing price of IBM and the lower panel with the calculated RSI 14 values of IBM. While analyzing the panel plotted with the RSI values, it can be seen that the trend and movement of the calculated values follow the same as the closing price of IBM. So, we can consider that RSI is a directional indicator. Some indicators are non-directional meaning that their movement will be inversely proportional to the actual stock movement and this can sometimes confuse traders and hard to understand too.


While observing the RSI chart, we could be able to see that the plot of RSI reveals trend reversals even before the market does. Simply speaking, the RSI shows a downtrend or an uptrend right before the actual market does. This shows that RSI is a leading indicator. A leading indicator is nothing but an indicator that takes into account the current value of a data series to predict future movements. RSI being a leading indicator helps in warning the traders about potential trend reversions before in time. The opposite of leading indicators is called lagging indicators. Lagging indicators are indicators that represent the current value by taking into account the historical values of a data series.


Step-5: Creating the trading strategy


In this step, we are going to implement the discussed Relative Strength Index trading strategy in python.


Python Implementation:



def implement_rsi_strategy(prices, rsi):    
    buy_price = []
    sell_price = []
    rsi_signal = []
    signal = 0

    for i in range(len(rsi)):
        if rsi[i-1] > 30 and rsi[i] < 30:
            if signal != 1:
                buy_price.append(prices[i])
                sell_price.append(np.nan)
                signal = 1
                rsi_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                rsi_signal.append(0)
        elif rsi[i-1] < 70 and rsi[i] > 70:
            if signal != -1:
                buy_price.append(np.nan)
                sell_price.append(prices[i])
                signal = -1
                rsi_signal.append(signal)
            else:
                buy_price.append(np.nan)
                sell_price.append(np.nan)
                rsi_signal.append(0)
        else:
            buy_price.append(np.nan)
            sell_price.append(np.nan)
            rsi_signal.append(0)
            
    return buy_price, sell_price, rsi_signal
            

buy_price, sell_price, rsi_signal = implement_rsi_strategy(ibm['close'], ibm['rsi_14'])


Code Explanation: First, we are defining a function named ‘implement_rsi_strategy’ which takes the stock prices (‘prices’), and the RSI values (‘rsi’) as parameters.


Inside the function, we are creating three empty lists (buy_price, sell_price, and rsi_signal) in which the values will be appended while creating the trading strategy.


After that, we are implementing the trading strategy through a for-loop. Inside the for-loop, we are passing certain conditions, and if the conditions are satisfied, the respective values will be appended to the empty lists. If the condition to buy the stock gets satisfied, the buying price will be appended to the ‘buy_price’ list, and the signal value will be appended as 1 representing to buy the stock. Similarly, if the condition to sell the stock gets satisfied, the selling price will be appended to the ‘sell_price’ list, and the signal value will be appended as -1 representing to sell the stock.


Finally, we are returning the lists appended with values. Then, we are calling the created function and stored the values into their respective variables. The list doesn’t make any sense unless we plot the values. So, let’s plot the values of the created trading lists.


Step-6: Plotting the trading signals


In this step, we are going to plot the created trading lists to make sense out of them.


Python Implementation: