Say to time Algos trading bye bye

Dear all ,

mene badi mahnat karke ye alogo code banaya he

NIFTY 200 Stock EQ

:bar_chart: Timeframe: 5-minute intraday chart,:repeat_button: Entry Base: EMA 9 & EMA 26 crossover,

:white_check_mark: Trade Confirmation Filters:Volume > 20-candle average,RSI > 56 (BUY) / RSI < 44 (SELL),ADX > 23 (strong trend),DI+ > DI− (BUY) / DI− > DI+ (SELL)

:stop_sign: Stop Loss: Recent swing low (BUY) / swing high (SELL),:bullseye: Target: Stop-loss ke equal (1:1 Risk-Reward)

:counterclockwise_arrows_button: Exit Conditions: Target hit, Stop-loss hit, Opposite EMA crossover (inverse signal)

:chart_decreasing: Trailing SL: EMA 26 + ATR based (profit me aane ke baad)

:gear: Risk & Control: Fixed loss per trade, Max daily trades limit

Ye code sare conformation ke sath trade me enter hota hu fir bhi din ke always 4 ke 4 trade loss me close hota hai . muje samaj nahi ata ki code wrong hai / strategy , koi bata sakata hai to usko mera aabhar manata hu , din jara sa profit nahi aur itane sare loss & fix loss (daily loss + data api + brokerage + 1 jan se static IP price ) me ye sochata hu ki EQ me hal hai to Option me kya hota hoga , kya app profit me hote ho algo trading me ? me mera code share kiya he

So Say to time Algos trading bye bye

import time
import traceback
from Dhan_Tradehull import Tradehull
import pandas as pd
from pprint import pprint
import talib
import pandas_ta as ta
import xlwings as xw
from datetime import datetime, time as dt_time
import json
import os
import winsound
import requests
import joblib # Import for AI Model

# --- Load Credentials from config.json ---
script_dir = os.path.dirname(os.path.abspath(__file__))
config_path = os.path.join(script_dir, 'config.json')
with open(config_path, 'r') as f:
    creds = json.load(f)

def log_error_to_json(stock_name, error_context, error):
    """Logs an error to a JSON file and sends a Telegram alert."""
    error_log_file = "errors.json"
    timestamp = str(datetime.now())
    error_message_full = str(error)
    traceback_full = traceback.format_exc()
    new_error = {
        "timestamp": timestamp,
        "stock": stock_name,
        "context": error_context,
        "error_message": error_message_full,
        "traceback": traceback_full
    }
    try:
        with open(error_log_file, 'r') as f:
            errors = json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        errors = []
    errors.append(new_error)
    with open(error_log_file, 'w') as f:
        json.dump(errors, f, indent=4)

   
# --- Load Watchlist from stocks.json ---
def load_stocks_from_json(filename="stocks.json"):
    """Loads the stock list from a JSON file located in the script's directory."""
    try:
        stocks_path = os.path.join(script_dir, filename)
        with open(stocks_path, 'r') as f:
            data = json.load(f)
        return data.get('stocks', [])
    except FileNotFoundError:
        print(f"Error: {filename} not found. Please ensure it exists in the script directory.")
        return []

pre_market_watchlist = load_stocks_from_json()

watchlist = []
print("--- Starting Pre-Market Scan ---")
try:
    if not pre_market_watchlist:
        print("!!! Error: Watchlist is empty. Please check stocks.json !!!")
    else:
        print(f"Fetching LTP for {len(pre_market_watchlist)} stocks in one go...")
        all_ltp_data = tsl.get_ltp_data(names=pre_market_watchlist)

        if not all_ltp_data:
            print("Could not fetch any LTP data. Exiting pre-market scan.")
        else:
            for name, ltp in all_ltp_data.items():
                if ltp is None:
                    print(f"\tCould not get LTP for {name}, skipping.")
                    continue

                # Price Filter
                if 150 < ltp < 5000:
                    watchlist.append(name)
                    print(f"\t>>> Selected {name} for trading (Price: {ltp:.2f})")
                else:
                    print(f"\tSkipping {name} due to price filter (Price: {ltp:.2f})")
