Skip to content
How a Trade MovesPart 5 of 7

FIX Messages: Order Status Is a State, Execution Type Is an Event

Two messages can report the exact same event, back to back, on the exact same order — and only one of them means the order is actually done. That's not a paradox. It's how every trading system on earth is built to work, and almost nobody notices the field doing the work.

Open Swiggy or Zomato while your food is on the way and you'll see two different things on the same screen. A status ribbon at the top — Preparing, Picked Up, Out for Delivery — tells you where things stand right now. A separate stream of notifications tells you what just happened: "the restaurant accepted your order," "your rider has picked it up." DoorDash and Uber Eats do the exact same split in the US. The ribbon and the notification are answering two different questions, even though every notification is what caused the ribbon to change.

A FIX ExecutionReport — the message an exchange or broker sends back every time something happens to an order — carries the same split, built into two fields with numbers instead of names: OrdStatus (tag 39) is the ribbon. ExecType (tag 150) is the notification. New BAs and QA engineers read them as if they're saying the same thing in two formats. They aren't, and the gap between them is exactly where a lot of production defects and half-written test cases live.

One message, two questions

FIX — the Financial Information eXchange protocol — is the language an OMS, EMS, and exchange gateway all speak to each other regardless of who built each one. When you submit an order, you send NewOrderSingle (MsgType 35=D), tagging it with your own reference number, ClOrdID (tag 11) — think of it as the tracking number you keep on your side, so every reply the exchange sends back can be matched to the order that caused it. Everything that happens to that order afterward — accepted, partly filled, fully filled, cancelled, rejected — comes back as an ExecutionReport (35=8), and every single one of those reports carries both fields at once:

  • OrdStatus (39) answers "what is this order's current state, considered as a whole?" It's cumulative. It only describes the order.
  • ExecType (150) answers "what specific event just caused this message to be sent?" It's momentary. It describes the report.

Early FIX usage leaned on order status alone to carry both meanings, which worked until a single order could be partially filled by five separate trades over the course of a day — at which point "the order's status" and "what specific thing just happened to it" stopped being answerable with one field. The protocol split them so a system could always tell the difference between the order's shape and the event that just reshaped it.

OrdStatus (39): the ribbon

ValueMeaningWhat it tells you
0NewOrder is live, nothing filled yet
1Partially FilledSome quantity has traded; the rest is still resting
2FilledThe entire order quantity has traded
4CanceledThe resting order was withdrawn before it could trade
6Pending CancelA cancel request was sent; the order hasn't confirmed cancellation yet
8RejectedThe order itself was never accepted onto the book
CExpiredThe order's time-in-force ran out before it filled
EPending ReplaceAn amendment was sent; the order hasn't confirmed the change yet

ExecType (150): the notification

ValueMeaningWhat it tells you
0NewThe order was just acknowledged by the exchange
FTradeA fill just happened — partial or full, the field doesn't distinguish which
4CanceledA cancel request just succeeded
5ReplacedAn amend request just succeeded
8RejectedThe new order itself was just rejected
CExpiredThe order just timed out
DRestatedThe exchange unilaterally changed the order's terms, with no request from the client
GTrade CorrectA previously reported fill's details were just corrected
HTrade CancelA previously reported fill was just reversed entirely

Rows G and H need one more piece to be usable, not just readable: neither message stands alone. Every ExecutionReport carries a unique ExecID (tag 17) the moment it's sent. A later Trade Correct or Trade Cancel doesn't just declare "something's wrong" — it carries ExecRefID (tag 19), pointing straight back at the ExecID of the specific fill it's correcting or reversing. Without that pointer, a system receiving a Trade Cancel would know that a fill got undone somewhere in this order's history, but not which one — and a position that's already been split across five partial fills can't be unwound by guessing.

Look closely at row F in the second table. Trade covers both a partial fill and a full fill — the same ExecType. What tells you which one happened is OrdStatus sitting right next to it on the identical message. That pairing is the whole essay in one line: the event can repeat identically while the state it produces keeps moving forward.

