Backtesting Guppy Multiple Moving Average (GMMA) with Python using Zerodha API

In this chapter, we shall dive into the intricate details of a backtesting program crafted for a stock trading strategy. 

This program, developed in Python, pivots on the analysis of stock trends utilizing the Guppy Multiple Moving Average (GMMA), often known as the Guppy Screener. Our journey through this chapter will unravel the methodology of tracking stock movements across various timeframes and executing trades anchored on distinct conditions signaled by the GMMA.

What is Backtesting?

  • Backtesting in Zerodha is a trading strategy evaluation technique based on historical data.
  • Traders use past data to assess how a particular strategy would have performed.
  • It involves applying technical rules to historical price data and analyzing the generated returns over a specific period.

How Does a Backtester Work?

  • Zerodha trading strategies are applied to historical price data.
  • Trades are reconstructed using this data.
  • It helps traders identify flaws in existing strategies or test new ones.
  • Different indicators are provided by backtesting software:
    • Total Return on Equity (ROE): Percentage of total equity invested.
    • Total Profit and Loss (P/L): Total profits and losses as a percentage of invested equity.
    • Total Gain/Loss Ratio: Ratio of trades resulting in gains vs. losses.
    • Annualized ROE: Expected return over a full calendar year.
    • Volatility: Market conditions in which strategies work (uptrends, downtrends).
    • Risk-Adjusted Returns: Calculating returns in relation to strategy risks.
  • These metrics offer insights into Zerodha trading strategy performance.

Understanding GMMA in Trading

GMMA Explained:

  • GMMA is a technique in technical analysis that employs two sets of exponential moving averages (EMAs): short-term EMAs and long-term EMAs.
  • The short-term EMAs capture the sentiment and activity of short-term traders, while the long-term EMAs reflect the behavior of long-term investors.

The Role of EMAs in GMMA:

  • The script calculates a series of EMAs for each stock, using periods like 3, 5, 7 days for short-term trends and 25, 28, 31 days for long-term trends.
  • The intersection and divergence of these EMAs serve as the crux for understanding market dynamics and potential trend reversals.

Buy Backtesting Strategy

Signal Generation for Buy Trades

  • Bullish Signal Identification: The core of the buy strategy begins with the detection of bullish signals. These are identified when short-term Exponential Moving Averages (EMAs) cross above the long-term EMAs. This crossover is indicative of a potential upward trend in the stock’s price, suggesting an opportune moment for initiating buy trades.

Trade Execution Logic for Buy Orders

  • Setting the Bid Value: Once a bullish signal is confirmed, the program calculates a bid value to execute the buy trade. This value is determined based on the stock’s recent high price, augmented by a fraction of its Average True Range (ATR). The ATR is a measure of market volatility and helps in adjusting the bid value to align with current market dynamics.
  • Trade Execution: The buy order is placed at the computed bid value. This strategy ensures that the buy order is executed only if the stock’s price continues to ascend, affirming the bullish trend signaled by the GMMA.
				
					tokenall=[5633, 6401, 3861249, 4451329, 2760193, 20993, 325121, 2524673, 41729, 49409, 54273, 60417, 70401, 1510401, 1195009, 1214721, 94977, 108033, 2714625, 2911489, 2763265, 3812865, 160001, 160769, 163073, 177665, 5215745, 3876097, 197633, 3771393, 225537, 173057, 261889, 1207553, 3463169, 2796801, 315393, 3378433, 2513665, 1850625, 340481, 341249, 3789569, 345089, 2747905, 348929, 359937, 356865, 364545, 3699201, 1270529, 377857, 3677697, 3060993, 381697, 2883073, 387073, 387841, 1346049, 408065, 2393089, 415745, 3920129, 424961, 1723649, 2661633, 2933761, 3011329, 4574465, 3001089, 4632577, 492033, 2061825, 511233, 2939649, 2672641, 519937, 2815745, 2674433, 582913, 593665, 3924993, 2977281, 2748929, 633601, 2819073, 636673, 2730497, 3834113, 2906881, 3364353, 731905, 3375873, 3930881, 737793, 738561, 141569, 3078657, 779521, 780289, 1492737, 1522689, 1102337, 857857, 3431425, 3076609, 1837825, 871681, 952577, 878593, 884737, 4343041, 877057, 895745, 2953217, 3465729, 897537, 2873089, 2952193, 2752769, 920065, 951809, 3026177, 969473, 3050241, 112129, 134657, 3721473, 2800641, 3385857, 4454401, 1152769, 806401, 617473, 2905857, 3660545, 3906305, 758529, 975873]
