Order States Explained
An order passes through a sequence of states between submission and resolution: accepted, working, partially filled, filled, cancelled, rejected, or expired. The distinction that causes the most damage in automation is that accepted is not filled — a successful HTTP response means the broker took the order, not that anything traded. Systems that collapse these states into a binary produce positions nobody decided to take.
Order state is the part of trading automation most likely to be modelled as a boolean and most likely to punish that.
The states
Submitted. Your system sent the order. Nothing has happened yet, and you may not know whether it arrived.
Accepted. The broker received and validated it. Commonly reported as a status such as received. Nothing has traded.
Working. The order is live at the broker or the exchange, waiting for its conditions to be met.
Partially filled. Some quantity executed; the remainder is still working or was cancelled depending on time in force. The position exists and is smaller than requested.
Filled. Complete execution. The only state that means what most people assume acceptance means.
Cancelled. Removed before completion, by you, by the broker, or by expiry of the time in force.
Rejected. Refused before anything happened. Cheap, informative, and the best of the failure states.
Expired. The time in force elapsed. A day order at the session boundary, which for futures is the daily maintenance break rather than midnight.
Additional states exist for orders being replaced, and brokers use their own vocabulary. The shape is consistent; the names are not.
Accepted is not filled
The single most consequential misunderstanding in order handling.
A successful response to an order submission means the broker accepted the instruction. It does not mean anything traded, and for a limit order away from the market it very likely means nothing did.
A system treating a 201 response as a fill believes it holds a position that may not exist. Every downstream decision — sizing the exit, enforcing concurrency limits, computing exposure — then runs on a fiction.
The authoritative signal is the fill, reported separately, with a quantity that may be less than requested. Persist the order identifier immediately on acceptance, because it is the only handle for tracking what actually happens.
The states that are easy to conflate
Three pairs worth separating explicitly.
Working versus filled. An order resting at the broker is protection you do not yet have. A stop that is working will act if triggered; a stop you believe is working but which expired will not.
Partially filled versus filled. A four-of-ten fill is a position, and it is not the position you sized for. Reading remaining quantity per leg rather than assuming the submitted amount is the fix.
Triggered versus filled. A stop that triggered has sent an order. A stop-limit that triggered may still be working unfilled, which means you hold the position you decided to close. Systems that treat a triggered stop as a closed position are wrong in the most expensive direction.
The state you cannot observe
Submission that times out is genuinely ambiguous. You do not know whether the order was accepted.
Both possibilities are live, and the naive response — resubmit — doubles the position if the first attempt landed. The correct recovery is to query the broker and establish what exists before taking any further action.
Where a broker permits a client-supplied order identifier, use it. It converts reconciliation from a guess based on timestamps and symbols into a lookup.
Modelling it in software
Represent the state explicitly. Not a boolean, not an inference from the last API response. A field with a known set of values and defined transitions.
Make transitions event-driven where possible. Some brokers stream order status changes, which is faster and cheaper than polling and removes the interval between polls as a source of staleness. Where polling is the only option, note that live-order queries can include recently filled and cancelled orders as well as working ones, so filtering matters.
Handle every terminal state. Filled, cancelled, rejected, and expired all end the order's life and each requires different downstream action. A system with handling for filled and a catch-all for everything else will eventually treat a rejection as a fill or vice versa.
Time out your own tracking. An order that has been in a non-terminal state longer than plausible is a signal that something is wrong, not a reason to keep waiting.
Reconcile against the broker. Internal state is a cache and the broker is authoritative. That reconciliation belongs off the order path, on worker thread pools, so it cannot delay a live submission.
The honest limits
State vocabulary varies between brokers, so a system written against one broker's names may misread another's. Map to your own internal states explicitly rather than storing broker strings.
Status reporting can lag reality briefly, particularly under load, so a state read is a snapshot rather than a guarantee.
And correct state modelling does not improve a strategy. It prevents a class of avoidable failure where the system acts on positions it does not hold. Position sizing bounds what those failures cost — capital divided by twenty as the ceiling on any single position, under the divide-by-20 rule, enforced on infrastructure you control.
Frequently asked questions
What are the main order states? Accepted, working, partially filled, filled, cancelled, rejected, and expired, with broker-specific variations in naming.
Does a successful order response mean it filled? No. It means the broker accepted the instruction. Fills are reported separately and may be partial.
What is the difference between triggered and filled? A triggered stop has sent an order. A stop-limit that triggered may still be working unfilled, leaving you in the position.
What should I do when a submission times out? Query the broker to establish what exists. Resubmitting doubles the position if the first attempt landed.
Should I poll or stream order status? Streaming where available is faster and removes polling interval as a source of staleness. Keep a query path for reconciliation regardless.
Disclaimer: This article is educational content about trading software and mechanics. It is not investment advice, financial advice, tax advice, legal advice, or a recommendation to buy or sell any security, nor a recommendation of any platform, broker, or strategy. Competitor features, pricing, and terms described here reflect publicly available information as of publication and change frequently; verify against each vendor's current official sources before making a decision. Options and futures trading involve substantial risk of loss and are not suitable for all investors. Please read Characteristics and Risks of Standardized Options before trading options. Automated trading carries additional risks including software defects, connectivity failures, broker API changes, and outages that may prevent orders from being placed, modified, or cancelled. Past performance does not indicate future results, and no platform, configuration, position-sizing rule, or risk setting can guarantee a profit or prevent a loss.
StaxInvesting LLC sells self-hosted trading software. It is not a broker-dealer, investment adviser, or financial institution, and it does not manage accounts, hold member funds, place trades on behalf of members, or access member brokerage accounts. Members run the software in their own cloud environment, connect their own brokerage accounts under their own credentials, and are solely responsible for their configuration, their credential security, and every trade executed in their account. Broker handling, approval levels, and available features vary; verify against your broker's current documentation. Consult a qualified financial adviser and tax professional regarding your individual circumstances.