The relative strength indicator (RSI) is a popular and handy indicator we can use to measure when a stock is ready to pullback or, has moved down too quickly and should shoot back up. It’s in the class of oscillator indicators and can be used in mean reversion strategies. The RSI ranges from 0–100 with values closer to 0 indicating an asset that is oversold and should move up, and values closer to 100 are too high and should move downward.

Typically, the RSI has a few parameters we can use when developing our strategy: the look back period and the level which triggers the signal.

For a standard RSI implementation, traders frequently use 14 bars for the lookback period and 30 to indicate a buy signal when something is oversold and 70 to trigger a sell/short when it’s overbought. Like many oscillators, we buy low and sell high. Of course, these parameters aren’t fixed, but just guidelines to help get you started. Shorter time periods often lead traders to expand their levels because the model becomes more sensitive to large, short-term moves that can occur.

Calculating the RSI

Hopefully you have a reasonable idea of what the RSI is trying to accomplish as a signal, so now let’s get into how we calculate it.

The RSI formula is given below as:

$$RSI_t = 100 - \bigg(\frac{100}{1 + RS_t} \bigg)$$

where RS_t is the relative strength factor at time t. This value is calculated by taking the average of all the up period gains in your lookback window and dividing it by the average of all your down period losses in the lookback window. So if a stock closes at $1 on day one, then closes at $1.50 on day two, you have one up period with a $0.50 gain. If on day three, it goes down to $1 again, then you have a down period with a $0.50 loss. To make sure we’re always dealing with positive numbers, we’ll take the absolute value of our losses when doing further calculations.

The part which can cause some confusion comes when calculating the averages of these gains and losses within your lookback period. That arises because the RS formula is slightly different for the initial value versus all of the others in the time series. It’s not that different to cause significant problems if you don’t initialize it correctly, but it can cause some confusion.

First of all, the relative strength factor is undefined if the number of days in your data are less than your lookback period, P; you don’t have at least P periods, so you can’t calculate a P-period RS factor. The very first period in your data that you can calculate, you take the simple average of your gains and divide by the simple average of your losses. This is the initial value (we can call this RS_0).

Beyond that first period, we change our average to be a smoothed average, where we take the average gains and losses from the period before, multiply those by P-1, add today’s gains and losses, then divide each by P (not shown because the values cancel), then divide the smoothed gains by the smoothed losses.

Mathematically (and with some abuse of notation) we’d have two possibilities for our RS value:

$$RS_t = \left\{ \begin{array}{11} \frac{\sum_{t=1}^P Gain_t}{\sum_{t=1}^P \mid Loss_t \mid} \quad \textrm{if} \; t = P\;\textrm{(initial value)} \\ \frac{Gain_t + (P - 1) / P \sum_{i=1}^{P-1} Gain_i}{\mid Loss_t \mid + (P-1) / P \sum_{i=1}^{P-1} \mid Loss_i \mid} \quad \textrm{if} \; t > P \end{array} \right. $$

Rather than write that out in words and math, let’s illustrate with a simple example in Python to make things clear.

Go ahead and import the following packages so we can get some data.

Calculating RSI in Python

import numpy as np
import pandas as pd
import yfinance as yf
import matplotlib.pyplot as plt

So we don’t have too much data to analyze, let’s just grab one year of data.

yfObj = yf.Ticker('AAPL')
data = yfObj.history(start='2010-01-01', end='2011-01-01')
data
aapl_rsi_example.png

Now that we have our gains and losses, we need to calculate the averages of each. If P=14, we’ll have a host of NaNs in our data frame until that point. Then, on our 14th period, we'll have our RS_0 - the initial value to kick off the our RSI series.

This can be done in just a single line of Python using the rolling method.

data[['init_avg_gain', 'init_avg_loss']] = data[
    ['gain', 'loss']].rolling(P).mean()
data.iloc[10:15]
aapl_2010-2011.png

We now have our initial average gain and loss values in the 14th row of our data frame. All the subsequent rows are going to be calculated using the second formula in our RS equation above.

avg_gain = np.zeros(len(data))
avg_loss = np.zeros(len(data))
for i, _row in enumerate(data.iterrows()):
    row = _row[1]
    if i < P - 1:
        last_row = row.copy()
        continue
    elif i == P-1:
        avg_gain[i] += row['init_avg_gain']
        avg_loss[i] += row['init_avg_loss']
    else:
        avg_gain[i] += ((P - 1) * avg_gain[i-1] + row['gain']) / P
        avg_loss[i] += ((P - 1) * avg_loss[i-1] + row['loss']) / P
        
    last_row = row.copy()
    
data['avg_gain'] = avg_gain
data['avg_loss'] = avg_loss
data.tail()
aapl_rsi_example3.png

We’re just a few steps away from having our raw indicator. We simply need to take the ratio between our avg_gain and avg_loss values, then plug those into the RSI formula, and we're set!

data['RS'] = data['avg_gain'] / data['avg_loss']
data['RSI'] = 100 - 100 / (1 + data['RS'])
data.tail()
aapl_rsi_example4.png

A few things to note about this. First, it’s possible (although didn’t occur in our data) to have a series without any losses. In this case, RS would become undefined via division by zero, and RSI would default to its maximum value of 100.

colors = plt.rcParams['axes.prop_cycle'].by_key()['color']

fig, ax = plt.subplots(2, figsize=(10, 8), sharex=True)
ax[0].plot(data['Close'], label='Closing Price')
ax[0].set_ylabel('Price ($)')
ax[0].set_title('Closing Price')

ax[1].plot(data['RSI'], label='RSI')
ax[1].axhline(70, label='Overbought', color=colors[1], linestyle='--')
ax[1].axhline(30, label='Oversold', color=colors[2], linestyle='--')
ax[1].set_title('RSI and Overbought/Sold Marks')
ax[1].set_xlabel('Date')
ax[1].set_ylabel('RSI Level')
ax[1].set_ylim([0, 100])
ax[1].legend(bbox_to_anchor=[1, 0.75])
plt.show()
aapl_rsi_chart.png

In the plots above, we can see that we would be selling or going short when the indicator crosses the blue, overbought line and selling or going long when the yellow, oversold line is reached. In this example, we never quite reach the oversold threshold. If you’re looking to adjust parameters for this model, you could update the time horizon by increasing or decreasing P or change the thresholds to make it more active.

Trading with the RSI

RSI is a long standing staple of technical analysis and trading, but it’s just one tool among many that you have at your disposal. There are a variety of ways to implement it and combine it with other signals and strategies to make it more robust.

At Raposa, we make implementing these strategies easy with a no-code interface to select your favorite models, manage your risk, run backtests, and implement strategies quickly and easy. It gets you out of code and into the markets.

Try our free demo here.