'Dhan_Tradehull' class methods support 'start_date' and 'end_date' arguments

@Dhan

Below error getting please help

PS D:\Dhan Practice> & C:/Users/Lenovo1/AppData/Local/Programs/Python/Python313/python.exe “d:/Dhan Practice/dhan_kbs_backtest_dhan_api.py”
Codebase Version 2.8 : Solved - Strike Selection Issue
-----Logged into Dhan-----
reading existing file all_instrument 2025-10-29.csv
Got the instrument file
— Starting MACD Crossover Backtest (using Dhan API) —
Period: 2023-10-30 to 2025-10-29
NOTE: This backtest uses the Dhan API for historical data.
Ensure your ‘Dhan_Tradehull’ class methods support ‘start_date’ and ‘end_date’ arguments.

— Pre-fetching and processing daily data for all stocks —

CRITICAL ERROR: ‘get_historical_data’ does not support ‘start_date’/‘end_date’.
Please modify your Dhan_Tradehull class to handle these arguments for backtesting.

import pdb
from Dhan_Tradehull import Tradehull
import pandas as pd
import talib
import numpy as np
import pandas_ta as ta
import datetime
import os
import time
import json

— Load Credentials & Initialize Dhan API —

try:
with open(‘config.json’, ‘r’) as f:
config = json.load(f)
client_id = config[‘client_id’]
access_token = config[‘access_token’]
except FileNotFoundError:
print(“Error: config.json not found. Please create it with your client_id and access_token.”)
exit()

tsl = Tradehull(client_id, access_token)

— Load Stock List from stocks.json —