ss=['ACC', 'ADANIENT', 'ADANIPORTS', 'ADANIPOWER', 'ALBK', 'ALOKTEXT', 'AMBUJACEM', 'ANDHRABANK', 'APOLLOTYRE', 'ARVIND', 'ASHOKLEY', 'ASIANPAINT', 'AUROPHARMA', 'AXISBANK', 'BANKBARODA', 'BANKINDIA', 'BATAINDIA', 'BHARATFORG', 'BHARTIARTL', 'BIOCON', 'CANBK', 'CENTRALBK', 'CENTURYTEX', 'CESC', 'CHAMBLFERT', 'CIPLA', 'COALINDIA', 'COLPAL', 'DABUR', 'DLF', 'DRREDDY', 'EXIDEIND', 'FEDERALBNK', 'GAIL', 'GMRINFRA', 'GODREJIND', 'GRASIM', 'GSPL', 'HAVELLS', 'HCLTECH', 'HDFC', 'HDFCBANK', 'HDIL', 'HEROMOTOCO', 'HEXAWARE', 'HINDALCO', 'HINDPETRO', 'HINDUNILVR', 'HINDZINC', 'IBREALEST', 'ICICIBANK', 'IDBI', 'IDEA', 'IDFC', 'IFCI', 'IGL', 'INDHOTEL', 'INDIACEM', 'INDUSINDBK', 'INFY', 'IOB', 'IOC', 'IRB', 'ITC', 'JINDALSTEL', 'JISLJALEQS', 'JPASSOCIAT', 'JPPOWER', 'JSWENERGY', 'JSWSTEEL', 'JUBLFOOD', 'KOTAKBANK', 'KTKBANK', 'LICHSGFIN', 'LT', 'LUPIN', 'M&M', 'MARUTI', 'MCDOWELL-N', 'MRF', 'NCC', 'NMDC', 'NTPC', 'OFSS', 'ONGC', 'OPTOCIRCUI', 'ORIENTBANK', 'PNB', 'POWERGRID', 'PTC', 'PUNJLLOYD', 'RAYMOND', 'RCOM', 'RECLTD', 'RELCAPITAL', 'RELIANCE', 'RELINFRA', 'RENUKA', 'SBIN', 'SCI', 'SINTEX', 'SOUTHBANK', 'SRTRANSFIN', 'SUNPHARMA', 'SUNTV', 'SUZLON', 'SYNDIBANK', 'TATACHEM', 'TATACOMM', 'TATAGLOBAL', 'TATAMOTORS', 'TATAMTRDVR', 'TATAPOWER', 'TATASTEEL', 'TCS', 'TECHM', 'TITAN', 'UCOBANK', 'ULTRACEMCO', 'UNIONBANK', 'UNITECH', 'VOLTAS', 'WELCORP', 'WIPRO', 'YESBANK', 'BHEL', 'BPCL', 'DISHTV', 'DIVISLAB', 'GVKPIL', 'NHPC', 'MPHASIS', 'SIEMENS', 'PEL', 'PETRONET', 'PFC', 'RPOWER', 'SAIL', 'ZEEL']

# inputs for backtesting 

sdate           ="2018-08-20 00:00:00"
todate          ="2019-09-16 09:16:00"
#sdate          ="2019-08-19"
#todate         ="2019-10-01"
time_frame      ="minute"
quu=50000
eexchange="NSE"
productt="MIS"
qu=int(quu)

# baktesting starting and ending date 

sdate_backtest          ="2018-08-14"
todate_backtest         ="2019-10-02"
##############
print("SCANNING START")
ttoken=5633
ttradingsymbol="ACC"

strike_rate = 0
strike_rate_list = []
timeframe = []
timeframe_list = []
pnl = 0
pnl_list = []

def time_con(time,df):
    z = 0
    chg = []
    chg_index = 0
    timeframe = time
    date = np.array([])
    for i in range(len(df)):
        date = np.hstack((date,str(df.loc[i,"date"])))
    open_np = df.open.values
    high_np = df.high.values
    low_np = df.low.values
    close_np = df.close.values
#     volume_np = df.volume.values
#     print(date)
    for j in range(len(date)):
        if date[j][11:16] == "09:15":
            chg.append(j)
    np_date=np.array([])
    np_open = np.array([])
    np_high = np.array([])
    np_low = np.array([])
    np_close = np.array([])
#     np_volume = np.array([])
    for i in range(len(date)-timeframe+1):
        if i>=z:
            chg_index+=1
            if (chg_index < len(chg)):
                for k in range(i,chg[chg_index],timeframe):
#                     print(date[k])
#                     print(k,k+timeframe)
                    np_date = np.hstack((np_date,date[k]))
                    np_open = np.hstack((np_open,open_np[k]))
                    np_high = np.hstack((np_high,np.amax(high_np[k:k+timeframe])))
#                     print(high_np[k:k+timeframe])
                    np_low = np.hstack((np_low,np.amin(low_np[k:k+timeframe])))
#                     print(low_np[k:k+timeframe])
                    np_close = np.hstack((np_close,close_np[k+timeframe-1]))
#                     np_volume = np.hstack((np_volume,np.sum(volume_np[k:k+timeframe])))
                    z = i+timeframe
                    z=chg[chg_index]
    else:
        con_df = pd.DataFrame({"date":np_date,"open":np_open,"high":np_high,"low":np_low,"close":np_close})
        return con_df


