fix(brokers): map Robinhood's partially_filled state to OPEN - #229
Merged
Conversation
STATE_TO_PORT_STATUS took only Robinhood's query-filter state enum
({open, canceled, filled, failed, pending}), so partially_filled --
which the order-response enum carries instead -- fell through
to_port_status's default and read as PENDING. A partially-filled GTC
limit is still resting and working at the venue, so OPEN is the
honest status; PENDING misdescribes it as not-yet-working.
The map now covers the union of both enums, since either shape could
arrive. partially_filled's spelling is doc-sourced only -- no order
object has ever been observed live (#198) -- and the docs disagree on
which enum applies, so the comment names that explicitly rather than
presenting it as verified.
Confirmed latent, not a behaviour fix: reconcile.py branches only on
FILLED and the terminal/dead set, so OPEN and PENDING already take
the identical "still resting, keep polling" path. This becomes live
only once a consumer distinguishes the two.
Closes #226
Refs #198
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
eaitbrahim
added a commit
that referenced
this pull request
Aug 11, 2026
…the CTS scoring fix (#241) A minor bump, not a patch, for three reasons that each require operator action or change behaviour the deployment is currently relying on. SCHEMA. `SCHEMA_VERSION` goes 9 -> 10 (#223). Both deployed databases are at 9 and must be migrated before this build can use them. BEHAVIOUR REQUIRING OPERATOR ACTION. #223 adds a second attested claim -- what CONTRACT a venue listing is, not only what the underlying asset is. It fails closed with no backfill, deliberately, so after this lands `keel assets screen` REJECTS every product with `instrument_wrapper: UNATTESTED` until `keel assets attest-instrument` is run once per product. Live trading is unaffected: rail 1 gates buys on `config.allowlist`, not on the screen. LIVE SCORING CHANGED. #227 fixed `is_round_number`, which returned True for every 2dp-quoted price and so handed BTC/ETH/PAXG a free CTS point on every bar. Scores on those three assets are genuinely lower under this build than under 0.5.7. Also ships: the Robinhood crypto adapter behind the broker port (#216/#218/#222/#229, not wired to the live path), the TUI activity feed (#235/#237), the CTS factor collinearity study (#224), `Preview.synthetic` at the confirm gate (#221), rail 9 seeing a bracket's own stop (#212), and CI gating merges on the `test` check (#234/#238). Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
What
STATE_TO_PORT_STATUSinpackages/keel-broker-robinhood/keel_broker_robinhood/translate.pyonly covered Robinhood's query-filterstateenum (open,canceled,filled,failed,pending). The order-response enum instead carriespartially_filledand omitspending. Apartially_filledorder therefore fell throughto_port_status's default and read asPENDING.partially_fillednow maps toOPEN: a partially-filled GTC limit is still resting and working at the venue, soOPENis the honest status;PENDINGreads as not-yet-working.OPENis already in the port's accepted status vocabulary (see the conformance suite's status assertion).The map now covers the union of both enums (
pendingwas kept, not removed) since either shape could plausibly arrive in a response.Unverified spelling
Per #198, no Robinhood order object has ever been observed live, and Robinhood's own docs disagree about which
stateenum applies to the order-response object.partially_filled's exact spelling is therefore doc-sourced, not confirmed. The added comment says so explicitly and names which of the two documented enums it came from, rather than presenting it as verified.to_port_status's fallthrough-to-PENDINGdefault is unchanged -- its docstring's reasoning (an unrecognised state means the adapter doesn't know the outcome;FAILEDwould falsely declare a terminal result) still holds and applies to any future unmapped state.Latent, not a behaviour fix
Confirmed by reading
keel/execution/reconcile.py: it branches only on_FILLED = "FILLED"(line 36) and_DEAD = frozenset({"CANCELLED", "CANCELED", "EXPIRED", "FAILED"})(line 37). Thestatus != _FILLEDcheck at line 102 treats every non-terminal status identically --OPENandPENDINGtake the exact same "still resting, keep polling" path today. This PR corrects a factual claim about the venue's vocabulary; it does not change behaviour until some future consumer distinguishesOPENfromPENDING.Closes #226
Refs #198
Test plan
TDD: added
test_to_port_status_maps_partially_filled_to_open_not_pending, confirmed red before the fix (to_port_status("partially_filled")returned"PENDING"), green after. Existingtest_to_port_status_defaults_an_unknown_state_to_pending_not_failedandtest_to_port_status_defaults_none_to_pendingpin that the fallthrough default is unchanged and unnarrowed.uv run ruff check keel tests packages scripts-- all checks passeduv run pytest -q-- 2445 passed, 1 skipped (baseline 2444 passed / 1 skipped + 1 new test; the one skip is the conformance candles probe,suite.py:254, as expected)uv run mypy-- Success: no issues found in 219 source files🤖 Generated with Claude Code