Introduction
Intraday trading strategies come and go, but only a few stand the test of time. One such strategy is the Zero Loss Intraday Strategy proposed by Mahesh Chander Kaushik, a well-known YouTuber renowned for his simple yet effective trading methods. I recently created a YouTube video backtesting this strategy using Python, and in this article, I’ll walk you through its rules, the backtesting results, and whether it holds up in the real market
Strategy Overview
- Original Proposer: Mahesh Chander Kaushik
- Type: Intraday – Pivot Range Breakout Strategy
- Time Frame: 5-minute Candles
- Trade Direction: Long and Short Only
- Indicators Used: Custom Indicator (Pivot Price Point)
- Product Type: Cash Segment
- Stock Universe: Top 10 Market Cap Stocks Only
Pivot Price Calculation Logic
A key aspect of this strategy is the calculation of the Pivot Price, which is determined at 11 AM each trading day using the formula:
Pivot Price Point = ( Yesterday’s Day Close + Today’s Intraday High + Today’s Intraday Low) / 3
At 11 AM:
- If the CMP < Pivot Price, a Short Trade is considered, but with additional conditions.
- If the Current Market Price (CMP) > Pivot Price, a Long Trade is initiated.
Strategy Rules for Long Trades
Entry Conditions (at 11 AM):
- If CMP is greater than the Pivot Price, take a long position.
Risk Management / Position Sizing:
- Limit position size to Rs.10,000 per order.
- The original strategy advises a total capital allocation of Rs.500,000 per stock.
Exit Conditions:
If the target is not met intraday, take delivery of the stock for a longer-term hold.
The target is an intraday gain of 0.7% (excluding brokerage and fees).
No stop-loss is placed.
Strategy Rules for Short Trades
Entry Conditions (at 11 AM):
- CMP is less than the Pivot Price.
- There are existing holdings of the stock.
- The overall profit position of the existing holdings for that stock is greater than 0.7%.
Risk Management / Position Sizing:
- Limit position size to Rs.10,000 per order.
- The original strategy suggests Rs.500,000 capital per stock.
Exit Conditions:
- The target is an intraday profit of 0.7% (excluding brokerage and fees).
- Stop-loss is set at the intraday high.
- If the target is not met, take delivery by selling from existing holdings. The loss from the trade is covered by the previous profit in holdings.
# ------------------------------------------------------------------------------------ # FabTrader Algorithmic Backtesting # ------------------------------------------------------------------------------------ # Copyright (c) 2022 FabTrader (Unit of Rough Sketch Company) # # LICENSE: PROPRIETARY SOFTWARE # - This software is the exclusive property of FabTrader. # - Unauthorized copying, modification, distribution, or use is strictly prohibited. # - Written permission from the author is required for any use beyond personal, # non-commercial purposes. # # CONTACT: # - Website: https://fabtrader.in # - Email: fabtraderinc@gmail.com # # Usage: Internal use only. Not for commercial redistribution. # Permissions and licensing inquiries should be directed to the contact email. import pandas as pd import datetime as dt import BrokerConnector from Symbol_master import Instruments class ZeroLossStrategyBacktest: def __init__(self, stocks, start_date, end_date, use_stoploss=True, target_pct=0.0075, max_investment=10000, starting_portfolio_value=100000): self.stocks = stocks self.start_date = start_date self.end_date = end_date self.use_stoploss = use_stoploss self.target_pct = target_pct self.max_investment = max_investment self.portfolio_value = starting_portfolio_value self.trade_log = [] def get_previous_trading_day_close(self, stock, current_date): """Find the previous valid trading day’s closing price.""" # >> Replace this code with your own function to retrieve historic candlestick data << # daily_data = Instruments.get_historical_data(stock, self.start_date, self.end_date) # >> Replace this code with your own function to retrieve historic candlestick data << # daily_data = daily_data.sort_index() previous_dates = daily_data.index[daily_data.index < current_date] if len(previous_dates) > 0: prev_day = previous_dates[-1] return daily_data.loc[prev_day]["Close"] return None def get_high_low_for_the_day(self, stock, current_date): """Get high & low from 5-minute data before 11 AM.""" curr_date = current_date.date() # >> Replace this code with your own function to retrieve historic candlestick data << # intraday_data = Instruments.get_historical_data(stock, curr_date, curr_date, "5minute") # >> Replace this code with your own function to retrieve historic candlestick data << # if intraday_data.empty: return None, None, None, None intraday_data_before_11am = intraday_data[intraday_data.index.time < pd.Timestamp("11:00").time()] if intraday_data_before_11am.empty: return None, None, None, None intraday_data_after_11am = intraday_data[intraday_data.index.time >= pd.Timestamp("11:00").time()] if intraday_data_after_11am.empty: return None, None, None, None return (intraday_data_before_11am["High"].max(), intraday_data_before_11am["Low"].min(), intraday_data_after_11am["High"].max(), intraday_data_after_11am["Low"].min(), intraday_data_after_11am.iloc[0]['Close'], intraday_data_after_11am.iloc[-1]['Close']) def backtest(self): """Runs the backtest for positional trades.""" for stock in self.stocks: print(f"Backtesting {stock}...") # >> Replace this code with your own function to retrieve historic candlestick data << # data = Instruments.get_historical_data(stock, self.start_date, self.end_date) # >> Replace this code with your own function to retrieve historic candlestick data << # data.to_csv(stock+'.csv') if data.empty: continue data = data.sort_index() for date, row in data.iterrows(): prev_close = self.get_previous_trading_day_close(stock, pd.Timestamp(date)) if prev_close is None: continue high_before_11am, low_before_11am, high_after_11am, low_after_11am, close_at_11am, close_at_eod = ( self.get_high_low_for_the_day(stock, date)) if high_before_11am is None or low_before_11am is None: continue pivot_level = (prev_close + high_before_11am + low_before_11am) / 3 if close_at_11am >= pivot_level: # Bullish Zone. Long Trade qty = self.max_investment // close_at_11am target = close_at_11am * (1 + self.target_pct) if target <= high_after_11am: # Target hit intraday pnl = (target - close_at_11am) * qty pnl_percentage = (pnl * 100) / (close_at_11am * qty) brokerage = 10 + (target * qty) * 0.0012 net_pnl = pnl - brokerage net_pnl_percentage = (net_pnl * 100) / (close_at_11am * qty) self.trade_log.append({ "tradingSymbol": stock, "tradeState": "completed", "entry_date": date, "direction": "Long", "qty": qty, "entry": close_at_11am, "exit": 0.0, "Target": target, "StopLoss": low_before_11am, "exit_date": date.date(), "pnl": pnl, "pnl%": pnl_percentage, "brokerage": brokerage, "netPnl": net_pnl, "netPnl%": net_pnl_percentage, "Trade Status": "Target Hit" }) self.portfolio_value += net_pnl else: # Trades that were not closed the same day will be carried over self.trade_log.append({ "tradingSymbol": stock, "tradeState": "active", "entry_date": date, "direction": "Long", "qty": qty, "entry": close_at_11am, "Target": target, "StopLoss": 0.0, 'exit_date': None, 'exit': 0.0, "pnl": 0, "pnl%": 0, "brokerage": 0.0, "netPnl": 0.0, "netPnl%": 0.0, "Trade Status": "Open" }) elif close_at_11am <= pivot_level: # Bearish Zone. Short Trade qty = self.max_investment // close_at_11am target = close_at_11am * (1 - self.target_pct) sl = high_before_11am if target >= low_after_11am: # Target hit intraday pnl = (close_at_11am - target) * qty pnl_percentage = (pnl * 100) / (close_at_11am * qty) brokerage = 10 + (target * qty) * 0.0012 net_pnl = pnl - brokerage net_pnl_percentage = (net_pnl * 100) / (close_at_11am * qty) self.trade_log.append({ "tradingSymbol": stock, "tradeState": "completed", "entry_date": date, "direction": "Short", "qty": qty, "entry": close_at_11am, "exit": sl, "Target": target, "StopLoss": low_before_11am, "exit_date": date.date(), "pnl": pnl, "pnl%": pnl_percentage, "brokerage": brokerage, "netPnl": net_pnl, "netPnl%": net_pnl_percentage, "Trade Status": "Target Hit" }) # self.portfolio_value += (target - pivot_level) * qty self.portfolio_value += net_pnl elif sl <= high_after_11am: # SL Hit Intraday pnl = (close_at_11am - sl) * qty pnl_percentage = (pnl * 100) / (close_at_11am * qty) brokerage = 10 + (sl * qty) * 0.0012 net_pnl = pnl - brokerage net_pnl_percentage = (net_pnl * 100) / (close_at_11am * qty) self.trade_log.append({ "tradingSymbol": stock, "tradeState": "completed", "entry_date": date, "direction": "Short", "qty": qty, "entry": close_at_11am, "Target": target, "StopLoss": low_before_11am, 'exit_date': date.date(), 'exit': sl, "pnl": pnl, "pnl%": pnl_percentage, "brokerage": brokerage, "netPnl": net_pnl, "netPnl%": net_pnl_percentage, "Trade Status": "SL Hit" }) self.portfolio_value += net_pnl else: # For Short Trades, close the trade at EOD (profit or loss doesnt matter) pnl = (close_at_11am - close_at_eod) * qty pnl_percentage = (pnl * 100) / (close_at_11am * qty) brokerage = 10 + (close_at_eod * qty) * 0.0012 net_pnl = pnl - brokerage net_pnl_percentage = (net_pnl * 100) / (close_at_11am * qty) self.trade_log.append({ "tradingSymbol": stock, "tradeState": "completed", "entry_date": date, "direction": "Short", "qty": qty, "entry": close_at_11am, "Target": target, "StopLoss": high_before_11am, 'exit_date': date.date(), 'exit': close_at_eod, "pnl": pnl, "pnl%": pnl_percentage, "brokerage": brokerage, "netPnl": net_pnl, "netPnl%": net_pnl_percentage, "Trade Status": "Force Closed" }) self.portfolio_value += net_pnl # For all carried over trades, check if target achieved later in the testing cycle for trade in self.trade_log: for date, row in data.iterrows(): last_close_price = row['Close'] if trade['Trade Status'] != "Open": continue if date < trade['entry_date']: continue if row['High'] > trade['Target']: pnl = (trade['Target'] - trade['entry']) * trade['qty'] pnl_percentage = (pnl * 100) / (trade['entry'] * trade['qty']) brokerage = 10 + (trade['Target'] * trade['qty']) * 0.0012 net_pnl = pnl - brokerage net_pnl_percentage = (net_pnl * 100) / (trade['entry'] * trade['qty']) trade['exit'] = trade['Target'] trade['exit_date'] = date.date() trade['pnl'] = pnl trade['pnl%'] = pnl_percentage trade["Trade Status"] = "Target Hit" trade["brokerage"] = brokerage trade['netPnl'] = net_pnl trade['netPnl%'] = net_pnl_percentage trade["tradeState"] = "completed" self.portfolio_value += net_pnl break # Enable this code below to: # Update latest price for all open trades so that notional profit/loss at the # end of backtesting can be calculated. All open trades will be force closed and # pnl will reflect the latest price at the end of the backtesting period # for trade in self.trade_log: # if trade['Trade Status'] != "Open": # continue # # pnl = (last_close_price - trade['entry']) * trade['qty'] # pnl_percentage = (pnl * 100) / (trade['entry'] * trade['qty']) # brokerage = 16 + (last_close_price * trade['qty']) * 0.0012 # net_pnl = pnl - brokerage # net_pnl_percentage = (net_pnl * 100) / (trade['entry'] * trade['qty']) # # trade['exit'] = last_close_price # trade['exit_date'] = end_date # trade['pnl'] = pnl # trade['pnl%'] = pnl_percentage # trade["brokerage"] = brokerage # trade['netPnl'] = net_pnl # trade['netPnl%'] = net_pnl_percentage # trade["Trade Status"] = "Force Closed" # trade["tradeState"] = "completed" # self.portfolio_value += net_pnl_percentage # Per original strategy, stoploss is optional and is applicable only for intraday # Enable this code below if you would like to extend this to the entire backtest period # if self.use_stoploss: # if date == trade['Buy Date']: # if row['Low'] < trade['StopLoss']: # pnl = (trade['StopLoss'] - trade['entry']) * trade['qty'] # pnl_percentage = (trade['StopLoss'] - trade['entry']) * trade['qty'] # brokerage = 16 + (trade['StopLoss'] * trade['qty']) * 0.0012 # net_pnl = pnl - brokerage # net_pnl_percentage = (net_pnl * 100) / (trade['entry'] * trade['qty']) # trade['exit'] = trade['StopLoss'] # trade['exit_date'] = date # trade['pnl'] = pnl # trade['pnl%'] = pnl_percentage # trade["brokerage"] = brokerage # trade['netPnl'] = net_pnl # trade['netPnl%'] = net_pnl_percentage # trade["Trade Status"] = "SL Hit" # self.portfolio_value += net_pnl_percentage # break self.generate_report() def generate_report(self): """Generate performance summary.""" trades_df = pd.DataFrame(self.trade_log) trades_df.to_csv(r'./Results/pivotmckoutput.csv', index = None, header=True) if trades_df.empty: print("No trades executed.") return print("\nTrade Summary:") total_trades = len(trades_df) completed_df = trades_df[trades_df['tradeState'] == 'completed'] completed_trades = len(trades_df.loc[trades_df["tradeState"]=="completed"]) open_trades = len(trades_df.loc[trades_df["tradeState"]=="active"]) winning_trades = len(completed_df.loc[completed_df["netPnl"] > 0]) win_rate = winning_trades / completed_trades * 100 gross_pnl = completed_df["pnl"].sum() total_brokerage = completed_df["brokerage"].sum() total_pnl = completed_df["netPnl"].sum() total_pnl_percent = completed_df["netPnl%"].sum() print(f"\nPerformance Metrics:") print(f"Total Trades: {total_trades}") print(f"Total Completed Trades: {completed_trades}") print(f"Total Open Trades: {open_trades}") print(f"Total Winning Trades: {winning_trades}") print(f"Win Rate: {win_rate:.0f}%") print(f"Total Gross Pnl: {gross_pnl:.2f}") print(f"Total Brokerage Paid : {total_brokerage:.2f}") print(f"Total Net Pnl: {total_pnl:.2f}") print(f"Starting Portfolio Balance %: {starting_portfolio:.2f}") print(f"Ending Portfolio Value: {self.portfolio_value:.2f}") self.trades_df = trades_df # Store results for analysis if __name__ == "__main__": #### Initial setup - Broker Connection #### pd.set_option("display.max_rows", None, "display.max_columns", None) #### Instrument Selection #### stocks = ["INFY", "HDFCBANK", "RELIANCE", "TCS", "BHARTIARTL", "ICICIBANK", "SBIN", "HINDUNILVR", "ITC", "BAJFINANCE"] #### Backtesting Period #### start_date = dt.date(2024, 1, 1) # Modify as needed end_date = dt.date(2024, 3, 31) # Modify as needed #### Backtest Starts #### starting_portfolio = 100000 backtester = ZeroLossStrategyBacktest( stocks=stocks, start_date=start_date, end_date=end_date, use_stoploss=True, # Change to False to test without stop loss starting_portfolio_value=starting_portfolio ) backtester.backtest()
Backtesting Results
Using Python, I backtested this strategy on historical intraday data for the top 10 market cap stocks. The key findings were:



