Please valide this code and help me to run this code on dhan cloud platform

# — Risk & Capital Architecture —

TOTAL_CAPITAL = 30000

MAX_DAILY_LOSS = -1500

CAPITAL_PER_TRADE = 10000

MAX_CONCURRENT_POSITIONS = TOTAL_CAPITAL // CAPITAL_PER_TRADE # 3, given current numbers

ATR_MULT_SL = 1.5 # Matches backtest

ATR_MULT_TGT = 3.0 # Matches backtest

# Where the day’s “already traded” state is persisted between runs.

STATE_DIR = “algo_state”

os.makedirs(STATE_DIR, exist_ok=True)

def state_file_for_today():

return os.path.join(STATE_DIR, f"traded\_{datetime.now().strftime('%Y%m%d')}.json")

def load_traded_state():

"""

Load today's 'already traded' set from disk. This is what fixes the

original bug: algo_memory used to reset to empty every time the script

ran, so if you re-run/re-schedule the scanner during the day (which you

must, since it trades off 5-min candles), a symbol that already fired

could fire again and open a duplicate position.

"""

path = state_file_for_today()

if os.path.exists(path):

    with open(path, "r") as f:

        return json.load(f)

return {}

def save_traded_state(state):

path = state_file_for_today()

with open(path, "w") as f:

    json.dump(state, f, indent=2)

def reconcile_with_broker(tsl, local_state, watchlist):

"""

Local state (the JSON file) is a convenience cache, not the source of

truth. Before trading, cross-check against live broker positions so

that:

  - a symbol with an open position is never re-entered, even if the

    local state file was deleted/corrupted/out of sync

  - a symbol whose local state says 'traded' but has NO live position

    (e.g. the order was rejected, or it hit target/SL and squared off)

    is treated correctly rather than blindly trusted either way

Returns (updated_state, currently_open_count).

"""

try:

    positions_df = tsl.get_positions()

except Exception as e:

    print(f"\[bold red\]Could not fetch live positions for reconciliation: {e}\[/bold red\]")

    \# Fail closed: if we can't verify broker state, don't trust the

    \# local file either — treat everything as untradeable this run.

    return {name: {"traded": "yes", "reason": "reconcile_failed"} for name in watchlist}, len(watchlist)



open_symbols = set()

if positions_df is not None and not positions_df.empty and "tradingsymbol" in positions_df.columns:

    qty_col = "net_qty" if "net_qty" in positions_df.columns else "quantity"

    if qty_col in positions_df.columns:

        open_rows = positions_df\[positions_df\[qty_col\] != 0\]

        open_symbols = set(open_rows\["tradingsymbol"\].tolist())



for name in watchlist:

    if name in open_symbols:

        \# Broker confirms a live position exists — lock it regardless

        \# of what the local file said.

        local_state.setdefault(name, {})

        local_state\[name\]\["traded"\] = "yes"

        local_state\[name\]\["reason"\] = "live_position_confirmed"

    elif local_state.get(name, {}).get("traded") == "yes" and local_state\[name\].get("reason") != "eod_flat":

        \# Local file thought we were in a trade, but broker shows no

        \# open position (order rejected, or already squared off /

        \# hit target/SL). Don't silently re-enter it in the same

        \# session — mark it as done for the day rather than treating

        \# a completed trade as a fresh signal.

        local_state\[name\]\["reason"\] = local_state\[name\].get("reason", "closed_or_never_filled")



currently_open_count = len(open_symbols & set(watchlist))

return local_state, currently_open_count

def order_was_filled_or_accepted(tsl, order_id):

"""

Never assume a placed order succeeded. Poll the order book / order

status endpoint and only commit to state + send a 'LIVE BUY/SELL'

alert once the broker actually confirms the order (accepted/open/

filled), not just because place_super_order() returned without an

exception.

"""

if not order_id:

    return False

try:

    order_status = tsl.get_order_detail(order_id=order_id)

    status = str(order_status.get("status", "")).lower()

    return status in ("open", "pending", "trigger pending", "complete", "traded")