Watching one order live

A trader buys 500 shares of Infosys at ₹1,550, limit order. Three ExecutionReports arrive over the next few minutes, and only the state field changes:

  1. Acknowledged. ExecType=0 (New), OrdStatus=0 (New), CumQty 0, LeavesQty 500. The order exists on the book; nothing has traded.
  2. First fill. ExecType=F (Trade), OrdStatus=1 (Partially Filled), LastQty 200, CumQty 200, LeavesQty 300. An event — a trade — just happened.
  3. Final fill. ExecType=F (Trade), OrdStatus=2 (Filled), LastQty 300, CumQty 500, LeavesQty 0. The identical event as message 2 — another trade — but this time it exhausts the order, so the state field reads differently.

Two messages, same ExecType, different OrdStatus. A system that only checks ExecType to decide "did the order finish?" gets message 2 and message 3 confused, because ExecType never says "finished" — only OrdStatus does. That single design fact is behind more mis-written reconciliation logic than any other FIX field pairing.

Run the identical sequence on a US order — 500 shares of Apple instead of Infosys, dollars instead of rupees — and nothing about the fields changes shape. ExecType=F still fires twice, OrdStatus still climbs from 1 to 2 on the second one. FIX doesn't have a regional dialect for this; a reconciliation engine built for NSE fills and one built for Nasdaq fills are checking the exact same two tags for the exact same reason.

The state that never records the event

Here's the nuance that separates a junior read of this from a senior one. Say the trader, watching that same order sit at 300 shares unfilled, sends an OrderCancelRequest — but the exchange fills the remaining 300 in the same instant the cancel arrives. The cancel loses the race.

The exchange doesn't answer that failed cancel with an ExecutionReport at all. It sends a completely different message, OrderCancelReject (35=9), carrying its own reason code — "too late to cancel." Meanwhile a separate ExecutionReport reports the fill: ExecType=F (Trade), OrdStatus=2 (Filled).

Notice what OrdStatus never says here: it never shows "cancel rejected." There's no such state, because a rejected cancel isn't something that happened to the order — it's an outcome of a request that failed to change anything. The order's actual state is exactly what it would have been if the cancel had never been sent at all. FIX keeps these separate on purpose: OrdStatus only ever describes the order, never the fate of a message that failed to touch it.

Example 1: India — the trade that stayed a trade

On 5 October 2012, a dealer at Emkay Global Financial Services meant to place a Nifty basket order and instead entered the order's value into the field for quantity. The resulting order hit NSE's trading system as a basket many multiples too large, and in the span of seconds the Nifty index dropped more than 900 points before trading was halted for roughly fifteen minutes while the exchange sorted out the damage — roughly 59 trades worth over $125 million had already executed.

Emkay asked NSE to annul the erroneous trades — India's term for what FIX calls a trade cancel. A disciplinary panel refused. Emkay appealed to the Securities Appellate Tribunal and lost there too. Every one of those erroneous fills stayed exactly what it was the moment it executed: ExecType=F (Trade), and nothing downstream ever carried ExecType=H (Trade Cancel) for them. Emkay had to absorb the roughly ₹51 crore loss as a live position, not a voided one, because the exchange's decision — not any technical malfunction — was that a trade cancel was not going to happen here.

Example 2: International — the trades that got busted

On 6 May 2010, US equity markets fell and partly recovered within about thirty-six minutes, a day now known simply as the Flash Crash. In the chaos, a large number of trades executed at prices wildly detached from anything resembling fair value — some individual stocks briefly traded for pennies or, at the other extreme, tens of thousands of dollars a share. After the close, the exchanges and FINRA jointly agreed to cancel every trade that had executed more than 60% away from its pre-crash reference price.

Every one of those cancellations is exactly what ExecType=H (Trade Cancel) exists for: a fill that had already been reported, already confirmed, already sitting in somebody's position — reversed after the fact because the market itself judged the price it traded at to be indefensible. The event that produced the original fill (ExecType=F) and the event that later erased it (ExecType=H) are two entirely different messages, sent hours apart, about the same trade.