stocks = [‘CIPLA’, ‘SBIN’, ‘MARUTI’, ‘BAJAJ-AUTO’, ‘DRREDDY’, ‘AXISBANK’, ‘EICHERMOT’,‘ADANIPORTS’, ‘WIPRO’, ‘ONGC’, ‘NESTLEIND’, ‘NTPC’, ‘POWERGRID’, ‘ETERNAL’,‘SUNPHARMA’, ‘BEL’, ‘ULTRACEMCO’, ‘ITC’, ‘TATACONSUM’, ‘HDFCBANK’, ‘HCLTECH’,‘TRENT’, ‘HINDUNILVR’, ‘M&M’, ‘ICICIBANK’, ‘JIOFIN’, ‘LT’, ‘RELIANCE’,‘INDIGO’, ‘COALINDIA’, ‘INFY’, ‘BAJFINANCE’, ‘ASIANPAINT’, ‘KOTAKBANK’,‘GRASIM’, ‘ADANIENT’, ‘MAXHEALTH’, ‘APOLLOHOSP’, ‘SBILIFE’, ‘BHARTIARTL’,‘HINDALCO’, ‘BAJAJFINSV’, ‘TECHM’, ‘TITAN’, ‘SHRIRAMFIN’,‘JSWSTEEL’, ‘HDFCLIFE’, ‘TCS’, ‘TATASTEEL’, ‘ABB’, ‘ADANIENSOL’,‘ADANIGREEN’, ‘ADANIPOWER’, ‘AMBUJACEM’, ‘BAJAJHFL’, ‘BAJAJHLDNG’,‘BANKBARODA’, ‘BOSCHLTD’, ‘BPCL’, ‘BRITANNIA’, ‘CANBK’, ‘CGPOWER’,‘CHOLAFIN’, ‘DIVISLAB’, ‘DLF’, ‘DMART’, ‘ENRIN’, ‘GAIL’, ‘GODREJCP’, ‘HAL’,‘HAVELLS’, ‘HINDZINC’, ‘HYUNDAI’, ‘ICICIGI’, ‘INDHOTEL’, ‘IOC’, ‘IRFC’,‘JINDALSTEL’, ‘JSWENERGY’, ‘LICI’, ‘LODHA’, ‘LTIM’, ‘MAZDOCK’, ‘MOTHERSON’,‘NAUKRI’, ‘NIFTY 100’, ‘PFC’, ‘PIDILITIND’, ‘PNB’, ‘RECLTD’, ‘SHREECEM’,‘SIEMENS’, ‘SOLARINDS’, ‘TATAPOWER’, ‘TORNTPHARM’, ‘TVSMOTOR’, ‘UNITDSPR’,‘VBL’, ‘VEDL’, ‘ZYDUSLIFE’, ‘360ONE’, ‘ABCAPITAL’, ‘ACC’, ‘ALKEM’,‘APLAPOLLO’, ‘ASHOKLEY’, ‘ASTRAL’, ‘ATGL’, ‘AUBANK’, ‘AUROPHARMA’,‘BANKINDIA’, ‘BDL’, ‘BHARATFORG’, ‘BHARTIHEXA’, ‘BHEL’, ‘BIOCON’,‘BLUESTARCO’, ‘BSE’, ‘COCHINSHIP’, ‘COFORGE’, ‘COLPAL’, ‘CONCOR’,‘COROMANDEL’, ‘CUMMINSIND’, ‘DABUR’, ‘DIXON’, ‘EXIDEIND’, ‘FEDERALBNK’,‘FORTIS’, ‘GLENMARK’, ‘GMRAIRPORT’, ‘GODFRYPHLP’, ‘GODREJPROP’, ‘HDFCAMC’,‘HEROMOTOCO’, ‘HINDPETRO’, ‘HUDCO’, ‘IDEA’, ‘IDFCFIRSTB’, ‘IGL’, ‘INDIANB’,‘INDUSINDBK’, ‘INDUSTOWER’, ‘IRB’, ‘IRCTC’, ‘IREDA’, ‘ITCHOTELS’,‘JUBLFOOD’, ‘KALYANKJIL’, ‘KEI’, ‘KPITTECH’, ‘LICHSGFIN’, ‘LTF’, ‘LUPIN’,‘M&MFIN’, ‘MANKIND’, ‘MARICO’, ‘MFSL’, ‘MOTILALOFS’, ‘MPHASIS’, ‘MRF’,‘MUTHOOTFIN’, ‘NATIONALUM’, ‘NHPC’, ‘NIFTY 200’, ‘NMDC’, ‘NTPCGREEN’,‘NYKAA’, ‘OBEROIRLTY’, ‘OFSS’, ‘OIL’, ‘PAGEIND’, ‘PATANJALI’, ‘PAYTM’,‘PERSISTENT’, ‘PHOENIXLTD’, ‘PIIND’, ‘POLICYBZR’, ‘POLYCAB’, ‘POWERINDIA’,‘PREMIERENE’, ‘PRESTIGE’, ‘RVNL’, ‘SAIL’, ‘SBICARD’, ‘SONACOMS’, ‘SRF’,‘SUPREMEIND’, ‘SUZLON’, ‘SWIGGY’, ‘TATACOMM’, ‘TATAELXSI’, ‘TATATECH’,‘TIINDIA’, ‘TORNTPOWER’, ‘UNIONBANK’, ‘UPL’, ‘VMM’, ‘VOLTAS’, ‘WAAREEENER’,‘YESBANK’]
stocks = [‘CIPLA’, ‘SBIN’, ‘MARUTI’, ‘BAJAJ-AUTO’, ‘DRREDDY’, ‘AXISBANK’, ‘EICHERMOT’,‘ADANIPORTS’, ‘WIPRO’, ‘ONGC’, ‘NESTLEIND’, ‘NTPC’, ‘POWERGRID’, ‘ETERNAL’,‘SUNPHARMA’, ‘BEL’, ‘ULTRACEMCO’, ‘ITC’, ‘TATACONSUM’, ‘HDFCBANK’, ‘HCLTECH’,‘TRENT’, ‘HINDUNILVR’, ‘M&M’, ‘ICICIBANK’, ‘JIOFIN’, ‘LARTUBRO’, ‘RELIANCE’,‘INDIGO’, ‘COALINDIA’, ‘INFY’, ‘BAJFINANCE’, ‘ASIANPAINT’, ‘KOTAKBANK’,‘GRASIM’, ‘ADANIENT’, ‘MAXHEALTH’, ‘APOLLOHOSP’, ‘SBILIFE’, ‘BHARTIARTL’,‘HINDALCO’, ‘BAJAJFINSV’, ‘TECHM’, ‘TITAN’, ‘SHRIRAMFIN’,‘JSWSTEEL’, ‘HDFCLIFE’, ‘TCS’, ‘TATASTEEL’, ‘ABB’, ‘ADANIENSOL’,‘ADANIGREEN’, ‘ADANIPOWER’, ‘AMBUJACEM’, ‘BAJAJHFL’, ‘BAJAJHLDNG’,‘BANKBARODA’, ‘BOSCHLTD’, ‘BPCL’, ‘BRITANNIA’, ‘CANBK’, ‘CGPOWER’,‘CHOLAFIN’, ‘DIVISLAB’, ‘DLF’, ‘DMART’, ‘ENRIN’, ‘GAIL’, ‘GODREJCP’, ‘HAL’,‘HAVELLS’, ‘HINDZINC’, ‘HYUNDAI’, ‘ICICIGI’, ‘INDHOTEL’, ‘IOC’, ‘IRFC’,‘JINDALSTEL’, ‘JSWENERGY’, ‘LICI’, ‘LODHA’, ‘LTIM’, ‘MAZDOCK’, ‘MOTHERSON’,‘NAUKRI’, ‘NIFTY 100’, ‘PFC’, ‘PIDILITIND’, ‘PNB’, ‘RECLTD’, ‘SHREECEM’,‘SIEMENS’, ‘SOLARINDS’, ‘TATAPOWER’, ‘TORNTPHARM’, ‘TVSMOTOR’, ‘UNITDSPR’,‘VBL’, ‘VEDL’, ‘ZYDUSLIFE’, ‘360ONE’, ‘ABCAPITAL’, ‘ACC’, ‘ALKEM’,‘APLAPOLLO’, ‘ASHOKLEY’, ‘ASTRAL’, ‘ATGL’, ‘AUBANK’, ‘AUROPHARMA’,‘BANKINDIA’, ‘BDL’, ‘BHARATFORG’, ‘BHARTIHEXA’, ‘BHEL’, ‘BIOCON’,‘BLUESTARCO’, ‘BSE’, ‘COCHINSHIP’, ‘COFORGE’, ‘COLPAL’, ‘CONCOR’,‘COROMANDEL’, ‘CUMMINSIND’, ‘DABUR’, ‘DIXON’, ‘EXIDEIND’, ‘FEDERALBNK’,‘FORTIS’, ‘GLENMARK’, ‘GMRAIRPORT’, ‘GODFRYPHLP’, ‘GODREJPROP’, ‘HDFCAMC’,‘HEROMOTOCO’, ‘HINDPETRO’, ‘HUDCO’, ‘IDEA’, ‘IDFCFIRSTB’, ‘IGL’, ‘INDIANB’,‘INDUSINDBK’, ‘INDUSTOWER’, ‘IRB’, ‘IRCTC’, ‘IREDA’, ‘ITCHOTELS’,‘JUBLFOOD’, ‘KALYANKJIL’, ‘KEI’, ‘KPITTECH’, ‘LICHSGFIN’, ‘LTF’, ‘LUPIN’,‘M&MFIN’, ‘MANKIND’, ‘MARICO’, ‘MFSL’, ‘MOTILALOFS’, ‘MPHASIS’, ‘MRF’,‘MUTHOOTFIN’, ‘NATIONALUM’, ‘NHPC’, ‘NIFTY 200’, ‘NMDC’, ‘NTPCGREEN’,‘NYKAA’, ‘OBEROIRLTY’, ‘OFSS’, ‘OIL’, ‘PAGEIND’, ‘PATANJALI’, ‘PAYTM’,‘PERSISTENT’, ‘PHOENIXLTD’, ‘PIIND’, ‘POLICYBZR’, ‘POLYCAB’, ‘POWERINDIA’,‘PREMIERENE’, ‘PRESTIGE’, ‘RVNL’, ‘SAIL’, ‘SBICARD’, ‘SONACOMS’, ‘SRF’,‘SUPREMEIND’, ‘SUZLON’, ‘SWIGGY’, ‘TATACOMM’, ‘TATAELXSI’, ‘TATATECH’,‘TIINDIA’, ‘TORNTPOWER’, ‘UNIONBANK’, ‘UPL’, ‘VMM’, ‘VOLTAS’, ‘WAAREEENER’,‘YESBANK’]

