Browse docs
Docs / Order Placement & Failure Recovery

Order Placement & Failure Recovery

How ThetaPrime places real orders, what can go wrong between the app and your broker, what the app does about each failure automatically — and the exact action you need to take when it can't fix something on its own.

This page is about Real mode. Paper mode never sends anything to the broker, so none of these failures apply there.

Recovery tools referenced below — Sq Off, Mark Exited (Manual), Manual Entry, Edit Trade State, and the Reconcile badge — are documented in detail in Strategy Usage and Security & Safety.


How ThetaPrime places an order

Every leg of every trade goes through the same protected sequence:

  1. MARKET order first. Fastest fill, placed with the exchange's default price-protection band. (ICICI/Breeze is the exception: its own SDK turns every market order into an aggressive LIMIT — priced 10% through the last traded price, capped at the circuit limit — before it ever reaches the exchange. Everything below still applies, but on Breeze what's resting is already that limit, not a true market order.)
  2. If the exchange rejects it ("no trades in this instrument" — common on deep ITM/OTM strikes that haven't traded today), it is automatically re-placed as a LIMIT order priced off the live order book, so it actually crosses instead of resting unfilled.
  3. If it sits unfilled (a thin book can leave a MARKET order open indefinitely with no rejection), ThetaPrime waits ~10 seconds, then cancels and re-places it as that same book-priced LIMIT. Before re-placing, it re-checks the original order — if it actually filled in the meantime, nothing extra is placed. On Breeze this step is deliberately skipped: the resting order is already priced 10% through the market, so re-placing it off the order book would make it less likely to fill, not more — ThetaPrime keeps the original and goes on waiting. The one exception is the fair-price guard in step 4, where re-placing is about the price being safe rather than fast.
  4. Fair-price guard on closes. When selling to close a deep in-the-money leg (e.g. a diagonal's long legs), the LIMIT price is never allowed below the option's own intrinsic value — a thin, mispriced bid can't hand you a fill worse than the option is mathematically worth. Worst case the order rests unfilled, and the retry cycle below takes over.
  5. Every fill is verified. ThetaPrime polls the order until it completes (up to ~60 seconds) and records the broker's real average fill price, not the last-traded price it saw before placing. If a MARKET order partially filled before being converted to a LIMIT, both parts are combined into one correct quantity and average price.

If a network error strikes while placing (the order may or may not have reached the broker), ThetaPrime looks the order up at the broker by its tag before assuming it never happened — a dropped response is not treated as a failed order.


Entry failures

A leg is rejected (margin shortfall, banned contract, etc.)

What ThetaPrime does: never leaves you with a partial, unhedged position. Any legs that did fill are closed again (rolled back) — except long hedge legs (LC/LP) when only the short legs were rejected: those are kept and parked, since re-buying them just to unwind costs you the spread twice. The trade shows ENTRY REJECTED — rolled back and automation will not retry the entry by itself.

What you do: - Fix the underlying cause (add margin, check the contract) before re-enabling. - If the rejection was real: open Trade Status → ⚠ Reset Live State to clear the failed attempt so a fresh entry can run. - If the broker actually shows the "rejected" legs open (a rare verification race): open Trade Status → Edit Trade State and use Confirm Active (broker-verified) — it re-checks every leg against the broker before changing anything. - If the rejection reason recorded is UNKNOWN rather than a real broker message, and/or Edit Trade State can't correct the leg ("Unknown leg") or Confirm Active won't verify it ("broker shows 0, expected N") even though the broker genuinely shows the position open — don't fight those screens or reach for Reset Live State yet. See Security & Safety → Worked example for the exact recovery sequence; resetting before recording the true broker position is what turns this into a bigger incident.

The broker won't trade the contract it just quoted you

Some brokers (ICICI/Breeze most often) return a live price for an option and then refuse an order on it — "This contract is presently not enabled for trading". Nothing in the contract master marks these apart beforehand, and a preview showing READY only means every leg is priceable, not that it can be traded.

What ThetaPrime does: remembers the refused contract for the rest of the day and picks the next qualifying strike on the following run, rather than offering the same dead contract every tick. If it was the first leg, nothing was placed, so the trade stays re-enterable — no reset needed. After 3 such refusals in one day the strategy stops trying ("…no entry today") instead of walking down the strike ladder placing orders that keep getting refused. The list clears on the next trading day.

The same refusal can happen on a leg you're already holding, when it comes time to close it rather than open it — see A close order doesn't confirm for what happens to that leg's close, and Before a profit exit fires for how the same remembered-refusal list keeps a profit exit from being misled by that leg's price in the meantime.

What you do: usually nothing. If it keeps happening on the same underlying, ask your broker whether those strikes are enabled on your account.

No price available for a leg

If any leg has no usable quote at entry time, nothing is placed at all — the entry is skipped cleanly rather than opening a partial position. No action needed; the next scheduled run retries.


Before a profit exit fires: the liquidity check

A broker can quote a live-looking LTP for a contract it then refuses to trade (see above) — and a "profit" computed from a price nobody could actually transact at isn't real.

What ThetaPrime does: before honoring Profit target, Profit target (% of premium), or Time-locked profit (see Strategy Composer → Exits — every other exit is unaffected), it checks that every leg the exit would close has a real market: some trading volume or open interest today, and not a contract already refused earlier in the session. If any leg fails that check, the exit is skipped for this cycle only — nothing closes, nothing is booked — and it's rechecked on the next monitor cycle. If the broker's quote call fails outright (returns nothing for any leg — e.g. a rate limit), the check is skipped and the exit proceeds, since that says nothing about liquidity. You'll see a profit_exit_suppressed entry in the event log if it happens — open that trade's Timeline to see it alongside the entry/adjustment events around it.

What you do: usually nothing — once the leg shows real activity (or the position's stop-loss/EOD fires instead, which are never gated this way), it proceeds normally. If it suppresses repeatedly on the same contract, treat it the same as the untradeable-contract case above.


Exit (close) failures

This is the area most heavily protected, because a failed close means your app and your broker disagree about whether you still hold risk.

A close order doesn't confirm

What ThetaPrime does: the trade status still advances, but that leg's own contribution to the booked P&L is held flat (0 pts) rather than guessed from its last price — a leg that never actually filled hasn't realized anything yet. The leg is flagged internally and retried on every monitor cycle (~5 minutes) until it genuinely resolves. Each retry first checks what happened to the previous close order:

Previous order turned out to be… Automatic action
Filled after all Adopts the real fill price, corrects the booked P&L, clears the flag. Done — no duplicate order is ever placed.
Still resting Cancels it, re-checks, then proceeds accordingly.
Rejected / cancelled, nothing filled Places a fresh close order.
Partially filled Stops and flags for you — see below.
Status unreadable (network) Waits for the next cycle. Never acts on uncertainty.

What you do: usually nothing — watch the notifications. The Reconcile badge may briefly show a mismatch until the retry succeeds.

Never hand-edit the strategy's _active_trade.json or .spec.json to "fix" an unconfirmed leg. The trade file and the Reports/Performance page's numbers are two separate stores — Performance only updates when the app itself records the correction (the automatic retry above, or Mark Exited (Manual) below). Editing the JSON file directly changes what the Trade Status card shows but leaves Performance on the old, unconfirmed number forever, with no error to tell you they've now disagreed.

Retry cap reached (10 attempts)

A close that is rejected every time (e.g. a margin block on the buy-back, or a halted contract) stops being retried after 10 attempts, and you get one notification: "close for … failed 10× — manual intervention needed."

What you do: 1. Close the leg yourself in your broker's own terminal. 2. Open Trade Status → Mark Exited (Manual) and record the price you actually got. This clears the flag and re-opens the strategy for future entries.

Partial fill on a close

If a close order filled only part of its quantity, ThetaPrime never guesses — placing another full-size order could over-close and open a position in the opposite direction. The leg is flagged with the exact filled/total quantity, and retries stop for that leg.

What you do: 1. Check the symbol in your broker terminal and close whatever quantity is genuinely still open. 2. Trade Status → Mark Exited (Manual) with your real average price to square the records.

Close blocked after market hours

A close triggered near the 15:30 close (or its retry) may fall outside market hours and get rejected. The flag simply persists overnight and retries resume automatically the next trading morning. For index options this is low-drama even on expiry day — an unclosed NIFTY/BANKNIFTY leg cash-settles automatically at expiry; there is no delivery risk. Stock options are different (physical settlement), which is why ThetaPrime's exits never let stock legs run that close to expiry in the first place.


Adjustment rolls

A roll (close the decayed short leg, sell a fresh strike) closes the old leg first. If that close doesn't confirm, the roll pauses — you'll see "Adjustment roll waiting — prior close … no new orders placed" — and the next cycle settles the pending order's fate before anything new is placed, using the same table as above.

If the close succeeds but the new leg fails to place, the position continues one-legged: still fully exit-managed (profit target, stop loss, EOD), just without the rolled leg. No user action is required; close it early via Sq Off if you don't want to run one-legged.


Manual close (Sq Off button)

The Sq Off button uses the exact same protected sequence as automated closes — including the illiquid-strike LIMIT fallback and the fair-price guard — so it is safe to use on a leg the automation is struggling with.

Two messages you may see:

  • "A close is already in progress for this strategy — retry in a minute." The automation (or another Sq Off click) is mid-close. This lock is what prevents the two of you from both placing close orders for the same legs. Wait for the current attempt to finish, check Trade Status, and only retry if the leg is genuinely still open.
  • A leg errors with a rejection message. Same recovery as the retry-cap case: close it in the broker terminal, then Mark Exited (Manual).

Mismatch alerts (Reconcile badge)

Every ~5 minutes ThetaPrime compares its records against your broker's actual positions and flags any mismatch — the safety net behind everything above. It only detects; it never closes anything on its own. See Orphan Position Detection for the badge, the two mismatch types, and the recovery walkthroughs (including partially-closed diagonals).


Quick reference

You see It means Your action
ENTRY REJECTED — rolled back A leg was rejected; filled legs were unwound Fix the cause; Reset Live State (or Confirm Active if broker shows legs open)
Orange ⚠ EXIT UNCONFIRMED badge A close order for that leg never confirmed filled — it may still be open at the broker Usually nothing; it auto-retries every ~5 min. Never edit the trade JSON by hand — see below
"…refused N selected contracts as untradeable — no entry today" The broker quoted, then refused, 3 strikes in a row Nothing today; if it repeats, ask the broker whether those strikes are enabled
Event log: profit_exit_suppressed A profit-taking exit would have fired, but a leg it'd close has no real market behind its price Nothing — rechecked next cycle; stop-loss/EOD still fire regardless
Notification: close failed 10× Close permanently rejected Close at broker → Mark Exited (Manual)
Notification / flag: PARTIAL FILL m/n Close half-filled; automation stopped on purpose Close remainder at broker → Mark Exited (Manual)
"Close already in progress" The close lock is doing its job Wait a minute; check Trade Status before retrying
"Adjustment roll waiting…" Prior roll-close still being settled Nothing — resolves next cycle
Position now one-legged after a roll New leg failed to place; exits still active Optional: Sq Off early
Red Reconcile badge App and broker records disagree Follow Orphan Position Detection
Repeated "still open / re-checking" Order status unreadable (network) Nothing — it acts only once status is readable

The one rule underneath all of this: ThetaPrime never places an order while the fate of a previous one is uncertain, and never auto-fixes a partial fill. When it stops and asks for you, it's because acting blindly could double a position — the two manual tools you'll ever need are your broker terminal to make reality right, and Mark Exited (Manual) to tell ThetaPrime about it.

Applies to ThetaPrime v0.46.7 · Page last updated 2026-09-28