Keep four different truths separate
| Responsibility | Question it answers | A dangerous shortcut |
|---|---|---|
| Order state | Is the order pending, working, partly filled, filled, rejected or canceled? | Treating “sent” as “accepted” |
| Execution records | Which individual fills occurred, for how much, and at what price? | Booking the same execution twice |
| Cash and position reconciliation | Does the internal ledger agree with independent broker or clearing records for the same scope and cutoff? | Trusting a green order-status label as proof of balances |
| Risk controls | What further activity is permitted given positions, working orders and uncertainty? | Checking only the last known filled position |
ExecType from the current order state in OrdStatus; pending cancellation does not mean canceled. See the FIX order-state specification, especially its introduction and cancellation scenarios. Interactive Brokers likewise documents duplicate status callbacks and fills during pending cancellation.One order, three messages, two fills
Here is a deliberately small offline Python example, executed with Python 3.14.4. The input contains only the validated fictional buy fills above; whole-dollar integer arithmetic is sufficient for this example.
events = [("F1", 3, 100), ("F1", 3, 100), ("F2", 2, 101)]
seen = {}
cash, position = 1000, 0
order_quantity = 5
for fill_id, quantity, price in events:
details = (quantity, price)
if fill_id in seen:
if seen[fill_id] != details:
raise ValueError("conflicting fill identifier")
continue
seen[fill_id] = details
cash -= quantity * price
position += quantity
state = "FILLED" if position == order_quantity else "PARTIAL"
wealth = cash + position * 101
print(state, position, cash, len(seen))
# FILLED 5 498 2
print(wealth)
# 1003seen, cash and position. The example has no concurrent writers, persistence, order submission, acknowledgement processing or reconciliation. Its two-state expression does not implement a real order state machine.When the connection fails
A rejected new order with no fills creates no position from that order. Record the reason and decide whether a corrected request is allowed; repeatedly resending an invalid request is not recovery. Rejection of a cancel request has a different meaning: the original order may still be working or may already have filled.
A missing acknowledgement is more difficult. The destination may have accepted the order before the response was lost. Mark its outcome as uncertain, retain its identity, and use the protocol's supported status and execution recovery procedure. Sending a fresh order blindly can double the intended trade. Until uncertainty is resolved, risk checks need a conservative treatment of the potentially live quantity. A successful local send or a restored socket does not settle the question.
Controls must lead to actions
Observability means retaining enough evidence to understand operation: correlated order and execution identifiers, event and receipt times, data age, rejects, unreconciled balances and the reason a control fired. Alerts need an owner, an escalation rule and a defined response.
Operators need tested controls to pause new orders, request cancellation, restrict a strategy and authorise resumption. Pausing new orders does not cancel existing ones; cancellation requests need confirmation, and neither action reverses completed fills. A kill control's scope must be explicit, including what happens when connectivity is unavailable. Recovery exercises and incident records are part of operating the system.
Sources
- FIX Trading Community — Order State Changes, FIX Latest, EP258, July 2020: introduction, state precedence and cancellation scenarios.
- Interactive Brokers — Order Status: duplicate callbacks, pending submission and pending cancellation.
- Interactive Brokers — Execution Details: fill identifiers, corrections and retrieval limits.
- PostgreSQL 18 — Transactions: local atomic updates and transaction boundaries.
- Python 3.14 — Mapping Types: dictionary membership, lookup and assignment used in the example.