Idempotent order placement
Chapter 20 flagged this: never blindly retry a place_order() call that timed out — you can't tell if the order reached the exchange before the connection dropped. This chapter builds the actual pattern.
The problem
try:
order_id = kite.place_order(**params) # times out — did it place or not?
except NetworkException:
order_id = kite.place_order(**params) # DANGER: may create a duplicate order
Solution: use tag, and check-before-resend
import uuid
def place_order_idempotent(kite, max_attempts=2, **order_params) -> str:
client_ref = order_params.pop("tag", None) or str(uuid.uuid4())[:20]
order_params["tag"] = client_ref
for attempt in range(max_attempts):
try:
return kite.place_order(**order_params)
except NetworkException:
# Before retrying, check if an order with this tag already exists
existing = [o for o in kite.orders() if o.get("tag") == client_ref]
if existing:
logging.info(f"Order with tag {client_ref} already placed, not resending")
return existing[0]["order_id"]
if attempt == max_attempts - 1:
raise
time.sleep(1)
Why tag, not order parameters, as the dedup key
Two legitimately different orders can have identical exchange/symbol/quantity/price/type (e.g. two separate signals both wanting to buy 10 INFY at market, minutes apart) — you can't dedup on order content alone without also risking blocking a valid second order. A client-generated unique tag, set *before* the network call and checked after a failure, is the correct idempotency key because you control its uniqueness independent of order content.
Kite Connect specifics
tag is limited to alphanumeric characters and a max length (check current docs, historically 20 chars) — a full UUID may need truncation; ensure your truncation still preserves enough entropy to avoid collisions within a single trading day.
This matters more than it seems
A duplicated order isn't just an annoyance — it's an unplanned position at 2x (or more) your intended size, with its own margin consumption and risk exposure, created by a network hiccup rather than a strategy decision. This is exactly the kind of bug that's rare enough to not show up in early testing and severe enough to matter a lot when it does.
Next: 059 — Place a GTT order