for t in range(1,2):
    dfw=kite.historical_data(ttoken,sdate_backtest,todate_backtest,time_frame,0)
    dfw=pd.DataFrame(dfw)
    df=pd.DataFrame(dfw[['date','open','high','low','close']])
    df = time_con(t,df)
    slow_ema = [3,5,7,9,11,13,15,17,19,21,23]
    fast_ema = [25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,200]
    def EMA(df, base, target, period, alpha=False):
        con = pd.concat([df[:period][base].rolling(window=period).mean(), df[period:][base]])
        if (alpha == True):
            # (1 - alpha) * previous_val + alpha * current_val where alpha = 1 / period
            df[target] = con.ewm(alpha=1 / period, adjust=False).mean()
        else:
            # ((current_val - previous_val) * coeff) + previous_val where coeff = 2 / (period + 1)
            df[target] = con.ewm(span=period, adjust=False).mean()
        df.fillna(0,inplace = True)
    #     return df
    for j in slow_ema:
        val = "ema"+"_"+str(j)
        EMA(df,"close",val,j)
    for k in fast_ema:
        val = "ema"+"_"+str(k)
        EMA(df,"close",val,k)
    def super_guppy(interval,df,anchor=0):
        anchor = 0
        ShowBreak = True
        ShowSwing = True
        ShowCon = False
        uOCCswing = False
        Lookback = 6
        emaFilter = False
        mult = 0
        buybreak = 0
        sellbreak = 0
        buy_barssince_var = 0
        sell_barssince_var = 0
        buybreak_barssince_var = 0
        sellbreak_barssince_var = 0
        barssince_lst = list()
        barssince_var = 0
        bar_count_var = 0
        buy1 = list()
        sell1 = list()
        buy2 = list()
        sell2 = list()
        buybreak1 = list()
        sellbreak1 = list()
        def barssince(b,barssince_var):
            barssince_lst = []
            barssince_var = 0 
            new_var = len(b)
            for i in b[::-1]:
                if i == 1:
                    break
                barssince_lst.append(i)
            barssince_var = len(barssince_lst)
            return barssince_var
            barssince_lst.clear()
        if interval < 1441 :
            if (anchor==0 or interval <= 0 or interval >= anchor or anchor > 1441 ):
                mult = 1
            else:
                if round(anchor/interval) > 1:
                    mult = round(anchor/interval)
                else:
                    mult = 1
        else:
            mult = 1
        #isIntraday Not
        if interval > 1441:
            if (anchor==0 or interval <= 0 or interval >= anchor or anchor < 52 ):
                mult = mult
            else:
                if round(anchor/interval) > 1:
                    mult = round(anchor/interval)
                else:
                    mult = 1
        else:
            mult = mult
        mult = 1
        for i in range(len(df)):
            emaF1 = df.loc[i,'ema_3']
            emaF2 = df.loc[i,'ema_5']
            emaF3 = df.loc[i,'ema_7']
            emaF4 = df.loc[i,'ema_9']
            emaF5 = df.loc[i,'ema_11']
            emaF6 = df.loc[i,'ema_13']
            emaF7 = df.loc[i,'ema_15']
            emaF8 = df.loc[i,'ema_17']
            emaF9 = df.loc[i,'ema_19']
            emaF10 = df.loc[i,'ema_21']
            emaF11 = df.loc[i,'ema_23']
            emaS1 = df.loc[i,'ema_25']
            emaS2 = df.loc[i,'ema_28']
            emaS3 = df.loc[i,'ema_31']
            emaS4 = df.loc[i,'ema_34']
            emaS5 = df.loc[i,'ema_37']
            emaS6 = df.loc[i,'ema_40']
            emaS7 = df.loc[i,'ema_43']
            emaS8 = df.loc[i,'ema_46']
            emaS9 = df.loc[i,'ema_49']
            emaS10 = df.loc[i,'ema_52']
            emaS11 = df.loc[i,'ema_55']
            emaS12 = df.loc[i,'ema_58']
            emaS13 = df.loc[i,'ema_61']
            emaS14 = df.loc[i,'ema_64']
            emaS15 = df.loc[i,'ema_67']
            emaS16 = df.loc[i,'ema_70']
            ema200 = df.loc[i,'ema_200'] 
            emafast = (emaF1 + emaF2 + emaF3 + emaF4 + emaF5 + emaF6 + emaF7 + emaF8 + emaF9 + emaF10 + emaF11)/11
            emaslow = (emaS1 + emaS2 + emaS3 + emaS4 + emaS5 + emaS6 + emaS7 + emaS8 + emaS9 + emaS10 + emaS11 + emaS12 + emaS13 + emaS14 + emaS15 + emaS16)/16
            #Fast EMA Color Rules
            colfastL = (emaF1>emaF2 and emaF2>emaF3 and emaF3>emaF4 and emaF4>emaF5 and emaF5>emaF6 and emaF6>emaF7 and emaF7>emaF8 and emaF8>emaF9 and emaF9>emaF10 and emaF10>emaF11)
            colfastS = (emaF1<emaF2 and emaF2<emaF3 and emaF3<emaF4 and emaF4<emaF5 and emaF5<emaF6 and emaF6<emaF7 and emaF7<emaF8 and emaF8<emaF9 and emaF9<emaF10 and emaF10<emaF11)
            #Slow EMA Color Rules
            colslowL = (emaS1>emaS2 and emaS2>emaS3 and emaS3>emaS4 and emaS4>emaS5 and emaS5>emaS6 and emaS6>emaS7 and emaS7>emaS8) and (emaS8>emaS9 and emaS9>emaS10 and emaS10>emaS11 and emaS11>emaS12 and emaS12>emaS13 and emaS13>emaS14 and emaS14>emaS15 and emaS15>emaS16)
            colslowS = (emaS1<emaS2 and emaS2<emaS3 and emaS3<emaS4 and emaS4<emaS5 and emaS5<emaS6 and emaS6<emaS7 and emaS7<emaS8) and (emaS8<emaS9 and emaS9<emaS10 and emaS10<emaS11 and emaS11<emaS12 and emaS12<emaS13 and emaS13<emaS14 and emaS14<emaS15 and emaS15<emaS16)
            if  emafast > emaslow and not colslowS and colfastL and (not ShowCon or colslowL) and (not emaFilter or emafast>ema200):
                if int(buy1[-1]) > 0:
                    buy = buy1[-1] + 1
                else:
                    buy = 1
            else:
                buy = 0
            buy1.append(buy)
            if  emafast < emaslow and not colslowL and colfastS and (not ShowCon or colslowS) and (not emaFilter or emafast<ema200):
                if int(sell1[-1]) > 0:
                    sell = sell1[-1] + 1
                else:
                    sell = 1
            else:
                sell = 0
            sell1.append(sell)
            #buy
            if buy>1 and colfastL and (uOCCswing and ((df.loc[i-1,'close']<df.loc[i-1,'open']) and (df.loc[i,'close']>df.loc[i,'open']))):
                buy3 = 1
            else:
                buy3 = buy
            buy2.append(buy3)
            #sell    
            if sell>1 and colfastS and (uOCCswing and ((df.loc[i-1,'close']<df.loc[i-1,'open']) and (df.loc[i,'close']>df.loc[i,'open']))):
                sell3 = 1
            else:
                sell3 = sell
            sell2.append(sell3)
            #buybreak
            if emafast > emaslow and not colslowS and (not emaFilter or emafast>ema200):
                if buybreak1[-1] > 0:
                    buybreak = buybreak1[-1] + 1
                else:
                    buybreak = 1
            else:
                buybreak = 0
            buybreak1.append(buybreak)
            if emafast < emaslow and not colslowL and (not emaFilter or emafast<ema200):
                if sellbreak1[-1] > 0:
                    sellbreak = sellbreak1[-1]+1
                else:
                    sellbreak = 1
            else:
                sellbreak = 0
            sellbreak1.append(sellbreak)
            #arrow plotting
            #buy_arrow
            buy_barssince_var = barssince(buy2[:-1],barssince_var)
            if (ShowSwing and buy3==1)and buy_barssince_var > 6:
                buy_arrow = 1
            else:
                buy_arrow = 0
            #sell arrow
            sell_barssince_var = barssince(sell2[:-1],barssince_var)
            if ShowSwing and (sell3==1 and sell_barssince_var > 6):
                sell_arrow = 1
            else:
                sell_arrow = 0
            #buybreak_arrow
            buybreak_barssince_var = barssince(buybreak1[:-1],barssince_var)
            sellbreak_barssince_var = barssince(sellbreak1[:-1],barssince_var)
            if ShowBreak and buybreak==1 and (sellbreak_barssince_var>Lookback) and (buybreak_barssince_var>Lookback):
                buybreak_arrow = 1
            else:
                buybreak_arrow = 0
            #sellbreak_arrow
            if ShowBreak and sellbreak==1 and (buybreak_barssince_var>Lookback) and (sellbreak_barssince_var>Lookback):
                sellbreak_arrow = 1
            else:
                sellbreak_arrow = 0
            if buy_arrow==1 and sell_arrow==0 and buybreak_arrow==0 and sellbreak_arrow==0:
                arrow_color = 'green'
            elif buy_arrow==0 and sell_arrow==1 and buybreak_arrow==0 and sellbreak_arrow==0:
                arrow_color = 'red'
            elif sell_arrow==0 and (buy_arrow==0 or buy_arrow==1) and buybreak_arrow==1 and sellbreak_arrow==0:
                arrow_color = 'aqua'
            elif buy_arrow==0 and (sell_arrow==1 or sell_arrow==0) and buybreak_arrow==0 and sellbreak_arrow==1:
                arrow_color = 'blue'
            else:
                arrow_color = 'none'
            df.loc[i,'arrow_color'] = arrow_color
        df = df[['date','open','high','low','close','arrow_color']]
        return df
    df=super_guppy(15,df)
    gup=df
    def bidatrema(df,period):
        df['hl']=abs(df['high']-df['low'])
        df['hpc']=abs(df['high']-df['close'].shift())
        df['lpc']=abs(df['low']-df['close'].shift())
        df['tr']=df[['hl','hpc','lpc']].max(axis=1)
        df['ATR']=pd.DataFrame.ewm(df["tr"], span=period,min_periods=period).mean()
        df.drop(["hl","hpc","lpc","tr"],axis = 1 , inplace =True)
    bidatrema(gup,14)
    df['bid_value'] = 0
    df['executed_value'] = 0
    df['diff'] = 0
    df['pnl'] = 0
    qty = int ( qu/ (gup.iloc[-1,2]) )
    var = False
    var_2 = False
    var_1 = False
    for i in range(len(df)):
        zz=df.loc[i,"date"]
        za=str(zz)[11:13]
        if za!="15":
            if var==True:
                df.loc[i,'diff'] =  df.loc[i,'close'] - bid_value
                df.loc[i,'pnl'] = ( bid_value /bid_value)*df.loc[i,'diff']
                var=False
                var1=False 
            var_2=True 
        if za!="15":
            var_2=False
        if var==False and var_2==False:
            if df.loc[i,'arrow_color']=='green':
                bid_value = (df.loc[i,'high']+df.loc[i,'ATR']*.25)+(df.loc[i,'ATR']*.1)
                df.loc[i,'bid_value'] =bid_value
                var=True
                var1=False
        if var==True and var1==False and var_2==False:
            if df.loc[i,'high'] > bid_value and df.loc[i,'low'] < bid_value:
                df.loc[i,'executed_value']=bid_value
                var1=True
        if var==True and var1==True and var_2==False:
            if df.loc[i,'arrow_color']=='red':
                df.loc[i,'diff'] =  df.loc[i,'close'] - bid_value
                df.loc[i,'pnl'] = ( bid_value /bid_value)*df.loc[i,'diff']
                var=False
                var1=False
    list_1 = df['pnl']
    pos_count = len(list(filter(lambda x: (x>0),list_1)))
    neg_count = len(list(filter(lambda x: (x<0),list_1)))
    total_trade = pos_count + neg_count
    try :
        strike_rate = pos_count/total_trade
    except:
        strike_rate = 0
    pnl = df['pnl'].sum()
    pnl_list.append(pnl)
    timeframe_list.append(t)
    strike_rate_list.append(strike_rate)