— Backtesting Configuration —

today = datetime.date.today()

IMPORTANT: Dhan API may have limits on historical data, especially for intraday timeframes (often 60-90 days).

Requesting 5 years of intraday data may not be supported and could lead to errors or incomplete results.

start_period = today - datetime.timedelta(days=2*365) # Approx 2 years
BACKTEST_START_DATE = start_period.strftime(“%Y-%m-%d”)
BACKTEST_END_DATE = today.strftime(“%Y-%m-%d”)
INITIAL_CAPITAL = 100000

— Strategy Configuration (from live script) —

ATR_PERIOD = 14
ATR_MULTIPLIER = 1.5
RISK_PER_TRADE_PERCENT = 0.02
MAX_RISK_PER_TRADE_RUPEES = 500

def run_macd_backtest():
“”"
Backtests the MACD crossover strategy using historical data from the Dhan API.
“”"
print(“— Starting MACD Crossover Backtest (using Dhan API) —”)
print(f"Period: {BACKTEST_START_DATE} to {BACKTEST_END_DATE}")
print(“NOTE: This backtest uses the Dhan API for historical data.”)
print(“Ensure your ‘Dhan_Tradehull’ class methods support ‘start_date’ and ‘end_date’ arguments.”)

capital = INITIAL_CAPITAL
open_positions = {} # Stores {symbol: {'entry_price', 'entry_time', 'stop_loss_price', 'quantity', 'direction'}}
trade_log = []

