Freeze quantity and order splitting

Exchanges cap the maximum quantity allowed in a single order for F&O contracts — the "freeze quantity" (e.g. NIFTY options might have a freeze limit of 1800 units — this changes periodically, always check current values, never hardcode). An order above this limit is rejected outright, not partially accepted.

def get_freeze_qty(nfo_df: pd.DataFrame, tradingsymbol: str) -> int:
    row = nfo_df[nfo_df.tradingsymbol == tradingsymbol].iloc[0]
    # freeze quantity isn't always in the instrument master directly;
    # NSE publishes it separately per contract — fetch/maintain this
    # from NSE's published freeze-quantity list, updated periodically.
    return FREEZE_QTY_LOOKUP.get(row["name"], 1800)

Splitting a large order into freeze-compliant chunks

def split_order_by_freeze_limit(total_qty: int, lot_size: int, freeze_qty: int) -> list[int]:
    max_lots_per_order = freeze_qty // lot_size
    chunk_qty = max_lots_per_order * lot_size
    chunks = []
    remaining = total_qty
    while remaining > 0:
        take = min(chunk_qty, remaining)
        chunks.append(take)
        remaining -= take
    return chunks
def place_large_fno_order(kite, exchange, symbol, total_qty, lot_size, freeze_qty, **order_kwargs):
    chunks = split_order_by_freeze_limit(total_qty, lot_size, freeze_qty)
    order_ids = []
    for qty in chunks:
        oid = kite.place_order(
            exchange=exchange, tradingsymbol=symbol, quantity=qty, **order_kwargs
        )
        order_ids.append(oid)
        time.sleep(0.3)   # avoid rate-limit bursts, chapter 20
    return order_ids

Why this matters even for "small" retail accounts

Freeze quantity limits apply per order regardless of account size — a trader running an index-option strategy with even a moderate lot count (not necessarily huge capital) can exceed the freeze limit on liquid near-the-money strikes during high-conviction trades. Not handling this means the order is rejected in full, and a naive bot might interpret that as "no liquidity" or some other wrong diagnosis instead of the actual cause.

Track chunk results independently

Each chunk is a separate order with its own order_id, fill status, and potential partial fill (chapter 56) — reconcile total filled quantity across all chunks, don't assume they all filled identically.

Next: 064 — Slicing large orders