except Exception as e:

    print(f"\[bold red\]Could not verify order {order_id} status: {e}\[/bold red\]")

    return False

# — Initialize Dhan API —

tsl = Tradehull(ClientCode=client_code, mode=“pin_totp”, pin=pin, totp_secret=totp_secret)

# NOTE: fill in client_code / pin / totp_secret / receiver_chat_id / bot_token above with

# your real values before running — these are placeholders, not live credentials.

# — Time-Window Checks (Indian Standard Time) —

now = datetime.now()

current_time_int = int(now.strftime(“%H%M”))

# 1. Block early orders (Before 09:30 AM)

if current_time_int < 930:

print("\[bold orange3\]Execution Halted: Waiting for the 09:30 AM Cool-Down filter to bypass opening noise.\[/bold orange3\]")

sys.exit()

# 2. Block late orders / Trigger EOD Check (After 03:15 PM)

if current_time_int >= 1515:

print("\[bold red\]Market handling window closed (> 15:15). Processing automatic terminal square-offs.\[/bold red\]")

sys.exit()

# — Global Risk Kill Switch Check —

# NOTE: this now fails CLOSED. If we can’t confirm PnL is within limits,

# we do not trade, instead of defaulting to 0 and proceeding.

try:

positions_df = tsl.get_positions()

if positions_df is not None and not positions_df.empty:

    if "net_pnl" in positions_df.columns:

        current_daily_pnl = positions_df\["net_pnl"\].sum()

    elif "pl" in positions_df.columns:

        current_daily_pnl = positions_df\["pl"\].sum()

    else:

        raise ValueError("Positions dataframe has neither 'net_pnl' nor 'pl' column")

else:

    current_daily_pnl = 0



print(f"\[bold cyan\]Live Account MTM Status: ₹{current_daily_pnl}\[/bold cyan\]")



if current_daily_pnl <= MAX_DAILY_LOSS:

    message = f"🚨 KILL SWITCH ACTIVE: Current P&L (₹{current_daily_pnl}) breached daily limit of ₹{MAX_DAILY_LOSS}. Run denied."

    print(f"\[bold white on red\]{message}\[/bold white on red\]")

    tsl.send_telegram_alert(message=message, receiver_chat_id=receiver_chat_id, bot_token=bot_token)

    sys.exit()

except Exception as e:

message = f"🚨 RISK CHECK FAILED: Could not verify P&L ({e}). Halting run — will not trade blind."

print(f"\[bold white on red\]{message}\[/bold white on red\]")

try:

    tsl.send_telegram_alert(message=message, receiver_chat_id=receiver_chat_id, bot_token=bot_token)

except Exception:

    pass

sys.exit()

# — Watchlist —

watchlist = [

"ADANIENT", "TCS", "SUNPHARMA", "CIPLA", "INDIGO", "TRENT", "TITAN", "LT",

"GRASIM", "M&M", "ASIANPAINT", "HINDUNILVR", "SBILIFE", "BHARTIARTL", "ADANIPORTS",

"BAJAJFINSV", "TECHM", "NESTLEIND", "RELIANCE", "JSWSTEEL", "DRREDDY", "AXISBANK",

"ICICIBANK", "INFY", "HCLTECH", "TATACONSUM", "HINDALCO", "SBIN", "MAXHEALTH",

"SHRIRAMFIN", "BAJFINANCE", "HDFCBANK", "HDFCLIFE", "COALINDIA", "TMPV", "NTPC",

"KOTAKBANK", "POWERGRID", "ITC", "ONGC", "ETERNAL", "JIOFIN", "TATASTEEL", "WIPRO"

]

# Load persisted state, then reconcile against live broker positions.

algo_memory = load_traded_state()

algo_memory, currently_open_count = reconcile_with_broker(tsl, algo_memory, watchlist)

save_traded_state(algo_memory)

print(f"[bold cyan]Currently open positions (broker-confirmed): {currently_open_count} / {MAX_CONCURRENT_POSITIONS}[/bold cyan]")

