feat(client): add perps support#133
Conversation
Port Perps support from the TypeScript SDK to the async clients: - Public reads on AsyncPublicClient and AsyncSecureClient: instruments, tickers (with statistics merge), book, fees, and paginated candles, funding history, and trades with SDK-owned opaque cursors. - Perps market data streaming through subscribe() with per-topic specs (trades, bbo, book, candles, tickers, statistics), channel dedupe, and reconnect with resubscribe. - Delegated credential lifecycle: open_perps_session creates or resumes credentials (CreateProxy typed-data signature, validation, default 7d expiry) and revoke_perps_credentials signs deleteProxy ops. - PerpsSession trading over WebSocket with EIP-712 signed msgpack ops: place_order (gtc/ioc/fok overloads, optional TP/SL triggers), post_orders, place_position_tp_sl, cancel_order(s), update_leverage, session account reads, and an async-iterable event stream with sequence-gap and reconnect resync events. - Funds: deposit_to_perps (EOA broadcast or gasless relayer call) and withdraw_from_perps (owner-signed Withdraw typed data). Signing is pinned byte-for-byte against golden vectors generated from the TypeScript implementation (msgpack encoding, Op data hash, and signatures). Verified with ruff, pyright, the unit suite, and the live integration suite including a 10 USDC deposit/withdraw roundtrip and GTC and TP/SL order place/cancel against production.
| if has_more and last_ts is not None: | ||
| next_cursor = encode_perps_cursor( | ||
| {**state, "start_timestamp": last_ts + interval_ms(state["interval"])} | ||
| ) |
There was a problem hiding this comment.
Candles pagination ignores end bound
Medium Severity
list_perps_candles sets has_more from the API more flag and a parsed last timestamp only, without comparing the next window start to end_timestamp. Account equity/PnL paginators in the same change stop when the last point reaches the end bound. Candle paging can advance start_timestamp past end, causing an invalid range, skipped buckets near the end, or extra empty pages.
Reviewed by Cursor Bugbot for commit c646fcd. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 4073de5. Configure here.
| self._scheduler.reset() | ||
| if emit_resync: | ||
| self._sequences.clear() | ||
| self._push(PerpsResyncEvent(reason="reconnect")) |
There was a problem hiding this comment.
Stale order buffer after reconnect
Medium Severity
After a WebSocket reconnect, _recent_orders is left intact while sequence state is cleared. place_order can then resolve from a pre-reconnect buffered update for the same order_id, returning an outdated order snapshot instead of waiting for the new placement’s orders-channel event.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 4073de5. Configure here.
fix(client): tighten Perps request typing
feat(client): support reduce-only Perps orders
feat(client): add Perps cancel-all orders


Ports Perps support from the TypeScript SDK (ts-sdk #133, #145, #147, #149, #154, #158–#162, #167, #172, and TP/SL from #153) to the Python SDK. Perps lands on the async clients only since the trading UX is WebSocket-centric.
Surface
Public reads (
AsyncPublicClientandAsyncSecureClient)fetch_perps_instruments/ticker/tickers/book/fees(tickers merge 24h statistics)list_perps_candles/funding_history/tradeswith SDK-owned opaque cursors (forward interval stepping for candles, backward windows with boundary trade-id dedupe for trades)Market data streaming
subscribe():PerpsTradesSpec,PerpsBboSpec,PerpsBookSpec,PerpsCandlesSpec,PerpsTickersSpec,PerpsStatisticsSpecDelegated credentials (
AsyncSecureClient)open_perps_session()creates credentials with a wallet-signed CreateProxy payload (default 7d expiry,expires_in=timedelta(...)override) or validates and resumes existingPerpsCredentialsrevoke_perps_credentials()signs a deleteProxy op with the owner accountPerpsSession(trading over WebSocket)Oppayloads committing to msgpack-encoded positional tuples, using the delegated session keyplace_order(gtc/ioc/fok overloads; gtc requirespriceand allowspost_only; optionaltake_profit/stop_losstriggers place a grouped bracket),post_orders,place_position_tp_sl,cancel_order(s),update_leveragetpsl) with sequence-gap detection and reconnectresynceventsFunds
deposit_to_perps(EOA broadcast or gasless relayer call for proxy/safe/deposit wallets)withdraw_from_perps(owner-signed Withdraw typed data; note the wire timestamp is seconds)Notable decisions
expires_intakes atimedelta; models exposeDecimalanddatetimeUserInputError(the TS session account reads raiseTransportError; flagged as an inconsistency there)place_orderreturns onePerpsOrderPlacement(order, tp_sl=None)result type instead of overloaded result shapesmsgpackadded as a core dependency for op signingVerification
Op.datahashes, and full signatures for createOrders (including grouped TP/SL rows), cancels, updateLeverage, deleteProxy, CreateProxy, and Withdraw; the deposit calldata is pinned against the TS encoder as welluv run ruff check .,uv run ruff format --check .,uv run pyright(0 errors),uv run pytest -m "not integration"(1782 passed)perps.test.tsand passed against production, including the metered flows: 10 USDC deposit/withdraw roundtrip with deposit confirmation tracking, GTC place/cancel, TP/SL bracket place/cancel, and credential create/resume/revoke/invalid-secretdeposit_wallet_clientfixture; the deposit test uses a relayer-enabled variant with the builder API key already provided by the integration workflowNote
High Risk
Introduces live trading, collateral movement, and cryptographic signing paths; mistakes could affect funds, orders, or auth. Large new surface area increases integration and regression risk despite test coverage.
Overview
Adds Perps (perpetuals) to the async clients, ported from the TypeScript SDK: new REST/WS endpoints on
Environment, amsgpackdependency for command signing, and broad public exports of Perps models.Public & secure clients gain
fetch_perps_*/list_perps_*for instruments, tickers, books, fees, candles, funding, and trades, plus Perps market streaming via newPerps*Spectypes wired intosubscribe().AsyncSecureClientadds delegated credential lifecycle (open_perps_session,revoke_perps_credentials),deposit_to_perps/withdraw_from_perps, and an ERC-20 approval for the Perps deposit contract in default trading approvals.PerpsSessionis a new WebSocket-centric trading surface: EIP-712–signed msgpack ops for place/cancel/leverage/TP-SL, private channel events with reconnect/resync, and REST account history with SDK-owned pagination cursors.Reviewed by Cursor Bugbot for commit 55adc5a. Bugbot is set up for automated code reviews on this repo. Configure here.