15 April 2020

Calculation of Supertrend (ST)

The Supertrend is one of the most widely used Indicator among the Indian traders. And unfortunately there is very little documentation on this subject at Investopedia or the chart school of Stockcharts or even with Zerodha Varsity. So it required quite some investigation to arrive at the right method to calculate the Supertrend.

In its simplest form, when the Supertrend is overlayed over a candlestick chart, if the Supertrend crosses to below the Price we take a Long Position and when the Supertrend crosses to above the Price, we take a Short position. Here is a snapshot of the Supertrend.

This article from Economic Times is probably a good place to start with to get a qucik overview of Supertrend.

So let us dive straight into and get the calculation of Supertrend. Check this Google Sheet for the Calculation of Supertrend. 

Given below is the pseudo code for the calculation of Supertrend:

Basic UpperrBand = (High + Low) / 2 + Multiplier * ATR
Basic LowerBand =  (High + Low) / 2 - Multiplier * ATR

Final UpperBand = IF((Current BasicUpperband < Previous Final UpperBand) OR 
                    (Previous Close > Previous Final UpperBand))  THEN 
                    (Current Basic UpperBand) ELSE
                    (Previous FinalUpperBand)

Final LowerBand = IF((Current Basic LowerBand > Previous Final LowerBand) OR
                    (Previous Close < Previous Final LowerBand)) THEN
                    (Current Basic LowerBand) ELSE
                    (Previous Final LowerBand)

SuperTrend = IF((Previous SuperTrend = Previous Final UpperBand) AND 
               (Current Close <= Current Final UpperBand)) THEN 
               Current Final UpperBand
             ELSE
                IF((Previous SuperTrend = Previous Final UpperBand) AND
                  (Current Close > Current Final UpperBand)) THEN
                  Current Final LowerBand
                ELSE
                   IF((Previous SuperTrend = Previous Final LowerBand) AND
                     (Current Close >= Current Final LowerBand)) THEN
                     Current Final LowerBand
                   ELSE
                      IF((Previous SuperTrend = Previous Final LowerBand) AND
                        (Current Close < Current Final LowerBand)) THEN
                        Current Final UpperBand

Now lets see how this is calculated in Python. Lets first download the OHLCV data from the above google sheet into a CSV file. Name it as "test data". We can use this as our input file for the Python program.

Lets first take a look at the Imports and Global Declarations that we will use in this program:
#imports used in the program
import numpy as np
import pandas as pd

#Global Declarations
input_file = "test data.csv"
multiplier = 2 # An integer to indicate the value to multiply the ATR.
period = 14
atr = 'ATR' + '_' + str(period)
st = 'SuperTrend' + '_' + str(period) + '_' + str(multiplier)

Since the Supertrend is based on Average True Range(ATR), lets define this function.
def AverageTrueRange(df_stock, period=14):
    """
    Function to compute the technical indicator Average True Range(ATR)
    
    Args:
        df: Pandas DataFrame which contains ['Date', 'Open', 'High', 'Low', 'Close', 'Volume'] columns
        period: the period for which ATR has to be calculated, generally the number of candlesticks. Default is 14
        
    Returns:
        df : Pandas DataFrame with two new columns
            1) True Range (TR)
            2) Average True Range (ATR_period)
    """
    
    """
    The True Range is calculated at the Higher of the following three:
    1. Current High minus the current Low
    2. Absolute Value of Current High minus the previous Close
    3. Absolute Value of Current Low minus the previous Close
    """
    atr = 'ATR' + '_' + str(period)
    
    df_stock['H-L'] = df_stock['High'] - df_stock['Low']
    df_stock['PC'] = df_stock['Close'].shift(1)
    df_stock['H-PC'] = abs(df_stock['High'] - df_stock['PC'])
    df_stock['L-PC'] = abs(df_stock['Low'] - df_stock['PC'])
    df_stock['TR'] = df_stock[['H-L' ,'H-PC','L-PC']].max(axis=1)
    df_stock[atr] = np.nan # define a column ATR and initialise it with nan values   
    df_stock.drop(['PC','H-PC','L-PC','H-L'], inplace=True, axis=1) # Drop the unwanted columns
    con = pd.concat([df_stock[:period]['TR'].rolling(window=period).mean(), df_stock[period:]['TR']])
    df_stock[atr] = con.ewm(alpha=1 / period, adjust=False).mean()
    # print(df_stock[atr][period:len(df_stock)])
    return df_stock
