[RFC] Refine live reconciliation for pending cancel/update orders
Context
This is a policy refinement rather than a bug fix.
The current live reconciliation policy for inflight orders treats SUBMITTED orders and PENDING_CANCEL / PENDING_UPDATE orders the same once the retry budget is exhausted in the runtime check path. That works for never-acknowledged submits, but it is too aggressive for ambiguous cancel/update outcomes, where the command may have timed out, never reached the venue, or reached the venue while the follow-up status query failed.
The specific failure mode is a network outage or prolonged connectivity problem that lasts long enough for inflight_check_retries to be exhausted. In that case, a live PENDING_CANCEL or PENDING_UPDATE order can be marked locally as CANCELED even though the venue never confirmed the cancel/modify request. Once that synthetic terminal state is applied, later venue recovery and reconciliation can no longer move the local order to the real final state, because the order has already been closed out incorrectly.
For those pending states, retry exhaustion alone does not prove the venue canceled or modified the order. Forcing a synthetic terminal event at that point can create a local state transition that is more certain than the actual venue outcome.
Proposed Change
Keep the existing SUBMITTED timeout rejection behavior.
For PENDING_CANCEL and PENDING_UPDATE, log a warning and leave the order unresolved so that venue reconciliation can determine the final state.
Why this is preferable
- It avoids a false terminal transition when the venue outcome is still ambiguous.
- It prevents a connectivity outage from hard-closing orders locally before the venue has actually confirmed the outcome.
- It keeps reconciliation as the source of truth for pending cancel/update outcomes.
- It keeps Rust and Python live execution behavior aligned.
Considerations
- Unresolved pending orders may remain tracked longer, which is safer than resolving them incorrectly.
- We may want a future follow-up for stale unresolved-order metrics or alerting, but that should be separate from the state transition policy itself.
- The change is intentionally narrow and does not alter the existing
SUBMITTEDtimeout behavior.
Alternatives considered
- Keep synthesizing
CANCELEDfor pending cancel/update orders after retry exhaustion. Simple, but it can misrepresent the real venue state. - Introduce a new local terminal state for unresolved outcomes. That adds complexity without solving the core reconciliation problem.
- Leave pending cancel/update orders unresolved until venue reconciliation resolves them. Preferred.
Related implementation notes
- Rust:
crates/live/src/manager.rs - Python:
nautilus_trader/live/execution_engine.py - Tests:
crates/live/tests/manager.rs,tests/unit_tests/live/test_execution_engine.py,tests/unit_tests/live/test_execution_order_checks.py - Docs:
docs/concepts/live.md,docs/how_to/configure_live_trading.md
Source: nautechsystems/nautilus_trader