# --- Pre-fetch and process daily data for Supertrend and price filtering ---
print("\n--- Pre-fetching and processing daily data for all stocks ---")
daily_data_cache = {}
for stock_symbol in stocks:
    try:
        # Fetch daily data for the entire backtest period
        # NOTE: Your get_historical_data function must accept start_date and end_date.
        daily_hist_data = tsl.get_historical_data(stock_symbol, 'NSE', 'day',
                                                  start_date=BACKTEST_START_DATE,
                                                  end_date=BACKTEST_END_DATE)
        
        if daily_hist_data is None or len(daily_hist_data) < ATR_PERIOD + 2:
            print(f"Not enough daily data for {stock_symbol}. Skipping for daily filters.")
            continue
        
        daily_hist_data.ta.supertrend(length=ATR_PERIOD, multiplier=3.0, append=True)
        st_col = f'SUPERT_{ATR_PERIOD}_3.0'
        daily_hist_data.rename(columns={st_col: 'supertrend'}, inplace=True)
        
        # Convert timestamp to date for indexing
        daily_hist_data['date_index'] = pd.to_datetime(daily_hist_data['timestamp']).dt.date
        daily_hist_data.set_index('date_index', inplace=True)
        daily_data_cache[stock_symbol] = daily_hist_data
        print(f"  Cached daily data for {stock_symbol}")
    except TypeError:
         print("\nCRITICAL ERROR: 'get_historical_data' does not support 'start_date'/'end_date'.")
         print("Please modify your Dhan_Tradehull class to handle these arguments for backtesting.")
         return
    except Exception as e:
        print(f"Error pre-fetching daily data for {stock_symbol}: {e}")

