Concept Lesson · Order Execution Safety
Double-Leg Atomicity:
both legs fill
or neither does.
A straddle is a bet on volatility, not direction. If only one leg fills, you've accidentally taken a directional bet with real money.
Background
What is a straddle?
Leg 1: SELL CE
Sell a Call option at the ATM strike. You profit if the market stays flat or drops.
Leg 2: SELL PE
Sell a Put option at the same strike. You profit if the market stays flat or rises.
Together, the two legs form a short straddle. Losses from one leg are offset by gains from the other — as long as both are in place.
A straddle with only one leg is just a naked option sell. That's an entirely different risk profile.
The problem
Placing two orders is not atomic
The exchange doesn't have a "place two orders atomically" API. You send leg 1, wait for fill, send leg 2, wait for fill. Between them, anything can happen: network errors, illiquid strikes, circuit breakers, API rate limits.
If leg 1 fills but leg 2 doesn't, you're holding a naked short — unlimited directional risk with no hedge.
The gap between leg 1 filling and leg 2 filling is where all the danger lives.
Design
Three invariants that must hold
2/2
Both legs must have non-zero filled quantities
=
Filled quantities must be exactly equal (CE qty == PE qty)
SL
Both legs must have valid SL orders placed on the exchange
If any one of these fails, the entire entry is unwound: SL orders cancelled, positions exited, tranche marked failed.
All three, or nothing. No partial straddles in production.
Mechanism
The 15-second imbalance budget
T + 0s
Leg 1 fills — clock starts. You now hold a naked short.
T + 0s to T + 15s
Leg 2 must fill within this window. System uses repeg or rescue mode pricing.
T + 15s
Budget expires. If leg 2 hasn't filled, the engine unwinds leg 1 immediately.
Why 15 seconds? Long enough for a LIMIT order to fill in normal liquidity. Short enough that a naked short doesn't accumulate meaningful losses on a trending day.
The budget is configured via DOUBLE_LEG_TIMEOUT_SECONDS. Set it to None to disable (Option Cracker's legacy mode).
Every second you hold only one leg, you're exposed to directional risk. The budget caps that exposure.
Scenarios
What can happen during a double-leg entry?
1. Both fill, equal qty, both SLs place
The happy path. Straddle is live.
Tranche activated.
2. Leg 1 never fills
No exposure created. Leg 2 is never attempted.
Entry skipped entirely.
3. Leg 1 fills, Leg 2 times out
Budget expires. Naked short exists.
Unwind leg 1.
4. Both fill, quantities don't match
CE = 325 units, PE = 200 units. Not a straddle.
Unwind ALL filled legs.
Four more scenarios on the next slides — all the ways SL placement can fail.
Scenario 2
Leg 1 never fills
This is the safest failure. Leg 2 is never even attempted because there's nothing to balance against.
# Leg 1 didn't fill → first_fill_time stays None
# The imbalance clock never starts
# Leg 2 loop is skipped entirely
filled_legs = [] # empty
# → "both failed" branch
# → emit entry_group_failed
# → no unwind needed (nothing to unwind)
No fill, no risk, no cleanup. Clean exit.
Scenario 3
Leg 1 fills, Leg 2 times out
T + 0s
CE fills at 200. Clock starts. You're naked short a call.
T + 5s
PE order placed at LIMIT. No fill yet — strike is illiquid.
T + 10s
Rescue mode kicks in — tries 5%, then 8% below LTP. Still no fill.
T + 15s
Budget expired. PE marked as failed. Engine enters unwind.
T + 16s
Cancel CE's SL order → place emergency BUY to exit the CE position.
The unwind BUY may cost more than the original SELL (market moved against you during those 15 seconds). That's the cost of atomicity — a small known loss instead of an unbounded naked position.
15 seconds of nakedness, then cleanup. The alternative was unlimited exposure.
Scenario 4
Both fill, but quantities don't match
Leg 1 (CE)
325 units
Full lot. Filled as expected.
Leg 2 (PE)
200 units
Partial fill. 125 units left unfilled when budget expired.
325 CE short + 200 PE short = not a straddle. You're net-short 125 units of CE with no PE offset. That's a directional bet on the market going down.
The engine requires exact equality. Different quantities → unwind both legs. Cancel both SLs, exit both positions.
Close, but not equal, is not good enough. A lopsided straddle is a directional bet in disguise.
Scenario 5
SL placement fails on Leg 1
Step 1
CE fills at 200. Position exists on the exchange.
Step 2
SL BUY order placement fails — trigger price out of range, API error, or validation rejection.
Step 3
Engine attempts emergency exit: BUY back the CE to close the position.
Step 4a
Emergency exit succeeds → CE position closed. Leg marked filled=False.
Step 5
Atomicity check sees 0 filled legs → entry fails cleanly. Leg 2 is never attempted (or unwound if already filled).
mark_tranche_entered is never called → no combined-premium SL, no WebSocket subscription, no active monitoring. The tranche simply doesn't exist.
SL failure = position without protection = unacceptable. Emergency exit is the only safe response.
Scenario 5b · worst case
SL fails AND emergency exit fails
→
→
Emergency exit
Also failed
→
The absolute worst case. You have a filled position with no SL and no exit. The system:
1
Track it
Position added to active_positions with sl_order_id=None and needs_emergency_exit=True
2
Alert
CRITICAL Telegram alert fires immediately. Operator must intervene manually.
3
Backstop
SL health monitor and end-of-day squareoff will find it on the next pass and attempt closure.
This is unrecoverable by automation alone. The CRITICAL alert means: human, look at this now.
Scenario 6
Both legs fill, SL fails on Leg 2
Step 1
CE fills. SL placed successfully for CE.
Step 2
PE fills within budget.
Step 3
SL placement for PE fails. PE marked filled=False due to SL failure.
Step 4
Atomicity check: filled_legs has only 1 of 2 legs (CE).
Step 5
Unwind both legs. Cancel CE's SL, exit CE position. Exit PE position.
Even though both legs technically filled, the PE is unprotected. The engine treats had_sl_failure=True with only 1 valid leg as a partial entry — and unwinds everything.
A straddle with one protected leg and one naked leg is worse than no straddle at all.
Mechanism
Rescue mode: aggressive pricing for Leg 2
When leg 1 fills, you're on the clock. Normal LIMIT pricing might not fill fast enough. Rescue mode uses progressively aggressive prices:
Each attempt fetches a fresh LTP, computes the limit price, places the order, and waits for fill or timeout. If the budget expires mid-attempt, it stops immediately.
Why sell below market?
You're selling options. Selling below LTP gives away edge (worse price for you) but dramatically increases fill probability. You're trading price quality for speed.
Target quantity
Leg 2 targets leg 1's actual filled quantity, not the original request. If leg 1 partial-filled to 200 of 325, leg 2 tries 200 to maximize matching odds.
Pay a little premium to fill fast, or pay a lot of directional risk to fill never.
Complete logic
The atomicity decision tree
filled_legs == 2 AND quantities equal AND both SLs valid?
YES → Activate tranche. Emit entry_group_activated.
↓ NO
filled_legs == 2 AND quantities differ?
UNWIND BOTH → Cancel SLs, exit both positions. Reason: "quantity_mismatch"
↓ NO
filled_legs == 1? (timeout, SL failure, or leg 2 failed)
UNWIND the filled leg → Cancel its SL, exit its position. Reason: "timeout" / "sl_failed" / "leg_failed"
↓ NO
filled_legs == 0?
No action needed → Emit entry_group_failed. Clean exit.
Every path leads to a balanced state: either a full straddle, or no straddle at all.
Important interaction
Combined-Premium SL only activates on success
Some strategies use a combined-premium (CP) SL that monitors the total premium of both legs via WebSocket. But it only sets up after a successful entry:
# Only reached if ALL THREE invariants hold:
# (1) both filled, (2) equal qty, (3) both SLs placed
self.controller.mark_tranche_entered(tranche_idx, summary)
# ↑ This call sets up CP SL, subscribes WebSocket, starts monitoring
# If any invariant failed, we're in the unwind branch above.
# mark_tranche_entered is never called.
# → No CP SL, no WebSocket, no monitoring.
# → Because there's no straddle to monitor.
The per-leg SLs (200% exchange-level) are the crash-safety net. The CP SL (30% combined-premium) is the active protection. If the WebSocket dies, the per-leg SLs catch it, and a CRITICAL alert fires.
Atomicity check gates everything downstream. No straddle = no combined monitoring.
Audit trail
Every scenario emits events
Success trail
- entry_group_started
- entry_order_placed [CE]
- entry_fill_confirmed [CE]
- sl_order_placed [CE]
- entry_order_placed [PE]
- entry_fill_confirmed [PE]
- sl_order_placed [PE]
- entry_group_activated
Failure trails
- entry_group_started
- entry_order_placed [CE]
- entry_fill_confirmed [CE]
- recovery_adjustment (if SL failed)
- entry_group_unwound
- reason: timeout
- reason: quantity_mismatch
- reason: sl_failed
- reason: leg_failed
Every decision is recorded. You can replay any entry and see exactly what happened and why.
Trade-off
Atomicity has a cost
What you lose
- Unwind costs money — the exit price is worse than the entry
- Rescue mode gives away edge (selling 5-10% below LTP)
- Partial fills that almost matched get fully unwound
- Some entries that would have worked get killed by the timer
What you avoid
- Naked short positions with unlimited directional risk
- Lopsided straddles that look hedged but aren't
- Unmonitored positions with no SL protection
- Silent failures that compound into large losses
The unwind might lose you Rs 500-2000. A naked short on a trending day can lose Rs 50,000+.
Small known losses beat large unknown losses. Every time.
New vocabulary
Words you'll use from now on
Imbalance
budget
The countdown
Max seconds you tolerate holding only one leg. Default 15s. Configured per strategy.
Rescue
mode
Aggressive fill
Progressively worse LIMIT prices (5%, 8%, 10% below LTP) to maximize fill probability when the clock is ticking.
Unwind
Undo a partial entry
Cancel SL orders, exit filled positions. Return to zero exposure. The system's "undo" button for broken straddles.
Naked
short
Unhedged sell
A sold option with no offsetting position. Unlimited loss potential in one direction. The thing atomicity prevents.
The rule
Both legs fill with equal quantities.
Both SLs place successfully.
Or everything is unwound.
No partial straddles. No unprotected legs. No silent failures.
← Back to course index