Strengths and Weaknesses
Strengths:
✔ Simple and easy-to-follow rules.
✔ No complicated indicators—just price action.
✔ Works well in a trending market.
✔ Suitable for traders with a long-term holding mindset.
Weaknesses:
❌ No defined stop-loss for long trades, which can be risky.
❌ Short trades require an existing profitable holding, limiting opportunities.
❌ Brokerage and slippages can eat into small profit margins.
Final Thoughts
Mahesh Chander Kaushik’s Zero Loss Intraday Strategy is a simple yet intriguing approach to trading, focusing on price levels rather than complex indicators. While it performs well in a steady market, traders should be cautious about its lack of a strict stop-loss mechanism and potential exposure to market downturns.
Would I personally use this strategy? With modifications! Adding a dynamic stop-loss and filtering trades based on volatility could improve overall performance.
Let me know your thoughts in the comments below, and don’t forget to check out my YouTube video where I explain the Python backtesting code and share detailed results!
Support this community : FabTrader.in is a one-person initiative dedicated to helping individuals on their F.I.R.E. journey. Running and maintaining this community takes time, effort, and resources. If you’ve found value in the content, consider making a donation to support this mission.
Disclaimer: The information provided in this article is for educational and informational purposes only and should not be construed as financial, investment, or legal advice. The content is based on publicly available information and personal opinions and may not be suitable for all investors. Investing involves risks, including the loss of principal. Always conduct your own research and consult a qualified financial advisor before making any investment decisions. The author and website assume no liability for any financial losses or decisions made based on the information presented.