if not daily_data_cache:
    print("No daily data cached. Cannot run backtest with daily filters.")
    return

# Generate a list of all trading days in the backtest period
all_trading_days = pd.bdate_range(start=BACKTEST_START_DATE, end=BACKTEST_END_DATE)

# --- Main Backtest Loop: Iterate day by day ---
for current_date in all_trading_days:
    current_date_str = current_date.strftime("%Y-%m-%d")
    current_date_obj = current_date.date()
    print(f"\n--- Processing Day: {current_date_str} ---")

    eligible_stocks_for_day = []
    # 1. Apply Daily Filters (Price and Supertrend)
    for stock_symbol in daily_data_cache.keys():
        if stock_symbol not in daily_data_cache:
            continue

        daily_df = daily_data_cache[stock_symbol]
        
        if current_date_obj not in daily_df.index:
            continue

        current_day_data = daily_df.loc[current_date_obj]
        
        # Price Filter (25 to 5000)
        if not (25 < current_day_data['close'] < 5000):
            continue
        
        # Supertrend Filter (Price > Supertrend)
        if pd.isna(current_day_data['supertrend']):
            continue # Supertrend not calculated for this day
        
        if current_day_data['close'] > current_day_data['supertrend']:
            eligible_stocks_for_day.append(stock_symbol)

    if not eligible_stocks_for_day and not open_positions:
        print(f"  No eligible stocks and no open positions for {current_date_str}. Skipping to next day.")
        continue

    # 2. Process Open Positions for Exits (SL, MACD Crossover, Trailing SL)
    for symbol, trade_info in list(open_positions.items()):
        try:
            # Fetch 15-min data for the current day for the open position
            # NOTE: Your get_intraday_data function must accept start_date and end_date.
            hist_data_15min = tsl.get_intraday_data(symbol, 'NSE', 15,
                                                   start_date=current_date_str,
                                                   end_date=current_date_str)
            
            if hist_data_15min is None or hist_data_15min.empty:
                continue

            hist_data_15min.sort_values('timestamp', inplace=True)

            macd, macdsignal, _ = talib.MACD(hist_data_15min['close'], fastperiod=12, slowperiod=26, signalperiod=9)
            atr = talib.ATR(hist_data_15min['high'], hist_data_15min['low'], hist_data_15min['close'], timeperiod=ATR_PERIOD)
            
            hist_data_15min['macd'] = macd
            hist_data_15min['macdsignal'] = macdsignal
            hist_data_15min['atr'] = atr
            hist_data_15min.dropna(inplace=True)

            if hist_data_15min.empty:
                continue

            # Iterate through candles of the current day for exit logic
            for i in range(1, len(hist_data_15min)):
                current_candle = hist_data_15min.iloc[i]
                previous_candle = hist_data_15min.iloc[i-1]

                # SL Hit
                if current_candle['low'] <= trade_info['stop_loss_price']:
                    exit_price = trade_info['stop_loss_price']
                    pnl = (exit_price - trade_info['entry_price']) * trade_info['quantity']
                    trade_log.append({
                        'symbol': symbol, 'direction': 'BUY',
                        'entry_time': trade_info['entry_time'], 'entry_price': trade_info['entry_price'],
                        'exit_time': current_candle['timestamp'], 'exit_price': exit_price,
                        'pnl': pnl, 'exit_reason': 'SL', 'quantity': trade_info['quantity']
                    })
                    print(f"  -> EXIT SL: {symbol} on {current_candle['timestamp'].strftime('%Y-%m-%d %H:%M')} at {exit_price:.2f}")
                    del open_positions[symbol]
                    break

                # MACD Crossover Exit
                if previous_candle['macd'] > previous_candle['macdsignal'] and current_candle['macd'] < current_candle['macdsignal']:
                    exit_price = current_candle['close']
                    pnl = (exit_price - trade_info['entry_price']) * trade_info['quantity']
                    trade_log.append({
                        'symbol': symbol, 'direction': 'BUY',
                        'entry_time': trade_info['entry_time'], 'entry_price': trade_info['entry_price'],
                        'exit_time': current_candle['timestamp'], 'exit_price': exit_price,
                        'pnl': pnl, 'exit_reason': 'MACD_EXIT', 'quantity': trade_info['quantity']
                    })
                    print(f"  -> EXIT MACD: {symbol} on {current_candle['timestamp'].strftime('%Y-%m-%d %H:%M')} at {exit_price:.2f}")
                    del open_positions[symbol]
                    break

                # Trail Stop-Loss
                new_sl = current_candle['close'] - (float(current_candle['atr']) * ATR_MULTIPLIER)
                if new_sl > trade_info['stop_loss_price']:
                    open_positions[symbol]['stop_loss_price'] = new_sl
        except TypeError:
            print("\nCRITICAL ERROR: 'get_intraday_data' does not support 'start_date'/'end_date'.")
            print("Please modify your Dhan_Tradehull class to handle these arguments for backtesting.")
            return
        except Exception as e:
            print(f"Error managing open position for {symbol} on {current_date_str}: {e}")

    # 3. Scan for New Entries
    for stock_symbol in eligible_stocks_for_day:
        if stock_symbol in open_positions:
            continue

        try:
            # Fetch 15-min data for the current day for new entries
            hist_data = tsl.get_intraday_data(stock_symbol, 'NSE', 15,
                                             start_date=current_date_str,
                                             end_date=current_date_str)

            if hist_data is None or len(hist_data) < 30:
                continue

            hist_data.sort_values('timestamp', inplace=True)

            macd, macdsignal, _ = talib.MACD(hist_data['close'], fastperiod=12, slowperiod=26, signalperiod=9)
            atr = talib.ATR(hist_data['high'], hist_data['low'], hist_data['close'], timeperiod=ATR_PERIOD)
            rsi = talib.RSI(hist_data['close'], timeperiod=14)

            hist_data['macd'] = macd
            hist_data['macdsignal'] = macdsignal
            hist_data['atr'] = atr
            hist_data['rsi'] = rsi
            hist_data.dropna(inplace=True)

            if hist_data.empty:
                continue

            # Iterate through candles of the current day for entry logic
            for i in range(1, len(hist_data)):
                current_candle = hist_data.iloc[i]
                previous_candle = hist_data.iloc[i-1]

                if current_candle['atr'] <= 0 or pd.isna(current_candle['atr']):
                    continue

                is_crossover = previous_candle['macd'] < previous_candle['macdsignal'] and current_candle['macd'] > current_candle['macdsignal']
                is_price_action = current_candle['close'] > previous_candle['close']
                is_rsi_valid = 58 < current_candle['rsi'] < 70

                if is_crossover and is_price_action and is_rsi_valid:
                    entry_price = current_candle['close']
                    stop_loss_price = entry_price - (float(current_candle['atr']) * ATR_MULTIPLIER)
                    risk_per_share = entry_price - stop_loss_price

                    if risk_per_share <= 0:
                        continue

                    risk_from_capital = capital * RISK_PER_TRADE_PERCENT
                    risk_amount = min(risk_from_capital, MAX_RISK_PER_TRADE_RUPEES)
                    quantity = int(risk_amount / risk_per_share)

                    if quantity > 0:
                        open_positions[stock_symbol] = {
                            'entry_price': entry_price,
                            'entry_time': current_candle['timestamp'],
                            'stop_loss_price': stop_loss_price,
                            'quantity': quantity,
                            'direction': 'BUY'
                        }
                        capital -= (entry_price * quantity)
                        print(f"  -> ENTER BUY: {stock_symbol} on {current_candle['timestamp'].strftime('%Y-%m-%d %H:%M')} at {entry_price:.2f}, Qty: {quantity}, SL: {stop_loss_price:.2f}")
                        break

        except Exception as e:
            print(f"Error scanning {stock_symbol} on {current_date_str} for new entries: {e}")

