All About Trading!

Share this post

Statistical Overbought & Oversold Levels on Technical Indicators

abouttrading.substack.com

Statistical Overbought & Oversold Levels on Technical Indicators

Mean-Reversion Trading with Technical Indicators

Sofien Kaabar, CFA
Jul 14, 2022
Share this post

Statistical Overbought & Oversold Levels on Technical Indicators

abouttrading.substack.com

We are constantly told about oversold and overbought levels as fixed numbers, but as markets move, so do their structure and statistical properties. We need to find a more objective and dynamic way to detect oversold and overbought levels on technical indicators.

I have released a new book called “Contrarian Trading Strategies in Python”. It features a lot of advanced contrarian indicators and strategies with a GitHub page dedicated to the continuously updated code. If you are interested, you could buy the PDF version directly through a PayPal payment of 9.99 EUR.

All About Trading! is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.

Please include your email in the note before paying so that you receive it on the right address. Also, once you receive it, make sure to download it through google drive.

Pay Kaabar using PayPal.Me
If you accept cookies, we’ll use them to improve and customize your experience and enable our partners to show you…www.paypal.com


Moving Averages

Moving averages help us confirm and ride the trend. They are the most known technical indicator and this is because of their simplicity and their proven track record of adding value to the analyses. We can use them to find support and resistance levels, stops and targets, and to understand the underlying trend. This versatility makes them an indispensable tool in our trading arsenal.

As the name suggests, this is your plain simple mean that is used everywhere in statistics and basically any other part in our lives. It is simply the total values of the observations divided by the number of observations. Mathematically speaking, it can be written down as:

The code for the moving average can be written down as the following:

# 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
            
    data = jump(data, lookback)
    
    return data

The below states that the moving average function will be called on the array named my_data for a lookback period of 200, on the column indexed at 3 (closing prices in an OHLC array). The moving average values will then be put in the column indexed at 4 which is the one we have added using the adder function.

my_data = ma(my_data, 200, 3, 4)

An exponential moving average is a special type that gives more weight to the recent values. To spare you the boring details, here is the code.

def ema(data, alpha, lookback, what, where):
    
    alpha = alpha / (lookback + 1.0)
    
    beta  = 1 - alpha
    
    data = ma(data, lookback, what, where)data[lookback + 1, where] = (data[lookback + 1, what] * alpha) + (data[lookback, where] * beta)    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 data

Check out my weekly market sentiment report to understand the current positioning and to estimate the future direction of several major markets through complex and simple models working side by side. Find out more about the report through this link:

Coalescence Report 1st May — 8th May 2022
This report covers the weekly market sentiment and positioning and any changes that have occurred which might present…coalescence.substack.com


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.

14-period RSI.

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:

def 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)
return Data
14-period RSI.

Objective Overbought & Oversold Levels

The way we will do this is by assuming that the levels are dynamic and move according to the recent movements of the RSI. This means that we will not be saying 30/70 are oversold levels but rather, we will calculate them based on a long-term moving average on the RSI. In our case, it will be the 500-period moving average applied on the 21-period RSI.

EURUSD in the first panel with the 21-period RSI along its objective barriers.

A long-term moving average with its volatility bands provides dynamic oversold/overbought levels. The parameters and lookback periods can be adjusted accordingly as the trader wishes. Doing so implies that we will be applying the Bollinger bands on the values of the RSI

def bollinger_bands(data, boll_lookback, standard_distance, what, where):
       
    # Adding a few columns
    data = adder(data, 2)
    
    # Calculating means
    data = ma(data, boll_lookback, what, where)
data = volatility(data, boll_lookback, what, where + 1)
    
    data[:, where + 2] = data[:, where] + (standard_distance * data[:, where + 1])
    data[:, where + 3] = data[:, where] - (standard_distance * data[:, where + 1])
    
    data = jump(data, boll_lookback)
    
    data = deleter(data, where, 2)
        
    return data
my_data = rsi(my_data, 21, 3, 4)
my_data = bollinger_bands(my_data, 500, 2, 4, 5)
EURUSD in the first panel with the 20-period RSI along its objective barriers.

We have a wide array of choices when using this technique, we can for example:

  • Use a 200-period RSI with a 50-period moving average and 2.0 standard deviations away.

  • Use a 5-period RSI with a 100-period moving average and 1.0 standard deviations away.

Therefore, to develop robust strategies, we need to back-test the different combinations and find the good ones.


Summary

To sum up, what I am trying to do is to simply contribute to the world of objective technical analysis which is promoting more transparent techniques and strategies that need to be back-tested before being implemented. This way, technical analysis will get rid of the bad reputation of being subjective and scientifically unfounded.

I recommend you always follow the the below steps whenever you come across a trading technique or strategy:

  • Have a critical mindset and get rid of any emotions.

  • Back-test it using real life simulation and conditions.

  • If you find potential, try optimizing it and running a forward test.

  • Always include transaction costs and any slippage simulation in your tests.

  • Always include risk management and position sizing in your tests.

Finally, even after making sure of the above, stay careful and monitor the strategy because market dynamics may shift and make the strategy unprofitable.

For the paperback link of the book, you may use the following link:

Contrarian Trading Strategies in Python
Amazon.com: Contrarian Trading Strategies in Python: 9798434008075: Kaabar, Sofien: Bookswww.amazon.com

All About Trading! is a reader-supported publication. To receive new posts and support my work, consider becoming a free or paid subscriber.

Share this post

Statistical Overbought & Oversold Levels on Technical Indicators

abouttrading.substack.com
Comments
TopNewCommunity

No posts

Ready for more?

© 2023 Sofien Kaabar
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing