Portfolio Margin exchange (binance.com-portfolio_margin)¶
Scoped to listenKey/user-data only, no market-data or WS API support¶
Id: 2e0e813a-696b-4160-a7a7-93157d71f556
Type: constraint
Status: active
Evidence: confirmed
Source: issue #452, commit b53277f2
binance.com-portfolio_margin only supports the user-data stream lifecycle (wss://fstream.binance.com/pm/ws/<listenKey>, listenKey acquired/kept-alive/closed via UBRA’s PAPI methods). There is no websocket_api_base_uri (no WS API/order placement) and no public market-data endpoint for this exchange.
Reason: Binance’s Portfolio Margin API itself doesn’t expose those surfaces (yet) — the scope here tracks what’s actually available upstream, not an arbitrary internal limitation.
Deliberately outside BINANCE_FUTURES_EXCHANGES¶
Id: 2c3e3ab6-80f3-416d-b083-bba90f59c8d6
Type: decision
Status: active
Evidence: confirmed
Source: commit b53277f2: “deliberately outside BINANCE_FUTURES_EXCHANGES”
Rejected alternative: folding binance.com-portfolio_margin into the existing BINANCE_FUTURES_EXCHANGES group, since it’s futures-adjacent and could reuse that code path.
Reason it was rejected: Portfolio Margin keeps the legacy /ws/<listenKey> URL form, while binance.com-futures / binance.com-futures-testnet already moved to the newer /private/ws?listenKey=...&events=... form. Grouping them would mean branching on exchange inside shared futures-path code for a URL-shape difference — kept as its own exchange entry instead so the routing stays a lookup, not a conditional.
Follow-up: graceful degradation for the not-yet-released dependency¶
Id: 08498697-bc88-4c1d-b477-b2b392f5c337
Type: workaround
Status: active
Evidence: confirmed
Source: commit f44ee416, found during self-review of PR #453
_init_ubra() previously let UBRA’s UnknownExchange exception propagate uncaught whenever the installed UBRA version didn’t yet recognize binance.com-portfolio_margin (true for any UBRA release before PAPI support landed). That crashed the calling stream thread instead of degrading to (None, None), which is what the surrounding AttributeError handling was already designed to produce. get_listen_key(), keepalive_listen_key(), and delete_listen_key() now check _init_ubra()’s return value and bail out cleanly instead.
Note: this is a controlled degradation for a specific, known “dependency not released yet” condition on a single exchange’s optional listenKey path — not a general precedent for swallowing errors. The suite’s broader convention is to fail loud on broken invariants rather than silently continue; this case is about a well-defined missing-feature check, not a corrupted-data path.
Revisit when: UBRA ships full Portfolio Margin support as a stable released version — at that point UnknownExchange for this exchange should no longer be reachable in practice, and the degradation path becomes dead code worth re-checking.