except Exception as e:
    print("\n" + "!"*60)
    print(f"--- ERROR during pre-market scan: {e} ---")
    print("!"*60 + "\n")
    log_error_to_json("N/A", "Pre-Market Scan", e)
print(f"--- Pre-Market Scan Complete. {len(watchlist)} stocks selected. ---")
print(f"Final Watchlist: {watchlist}\n")

CONFIG = {
    "leverage": 5.0,             # Intraday leverage provided by the broker
    "fixed_loss_per_trade": 100, # Risk a fixed 100 INR per trade
    "max_daily_trades": 4,      # Maximum number of trades to take per day
    "max_risk_pct": 0.009,       # Max risk as % of LTP. 0.006 = 0.6%. Trade skipped if SL is wider.
    "rr_ratio": 1,               # Risk:Reward Ratio for target calculation (e.g., 1:1)
    "trade_direction": "BOTH"    # "LONG" for buys only, "SHORT" for sells only, "BOTH" for both
	,"min_trade_value_inr": 4000, # Minimum trade value in INR to consider taking a trade
    "max_daily_loss_inr": 250,   # Stop trading if daily loss exceeds this amount (e.g., 250 INR)
    "target_daily_profit": 200,  # Target daily profit to stop trading
    "swing_lookback_period": 6  # Lookback period for calculating swing low/high for SL
	,"tsl_activation_r": 0.5      # Activate TSL after price moves 0.5R in profit
}



single_order     = {'name':None, 'date':None , 'entry_time': None, 'entry_price': None, 'buy_sell': None, 'qty': None, 'sl': None,
                    'exit_time': None, 'exit_price': None, 'pnl': None, 'remark': None, 'traded':None, 'entry_trigger': None,
                    'entry_rsi': None, 'entry_volume': None, 'entry_atr': None, # Entry indicators
                    'mfe': 0, 'mae': 0, 'mfe_total': 0, 'mae_total': 0, 'breakeven_triggered': False} 
orderbook        = {}

excel_file_path = 'Live Trade Data.xlsx'
try:
    wb = xw.Book(excel_file_path)
    # Ensure sheets exist
    if 'Live_Trading' not in [s.name for s in wb.sheets]:
        wb.sheets.add('Live_Trading')
    if 'completed_orders' not in [s.name for s in wb.sheets]:
        wb.sheets.add('completed_orders')
    if 'Skipped_Trades' not in [s.name for s in wb.sheets]:
        wb.sheets.add('Skipped_Trades')
except FileNotFoundError:
    wb = xw.Book()
    wb.sheets.add('Live_Trading')
    wb.sheets.add('completed_orders')
    wb.sheets.add('Skipped_Trades')
    wb.sheets['Sheet1'].delete() # remove default sheet
    wb.save(excel_file_path)

live_Trading = wb.sheets['Live_Trading']
completed_orders_sheet = wb.sheets['completed_orders']
skipped_trades_sheet = wb.sheets['Skipped_Trades']
completed_orders = []
skipped_trades = []

live_Trading.range("A2:Z100").value = None

for name in watchlist:
    orderbook[name] = single_order.copy()

print("Performing initial fill of the Live_Trading sheet with all watchlist stocks...")
initial_df = pd.DataFrame(orderbook).T
live_Trading.range('A1').value = initial_df

trades_today_count = 0
current_day_tracker = datetime.now().date()

def save_state():
    """Saves the current state of the bot to a JSON file."""
    state = {
        "orderbook": orderbook,
        "completed_orders": completed_orders,
        "skipped_trades": skipped_trades,
        "trades_today_count": trades_today_count,
        "current_day_tracker": str(current_day_tracker)
    }
    with open("EMA_stratergy_state.json", "w") as f:
        json.dump(state, f, indent=4)
    update_excel_sheets()
    print("--- State Saved ---")
def load_state():
    """Loads the bot's state from a JSON file."""
    try:
        with open("EMA_stratergy_state.json") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return None
