Handle order rejections

Rejections happen at two points: at placement (place_order() raises OrderException immediately) or after placement (order enters the book then gets rejected by the exchange/RMS, status becomes REJECTED with status_message populated).

Common rejection reasons and what they mean

status_message (paraphrased)CauseFix
"Insufficient funds"Not enough marginRe-check margins (ch 12) before placing, add buffer
"RMS:Margin Exceeds"Same, RMS-level blockSame
"Price out of range" / "circuit limit"Order price outside today's circuit bandFetch circuit limits from quote data before pricing (ch 77)
"Trigger price out of range"SL/SL-M trigger too close to LTPWiden trigger distance (ch 51)
"Quantity freeze limit exceeded"Order size exceeds exchange per-order capSplit into multiple orders (ch 63)
"Invalid tradingsymbol"Symbol/exchange mismatch, or stale instrument tokenRe-resolve from a freshly cached instrument master (ch 21)
"Market is closed"Trying to place regular variety outside market hoursUse amo variety instead (ch 62)

Catch both failure points explicitly

from kiteconnect.exceptions import OrderException

def place_order_safely(kite, **order_params) -> dict:
    try:
        order_id = kite.place_order(**order_params)
    except OrderException as e:
        logging.error(f"Order rejected at placement: {e}")
        return {"success": False, "reason": str(e), "order_id": None}

    result = poll_order_status(kite, order_id)
    if result["status"] == "REJECTED":
        logging.error(f"Order rejected post-placement: {result['status_message']}")
        return {"success": False, "reason": result["status_message"], "order_id": order_id}

    return {"success": True, "reason": None, "order_id": order_id}

Never silently swallow a rejection

A rejection means your strategy's intended action did not happen — if your code catches the exception and moves on without surfacing this (alerting, chapter 91; logging, chapter 89), you get a silent divergence between what your strategy *thinks* its position is and what it *actually* is. This class of bug is one of the most dangerous in unattended trading — always fail loudly on a rejection your bot can't self-correct.

Next: 058 — Idempotent order placement