Follow-up to #192, which merged before the independent review's fixes had landed. All findings below are present on main as of 9422c25.
None of this is reachable today. keel/execution/executor.py:486 still calls broker.place_order(product_id, side, order_configuration) — a different signature from the port's place_order(spec) — and keel/commands/_common.py still constructs CoinbaseClient. Installing the package registers an entry point and nothing else. These become live-money defects the moment the port migration wires it up.
Blockers
- B1 —
str(Decimal) emits scientific notation. translate.py:127,136-137,148-150. Verified: str(Decimal('0.00000001')) == '1E-8', and BTC's asset_increment is exactly 0.00000001 per tests/fixtures/rh_trading_pairs.json. A dust-sized exit puts "asset_quantity": "1E-8" on the wire. Use format(d, 'f').
The guarding test (tests/broker_robinhood/test_translate.py:108-120, test_decimals_are_rendered_as_exact_strings_not_floats) asserts the property using two values that cannot trigger the exponent form.
- B2 —
place_order reports success for a rejected order. adapter.py:~310-335 checks only that an id came back and never reads state. A StopLimitGTC answered 200 {"id": ..., "state": "failed"} records a protective stop that does not exist at the venue.
- B3 —
estimated_price namespace inconsistent. transport.py:355 uses /api/v2/crypto/trading/estimated_price/ while transport.py:347 uses /api/v2/crypto/marketdata/best_bid_ask/. Settle from the docs and pin the URL in a comment.
- B4 —
get_order/cancel_order omit account_number. transport.py:376,387, where create_order and holdings both send it. A resulting 404 launders through _request → None → _terminal_unknown → status FAILED with zeroed money for a live resting order, corrupting reconciliation.
Should-fix
- S1 Silent-zero previews:
_estimated_price returns Decimal("0") on failure and preview_order never populates Preview.errors, so "costs nothing" and "could not price it" are indistinguishable at the confirm gate.
- S2
preview_order approves symbols place_order refuses — the LimitGTC | StopLimitGTC branch never calls to_symbol, so ETH-USDC previews cleanly then raises UnsupportedOrder, which the port forbids catching and retrying.
- S3
response.json() default float parsing launders unquoted JSON numbers through float before Decimal sees them. All eight fixtures quote every money value, so this is untested. Use parse_float=Decimal.
- S4
RobinhoodTransport has no tests for the code that talks to the venue — _request, _paginate, _account(), and every literal endpoint path are uncovered. This is what let B3 ship.
- S5 Query string hand-built without percent-encoding (
transport.py:264-269); the same raw string is signed and sent, so an encoding divergence breaks signature verification.
- S6
cancel_order can raise on the exit path (adapter.py:430), against the package's own stated principle that "a raise on the exit path can trap a position".
What the review found clean
The cancel contract is correct — no path returns True without a per-id venue confirmation; the re-poll is real and test-proven; an already-filled order returns False. market_ioc_quote is genuinely unreachable, double-gated, with no divide-by-estimated-price anywhere. Every Preview is constructed synthetic=True. The canned transport is genuinely offline. Packaging is dev-group only, so keel-trader gains no runtime dependency on pynacl.
Follow-up to #192, which merged before the independent review's fixes had landed. All findings below are present on
mainas of9422c25.None of this is reachable today.
keel/execution/executor.py:486still callsbroker.place_order(product_id, side, order_configuration)— a different signature from the port'splace_order(spec)— andkeel/commands/_common.pystill constructsCoinbaseClient. Installing the package registers an entry point and nothing else. These become live-money defects the moment the port migration wires it up.Blockers
str(Decimal)emits scientific notation.translate.py:127,136-137,148-150. Verified:str(Decimal('0.00000001'))=='1E-8', and BTC'sasset_incrementis exactly0.00000001pertests/fixtures/rh_trading_pairs.json. A dust-sized exit puts"asset_quantity": "1E-8"on the wire. Useformat(d, 'f').The guarding test (
tests/broker_robinhood/test_translate.py:108-120,test_decimals_are_rendered_as_exact_strings_not_floats) asserts the property using two values that cannot trigger the exponent form.place_orderreports success for a rejected order.adapter.py:~310-335checks only that anidcame back and never readsstate. AStopLimitGTCanswered200 {"id": ..., "state": "failed"}records a protective stop that does not exist at the venue.estimated_pricenamespace inconsistent.transport.py:355uses/api/v2/crypto/trading/estimated_price/whiletransport.py:347uses/api/v2/crypto/marketdata/best_bid_ask/. Settle from the docs and pin the URL in a comment.get_order/cancel_orderomitaccount_number.transport.py:376,387, wherecreate_orderandholdingsboth send it. A resulting 404 launders through_request→None→_terminal_unknown→ statusFAILEDwith zeroed money for a live resting order, corrupting reconciliation.Should-fix
_estimated_pricereturnsDecimal("0")on failure andpreview_ordernever populatesPreview.errors, so "costs nothing" and "could not price it" are indistinguishable at the confirm gate.preview_orderapproves symbolsplace_orderrefuses — theLimitGTC | StopLimitGTCbranch never callsto_symbol, soETH-USDCpreviews cleanly then raisesUnsupportedOrder, which the port forbids catching and retrying.response.json()default float parsing launders unquoted JSON numbers throughfloatbeforeDecimalsees them. All eight fixtures quote every money value, so this is untested. Useparse_float=Decimal.RobinhoodTransporthas no tests for the code that talks to the venue —_request,_paginate,_account(), and every literal endpoint path are uncovered. This is what let B3 ship.transport.py:264-269); the same raw string is signed and sent, so an encoding divergence breaks signature verification.cancel_ordercan raise on the exit path (adapter.py:430), against the package's own stated principle that "a raise on the exit path can trap a position".What the review found clean
The cancel contract is correct — no path returns
Truewithout a per-id venue confirmation; the re-poll is real and test-proven; an already-filled order returnsFalse.market_ioc_quoteis genuinely unreachable, double-gated, with no divide-by-estimated-price anywhere. EveryPreviewis constructedsynthetic=True. The canned transport is genuinely offline. Packaging is dev-group only, sokeel-tradergains no runtime dependency on pynacl.