def update_excel_sheets():
    global wb, live_Trading, completed_orders_sheet, skipped_trades_sheet
    try:
        orderbook_df = pd.DataFrame(orderbook).T
        if not orderbook_df.empty and 'traded' in orderbook_df.columns and not orderbook_df['traded'].isnull().all():
            orderbook_df_sorted = orderbook_df.sort_values(by='traded', ascending=False, na_position='last')
            live_Trading.range('A1').value = orderbook_df_sorted
        else:
            live_Trading.range('A1').value = orderbook_df
            
        completed_orders_df = pd.DataFrame(completed_orders)

        for col in ['mfe', 'mae', 'mfe_total', 'mae_total']:
            if col not in completed_orders_df.columns:
                completed_orders_df[col] = 0
            else:
                completed_orders_df[col] = completed_orders_df[col].fillna(0)

        if completed_orders_sheet.range('A1').value is None:
            completed_orders_sheet.range('A1').value = completed_orders_df
        else:
            last_row = completed_orders_sheet.range('A' + str(completed_orders_sheet.cells.last_cell.row)).end('up').row
            existing_count = max(0, last_row - 1)
            if len(completed_orders_df) > existing_count:
                new_rows = completed_orders_df.iloc[existing_count:]
                completed_orders_sheet.range(f'A{last_row + 1}').options(index=False, header=False).value = new_rows
            elif len(completed_orders_df) < existing_count:
                completed_orders_sheet.clear_contents()
                completed_orders_sheet.range('A1').value = completed_orders_df

        skipped_trades_df = pd.DataFrame(skipped_trades)
        skipped_trades_sheet.range('A1').value = skipped_trades_df
        wb.save()

    except Exception as e:
        print("\n" + "!"*60)
        print(f"--- ERROR updating Excel sheets: {e} ---")
        print("!"*60 + "\n")
        log_error_to_json("N/A", "update_excel_sheets", e)
              if "RPC server" in str(e) or "disconnected" in str(e):
            print("--- Excel disconnected. Attempting to reconnect... ---")
            try:
                wb = xw.Book(excel_file_path)
                live_Trading = wb.sheets['Live_Trading']
                completed_orders_sheet = wb.sheets['completed_orders']
                skipped_trades_sheet = wb.sheets['Skipped_Trades']
                print("--- Excel Reconnected Successfully. ---")
            except Exception as rec_e:
                print(f"--- Reconnection failed: {rec_e} ---")

def calculate_indicators(df):
    df['ema9'] = talib.EMA(df['close'], timeperiod=9)
    df['ema26'] = talib.EMA(df['close'], timeperiod=26)
    df['rsi14'] = talib.RSI(df['close'], timeperiod=14)
    df['atr14'] = talib.ATR(df['high'], df['low'], df['close'], timeperiod=14)
    df['volume_avg_20'] = df['volume'].rolling(window=20).mean()
    df['adx14'] = talib.ADX(df['high'], df['low'], df['close'], timeperiod=14)
    df['plus_di'] = talib.PLUS_DI(df['high'], df['low'], df['close'], timeperiod=14) # Trend Direction
    df['minus_di'] = talib.MINUS_DI(df['high'], df['low'], df['close'], timeperiod=14) # Trend Direction
    return df
