Skip to content

fix(broker-alpaca): NaN-safe money coercion, host-map-only construction, quoted path segments - #384

Merged
eaitbrahim merged 1 commit into
mainfrom
fix/369-alpaca-review-hardening
Aug 19, 2026
Merged

fix(broker-alpaca): NaN-safe money coercion, host-map-only construction, quoted path segments#384
eaitbrahim merged 1 commit into
mainfrom
fix/369-alpaca-review-hardening

Conversation

@eaitbrahim

Copy link
Copy Markdown
Contributor

Follow-up to #382 (merged pre-review): the adversarial review's five findings, applied TDD red-first.

  1. [major] NaN/Infinity coercion hole_decimal_or_none parsed 'nan'/'Infinity' successfully (JSON NaN arrives via parse_constant), crashing preview_order at bid > 0 and quote_size / crossed, get_balances at min(buying_power, cash), and silently poisoning Candle OHLCV — contradicting the preview docstring's populate-errors invariant. Now is_finite()-guarded; three red-first tests cover quote sides, balances, and bar leaves.
  2. [minor] Host-map-only construction — the dead trading_host=/data_host= overrides (zero callers anywhere) are REMOVED; TRADING_HOSTS is the only endpoint→host map and the README's "no configuration path from a paper credential to the live host, by construction" is now literally true (pinned by test).
  3. [minor] Quoted URL path segments — symbol/order-id interpolations percent-encoded (safe="") at all four sites; A/B-USD and a crafted order id can no longer reshape the request (asserted on the outgoing URL).
  4. [nit] Naive timestamps refused — offset-less RFC3339 no longer silently read as local time; ValueError per the module's refusal convention.
  5. [nit] SEC fee framing — the provenance note now states the advisory rate (.60/$M) is already in force and Alpaca's page is the stale side; the encoded venue-published .90/$M stays deliberate (conservative direction, ~$0.02/$10k) with the delta as the re-measurement trigger.

Gates: uv run pytest -q → 3110 passed, 1 skipped; ruff clean; mypy clean (249 files).

Refs #369 (Phase A hardening).

…on, quoted path segments

Adversarial-review findings on the new keel-broker-alpaca package:

1. _decimal_or_none now rejects non-finite Decimals explicitly: JSON NaN/Infinity
   tokens arrive via parse_constant (not parse_float) and Decimal(str(...)) parses
   them without raising, so the except never fired -- NaN crashed preview_order
   (bid > 0) and get_balances (min()), Infinity compared as a real price, and NaN
   silently poisoned Candle OHLCV. Non-finite now lands in the same None path as
   any unparseable leaf, keeping the preview's errors invariant true.
2. Removed the dead trading_host=/data_host= constructor escapes (zero callers):
   TRADING_HOSTS is now the only map from an environment choice to a host, making
   the README's no-paper-to-live-path claim literally true of the signature.
3. Percent-encode interpolated path segments (quote(..., safe='')) at every site:
   /v2/orders/{id} on GET/DELETE and /v2/stocks/{symbol}/bars|quotes/latest, so a
   '/' or '?' inside an id or symbol can never reshape the request.
4. to_unix_seconds refuses offset-less timestamps (ValueError) instead of letting
   .timestamp() silently assume the host's local zone.
5. Reworded the SEC Section 31 provenance: advisory 2026-2 (.60/M) is already
   in force (2026-04-04), Alpaca's page is the stale side, and the encoded .90
   deliberately tracks the venue's own figure (~/bin/zsh.02/k, conservative) with the
   delta as the re-measurement trigger.

All tests written red-first; gates: 3110 passed/1 skipped, ruff clean, mypy clean.
@eaitbrahim
eaitbrahim merged commit 81a6195 into main Aug 19, 2026
5 checks passed
@eaitbrahim
eaitbrahim deleted the fix/369-alpaca-review-hardening branch August 19, 2026 00:29
eaitbrahim added a commit that referenced this pull request Aug 20, 2026
Minor, not patch. Three things since v0.9.3 change what an implementor or a
deployment can rely on:

* A NEW DISTRIBUTION. `keel-broker-alpaca` (#382, #384) plus the
  paper-equities profile that selects it (#386), so a deployment can now be
  US equities via the broker port rather than crypto only.
* THE PORT CONTRACT MOVED TWICE. `market_clock`/`market_schedule` made
  venues session-aware (#385), and `place_order` gained `idempotency_key`
  (#419). Both carry defaults so no CALLER breaks, but a third-party adapter
  that does not accept them is no longer a `Broker` -- the conformance suite
  now says so. That is exactly the kind of change a patch bump must not
  hide.
* THE OPERATOR CONSOLE. The TUI became keel's console across #399-#408, and
  `keel update` (#415/#417) makes a deployment self-updating.

Every pinned sibling moves with it. The four production distributions are
required `==` at this exact version (`RELEASING.md`, "Release assets"), so a
bump that missed one would install a mixed set -- the `keel-trader 0.5.7`
against `keel-core 0.5.5` failure `keel versions` exists to catch, and which
`~/keel` actually ran across two releases.

Also in this window, on the Robinhood adapter: the best_bid_ask fixture
corrected against the live venue (#414), a credential guard that catches the
error it only claimed to (#416), pre-flight sizing reported on the preview
(#418), transport backoff (#420), and the fenced one-order probe (#421).

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant