bridge: a completed order does not return to the open book (ibx#262) - #360
Open
userFRM wants to merge 1 commit into
Open
bridge: a completed order does not return to the open book (ibx#262)#360userFRM wants to merge 1 commit into
userFRM wants to merge 1 commit into
Conversation
userFRM
force-pushed
the
fix/terminal-order-not-resurrected
branch
from
July 30, 2026 15:44
c7661be to
d1380a3
Compare
userFRM
force-pushed
the
fix/terminal-order-not-resurrected
branch
from
July 31, 2026 11:31
d1380a3 to
749b71b
Compare
Nothing remembered that an order had reached a terminal state, so a replayed frame put it back into the open book. `push_order_info` was a bare insert with no ordering rule, and `drain_open_orders` filters on the string it writes — so the reconnect open-order burst racing a fill, or any message the gateway resends, made `req_open_orders` report a completed order as live. A strategy then re-manages a position it already has, or cancels an order that no longer exists, with the open-order snapshot corroborating the wrong picture. The cached status cannot be what carries that knowledge. Completing an order evicts its cache row, so the replayed frame finds nothing to refuse and inserts itself — the ordinary path, not an edge case. A cached string is also overwritten by the next terminal report, so a report arriving between the completion and the replay would erase the evidence. A set of completed order ids carries it instead, recorded where the completed-order record is already written and surviving the eviction. Entries are held by age rather than by count: what the memory has to outlive is the window in which a stale frame for the order can still arrive, and a reconnect replays recent activity within seconds — that is a duration, not a number of orders. Counting instead meant a busy session's unrelated completions pushed a still-relevant entry out, and the replay it was there to refuse got back in. A generous cap remains as a backstop against a session completing orders faster than they expire: expired entries are pruned first, and because a session fast enough to need the backstop can still be over the cap with nothing yet expired, the oldest survivors are evicted next until the set is back under it — the cap bounds the set itself, not just the entries old enough to prune. The rule covers every status the engine treats as ending an order's life — filled, cancelled and rejected are the three it acts on by removing the order from its book. `Inactive` is not among them, because it returns to working when the condition holding the order clears, and neither is `Uncertain`, which states the opposite of a conclusion. A trade cancel or trade correction restates an execution the gateway has already reported and can legitimately return a filled order to a working quantity. That is the gateway's own statement rather than a replay of an older one, so those two execution types write through a separate entry point that supersedes the completion rather than being refused by it. Closes deepentropy#262.
userFRM
force-pushed
the
fix/terminal-order-not-resurrected
branch
from
July 31, 2026 12:52
749b71b to
c46907b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Nothing remembered that an order had reached a terminal state, so a replayed frame put it back into the open book.
push_order_infowas a bare insert with no ordering rule, anddrain_open_ordersfilters on the string it writes. The reconnect open-order burst racing a fill — or any message the gateway resends — madereq_open_ordersreport a completed order as live. A strategy then re-manages a position it already has, or cancels an order that no longer exists, with the open-order snapshot corroborating the wrong picture.Why the cached status cannot carry it
Two ways a status-only guard is bypassed:
What this changes
A bounded set of completed order ids carries the knowledge instead, recorded where the completed-order record is already written and surviving the eviction. It is capped and oldest-first, because a session completes orders indefinitely; the width only has to outlive a reconnect's replay of recent activity, which is what resurrects an order.
The rule covers every status the engine treats as ending an order's life —
Filled,CancelledandRejectedare the three it acts on by removing the order from its book.Inactiveis not among them, because it returns to working when the condition holding the order clears, and neither isUncertain, which states the opposite of a conclusion.A trade cancel (
150=H) or trade correction (150=G) restates an execution the gateway has already reported and can legitimately return a filled order to a working quantity. That is the gateway's own statement, not a replay of an older one, so those two execution types write through a separate entry point that supersedes the completion rather than being refused by it.Tests
a_completed_order_is_not_returned_to_the_open_book— all three terminal statuses, all four open ones.a_completion_outlives_the_cache_row_it_evicts— the ordinary completion path.an_intervening_report_does_not_erase_the_completion—Filled → Cancelled → Submitted.a_trade_correction_can_reopen_a_completed_order— and the order stops being remembered afterwards.a_fill_still_writes_over_a_working_status— the ordinary direction, so the guards are not passing against a cache that refuses everything.the_completed_memory_is_bounded— capped, oldest evicted first.Each fails by name against a compiling reversion of the production change it covers.
Closes #262.
Test plan
a_completion_outlives_the_cache_row_it_evictsby name.Filledfailsa_completed_order_is_not_returned_to_the_open_book.a_trade_correction_can_reopen_a_completed_order.the_completed_memory_is_bounded.a_fill_still_writes_over_a_working_status, so the guards are not passing against a cache that refuses everything.cargo check --offlineclean on--lib,--lib --features python,--bins,--examples, and each integration target individually.tests/ib_paper_compatcompared against a clean checkout of the base commit — identical sorted diagnostic sets.cargo test --offline --lib— only the two knownconfig::expiry_testsfailures, which fail on the base commit for missing legacy tzdata (fixed separately in config: resolve the legacy timezone names IB states its times in (ibx#335) #336).