main_df = pd.DataFrame({'timeframe':timeframe_list,'pnl':pnl_list,'strike_rate':strike_rate_list})
main_df.to_csv('pnlbuy.csv')
print("complete")
				
			

Output – 

				
					SCANNING START
complete
				
			

For each executed buy trade, the program meticulously computes the PnL. This involves tracking the price movement post-execution and quantifying the trade’s success or failure.

				
					display(main_df)
				
			

Output – 

				
					timeframe	pnl	strike_rate
0	1	-73.269911	0.214286
				
			

Sell Backtesting Strategy

Signal Generation for Sell Trades

  • Bearish Signal Detection: The sell strategy hinges on identifying bearish signals, which occur when short-term EMAs fall below the long-term EMAs. This scenario typically signals a potential downward trend in the stock’s price, presenting a timely moment for placing sell orders.

Trade Execution Logic for Sell Orders

  • Sell Order Trigger: Following the execution of a buy order, the system closely monitors for a bearish signal as indicated by the GMMA. Upon detecting such a signal, a sell order is promptly initiated. This strategy is designed to capitalize on the change in trend, aiming to secure profits or minimize losses from the preceding buy trade.
				
					## fno
tokenall=[5633, 6401, 3861249, 4451329, 2760193, 20993, 325121, 2524673, 41729, 49409, 54273, 60417, 70401, 1510401, 1195009, 1214721, 94977, 108033, 2714625, 2911489, 2763265, 3812865, 160001, 160769, 163073, 177665, 5215745, 3876097, 197633, 3771393, 225537, 173057, 261889, 1207553, 3463169, 2796801, 315393, 3378433, 2513665, 1850625, 340481, 341249, 3789569, 345089, 2747905, 348929, 359937, 356865, 364545, 3699201, 1270529, 377857, 3677697, 3060993, 381697, 2883073, 387073, 387841, 1346049, 408065, 2393089, 415745, 3920129, 424961, 1723649, 2661633, 2933761, 3011329, 4574465, 3001089, 4632577, 492033, 2061825, 511233, 2939649, 2672641, 519937, 2815745, 2674433, 582913, 593665, 3924993, 2977281, 2748929, 633601, 2819073, 636673, 2730497, 3834113, 2906881, 3364353, 731905, 3375873, 3930881, 737793, 738561, 141569, 3078657, 779521, 780289, 1492737, 1522689, 1102337, 857857, 3431425, 3076609, 1837825, 871681, 952577, 878593, 884737, 4343041, 877057, 895745, 2953217, 3465729, 897537, 2873089, 2952193, 2752769, 920065, 951809, 3026177, 969473, 3050241, 112129, 134657, 3721473, 2800641, 3385857, 4454401, 1152769, 806401, 617473, 2905857, 3660545, 3906305, 758529, 975873]
ss=['ACC', 'ADANIENT', 'ADANIPORTS', 'ADANIPOWER', 'ALBK', 'ALOKTEXT', 'AMBUJACEM', 'ANDHRABANK', 'APOLLOTYRE', 'ARVIND', 'ASHOKLEY', 'ASIANPAINT', 'AUROPHARMA', 'AXISBANK', 'BANKBARODA', 'BANKINDIA', 'BATAINDIA', 'BHARATFORG', 'BHARTIARTL', 'BIOCON', 'CANBK', 'CENTRALBK', 'CENTURYTEX', 'CESC', 'CHAMBLFERT', 'CIPLA', 'COALINDIA', 'COLPAL', 'DABUR', 'DLF', 'DRREDDY', 'EXIDEIND', 'FEDERALBNK', 'GAIL', 'GMRINFRA', 'GODREJIND', 'GRASIM', 'GSPL', 'HAVELLS', 'HCLTECH', 'HDFC', 'HDFCBANK', 'HDIL', 'HEROMOTOCO', 'HEXAWARE', 'HINDALCO', 'HINDPETRO', 'HINDUNILVR', 'HINDZINC', 'IBREALEST', 'ICICIBANK', 'IDBI', 'IDEA', 'IDFC', 'IFCI', 'IGL', 'INDHOTEL', 'INDIACEM', 'INDUSINDBK', 'INFY', 'IOB', 'IOC', 'IRB', 'ITC', 'JINDALSTEL', 'JISLJALEQS', 'JPASSOCIAT', 'JPPOWER', 'JSWENERGY', 'JSWSTEEL', 'JUBLFOOD', 'KOTAKBANK', 'KTKBANK', 'LICHSGFIN', 'LT', 'LUPIN', 'M&M', 'MARUTI', 'MCDOWELL-N', 'MRF', 'NCC', 'NMDC', 'NTPC', 'OFSS', 'ONGC', 'OPTOCIRCUI', 'ORIENTBANK', 'PNB', 'POWERGRID', 'PTC', 'PUNJLLOYD', 'RAYMOND', 'RCOM', 'RECLTD', 'RELCAPITAL', 'RELIANCE', 'RELINFRA', 'RENUKA', 'SBIN', 'SCI', 'SINTEX', 'SOUTHBANK', 'SRTRANSFIN', 'SUNPHARMA', 'SUNTV', 'SUZLON', 'SYNDIBANK', 'TATACHEM', 'TATACOMM', 'TATAGLOBAL', 'TATAMOTORS', 'TATAMTRDVR', 'TATAPOWER', 'TATASTEEL', 'TCS', 'TECHM', 'TITAN', 'UCOBANK', 'ULTRACEMCO', 'UNIONBANK', 'UNITECH', 'VOLTAS', 'WELCORP', 'WIPRO', 'YESBANK', 'BHEL', 'BPCL', 'DISHTV', 'DIVISLAB', 'GVKPIL', 'NHPC', 'MPHASIS', 'SIEMENS', 'PEL', 'PETRONET', 'PFC', 'RPOWER', 'SAIL', 'ZEEL']