# --- Close any remaining open positions at the end of the backtest period ---
print("\n--- Closing all remaining open positions at the end of backtest ---")
for symbol, trade_info in list(open_positions.items()):
    try:
        last_data = tsl.get_intraday_data(symbol, 'NSE', 15,
                                         start_date=BACKTEST_END_DATE,
                                         end_date=BACKTEST_END_DATE)
        exit_price = last_data['close'].iloc[-1] if last_data is not None and not last_data.empty else trade_info['entry_price']
        
        pnl = (exit_price - trade_info['entry_price']) * trade_info['quantity']
        trade_log.append({
            'symbol': symbol, 'direction': 'BUY',
            'entry_time': trade_info['entry_time'], 'entry_price': trade_info['entry_price'],
            'exit_time': datetime.datetime.strptime(BACKTEST_END_DATE + " 15:30:00", "%Y-%m-%d %H:%M:%S"),
            'exit_price': exit_price,
            'pnl': pnl, 'exit_reason': 'EOD_CLOSE', 'quantity': trade_info['quantity']
        })
        print(f"  -> EOD CLOSE: {symbol} at {exit_price:.2f}")
        del open_positions[symbol]
    except Exception as e:
        print(f"Error closing remaining position for {symbol}: {e}")


# --- Generate and Print Report ---
if not trade_log:
    print("\nNo trades were executed during the backtest period.")
    return