# — Core Scanner Engine —

for name in watchlist:

print(f"\[bold yellow\]Scanning: {name}\[/bold yellow\]")



try:

    \# Loop Risk Intercept (kill switch)

    positions_df = tsl.get_positions()

    if positions_df is not None and not positions_df.empty:

        if "net_pnl" in positions_df.columns:

            current_daily_pnl = positions_df\["net_pnl"\].sum()

        elif "pl" in positions_df.columns:

            current_daily_pnl = positions_df\["pl"\].sum()

        else:

            raise ValueError("Positions dataframe has neither 'net_pnl' nor 'pl' column")



        if current_daily_pnl <= MAX_DAILY_LOSS:

            print(f"\[bold white on red\]🚨 Kill Switch triggered mid-scan! MTM: ₹{current_daily_pnl}. Breaking execution loop.\[/bold white on red\]")

            break



    \# Portfolio-level capital cap: don't open more concurrent

    \# positions than TOTAL_CAPITAL / CAPITAL_PER_TRADE allows.

    if currently_open_count >= MAX_CONCURRENT_POSITIONS:

        print(f"\[bold orange3\]Max concurrent positions ({MAX_CONCURRENT_POSITIONS}) reached. Skipping new entries this pass.\[/bold orange3\]")

        break



    \# Skip symbols already handled today (broker-confirmed open

    \# position, or a completed/rejected trade from earlier today).

    if algo_memory.get(name, {}).get("traded") == "yes":

        continue



    \# 1. Fetch Structural Market Matrix and Calculate Indicators

    chart = tsl.get_historical_data(tradingsymbol=name, exchange="NSE", timeframe="5")

    chart\["rsi"\] = chart.ta.rsi(length=14)

    chart\["ema"\] = chart.ta.ema(length=30)

    chart\["atr"\] = chart.ta.atr(length=14)



    if len(chart) < 2:

        print(f"\[bold orange3\]Not enough candle history yet for {name}, skipping.\[/bold orange3\]")

        continue



    \# 2. Extract Gap Data

    ohlc_data = tsl.get_ohlc_data(names=name)

    previous_close = ohlc_data\[name\]\["ohlc"\]\["close"\]

    todays_open = ohlc_data\[name\]\["ohlc"\]\["open"\]



    if not previous_close:

        print(f"\[bold orange3\]Missing previous close for {name}, skipping.\[/bold orange3\]")

        continue



    gap = round((todays_open - previous_close) / previous_close \* 100, 2)



    completed_candle = chart.iloc\[-2\]

    current_atr = completed_candle\["atr"\]

    close_price = completed_candle\["close"\]:



    if current_atr is None or current_atr != current_atr : # NaN check

        print(f"\[bold orange3\]ATR not yet available for {name} (warm-up period), skipping.\[/bold orange3\]")

        continue



    \# --- Rule Matrix Validations ---

    \# Long and short conditions are structurally symmetric and mutually

    \# exclusive (EMA side, RSI side, gap side all point the same

    \# direction), so a symbol can't satisfy both in the same pass.

    price_above_ema = close_price > completed_candle\["ema"\]

    rsi_above_60 = completed_candle\["rsi"\] > 60

    gap_up_by_0_5 = gap >= 0.5



    price_below_ema = close_price < completed_candle\["ema"\]

    rsi_below_40 = completed_candle\["rsi"\] < 40

    gap_down_by_0_5 = gap <= -0.5



    \# 3A. Execute Optimized BUY

    \# Direction check: enter above EMA, momentum RSI>60, gap up ->

    \# trend-following long. SL below entry, target above entry. Correct.

    if price_above_ema and rsi_above_60 and gap_up_by_0_5:

        print(f"\[bold green\]   Target spotted (LONG): {name}\[/bold green\]")



        quantity = int(CAPITAL_PER_TRADE / close_price)

        entry_price = round(close_price \* 1.002, 1)  # tight limit slippage buffer, chasing the move

        sl_price = round(close_price - (current_atr \* ATR_MULT_SL), 1)

        target_price = round(close_price + (current_atr \* ATR_MULT_TGT), 1)



        \# Sanity checks on the bracket before firing the order.

        if quantity <= 0:

            print(f"\[bold orange3\]Computed quantity <=0 for {name}, skipping.\[/bold orange3\]")

            continue

        if not (sl_price < entry_price < target_price):

            print(f"\[bold red\]Bracket sanity check failed for {name} (SL {sl_price} / Entry {entry_price} / Target {target_price}), skipping.\[/bold red\]")

            continue



        order_id = tsl.place_super_order(

            tradingsymbol=name, exchange="NSE", transaction_type="BUY",

            quantity=quantity, order_type="LIMIT", trade_type="MIS",

            price=entry_price, target_price=target_price, stop_loss_price=sl_price,

            trailing_jump=0.5

        )

        time.sleep(1)



        if order_was_filled_or_accepted(tsl, order_id):

            algo_memory\[name\] = {"traded": "yes", "direction": "BUY", "target": target_price,

                                  "sl": sl_price, "order_id": order_id}

            currently_open_count += 1

            save_traded_state(algo_memory)



            tsl.send_telegram_alert(

                message=f"🚀 LIVE BUY: {quantity} shares of {name}.\\nEntry Limit: {entry_price}\\nTarget (ATR): {target_price}\\nSL (ATR): {sl_price}",

                receiver_chat_id=receiver_chat_id, bot_token=bot_token

            )

        else:

            print(f"\[bold red\]Order for {name} was NOT confirmed by broker — not marking as traded, not alerting.\[/bold red\]")



    \# 3B. Execute Optimized SHORT SELL

    \# Direction check: enter below EMA, weak RSI<40, gap down -> trend-

    \# following short. SL above entry, target below entry. Correct.

    elif price_below_ema and rsi_below_40 and gap_down_by_0_5:

        print(f"\[bold red\]            Target spotted (SHORT): {name}\[/bold red\]")



        quantity = int(CAPITAL_PER_TRADE / close_price)

        entry_price = round(close_price \* 0.998, 1)

        sl_price = round(close_price + (current_atr \* ATR_MULT_SL), 1)

        target_price = round(close_price - (current_atr \* ATR_MULT_TGT), 1)



        if quantity <= 0:

            print(f"\[bold orange3\]Computed quantity <=0 for {name}, skipping.\[/bold orange3\]")

            continue

        if not (target_price < entry_price < sl_price):

            print(f"\[bold red\]Bracket sanity check failed for {name} (Target {target_price} / Entry {entry_price} / SL {sl_price}), skipping.\[/bold red\]")

            continue



        order_id = tsl.place_super_order(

            tradingsymbol=name, exchange="NSE", transaction_type="SELL",

            quantity=quantity, order_type="LIMIT", trade_type="MIS",

            price=entry_price, target_price=target_price, stop_loss_price=sl_price,

            trailing_jump=0.5

        )

        time.sleep(1)



        if order_was_filled_or_accepted(tsl, order_id):

            algo_memory\[name\] = {"traded": "yes", "direction": "SELL", "target": target_price,

                                  "sl": sl_price, "order_id": order_id}

            currently_open_count += 1

            save_traded_state(algo_memory)



            tsl.send_telegram_alert(

                message=f"💥 LIVE SHORT: {quantity} shares of {name}.\\nEntry Limit: {entry_price}\\nTarget (ATR): {target_price}\\nSL (ATR): {sl_price}",

                receiver_chat_id=receiver_chat_id, bot_token=bot_token

            )

        else:

            print(f"\[bold red\]Order for {name} was NOT confirmed by broker — not marking as traded, not alerting.\[/bold red\]")



except Exception as e:

    print(f"\[bold red\]Skipping {name} due to active processing error: {e}\[/bold red\]")

    continue

print(“[bold green]Trading execution pass complete.[/bold green]”)