sdate           ="2019-08-20 00:00:00"
todate          ="2019-09-16 09:16:00"
#sdate          ="2019-08-19"
#todate         ="2019-10-01"

# inputs for backtesting

time_frame      ="minute"
quu=50000
eexchange="NSE"
productt="MIS"
qu=int(quu)

# Backtesting Dates
sdate_backtest          ="2019-08-14"
todate_backtest         ="2019-10-02"

print("SCANNING START")
ttoken=5633
ttradingsymbol="ACC"


def time_con(time,df):
    z = 0
    chg = []
    chg_index = 0
    timeframe = time
    date = np.array([])
    for i in range(len(df)):
        date = np.hstack((date,str(df.loc[i,"date"])))
    open_np = df.open.values
    high_np = df.high.values
    low_np = df.low.values
    close_np = df.close.values
#     volume_np = df.volume.values
#     print(date)
    for j in range(len(date)):
        if date[j][11:16] == "09:15":
            chg.append(j)
    np_date=np.array([])
    np_open = np.array([])
    np_high = np.array([])
    np_low = np.array([])
    np_close = np.array([])
#     np_volume = np.array([])
    for i in range(len(date)-timeframe+1):
        if i>=z:
            chg_index+=1
            if (chg_index < len(chg)):
                for k in range(i,chg[chg_index],timeframe):
