Order Types and Execution Tactics in Indian Markets
Market, limit, SL and SL-M as your broker actually implements them, plus freeze quantities, market protection and the slicing logic an automated system needs.
A strategy produces a decision: buy this instrument, this quantity, now. Everything between that decision and a fill is execution, and it is where a measurable share of retail edge is lost. The choice of order type is the largest single lever in that gap, and it is usually made once, early, and never revisited.
The order types you actually have
Layered model
What each order type guarantees, and what it gives up
These map onto exchange order attributes rather than broker features, so the names are consistent across APIs. The edge-case behaviour is not.
A market order supplies no price and is near-certain to fill, giving up price certainty. A limit order caps the price and may rest unfilled for the whole session. An SL order carries a trigger and a limit price, becoming a limit order when triggered, so it can trigger and still not fill. An SL-M order carries only a trigger and becomes a market order, so it exits but at whatever price is available.
The SL row is the one that surprises people. A triggered stop-limit resting unfilled below the market is an open position with no protection at all.
Every broker API in India exposes broadly the same four order types, because they map onto exchange order attributes rather than broker features. The names are consistent; the edge-case behaviour is not.
Type
Prices you supply
Guarantees
Gives up
MARKET
None
Fill, in almost all conditions
Price certainty
LIMIT
Limit price
Price no worse than your limit
Certainty of any fill
SL
Trigger + limit
Becomes a limit order at the trigger
May not fill if price gaps past the limit
SL-M
Trigger only
Becomes a market order at the trigger
Price on the resulting fill
The four core order types and what each guarantees
The row that deserves attention is SL. A stop-loss limit order that triggers during a fast move can end up resting unfilled below the market, which means you have a working order and an open position rather than the protection you intended. That is the failure mode that turns a planned small loss into an unbounded one.
Market or limit: the actual trade-off
Decision tree
Price the cost of the trades you miss, not just the spread
The spread you save on a passive order is visible in your fills. The trades a passive order failed to catch leave no record at all.
The deciding question is what a missed trade costs the strategy. Where missing winners hurts, as with breakout, momentum and stop exits, use an aggressive order: market, or a limit priced through the touch to cap the worst case. Where missing is acceptable, as with mean reversion entries and target exits, rest a passive limit at or better than the signal price and collect the spread instead of paying it.
On a breakout strategy the unfilled limit orders are disproportionately the winners, so passive execution biases live results below the backtest in a way slippage analysis never shows.
The textbook framing is that market orders cost you the spread and limit orders do not. That is incomplete. A limit order that does not fill has a cost too — the cost of not having the position the strategy asked for — and that cost is invisible in your fill records, which is why it is systematically underestimated.
The correct way to frame it is by asking what your strategy loses when it misses a trade. A mean-reversion strategy entering into weakness is often fine with a resting limit order; if it does not fill, the opportunity was probably not there. A breakout strategy is the opposite: the trades it fails to enter are disproportionately the ones that worked, so a limit order introduces a bias that no amount of saved spread compensates for.
A reasonable default policy
Entries on a breakout or momentum signal: market, or a limit priced through the touch to behave like an aggressive order with a cap.
Entries on mean reversion: resting limit at or better than the signal price.
Exits at a target: limit. You are not in a hurry and the price is the point.
Exits at a stop: market or SL-M. Certainty of exit dominates price.
Anything on expiry day near the money: favour certainty. Spreads widen and the book thins exactly when your stop matters.
How a stop-loss order actually behaves
Process flow
A stop is two events separated by time
The trigger is evaluated by the exchange; only when it fires does an order enter the book. Everything difficult about stops lives in the gap between those two moments.
The stop order is accepted and its trigger is held by the exchange. When the last traded price reaches the trigger, the first of the two events occurs. An order then enters the book, a market order for SL-M and a limit order for SL. The market continues moving during that transition. The result is either a fill at an uncertain price or, for SL, an order resting unfilled. A system needs a fallback that escalates an unfilled stop to market after a few seconds.
A triggered stop that has not filled within seconds is an unprotected position. The only safe response at that point is to stop optimising for price.
A stop order is two events separated in time, and treating it as one is the source of most confusion about why a stop "did not work". The trigger is evaluated by the exchange against the last traded price; only when it fires does an actual order enter the book.
Between those two events, the market can move. With SL-M the resulting market order will fill somewhere, possibly well past your trigger. With SL the resulting limit order may not fill at all. Neither behaviour is a malfunction; both are what you asked for.
execution/stop_orders.pypython
SL_M_SUPPORTED = {"NFO": False, "NSE": True} # verify per broker and segment# How far through the trigger to price a protective limit, as a fraction.# Wide enough to fill in a fast market, tight enough to bound the loss.STOP_LIMIT_SLACK = 0.004def build_stop(exchange: str, side: str, trigger: float, quantity: int) -> dict: """Prefer SL-M for certainty of exit; fall back to a slack-priced SL.""" if SL_M_SUPPORTED.get(exchange, False): return { "order_type": "SL-M", "trigger_price": round(trigger, 2), "quantity": quantity, "side": side, } # SL-M unavailable: price the limit through the trigger so it still fills # in a fast market, and accept a bounded amount of extra slippage. slack = trigger * STOP_LIMIT_SLACK limit = trigger - slack if side == "SELL" else trigger + slack return { "order_type": "SL", "trigger_price": round(trigger, 2), "price": round(limit, 2), "quantity": quantity, "side": side, }def on_stop_triggered_but_unfilled(order, book, seconds_waiting: int) -> dict | None: """A triggered SL resting unfilled is an open position with no protection.""" if seconds_waiting < 3: return None # Escalate: cancel the resting limit and exit at market. return {"action": "replace_with_market", "cancel": order.id, "reason": "stop unfilled"}
Choosing a stop mechanism explicitly, with a fallback when SL-M is unavailable and a monitored fallback when the limit does not fill.
The escalation function is the part worth copying. A triggered stop-limit that has not filled within a few seconds is a position with no protection, and the only safe response is to stop being clever about price. Systems that lack this path are the ones where a small planned loss becomes the day's largest.
Freeze quantity and order slicing
Comparison chart
What slicing costs, and what spacing recovers
Illustrative slippage per slice against the decision price. Firing every slice in the same second walks the book; spacing them does not.
Illustrative ticks of slippage from the decision price for each of five slices. Fired in the same second, slippage grows from half a tick on the first slice to one, one point eight, two point nine and four point two ticks by the fifth as the book is consumed. Spaced two seconds apart, the same slices show half, zero point seven, zero point nine, one point two and one point five ticks, because resting liquidity has time to replenish.
Spacing addresses two problems at once: it reduces impact, and it keeps a burst of slices from crossing the 10 orders-per-second registration threshold.
Exchanges publish a freeze quantity for each derivatives contract — the maximum quantity permitted on a single order. Submit more and the order is rejected outright, not partially accepted. Any system that sizes positions dynamically will eventually generate an order above the limit and needs to handle it before it happens rather than after.
Slicing solves the rejection and creates two new problems. The slices are separate orders, so they incur separate brokerage, and they hit the book sequentially, so later slices execute at worse prices than the first. Both effects are real costs that a backtest assuming a single fill at one price does not include.
A slicing routine that behaves
1
Fetch the current freeze quantity per contract, do not hard-code it
Exchanges revise these. A hard-coded value becomes a rejection on a random morning after a contract revision.
2
Slice to whole lots below the freeze limit, with headroom
Sizing slices at exactly the limit leaves no room for a mid-session revision. Leave a margin.
3
Space the slices deliberately
A short interval between slices reduces impact and keeps peak order rate under control.
4
Track the aggregate, not the slices
The strategy asked for one position. Partial fills across slices must be reconciled into one logical position with one average price.
5
Define what happens on a partial aggregate fill
If four of six slices fill and the price runs away, do you complete, hold or reverse? Decide before it happens.
6
Log every slice against the parent decision
Without a parent identifier, reconstructing what the strategy intended from six unrelated fills is guesswork.
Validity and product type: the quiet incident generators
These two fields cause more live problems than order-type selection, precisely because they are set once during integration and then never examined again.
Flag
Options
What goes wrong
Validity
DAY, IOC
A DAY order intended as immediate rests all session and fills hours later
Product
MIS, NRML, CNC
An intraday strategy on NRML consumes far more margin than planned
Product
MIS
Auto-squared off by the broker at a fixed time, possibly before your own exit logic runs
Disclosed qty
Optional
Set unnecessarily, changing how the order is displayed and filled
Trigger vs price
Both on SL
Swapped in the payload — rejected, or worse, accepted with the wrong meaning
The flags most often set wrong
Making execution choices auditable
Execution quality cannot be improved without being measured, and measuring it requires recording the intent alongside the outcome. The single most useful field is the price at the moment the decision was made, because the difference between that and your average fill is the number you are actually trying to reduce.
Fields to record on every order
Decision price and decision timestamp
The mid or last price when the strategy decided. Without it, slippage is not computable.
Order type, validity and product, as sent
As sent, not as configured. Defaults change and configuration drifts.
Parent decision ID linking every slice and modification
One logical intent may produce a dozen exchange orders. They need to be reassembled.
Average fill price and total filled quantity
Plus whether the fill was complete. Partial fills that were never noticed are a recurring source of position mismatch.
Every rejection with the broker's reason string verbatim
Rejection patterns are the earliest warning that a limit or flag has changed underneath you.
Whether a fallback path was taken
SL-M unavailable, stop escalated to market, slice count exceeded. These should be visible in aggregate, not buried.
With those fields you can answer the question that actually improves returns: for this strategy, on this instrument, at this time of day, does an aggressive order or a passive one net more after costs? That is an empirical question about your own order flow, and it has a different answer for different strategies. Slippage and impact cost covers how to turn those records into a number.
Frequently asked questions
What is the difference between SL and SL-M orders?
Both carry a trigger price. An SL order also carries a limit price, so when the trigger fires a limit order enters the book and may not fill if price has moved past the limit. An SL-M order carries only a trigger, so when it fires a market order enters the book and will almost certainly fill, but at whatever price is available. SL bounds the price and risks no fill; SL-M guarantees the exit and risks the price.
Can a market order fail to fill in Indian F&O?
Yes. Exchanges apply a market protection mechanism that converts a derivatives market order into a limit order priced a set percentage away from the last traded price, preventing it from sweeping a thin book. In a fast market that protection limit can be breached, leaving the order unfilled or partially filled. Code that assumes a market order always fills completely is wrong in precisely the conditions that matter.
What is freeze quantity and why does it reject my orders?
Exchanges publish a maximum permitted quantity per single order for each derivatives contract. An order above it is rejected outright rather than partially accepted. Systems that size positions dynamically will eventually exceed it, so large orders must be sliced into multiple smaller ones. Freeze quantities are revised periodically, so they should be fetched rather than hard-coded.
Do limit orders bias live results against a backtest?
Yes, and in a way that leaves no record. Unfilled limit orders are not randomly distributed: on a breakout strategy the orders that miss are the ones where price moved away decisively, which are disproportionately the winners. A backtest assuming limit fills at the touch is assuming fills on trades that would in reality have been missed.
Is broker auto-square-off a safe exit for intraday strategies?
No. Intraday product types are squared off by the broker near the end of the session at market, at a time of the broker's choosing within its stated window. Relying on it outsources the most important order of the day to a process indifferent to price. Strategies should exit on their own schedule well before that window, and treat any broker square-off that occurs as an incident to investigate.
What should be logged to measure execution quality?
The price and timestamp at the moment the strategy decided, the order type, validity and product as actually sent, a parent identifier linking every slice and modification to one logical intent, the average fill price and filled quantity, every rejection with the broker's verbatim reason, and whether any fallback path was taken. Slippage cannot be computed without the decision price.
The engineering team builds QuaTick’s order routing, market-data pipeline and broker integrations. These pieces cover the parts of automation that only show up in production.