Now lets get to the main program:
# Start of Main Program
df_stock = pd.read_csv(input_file)
df_stock = df_stock.reset_index(drop=True) # just reset the index without inserting it as a column in the DataFrame
df_stock = AverageTrueRange(df_stock, 14)
Next lets calculate the Basic Upper Bands and Lower Bands
# Calculate Basic Upperband and Basic Lowerbands
df_stock['Basic_UB'] = (df_stock['High'] + df_stock['Low']) / 2 + multiplier*df_stock[atr]
df_stock['Basic_LB'] = (df_stock['High'] + df_stock['Low']) / 2 - multiplier*df_stock[atr]
Next lets calculate the final upperband and final lowerband
# Calculate Final upperband and Final lowerbands
df_stock['Final_UB'] = 0.00
df_stock['Final_LB'] = 0.00

for i in range(period - 1, len(df_stock)):
    if (df_stock.loc[i, 'Basic_UB'] < df_stock.loc[i-1, 'Final_UB']) | (df_stock.loc[i-1, 'Close'] > df_stock.loc[i-1, 'Final_UB']):
        df_stock.loc[i,'Final_UB'] = df_stock.loc[i, 'Basic_UB']
    else:
        df_stock.loc[i, 'Final_UB'] = df_stock.loc[i-1, 'Final_UB']
    
    if (df_stock.loc[i, 'Basic_LB'] > df_stock.loc[i-1, 'Final_LB']) | (df_stock.loc[i-1, 'Close'] < df_stock.loc[i-1, 'Final_LB']):
        df_stock.loc[i, 'Final_LB'] = df_stock.loc[i, 'Basic_LB']
    else:
        df_stock.loc[i, 'Final_LB'] = df_stock.loc[i-1, 'Final_LB']
Next lets calculate the Supertrend
# Calculate the SuperTrend
df_stock[st] = 0.00
i = 0 
for i in range(period-1, len(df_stock)):
    if (df_stock.loc[i-1, st] == df_stock.loc[i-1, 'Final_UB']) & (df_stock.loc[i, 'Close'] <= df_stock.loc[i, 'Final_UB']):
        df_stock.loc[i, st] = df_stock.loc[i, 'Final_UB']
    else:
        if (df_stock.loc[i-1, st] == df_stock.loc[i-1, 'Final_UB']) & (df_stock.loc[i, 'Close'] > df_stock.loc[i, 'Final_UB']):
            df_stock.loc[i, st] = df_stock.loc[i, 'Final_LB']
        else:
            if (df_stock.loc[i-1, st] == df_stock.loc[i-1, 'Final_LB']) & (df_stock.loc[i, 'Close'] >= df_stock.loc[i, 'Final_LB']):
                df_stock.loc[i, st] = df_stock.loc[i, 'Final_LB']
            else:
                if (df_stock.loc[i-1, st] == df_stock.loc[i-1, 'Final_LB']) & (df_stock.loc[i, 'Close'] < df_stock.loc[i, 'Final_LB']):
                    df_stock.loc[i, st] = df_stock.loc[i, 'Final_UB']
And finally some dressing
df_stock[st] = df_stock[st].replace(0, np.nan) # Replace the 0 with nan values
df_stock.drop(['Basic_UB','Basic_LB','Final_UB','Final_LB'], inplace=True, axis=1) # Drop unnecessary columns

And this is the Output of the Supertrend calculated thus:
13    11150.678571
14    11119.719388
15    11094.901931
16    11043.392865
17    11043.392865
    
75    11796.310807
76    11796.310807
77    11796.310807
78    11796.310807
79    11796.310807
Name: SuperTrend_14_2, Length: 67, dtype: float64

And a good point to Note is that the Supertrend calculated thus tallies with the the value of Supertrend used in the charts of TradingView . For the Indian Traders, it means that it works well with Zerodha Kite and Fyers Web as well.

Note: A better approach is explored by  user arkochhar in GitHub repo : https://github.com/arkochhar/Technical-Indicators/blob/master/indicator/indicators.py

No comments:

Post a Comment