The Stochastic-RSI Indicator — A Primer.
Creating, Coding, and Discussing the Structured Stochastic-RSI Indicator.
The Stochastic-RSI is another known indicator created by fusing together the already known RSI and stochastic indicators. Its utility is controversial but we will try to shed some light on it by using it in a systematic trading framework. The aim of the article is to code the RSI, stochastic, and the Stochastic-RSI indicators.
I have just released a new book after the success of my previous one “The Book of Trading Strategies”. It features advanced trend-following indicators and strategies with a GitHub page dedicated to the continuously updated code. Also, this book features the original colors after having optimized for printing costs. If you feel that this interests you, feel free to visit the below Amazon link, or if you prefer to buy the PDF version, you could contact me on LinkedIn.
The Relative Strength Index
The RSI is without a doubt the most famous momentum indicator out there, and this is to be expected as it has many strengths especially in ranging markets. It is also bounded between 0 and 100 which makes it easier to interpret. Also, the fact that it is famous, contributes to its potential.
This is because the more traders and portfolio managers look at the RSI, the more people will react based on its signals and this in turn can push market prices. Of course, we cannot prove this idea, but it is intuitive as one of the basis of Technical Analysis is that it is self-fulfilling.
The RSI is calculated using a rather simple way. We first start by taking price differences of one period. This means that we have to subtract every closing price from the one before it. Then, we will calculate the smoothed average of the positive differences and divide it by the smoothed average of the negative differences. The last calculation gives us the Relative Strength which is then used in the RSI formula to be transformed into a measure between 0 and 100.
To calculate the relative strength index, we need an OHLC array (not a data frame). This means that we will be looking at an array of 4 columns. The function for the Relative Strength Index is therefore:
# The function to add a number of columns inside an array def adder(Data, times): for i in range(1, times + 1): new_col = np.zeros((len(Data), 1), dtype = float) Data = np.append(Data, new_col, axis = 1) return Data
# The function to delete a number of columns starting from an index def deleter(Data, index, times): for i in range(1, times + 1): Data = np.delete(Data, index, axis = 1) return Data # The function to delete a number of rows from the beginning def jump(Data, jump): Data = Data[jump:, ] return Data
# Example of adding 3 empty columns to an array my_ohlc_array = adder(my_ohlc_array, 3)
# Example of deleting the 2 columns after the column indexed at 3 my_ohlc_array = deleter(my_ohlc_array, 3, 2)
# Example of deleting the first 20 rows my_ohlc_array = jump(my_ohlc_array, 20)
# Remember, OHLC is an abbreviation of Open, High, Low, and Close and it refers to the standard historical data file
def ma(Data, lookback, close, where): Data = adder(Data, 1) for i in range(len(Data)): try: Data[i, where] = (Data[i - lookback + 1:i + 1, close].mean()) except IndexError: pass # Cleaning Data = jump(Data, lookback) return Data
def ema(Data, alpha, lookback, what, where): alpha = alpha / (lookback + 1.0) beta = 1 - alpha # First value is a simple SMA Data = ma(Data, lookback, what, where) # Calculating first EMA Data[lookback + 1, where] = (Data[lookback + 1, what] * alpha) + (Data[lookback, where] * beta) # Calculating the rest of EMA for i in range(lookback + 2, len(Data)): try: Data[i, where] = (Data[i, what] * alpha) + (Data[i - 1, where] * beta) except IndexError: pass return Datadef rsi(Data, lookback, close, where): # Adding a few columns Data = adder(Data, 5) # Calculating Differences for i in range(len(Data)): Data[i, where] = Data[i, close] - Data[i - 1, close] # Calculating the Up and Down absolute values for i in range(len(Data)): if Data[i, where] > 0: Data[i, where + 1] = Data[i, where] elif Data[i, where] < 0: Data[i, where + 2] = abs(Data[i, where]) # Calculating the Smoothed Moving Average on Up and Down absolute values lookback = (lookback * 2) - 1 # From exponential to smoothed Data = ema(Data, 2, lookback, where + 1, where + 3) Data = ema(Data, 2, lookback, where + 2, where + 4)
# Calculating the Relative Strength Data[:, where + 5] = Data[:, where + 3] / Data[:, where + 4] # Calculate the Relative Strength Index Data[:, where + 6] = (100 - (100 / (1 + Data[:, where + 5])))
# Cleaning Data = deleter(Data, where, 6) Data = jump(Data, lookback)
The Stochastic Oscillator
The stochastic oscillator seeks to find oversold and overbought zones by incorporating the highs and lows using a normalization formula as shown below:
An overbought level is an area where the market is perceived to be extremely bullish and is bound to consolidate. An oversold level is an area where market is perceived to be extremely bearish and is bound to bounce. Hence, the stochastic oscillator is a contrarian indicator that seeks to signal reactions of extreme movements.
def stochastic_oscillator(data, lookback, high, low, close, where, slowing = False, smoothing = False, slowing_period = 1, smoothing_period = 1): data = adder(data, 1) for i in range(len(data)): try: data[i, where] = (data[i, close] - min(data[i - lookback + 1:i + 1, low])) / (max(data[i - lookback + 1:i + 1, high]) - min(data[i - lookback + 1:i + 1, low])) except ValueError: pass data[:, where] = data[:, where] * 100 if slowing == True and smoothing == False: data = ma(data, slowing_period, where, where + 1) if smoothing == True and slowing == False: data = ma(data, smoothing_period, where, where + 1) if smoothing == True and slowing == True: data = ma(data, slowing_period, where, where + 1) data = ma(data, smoothing_period, where, where + 2) data = jump(data, lookback)
If you are also interested by more technical indicators and strategies, then my book might interest you:
This mash-up is a simple application of the stochastic (normalization) function on the RSI’s values to give us a normalized momentum. The basic idea is that by harnessing the power of these two famous indicators together, can we get something better? The answer depends on many variables and no conclusive one can be given. Our aim here is to create the Stochastic-RSI.
The indicator is simply applying the stochastic (normalization) function on the RSI values. It is used exactly the same way as its two components.
def stoch_rsi(data, lookback, high, low, close, where): # Calculating RSI of the Closing prices data = rsi(data, lookback, close, where) # Adding two columns data = adder(data, 2) for i in range(len(data)): try: data[i, where + 1] = (data[i, where] - min(data[i - lookback + 1:i + 1, where])) / (max(data[i - lookback + 1:i + 1, where]) - min(data[i - lookback + 1:i + 1, where])) except ValueError: pass data[:, where + 1] = data[:, where + 1] * 100 # Signal Line using a 3-period moving average data = ma(data, 3, where + 1, where + 2) data = deleter(data, where, 2) data = jump(data, lookback) return data
Remember to always do your back-tests. You should always believe that other people are wrong. My indicators and style of trading may work for me but maybe not for you.
I am a firm believer of not spoon-feeding. I have learnt by doing and not by copying. You should get the idea, the function, the intuition, the conditions of the strategy, and then elaborate (an even better) one yourself so that you back-test and improve it before deciding to take it live or to eliminate it. My choice of not providing specific Back-testing results should lead the reader to explore more herself the strategy and work on it more.
To sum up, are the strategies I provide realistic? Yes, but only by optimizing the environment (robust algorithm, low costs, honest broker, proper risk management, and order management). Are the strategies provided only for the sole use of trading? No, it is to stimulate brainstorming and getting more trading ideas as we are all sick of hearing about an oversold RSI as a reason to go short or a resistance being surpassed as a reason to go long. I am trying to introduce a new field called Objective Technical Analysis where we use hard data to judge our techniques rather than rely on outdated classical methods.