report_df = pd.DataFrame(trade_log)

report_df['pnl_percent'] = (report_df['pnl'] / (report_df['entry_price'] * report_df['quantity'])) * 100

total_pnl = report_df['pnl'].sum()
total_trades = len(report_df)
winning_trades = report_df[report_df['pnl'] > 0]
losing_trades = report_df[report_df['pnl'] <= 0]

win_rate = (len(winning_trades) / total_trades) * 100 if total_trades > 0 else 0

total_profit = winning_trades['pnl'].sum()
total_loss = abs(losing_trades['pnl'].sum())

profit_factor = total_profit / total_loss if total_loss > 0 else float('inf')
avg_win = winning_trades['pnl'].mean() if not winning_trades.empty else 0
avg_loss = abs(losing_trades['pnl'].mean()) if not losing_trades.empty else 0
risk_reward_ratio = avg_win / avg_loss if avg_loss > 0 else float('inf')

print("\n\n--- Backtest Report (Dhan API) ---")
print("="*50)
print(f"Period:                 {BACKTEST_START_DATE} to {BACKTEST_END_DATE}")
print(f"Initial Capital:        ₹{INITIAL_CAPITAL:,.2f}")
print(f"Final Capital:          ₹{(INITIAL_CAPITAL + total_pnl):,.2f}")
print("-" * 50)
print(f"Total Net P&L:          ₹{total_pnl:,.2f}")
print(f"Total Trades:           {total_trades}")
print(f"Win Rate:               {win_rate:.2f}%")
print(f"Profit Factor:          {profit_factor:.2f}")
print(f"Average Win / Loss:     ₹{avg_win:,.2f} / ₹{avg_loss:,.2f}")
print(f"Observed R:R Ratio:     {risk_reward_ratio:.2f}:1")
print("-" * 50)
print(f"Total Profit from Wins: ₹{total_profit:,.2f} ({len(winning_trades)} trades)")
print(f"Total Loss from Losses: ₹{total_loss:,.2f} ({len(losing_trades)} trades)")
print("="*50)

report_df.to_csv("macd_backtest_report_dhan_api.csv", index=False)
print("\nDetailed trade log saved to 'macd_backtest_report_dhan_api.csv'")

if name == “main”:
run_macd_backtest()
`

Hello @DKS ,

Thank you for bringing this up. @Tradehull_Imran may be able to assist better here.

Hi @DKS ,

We’ll be updating and releasing a new version of the codebase soon, which will include support for fetching 5 years of historical data.

1 Like