All About Trading!

Share this post

Modern Trading Techniques — A Reversal Strategy in Python.

abouttrading.substack.com

Modern Trading Techniques — A Reversal Strategy in Python.

Creating a Double Reversal Strategy Using the RSI & Stochastic.

Sofien Kaabar, CFA
Mar 20, 2022
Share this post

Modern Trading Techniques — A Reversal Strategy in Python.

abouttrading.substack.com

This article discusses an interesting strategy based on two methods and two indicators. The aim is to find reversal signals that are used for medium-term extended periods.

Share


The Relative Strength Index

First introduced by J. Welles Wilder Jr., the RSI is one of the most popular and versatile technical indicators. Mainly used as a contrarian indicator where extreme values signal a reaction that can be exploited. Typically, we use the following steps to calculate the default RSI:

  • Calculate the change in the closing prices from the previous ones.

  • Separate the positive net changes from the negative net changes.

  • Calculate a smoothed moving average on the positive net changes and on the absolute values of the negative net changes.

  • Divide the smoothed positive changes by the smoothed negative changes. We will refer to this calculation as the Relative Strength — RS.

  • Apply the normalization formula shown below for every time step to get the RSI.

GBPUSD in the first panel with the 13-period RSI in the second panel.

The above chart shows the hourly values of the GBPUSD in black with the 13-period RSI. We can generally note that the RSI tends to bounce close to 25 while it tends to pause around 75. To code the RSI in Python, we need an OHLC array composed of four columns that cover open, high, low, and close prices.

def adder(data, times):
    
    for i in range(1, times + 1):
    
        new = np.zeros((len(data), 1), dtype = float)
        
        data = np.append(data, new, axis = 1)    
    return data
def deleter(data, index, times):
    
    for i in range(1, times + 1):
    
        data = np.delete(data, index, axis = 1)    
    return data
   
def jump(data, jump):
    
    data = data[jump:, ]
    
    return data
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
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
def rsi(data, lookback, close, where):
    
    data = adder(data, 5)
    
    for i in range(len(data)):
        
        data[i, where] = data[i, close] - data[i - 1, close]
     
    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])
            
    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)    
    data[:, where + 5] = data[:, where + 3] / data[:, where + 4]
    
    data[:, where + 6] = (100 - (100 / (1 + data[:, where + 5])))    
    data = deleter(data, where, 6)
    data = jump(data, lookback)     
    return data

Share


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. We will create the below function that calculates the stochastic on an OHLC data:

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)    
    return data
EURUSD hourly data in the first panel with the 14-period stochastic oscillator.
my_data = stochastic_oscillator(my_data, 
                             100, 
                             1, 
                             2, 
                             3, 
                             5, 
                             slowing = True, 
                             smoothing = False, 
                             slowing_period = 3, 
                             smoothing_period = 1)
# my_data must be an OHLC array with four columns composed of open, high, low, and close prices

Share


Creating the Strategy

The strategy has rare signals but they are generally of higher quality than plain vanilla indicators. We will look for a normal divergence on a 13-period RSI while the 100-period stochastic oscillator is around the extreme levels. The following are the details:

  • Long (Buy) whenever the 100-period stochastic oscillator is below 20 while the RSI has just confirmed a bullish divergence.

  • Short (Sell) whenever the 100-period stochastic oscillator is above 80 while the RSI has just confirmed a bearish divergence.

Naturally, when prices are rising and making new tops while a price-based indicator is making lower tops, a weakening is occurring and a possibility to change the bias from long to short can present itself. That is what we call a normal divergence. We know that:

  • When prices are making higher highs while the indicator is making lower highs, it is called a bearish divergence, and the market might stall.

  • When prices are making lower lows while the indicator is making higher lows, it is called a bullish divergence, and the market might show some upside potential.

Signal chart.
def divergence(Data, indicator, lower_barrier, upper_barrier, width, buy, sell):
    Data = adder(Data, 2)
    for i in range(len(Data)):
        try:
            if Data[i, indicator] < lower_barrier:
                for a in range(i + 1, i + width):
                    # First trough
                    if Data[a, indicator] > lower_barrier:
                        for r in range(a + 1, a + width):
                            if Data[r, indicator] < lower_barrier and \
                            Data[r, indicator] > Data[i, indicator] and Data[r, 3] < Data[i, 3]:
                                for s in range(r + 1, r + width):
                                    # Second trough
                                    if Data[s, indicator] > lower_barrier:
                                        Data[s, buy] = 1
                                        break
                                    else:
                                        break
                            else:
                                break
                        else:
                            break
                    else:
                        break
        except IndexError:
            pass
    for i in range(len(Data)):
        try:
            if Data[i, indicator] > upper_barrier:
                for a in range(i + 1, i + width):
                    # First trough
                    if Data[a, indicator] < upper_barrier:
                        for r in range(a + 1, a + width):
                            if Data[r, indicator] > upper_barrier and \
                            Data[r, indicator] < Data[i, indicator] and Data[r, 3] > Data[i, 3]:
                                for s in range(r + 1, r + width):
                                    # Second trough
                                    if Data[s, indicator] < upper_barrier:
                                        Data[s, sell] = -1
                                        break
                                    else:
                                        break
                            else:
                                break
                        else:
                            break
                    else:
                        break
        except IndexError:
            pass 
    return Data

The signal charts show the moment a divergence is validated while the stochastic oscillator is showing extreme readings.

Signal chart.
def signal(data, stochastic_column, bull_divergence, column, bear_divergence, buy, sell):
    data = adder(data, 5)
for i in range(len(data)):
        
        if data[i, bull_divergence] == 1 and data[i, stochastic_column] < 20:
            
            data[i, buy] = 1
            
        if data[i, bear_divergence] == -1 and data[i, stochastic_column] > 80:
            
            data[i, sell] = -1       
            
    return data
Signal chart.

Share


Conclusion

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.


One Last Word

I have recently started an NFT collection that aims to support different humanitarian and medical causes. The Society of Light is a set of limited collectibles which will help make the world slightly better as each sale will see a percentage of it sent directly to the charity attributed to the avatar. As I always say, nothing better than a bullet list to outline the benefits of buying these NFT’s:

  • High-potential gain: By concentrating the remaining sales proceedings on marketing and promoting The Society of Light, I am aiming to maximize their value as much as possible in the secondary market. Remember that trading in the secondary market also means that a portion of royalties will be donated to the same charity.

  • Art collection and portfolio diversification: Having a collection of avatars that symbolize good deeds is truly satisfying. Investing does not need to only have selfish needs even though there is nothing wrong with investing to make money. But what about investing to make money, help others, and collect art?

  • Donating to your preferred cause(s): This is a flexible way of allocating different funds to your charities.

  • A free copy of my book in PDF: Any buyer of any NFT will receive a free copy of my latest book shown in the link of the article.

Click here to buy Morgan and support his cause with burn victims.
Share this post

Modern Trading Techniques — A Reversal Strategy in Python.

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