The Relative Strength Index indicator is popular, for a reason, but in this article, I backtested the default settings of the RSI indicator, and made it better.
Investments and trading is a wonderful world where large companies and small investors can participate from the comfort of their homes. You only need a device with internet access and a desire to learn and achieve great things.
Related reading: –Python Trading Strategy (Backtesting, Code, List, And Plenty of Coding Examples)
First, let’s look at its default settings:
The RSI indicator and its default settings
Someone with basic knowledge of trading strategies knows that there are well-known technical indicators that are used to help you in your trading strategies.
One of them is the famous RSI (Relative Strength Index) which was invented by J. Wells Wilder with the aim of improve momentum.
The indicator has a default setting of 14 periods and it is usually said that when the RSI is greater than 70 or more, it indicates that a security is overbought or overvalued, and therefore, it can initiate a trend reversal or a pullback.
An RSI reading of 30 or lower is commonly interpreted as oversold or undervalued which may indicate a trend reversal or a reversal upward.
But have you asked yourself, can we change the default settings?
Well, the answer is yes, and now other questions arise:
How do I do it? Would it be beneficial to do so?
Well, one of the available options is to do backtesting in Python (or any other platform), and that is what we will do next. We will use the RSI with the non-default configuration and also the default configuration for comparison.
Let’s show you how we did this using Python:
Download historical data using Python
There are tools like yfinance that allow us to download historical data from Yahoo Finance for free, including fundamental data such as income statements, trading multiples, and dividends, among others.
To work with this type of data, we can create a Python notebook, a web-based environment for creating and editing Python scripts, like a Jupyter notebook or Google Colaboratory.
In our Python notebook, we can import the yfinance and pandas libraries to manipulate the data frame later. Then, we can define a variable and download the data frame of SPY historical prices using the yf.download() function of yfinance.
If we print the data, here is the result:
We now have the daily open, high, low, close, adjusted close and volume values for the SPY since 1993!
Please also read our separate article about how to download data for your trading strategy from Yahoo!Finance with python.
Calculate the RSI indicator
The formula to calculate the relative strength index is not difficult, but there is a Python library that can do it for us. Pandas_ta is an easy-to-use library that leverages the Pandas package with hundreds of technical indicators, all for free(!).
As we did before with yfinance and pandas, we need to import pandas_ta into our notebook. The function to calculate the RSI is called pta.rsi(). Inside the parenthesis are two entries: the daily closing price of the SPY and the duration of the RSI that we want, in this case as we will make a comparison of the non-default configuration with the default, I will use 2 and 14 days. Further down in the article, we show that a low number of days in the RSI works best.
So to our data frame called data, we will add two new columns called ‘rsi’ and ‘normal’ which will be the RSI with the non-default and default settings, we will use the above mentioned formula to calculate the RSI in both cases.
RSI Trading Rules for our trading strategy and backtest
The trading rules of the strategy are quite simple. For RSI with non-default settings:
- We buy SPY at the close when the RSI(2) is below 10. We sell when the RSI(2) crosses above 60.
For the RSI with default settings:
- We buy the SPY at the close when the RSI(14) is below 30. We sell when the RSI(14) crosses above 70.
Generating RSI trading signals
To generate the trading signals, we will have to loop through the data frame using the for function and checking the conditions with the if function.
This strategy has two different signals, one for buying and one for selling. This means that we will scan each row in the data frame one by one, checking two conditions on each signal:
For the buy signal, the RSI must be below ten and z=1 (see z described below). For sell signal, RSI must be above 60 and z=0
We create another column in the data frame called regime and set 1 if the buy signal is activated and -1 when the sell signal is activated.
What is z? z is a variable that we set equal to 1, so if, for example, there are two consecutive days where the RSI(2) is below 10, the signal is only generated on the first day because after that, the z value changes to 0. The two conditions are no longer met. The same goes for the sell signal.
It is also important to note that we put 1 in the next row (i+1) where the buy signal is generated because if the signal is activated at the close of the day, it means that we neither gained nor lost any money that day. If we did not put i+1 we would consider returning the SPY on the day we bought it, even if we bought it at closing.
Now, to make it easier to calculate the returns, we are going to fill the regime columns between where the buy signals are generated and when the sell signals are activated with 1 (before this, the value in these rows was 0).
In other words, if the previous row is equal to 1, place a 1 in the current column. Eventually, we will reach a point where the previous column is -1 and the condition is no longer met.
Similarly, we work with the RSI with the default configuration.
Calculating, backtesting, and plotting the returns of the strategy
Finally, to calculate the returns and performance of the RSI strategy with the non-default settings, we need to do a final loop through the data frame.
Before that, we are going to create a new column called change, where we are going to calculate the daily change of SPY. To do this, we will use the pct_change() function and add 1 to the end.
Then we will create another column called returns where if the regime value is not equal to zero we put the daily change of the SPY, if not we put only 1.
In a similar way, we work with the RSI, which has the default configuration as shown below:
To plot the cumulative returns of the strategy, we will use the cumprod() function followed by the .plot() function to make a graph showing the compound returns. We add a label inside the parenthesis to differentiate market returns from strategy returns.
And that is! You have just backtested a trading strategy using Python.
Equity curve Explanation
In the previous graph, we can see that the returns with the RSI with non-default configuration, in our case RSI(2) (orange color), were better than the returns obtained with the default configuration RSI(14) (green color).
You can also try other periods by changing the period you want in the Python code. In our case, we work with 2 in the non-default configuration, but we can work with 3, for example in the following way:
We can also change the overbought and oversold values in our Python code and analyze which gave better results, in our case we work in the non-default configuration of the RSI with a period of 2. But we can work with other periods to compare them with the default configuration of the RSI, then we compare the default configuration of the RSI with those of period 1,2,3 and 4 respectively:
You can study many other cases by changing the number of periods or the figure that will be the overbought or oversold.
I backtested the default settings of the RSI indicator, and made it better
As we could see, by changing some configurations of the RSI indicator, we were able to obtain better results. We can do these tests with basic knowledge of Python and a little bit of creativity.