#                     print(date[k])
#                     print(k,k+timeframe)
                    np_date = np.hstack((np_date,date[k]))
                    np_open = np.hstack((np_open,open_np[k]))
                    np_high = np.hstack((np_high,np.amax(high_np[k:k+timeframe])))
#                     print(high_np[k:k+timeframe])
                    np_low = np.hstack((np_low,np.amin(low_np[k:k+timeframe])))
#                     print(low_np[k:k+timeframe])
                    np_close = np.hstack((np_close,close_np[k+timeframe-1]))
#                     np_volume = np.hstack((np_volume,np.sum(volume_np[k:k+timeframe])))
                    z = i+timeframe
                    z=chg[chg_index]
    else:
        con_df = pd.DataFrame({"date":np_date,"open":np_open,"high":np_high,"low":np_low,"close":np_close})
        return con_df

strike_rate = 0
strike_rate_list = []
timeframe = []
timeframe_list = []
pnl = 0
pnl_list = []

for t in range(1,2):
    dfw=kite.historical_data(ttoken,sdate_backtest,todate_backtest,time_frame,0)
    dfw=pd.DataFrame(dfw)
    df=pd.DataFrame(dfw[['date','open','high','low','close']])
    df= time_con(t,df)
    slow_ema = [3,5,7,9,11,13,15,17,19,21,23]
    fast_ema = [25,28,31,34,37,40,43,46,49,52,55,58,61,64,67,70,200]
    def EMA(df, base, target, period, alpha=False):
        con = pd.concat([df[:period][base].rolling(window=period).mean(), df[period:][base]])
        if (alpha == True):
            # (1 - alpha) * previous_val + alpha * current_val where alpha = 1 / period
            df[target] = con.ewm(alpha=1 / period, adjust=False).mean()
        else:
            # ((current_val - previous_val) * coeff) + previous_val where coeff = 2 / (period + 1)
            df[target] = con.ewm(span=period, adjust=False).mean()
        df.fillna(0,inplace = True)
    #     return df
    for j in slow_ema:
        val = "ema"+"_"+str(j)
        EMA(df,"close",val,j)
    for k in fast_ema:
        val = "ema"+"_"+str(k)
        EMA(df,"close",val,k)
    def super_guppy(interval,df,anchor=0):
        anchor = 0
        ShowBreak = True
        ShowSwing = True
        ShowCon = False
        uOCCswing = False
        Lookback = 6
        emaFilter = False
        mult = 0
        buybreak = 0
        sellbreak = 0
        buy_barssince_var = 0
        sell_barssince_var = 0
        buybreak_barssince_var = 0
        sellbreak_barssince_var = 0
        barssince_lst = list()
        barssince_var = 0
        bar_count_var = 0
        buy1 = list()
        sell1 = list()
        buy2 = list()
        sell2 = list()
        buybreak1 = list()
        sellbreak1 = list()
        def barssince(b,barssince_var):
            barssince_lst = []
            barssince_var = 0 
            new_var = len(b)
            for i in b[::-1]:
                if i == 1:
                    break
                barssince_lst.append(i)
            barssince_var = len(barssince_lst)
            return barssince_var
            barssince_lst.clear()
        if interval < 1441 :
            if (anchor==0 or interval <= 0 or interval >= anchor or anchor > 1441 ):
                mult = 1
            else:
                if round(anchor/interval) > 1:
                    mult = round(anchor/interval)
                else:
                    mult = 1
        else:
            mult = 1
        #isIntraday Not
        if interval > 1441:
            if (anchor==0 or interval <= 0 or interval >= anchor or anchor < 52 ):
                mult = mult
            else:
                if round(anchor/interval) > 1:
                    mult = round(anchor/interval)
                else:
                    mult = 1
        else:
            mult = mult
        mult = 1
        for i in range(len(df)):
            emaF1 = df.loc[i,'ema_3']
            emaF2 = df.loc[i,'ema_5']
            emaF3 = df.loc[i,'ema_7']
            emaF4 = df.loc[i,'ema_9']
            emaF5 = df.loc[i,'ema_11']
            emaF6 = df.loc[i,'ema_13']
            emaF7 = df.loc[i,'ema_15']
            emaF8 = df.loc[i,'ema_17']
            emaF9 = df.loc[i,'ema_19']
            emaF10 = df.loc[i,'ema_21']
            emaF11 = df.loc[i,'ema_23']
            emaS1 = df.loc[i,'ema_25']
            emaS2 = df.loc[i,'ema_28']
            emaS3 = df.loc[i,'ema_31']
            emaS4 = df.loc[i,'ema_34']
            emaS5 = df.loc[i,'ema_37']
            emaS6 = df.loc[i,'ema_40']
            emaS7 = df.loc[i,'ema_43']
            emaS8 = df.loc[i,'ema_46']
            emaS9 = df.loc[i,'ema_49']
            emaS10 = df.loc[i,'ema_52']
            emaS11 = df.loc[i,'ema_55']
            emaS12 = df.loc[i,'ema_58']
            emaS13 = df.loc[i,'ema_61']
            emaS14 = df.loc[i,'ema_64']
            emaS15 = df.loc[i,'ema_67']
            emaS16 = df.loc[i,'ema_70']
            ema200 = df.loc[i,'ema_200'] 
            emafast = (emaF1 + emaF2 + emaF3 + emaF4 + emaF5 + emaF6 + emaF7 + emaF8 + emaF9 + emaF10 + emaF11)/11
            emaslow = (emaS1 + emaS2 + emaS3 + emaS4 + emaS5 + emaS6 + emaS7 + emaS8 + emaS9 + emaS10 + emaS11 + emaS12 + emaS13 + emaS14 + emaS15 + emaS16)/16
            #Fast EMA Color Rules
            colfastL = (emaF1>emaF2 and emaF2>emaF3 and emaF3>emaF4 and emaF4>emaF5 and emaF5>emaF6 and emaF6>emaF7 and emaF7>emaF8 and emaF8>emaF9 and emaF9>emaF10 and emaF10>emaF11)
            colfastS = (emaF1<emaF2 and emaF2<emaF3 and emaF3<emaF4 and emaF4<emaF5 and emaF5<emaF6 and emaF6<emaF7 and emaF7<emaF8 and emaF8<emaF9 and emaF9<emaF10 and emaF10<emaF11)
            #Slow EMA Color Rules
            colslowL = (emaS1>emaS2 and emaS2>emaS3 and emaS3>emaS4 and emaS4>emaS5 and emaS5>emaS6 and emaS6>emaS7 and emaS7>emaS8) and (emaS8>emaS9 and emaS9>emaS10 and emaS10>emaS11 and emaS11>emaS12 and emaS12>emaS13 and emaS13>emaS14 and emaS14>emaS15 and emaS15>emaS16)
            colslowS = (emaS1<emaS2 and emaS2<emaS3 and emaS3<emaS4 and emaS4<emaS5 and emaS5<emaS6 and emaS6<emaS7 and emaS7<emaS8) and (emaS8<emaS9 and emaS9<emaS10 and emaS10<emaS11 and emaS11<emaS12 and emaS12<emaS13 and emaS13<emaS14 and emaS14<emaS15 and emaS15<emaS16)
            if  emafast > emaslow and not colslowS and colfastL and (not ShowCon or colslowL) and (not emaFilter or emafast>ema200):
                if int(buy1[-1]) > 0:
                    buy = buy1[-1] + 1
                else:
                    buy = 1
            else:
                buy = 0
            buy1.append(buy)
            if  emafast < emaslow and not colslowL and colfastS and (not ShowCon or colslowS) and (not emaFilter or emafast<ema200):
                if int(sell1[-1]) > 0:
                    sell = sell1[-1] + 1
                else:
                    sell = 1
            else:
                sell = 0
            sell1.append(sell)
            #buy
            if buy>1 and colfastL and (uOCCswing and ((df.loc[i-1,'close']<df.loc[i-1,'open']) and (df.loc[i,'close']>df.loc[i,'open']))):
                buy3 = 1
            else:
                buy3 = buy
            buy2.append(buy3)
            #sell    
            if sell>1 and colfastS and (uOCCswing and ((df.loc[i-1,'close']<df.loc[i-1,'open']) and (df.loc[i,'close']>df.loc[i,'open']))):
                sell3 = 1
            else:
                sell3 = sell
            sell2.append(sell3)
            #buybreak
            if emafast > emaslow and not colslowS and (not emaFilter or emafast>ema200):
                if buybreak1[-1] > 0:
                    buybreak = buybreak1[-1] + 1
                else:
                    buybreak = 1
            else:
                buybreak = 0
            buybreak1.append(buybreak)
            if emafast < emaslow and not colslowL and (not emaFilter or emafast<ema200):
                if sellbreak1[-1] > 0:
                    sellbreak = sellbreak1[-1]+1
                else:
                    sellbreak = 1
            else:
                sellbreak = 0
            sellbreak1.append(sellbreak)
            #arrow plotting
            #buy_arrow
            buy_barssince_var = barssince(buy2[:-1],barssince_var)
            if (ShowSwing and buy3==1)and buy_barssince_var > 6:
                buy_arrow = 1
            else:
                buy_arrow = 0
            #sell arrow
            sell_barssince_var = barssince(sell2[:-1],barssince_var)
            if ShowSwing and (sell3==1 and sell_barssince_var > 6):
                sell_arrow = 1
            else:
                sell_arrow = 0
            #buybreak_arrow
            buybreak_barssince_var = barssince(buybreak1[:-1],barssince_var)
            sellbreak_barssince_var = barssince(sellbreak1[:-1],barssince_var)
            if ShowBreak and buybreak==1 and (sellbreak_barssince_var>Lookback) and (buybreak_barssince_var>Lookback):
                buybreak_arrow = 1
            else:
                buybreak_arrow = 0
            #sellbreak_arrow
            if ShowBreak and sellbreak==1 and (buybreak_barssince_var>Lookback) and (sellbreak_barssince_var>Lookback):
                sellbreak_arrow = 1
            else:
                sellbreak_arrow = 0
            if buy_arrow==1 and sell_arrow==0 and buybreak_arrow==0 and sellbreak_arrow==0:
                arrow_color = 'green'
            elif buy_arrow==0 and sell_arrow==1 and buybreak_arrow==0 and sellbreak_arrow==0:
                arrow_color = 'red'
            elif sell_arrow==0 and (buy_arrow==0 or buy_arrow==1) and buybreak_arrow==1 and sellbreak_arrow==0:
                arrow_color = 'aqua'
            elif buy_arrow==0 and (sell_arrow==1 or sell_arrow==0) and buybreak_arrow==0 and sellbreak_arrow==1:
                arrow_color = 'blue'
            else:
                arrow_color = 'none'
            df.loc[i,'arrow_color'] = arrow_color
        df = df[['date','open','high','low','close','arrow_color']]
        return df
    df=super_guppy(15,df)
    gup=df
    def bidatrema(df,period):
        df['hl']=abs(df['high']-df['low'])
        df['hpc']=abs(df['high']-df['close'].shift())
        df['lpc']=abs(df['low']-df['close'].shift())
        df['tr']=df[['hl','hpc','lpc']].max(axis=1)
        df['ATR']=pd.DataFrame.ewm(df["tr"], span=period,min_periods=period).mean()
        df.drop(["hl","hpc","lpc","tr"],axis = 1 , inplace =True)
    bidatrema(gup,14)
    df['bid_value'] = 0
    df['executed_value'] = 0
    df['diff'] = 0
    df['pnl'] = 0
    qty = int ( qu/ (gup.iloc[-1,2]) )
    var = False
    var_2 = False
    var_1 = False
    for i in range(len(df)):
        zz=df.loc[i,"date"]
        za=str(zz)[11:13]
        if za=="15":
            if var==True:
                df.loc[i,'diff'] =  bid_value - df.loc[i,'close']
                df.loc[i,'pnl'] = ( bid_value /bid_value)*df.loc[i,'diff']
                ##
                var=False
                var1=False 
            var_2=True
        if za!="15":
            var_2=False
        if var==False and var_2==False:
            if df.loc[i,'arrow_color']=='red':
                bid_value = (df.loc[i,'low']-df.loc[i,'ATR']*.25)-(df.loc[i,'ATR']*.1)
                df.loc[i,'bid_value'] =bid_value
                var=True
                var1=False
        if var==True and var1==False and var_2==False:
            if df.loc[i,'high'] > bid_value and df.loc[i,'low'] < bid_value:
                df.loc[i,'executed_value']=bid_value
                var1=True
        if var==True and var1==True and var_2==False:
            if df.loc[i,'arrow_color']=='green':
                df.loc[i,'diff'] =  bid_value - df.loc[i,'close'] 
                df.loc[i,'pnl'] = ( bid_value /bid_value)*df.loc[i,'diff']
                var=False
                var1=False
    list_1 = df['pnl']
    pos_count = len(list(filter(lambda x: (x>0),list_1)))
    neg_count = len(list(filter(lambda x: (x<0),list_1)))
    total_trade = pos_count + neg_count
    try :
        strike_rate = pos_count/total_trade
    except:
        strike_rate = 0
    #print(strike_rate)
    pnl = df['pnl'].sum()
    pnl_list.append(pnl)
    timeframe_list.append(t)
    strike_rate_list.append(strike_rate)
main_df = pd.DataFrame({'timeframe':timeframe_list,'pnl':pnl_list,'strike_rate':strike_rate_list})
main_df.to_csv('pnlsell.csv')
print("complete")
				
			

Output – 

				
					SCANNING START
complete
				
			
Similar to the buy strategy, the PnL for each sell trade is rigorously calculated. This involves assessing the price at which the sell order was executed against the buy price to determine the trade’s outcome.
				
					display(main_df)
				
			

Output – 

				
						timeframe	pnl	strike_rate
0	1	-11.92732	0.333333
				
			
In conclusion, separating the buy and sell components of the backtesting strategy allows for a more nuanced analysis of each aspect of the trading strategy. It provides clearer insights into the effectiveness of the GMMA indicator in different market scenarios and enhances the ability to fine-tune the strategy for improved performance.
Post a comment

Leave a Comment

Your email address will not be published. Required fields are marked *

×Close