Put the two cases side by side and the lesson isn't a technology lesson at all — it's that "can this execution be un-reported" is a market's discretionary call, made under pressure, and FIX simply needed a field that could carry either answer without disturbing anything else on the message.

Why it matters for a BA or QA

A ticket that says "handle order cancellation" is really asking about at least three different message shapes: a successful cancel (ExecType=4, OrdStatus=4), a cancel that failed because the order already filled (OrderCancelReject, no ExecType involved at all), and a trade cancel that reverses a fill days later (ExecType=H, carrying an ExecRefID that must match the original fill's ExecID exactly, or the wrong position gets unwound). Writing a requirement that survives contact with engineering means naming which of the three you actually mean — the same discipline the acronym wall demands of KYC versus CDD versus EDD: terms that sound adjacent are not interchangeable, and a requirement or test case that treats them as synonyms is the one that misses the defect its own system just produced.

A test plan for an OMS reconciliation feature should never just say "verify a partial fill updates the position." It should specify: on receipt of ExecType=F with OrdStatus=1, LeavesQty must still be non-zero; on receipt of the next ExecType=F for the same ClOrdID, if LeavesQty reaches zero, OrdStatus must read 2, not 1 again. That's the difference between a test case that would have caught the Emkay-style "did this really finish filling" ambiguity and one that only ever exercises the happy path.

Lighthouse insight

A ribbon and a notification aren't competing for the same job — they're doing two different ones, and a food delivery app makes that obvious because you see both on one screen at once. A FIX ExecutionReport hides the same split behind two numeric tags on the same message, which is exactly why it's so easy for a new BA to read OrdStatus and ExecType as redundant. They're not describing the same thing twice. One is describing the order. The other is describing the message. Everything from a routine partial fill to a market's decision to bust a trade — and, two years later in Mumbai, another market's decision not to — comes down to keeping those two questions separate — because the moment a system conflates them, it loses the ability to say what actually happened versus what is currently true.

Reference anchors

Continue the system

A curated path through the next concept, so one essay becomes a map.

Related essays

Capital Markets

Infrastructure: The Nine Systems Behind Every Trade

Nobody sees the machines that turn a click into a trade.

OMS, EMS, FIX, matching engines, CCPs, SWIFT, custodians — the machinery nobody sees between a click and a settled trade, and why almost none of it lives on a screen.

Surya · 5 min read

Capital Markets

Settlement Fails: What Happens When a Trade Refuses to Deliver

An executed trade is a promise, not a delivery.

An executed trade is a promise, not a delivery. When one side can't keep that promise by settlement date — a missing instruction, a share that hasn't arrived yet, or simply not having what it sold — a market has to have already decided what happens next. India guarantees the buyer through auction and close-out. The US makes the defaulting broker fix it under threat of losing short-sale privileges. Europe mostly just sends a bill. Three different judgments about what actually deters a broken promise.

Surya · 12 min read

Capital Markets

Rho: The Greek That Only Shows Up When You Wait Long Enough

Between 2022 and 2023, the Fed's fastest rate-hiking cycle in four decades and the RBI's own climb from 4% to 6.5% quietly changed how much every stock option granted to an employee in the US or India was worth on paper — not because any company performed differently, but because the risk-free rate buried inside the Black-Scholes formula moved.

Between 2022 and 2023, the Fed's fastest rate-hiking cycle in four decades and the RBI's own climb from 4% to 6.5% quietly changed how much every stock option granted to an employee in the US or India was worth on paper — not because any company performed differently, but because the risk-free rate buried inside the Black-Scholes formula moved. That sensitivity is called rho, and it's the one Greek a trader on India's weekly-options-dominated market can go an entire career without ever needing to check — until the option in question is built to last years, not days. What rho actually measures, why it barely matters where India trades the most, and where it quietly matters enormously — with paired Indian and international examples throughout.

Surya · 7 min read