def get_entry_signal(df):
 
    Returns 'BUY', 'SELL', or None.
    """
    last_candle = df.iloc[-2]
    prev_candle = df.iloc[-3]

    is_bullish_crossover = prev_candle['ema9'] < prev_candle['ema26'] and last_candle['ema9'] > last_candle['ema26']
    if CONFIG["trade_direction"] in ["LONG", "BOTH"] and is_bullish_crossover:
        is_volume_strong = last_candle['volume'] > (last_candle['volume_avg_20'])
        is_rsi_strong = last_candle['rsi14'] > 56
        is_trend_strong = last_candle['adx14'] > 23 and last_candle['plus_di'] > last_candle['minus_di'] # ADX Direction Check
        
        if is_volume_strong and is_rsi_strong and is_trend_strong:
            print(f"--- 🔥 STRONG BUY SIGNAL (EMA Cross + Vol Spike + DI+) ---")
            return 'BUY'
 # --- SELL Signal Logic ---
    is_bearish_crossover = prev_candle['ema9'] > prev_candle['ema26'] and last_candle['ema9'] < last_candle['ema26']
    if CONFIG["trade_direction"] in ["SHORT", "BOTH"] and is_bearish_crossover:
        is_volume_strong = last_candle['volume'] > (last_candle['volume_avg_20'] * 1.5)
        is_rsi_weak = last_candle['rsi14'] < 44
        is_trend_strong = last_candle['adx14'] > 23 and last_candle['minus_di'] > last_candle['plus_di'] # ADX Direction Check
        
        if is_volume_strong and is_rsi_weak and is_trend_strong:
            print(f"--- 🔥 STRONG SELL SIGNAL (EMA Cross + Vol Spike + DI-) ---")
            return 'SELL'
            
    return None

def get_inverse_exit_signal(df):
    """
    Analyzes the last two candles for a simple EMA crossover to be used as an exit signal.
    This does NOT check for RSI, Volume, or ADX filters.
    Returns 'BUY', 'SELL', or None.
    """
    last_candle = df.iloc[-2]
    prev_candle = df.iloc[-3]
    if prev_candle['ema9'] < prev_candle['ema26'] and last_candle['ema9'] > last_candle['ema26']:
        return 'BUY'
    if prev_candle['ema9'] > prev_candle['ema26'] and last_candle['ema9'] < last_candle['ema26']:
        return 'SELL'
    return None

def calculate_sl_tg(signal, ltp, df):
      lookback = CONFIG["swing_lookback_period"]
    recent_candles = df.iloc[-(lookback + 1):-1]

    if signal == 'BUY':
        stop_loss_price = round(recent_candles['low'].min() - 0.05, 1)
        risk_per_share = ltp - stop_loss_price
        target_price = round(ltp + (risk_per_share * CONFIG["rr_ratio"]), 1)
    elif signal == 'SELL':
        stop_loss_price = round(recent_candles['high'].max() + 0.05, 1)
        risk_per_share = stop_loss_price - ltp
        target_price = round(ltp - (risk_per_share * CONFIG["rr_ratio"]), 1)
    else:
        return None, None

    return stop_loss_price, target_price

def manage_open_position(name, ltp, df):
     pass


def _log_and_skip_trade(name, context, reason, signal=None, price=None, **kwargs):
    """Helper function to log a skipped trade without saving state immediately."""
    print(f"Skipping {name}: {context} - {reason}")
    entry = {'date': str(datetime.now().date()), 'name': name, 'time': str(datetime.now().time())[:8], 'context': context, 'reason': reason, 'signal': signal, 'price': price}
    entry.update(kwargs) # Add extra indicators (RSI, ADX, etc.) if provided
    skipped_trades.append(entry)

def calculate_position_and_validate(name, ltp, stop_loss_price, signal=None):
    """Calculates quantity based on fixed loss and validates margin. Returns qty or None."""

    risk_per_share = abs(ltp - stop_loss_price)
    if risk_per_share <= 0:
        _log_and_skip_trade(name, 'Validation', f'Invalid risk per share: {risk_per_share:.2f}', signal=signal, price=ltp)
        return None
    if (risk_per_share / ltp) > CONFIG["max_risk_pct"]:
        _log_and_skip_trade(name, 'Validation', f'SL is too wide ({risk_per_share/ltp:.2%} > {CONFIG["max_risk_pct"]:.2%})', signal=signal, price=ltp)
        return None
    qty = int(CONFIG["fixed_loss_per_trade"] / risk_per_share)
    if qty == 0:
        _log_and_skip_trade(name, 'Validation', f'Qty is 0 (Risk/share {risk_per_share:.2f} too high)', signal=signal, price=ltp)
        return None
    trade_value = qty * ltp
    if trade_value < CONFIG["min_trade_value_inr"]:
        _log_and_skip_trade(name, 'Validation', f"Trade value {trade_value:.2f} < min {CONFIG['min_trade_value_inr']}", signal=signal, price=ltp)
        return None

    margin_available = tsl.get_balance()
    margin_required = ((qty * ltp) / CONFIG["leverage"]) + 300
    if margin_available < margin_required:
        _log_and_skip_trade(name, 'Validation', f'Insufficient margin (Avail: {margin_available:.2f}, Req: {margin_required:.2f})', signal=signal, price=ltp)
        return None
        return qty

def place_trade(name, buy_sell_label, qty, stop_loss_price, target_price):
    """Places entry and SL orders and updates the orderbook."""
    global trades_today_count

    # --- Final Safety Checks Before Placement ---
    if trades_today_count >= CONFIG["max_daily_trades"]:
        print(f"!!! STOPPING TRADE for {name}: Max daily trades ({CONFIG['max_daily_trades']}) limit reached. !!!")
        return

    if orderbook[name]['traded'] == "yes":
        print(f"!!! STOPPING TRADE for {name}: Position already exists. !!!")
        return
    transaction_type = 'BUY' if buy_sell_label == 'BUY' else 'SELL'
    sl_transaction_type = 'SELL' if buy_sell_label == 'BUY' else 'BUY'
    try:
        entry_orderid = tsl.order_placement(tradingsymbol=name, exchange='NSE', quantity=qty, price=0, trigger_price=0, order_type='MARKET', transaction_type=transaction_type, trade_type='MIS')
        entry_price = None
                if entry_orderid:
            time.sleep(1) # Wait 1 second
            entry_price = tsl.get_executed_price(orderid=entry_orderid)
            if not entry_price: # If still not found, retry after 2 more seconds
                time.sleep(2)
                entry_price = tsl.get_executed_price(orderid=entry_orderid)

        if not entry_price:
            if entry_orderid:
                status = tsl.get_order_status(orderid=entry_orderid)
                raise Exception(f"Order placed but price not found. Status: {status}")
            else:
                raise Exception("Order placement failed (No Order ID returned).")

        sl_orderid = tsl.order_placement(tradingsymbol=name, exchange='NSE', quantity=qty, price=0, trigger_price=stop_loss_price, order_type='STOPMARKET', transaction_type=sl_transaction_type, trade_type='MIS')

        orderbook[name].update({
            'entry_orderid': entry_orderid, 'entry_price': entry_price, 'tg': target_price,
            'sl': stop_loss_price, 'sl_orderid': sl_orderid, 'traded': "yes"
        })
        print(f"   >>> {buy_sell_label} Trade Placed for {name}: Entry @ {entry_price:.2f}, Target @ {target_price:.2f}, SL @ {stop_loss_price:.2f}")
        winsound.Beep(1000, 500) # Beep for successful order placement
        trades_today_count += 1
        print(f"--- Trade Count Updated: {trades_today_count}/{CONFIG['max_daily_trades']} ---")
        save_state()
    except Exception as e:
        print("\n" + "!"*60)
        print(f"--- ERROR IN ENTRY ORDER for {name} ---")
        print(traceback.format_exc())
        print("!"*60 + "\n")
        log_error_to_json(name, "place_trade", e)
        orderbook[name] = single_order.copy() # Reset on failure

def close_position(name, remark, exit_price):
        orderbook[name]['exit_time'] = str(datetime.now().time())[:8]
    orderbook[name]['exit_price'] = exit_price
    pnl_multiplier = 1 if orderbook[name]['buy_sell'] == 'BUY' else -1
    orderbook[name]['pnl'] = round((exit_price - orderbook[name]['entry_price']) * orderbook[name]['qty'] * pnl_multiplier, 1)
    orderbook[name]['remark'] = remark

    completed_orders.append(orderbook[name].copy())
    orderbook[name] = single_order.copy()
    if "TG_hit" in remark:
        winsound.Beep(1500, 1000) # Shorter beep
    elif "SL_hit" in remark:
        winsound.Beep(500, 1000) # Lower pitch beep for SL hit
    save_state()
print("--- Attempting to load previous state ---")
loaded_state = load_state()
if loaded_state:
    loaded_date = datetime.strptime(loaded_state.get("current_day_tracker"), '%Y-%m-%d').date()
    # Always load historical completed and skipped trades
    completed_orders = loaded_state.get("completed_orders", [])
    skipped_trades = loaded_state.get("skipped_trades", [])

    if loaded_date == datetime.now().date():
        # If it's the same day, restore the full state
        orderbook = loaded_state.get("orderbook", orderbook)
        trades_today_count = loaded_state.get("trades_today_count", trades_today_count)
        print("--- State successfully loaded for today! ---")
        # Ensure all watchlist stocks are in the orderbook
        for stock_name in watchlist:
            if stock_name not in orderbook:
                orderbook[stock_name] = single_order.copy()
            else:
                # Patch existing orders with new keys (like mfe, mae) if missing in old state
                for key, val in single_order.items():
                    if key not in orderbook[stock_name]:
                        orderbook[stock_name][key] = val
    else:
        print("--- Saved state is from a previous day. Resetting daily counters but keeping historical trades. ---")
        # Daily counters are already reset at initialization, so we just need to log this.
update_excel_sheets()
while True:
	current_dt = datetime.now()
	print(f"starting while Loop {current_dt.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
	live_broker_orderbook = tsl.get_orderbook()
	total_pnl = tsl.get_live_pnl()
	current_balance = tsl.get_balance()
	# print("--- Live Broker Orderbook ---")
	# pprint(live_broker_orderbook)
	# print("-----------------------------\n")
	print(f"Live PNL: {total_pnl}, Balance: {current_balance}")
	if current_dt.date() != current_day_tracker:
		print("New day detected. Resetting daily trade counter.")
		trades_today_count = 0
		current_day_tracker = current_dt.date()
	current_time = current_dt.time()
	# if current_time < dt_time(9, 45):
	# 	print(f"Waiting for market to open. Current time: {current_time.strftime('%H:%M:%S')}")
	# 	time.sleep(5)
	# 	continue
	# if current_time > dt_time(14, 50):
	# 	order_details = tsl.cancel_all_orders()
	# 	print(f"Market over Closing all trades !! Bye Bye See you Tomorrow", current_time)
	# 	update_excel_sheets()
	# 	save_state() # Final save before exiting
	# 	send_telegram_message(f"🛑 Market Over! Bot stopping. Final PnL: {total_pnl}")
	# 	time.sleep(1)
	# 	break

	all_ltp = tsl.get_ltp_data(names=watchlist)
	open_positions = [name for name, details in orderbook.items() if details.get('traded') == 'yes']
	if total_pnl is not None and total_pnl >= CONFIG["target_daily_profit"]:
		print(f"🎉 Daily Profit Target Hit: {total_pnl} >= {CONFIG['target_daily_profit']} INR. Closing all positions and exiting.")
		for name in open_positions:
			try:
				if orderbook[name].get('sl_orderid'):
					tsl.cancel_order(OrderID=orderbook[name]['sl_orderid'])
				bought = orderbook[name]['buy_sell'] == "BUY"
				txn_type = 'SELL' if bought else 'BUY'
				tsl.order_placement(name, 'NSE', orderbook[name]['qty'], 0, 0, 'MARKET', txn_type, 'MIS')
				exit_price = all_ltp.get(name, orderbook[name]['entry_price']) if all_ltp else orderbook[name]['entry_price']
				close_position(name, "Daily_Target_Hit", exit_price)
			except Exception as e:
				print(f"Error closing {name}: {e}")
		save_state()
		update_excel_sheets()
		break
	if trades_today_count >= CONFIG["max_daily_trades"]:
		print(f"Max daily trades ({CONFIG['max_daily_trades']}) reached. Managing open positions only.")
		if not open_positions: # If no positions are open
			print("All trades for the day are complete. Shutting down.")
			save_state()
			update_excel_sheets() # Final update before exiting
			send_telegram_message(f"🛑 Max Daily Trades Reached ({trades_today_count}). Bot stopping.")
			break # Exit the main while loop
def process_stock(name):
		print(f"Scanning        {name} {current_dt.strftime('%Y-%m-%d %H:%M:%S')}")
		try:
			if any(order.get('name') == name and order.get('date') == str(current_dt.date()) for order in completed_orders):
				return # Use return to exit the function for this stock
chart_5m = tsl.get_historical_data(tradingsymbol=name, exchange='NSE', timeframe="5")
			if chart_5m is None or len(chart_5m) < 35:
				_log_and_skip_trade(name, "Data Check", "Insufficient historical data")
				return
chart_5m = calculate_indicators(chart_5m)
			if orderbook[name]['traded'] == "yes":
				ltp = all_ltp.get(name)
				if ltp is None:
					print(f"Could not get LTP for open position {name}. Skipping management.")
					return
				bought = orderbook[name]['buy_sell'] == "BUY"
				sold = orderbook[name]['buy_sell'] == "SELL"
				current_pnl_per_share = (ltp - orderbook[name]['entry_price']) if bought else (orderbook[name]['entry_price'] - ltp)
				if current_pnl_per_share > orderbook[name]['mfe']:
					orderbook[name]['mfe'] = current_pnl_per_share
				if current_pnl_per_share < orderbook[name]['mae']:
					orderbook[name]['mae'] = current_pnl_per_share
		         orderbook[name]['mfe_total'] = round(orderbook[name]['mfe'] * orderbook[name]['qty'], 2)
				orderbook[name]['mae_total'] = round(orderbook[name]['mae'] * orderbook[name]['qty'], 2)

				last_candle = chart_5m.iloc[-2]
				prev_candle = chart_5m.iloc[-3]

				sl_hit = tsl.get_order_status(orderid=orderbook[name]['sl_orderid']) == "TRADED"
				tg_hit = (ltp >= orderbook[name]['tg']) if bought else (ltp <= orderbook[name]['tg'])

				if sl_hit:
					exit_price = tsl.get_executed_price(orderid=orderbook[name]['sl_orderid']) or orderbook[name]['sl']
					close_position(name, "Bought_SL_hit" if bought else "Sold_SL_hit", exit_price)
					return
				elif tg_hit:
					tsl.cancel_order(OrderID=orderbook[name]['sl_orderid'])
					time.sleep(1)
					txn_type = 'SELL' if bought else 'BUY'
					square_off_order = tsl.order_placement(name, 'NSE', orderbook[name]['qty'], 0, 0, 'MARKET', txn_type, 'MIS')
					exit_price = tsl.get_executed_price(orderid=square_off_order) or ltp
					close_position(name, "Bought_TG_hit" if bought else "Sold_TG_hit", exit_price)
					return
# --- INVERSE SIGNAL EXIT LOGIC ---
						entry_time_str = orderbook[name]['entry_time']
				try:
					entry_dt = datetime.combine(current_dt.date(), datetime.strptime(entry_time_str, "%H:%M:%S").time())
					seconds_since_entry = (current_dt - entry_dt).total_seconds()
				except Exception:
					seconds_since_entry = 601 # Default to allow check if time parsing fails

				if seconds_since_entry > 600:
					inverse_signal = get_inverse_exit_signal(chart_5m) # Use the simpler exit signal function
					if inverse_signal and ((bought and inverse_signal == 'SELL') or (sold and inverse_signal == 'BUY')):
						print(f"--- INVERSE SIGNAL DETECTED for {name}. Exiting position. ---")
						tsl.cancel_order(OrderID=orderbook[name]['sl_orderid'])
						time.sleep(1) # Wait for cancellation to process
						txn_type = 'SELL' if bought else 'BUY'
						square_off_order = tsl.order_placement(name, 'NSE', orderbook[name]['qty'], 0, 0, 'MARKET', txn_type, 'MIS')
						exit_price = tsl.get_executed_price(orderid=square_off_order) or ltp
						close_position(name, "Inverse_Signal_Exit", exit_price)
						return
				else: # Only trail if no other exit condition was met
					try:
						initial_risk = abs(orderbook[name]['entry_price'] - orderbook[name]['sl'])
						current_pnl_per_share = (ltp - orderbook[name]['entry_price']) if bought else (orderbook[name]['entry_price'] - ltp)
			
						if current_pnl_per_share >= (initial_risk * CONFIG["tsl_activation_r"]):
							if bought:
								new_trailing_sl = round(last_candle['ema26'] - (last_candle['atr14'] * 0.8), 1)
								# We only trail up, never down. And SL should not be above LTP.
								if new_trailing_sl > orderbook[name]['sl'] and new_trailing_sl < ltp:
									print(f"--- TRAILING SL (BUY) for {name}: Old SL: {orderbook[name]['sl']:.2f}, New SL: {new_trailing_sl:.2f} ---")
									new_sl_orderid = tsl.modify_order(order_id=orderbook[name]['sl_orderid'], quantity=orderbook[name]['qty'], price=0, trigger_price=new_trailing_sl, order_type='STOPMARKET')
									if new_sl_orderid:
										orderbook[name].update({'sl_orderid': new_sl_orderid, 'sl': new_trailing_sl})
										save_state()
							elif sold:
								new_trailing_sl = round(last_candle['ema26'] + (last_candle['atr14'] * 0.8), 1)
								# We only trail down, never up. And SL should not be below LTP.
								if new_trailing_sl < orderbook[name]['sl'] and new_trailing_sl > ltp:
									print(f"--- TRAILING SL (SELL) for {name}: Old SL: {orderbook[name]['sl']:.2f}, New SL: {new_trailing_sl:.2f} ---")
									new_sl_orderid = tsl.modify_order(order_id=orderbook[name]['sl_orderid'], quantity=orderbook[name]['qty'], price=0, trigger_price=new_trailing_sl, order_type='STOPMARKET')
									if new_sl_orderid:
										orderbook[name].update({'sl_orderid': new_sl_orderid, 'sl': new_trailing_sl})
										save_state()
					except Exception as e:
						log_error_to_json(name, "Trailing_SL", e)
			elif orderbook[name]['traded'] is None:
				if trades_today_count >= CONFIG["max_daily_trades"]:
					return
				signal = get_entry_signal(chart_5m)
				if not signal:
					return
				# Daily loss limit check
				todays_pnl = sum(t.get('pnl', 0) for t in completed_orders if t.get('date') == str(current_dt.date()))
				if todays_pnl < -CONFIG["max_daily_loss_inr"]:
					_log_and_skip_trade(name, 'Risk Management', 'Daily loss limit reached', signal=signal, price=all_ltp.get(name))
					return
				ltp = all_ltp.get(name)
				if ltp is None or ltp == 0:
					_log_and_skip_trade(name, 'Data Fetch', 'Could not get LTP', signal=signal)
					return
				stop_loss_price, target_price = calculate_sl_tg(signal, ltp, chart_5m)
				qty = calculate_position_and_validate(name, ltp, stop_loss_price, signal=signal)
				if qty:
					last_candle = chart_5m.iloc[-2]						                                                    entry_rsi=last_candle['rsi14'], entry_adx=last_candle['adx14'], entry_atr=last_candle['atr14'])
								return
							
							
					orderbook[name].update({
						'name': name, 'date': str(current_dt.date()), 'entry_time': str(current_dt.time())[:8],
						'buy_sell': signal, 'qty': qty,
						'entry_rsi': round(last_candle['rsi14'], 2), 'entry_volume': int(last_candle['volume']),
						'entry_atr': round(last_candle['atr14'], 2), 'entry_adx': round(last_candle['adx14'], 2)
					})
					place_trade(name, signal, qty, stop_loss_price, target_price)

		except Exception as e:
			print("\n" + "!"*60)
			print(f"--- FATAL ERROR in main loop for {name}: {e} ---")
			print(traceback.format_exc())
			print("!"*60 + "\n")
			log_error_to_json(name, "Main Loop", e)

		time.sleep(2) # 2-second delay to prevent API rate limits

	# --- Main Loop: Manage open positions and scan for new trades ---
	open_positions = [name for name, details in orderbook.items() if details.get('traded') == 'yes']
	other_stocks = [name for name in watchlist if name not in open_positions]

	# 1. Always process open positions first
	if open_positions:
		print("--- Managing Open Positions ---")
		for name in open_positions:
			process_stock(name)

	# 2. Scan for new trades only if the daily limit has not been reached
	if trades_today_count < CONFIG["max_daily_trades"]:
		chunk_size = 20
		for i in range(0, len(other_stocks), chunk_size):
			stock_chunk = other_stocks[i:i + chunk_size]
			print(f"\n--- Scanning batch of {len(stock_chunk)} stocks for new entries... ---")
			for name in stock_chunk:
				process_stock(name)
	else:
		print(f"--- Max daily trades ({trades_today_count}/{CONFIG['max_daily_trades']}) reached. Skipping scan for new entries. ---")
save_state() # Save state once at the end of the main loop
	print(f"--- Scan cycle finished. Waiting for next cycle. ---")
	time.sleep(5)