Skip to main content
Pre-release

This release is a pre-release and may not be stable for production use.

RISEx Python SDK

An independently maintained, asynchronous Python library for RISEx REST and WebSocket APIs. Python 3.11+; distribution risex-python-sdk, import risex. The package has no dependency on a trading application, strategies, or application configuration.

New to the repository? Start with the developer guide for installation, your first REST call, and your first WebSocket stream.

Documentation Use it for
Developer guide Setup, credentials, common workflows, recovery and troubleshooting
Public API reference Methods, return types, request fields, configuration and errors
Contributing Repository layout, local checks, fixtures and package verification
Testnet integration Wallet preparation and explicit funded test gates
Protocol contracts Wire formats, signatures and provider discrepancies
Security Credential handling and reporting security issues

The SDK's public API consists of Python methods, request/response models and stream events, documented in the public API reference. It does not expose an HTTP server, so it has no SDK-owned Swagger UI or OpenAPI routes. RISEx's testnet Swagger UI documents the upstream exchange routes used by the SDK.

Implemented: public market data; account balances, positions and history; delegated signer registration/revocation; EIP-712 order permits; limit/market orders, cancellation and outcome reconciliation; public and private streams; and a checksummed local orderbook.

This is an alpha release (0.1.0a2). Offline lifecycle tests and live public checks pass, as do live testnet account reads and authenticated order/position snapshots. Funded transactions, live fills and private reconnect/expiry scenarios remain to be validated, with explicit test gates provided.

Installation and environments

Install the alpha release from PyPI:

python -m pip install risex-python-sdk==0.1.0a2

For local development:

python -m venv .venv
source .venv/bin/activate
python -m pip install -e ".[dev]"

An independent consumer can install the checkout with pip install -e /absolute/path/to/risex-python-sdk, or install the built wheel. The SDK is distributed under the MIT license.

The repository owner's manual Publish to PyPI workflow builds, validates and publishes releases from main using GitHub Trusted Publishing. Before the first release, register a pending GitHub Trusted Publisher in your PyPI account:

Field Value
PyPI project name risex-python-sdk
GitHub owner loinsssss
Repository risex-python-sdk
Workflow filename publish.yml
Environment pypi

Run the workflow on main with the exact package version, initially 0.1.0a2. Only the repository owner's account can run the publishing job. No PyPI token needs to be stored in GitHub. For subsequent releases, update the version in pyproject.toml, risex.__version__ and the HTTP user agent together; PyPI release versions cannot be overwritten.

from risex import RiseXClient, RiseXConfig

client = RiseXClient()  # mainnet, the default
client = RiseXClient(mainnet=True)  # mainnet
client = RiseXClient(mainnet=False)  # testnet: both REST and WebSocket
client = RiseXClient(RiseXConfig(mainnet=False, request_timeout=15))

Each constructed client must be closed, preferably with async with. RiseXConfig.testnet() is also supported. Custom transport URLs can be supplied explicitly through config. Conflicting config/mainnet arguments are rejected. Imports do not connect, load .env, read keys, or create files.

Public REST

import asyncio
from risex import RiseXClient


async def main():
    async with RiseXClient(mainnet=False) as client:
        markets = await client.get_markets(market_ids=[1])
        print(markets.markets[0].config.name, markets.markets[0].mark_price)
        book = await client.get_orderbook(1, limit=10)
        print(book.bids, book.asks)


asyncio.run(main())

Market IDs are numeric provider IDs. Prices and quantities use exact Decimal values; market metadata supplies step_size, step_price, and min_order_size. quantity_to_steps() and price_to_ticks() reject invalid precision instead of rounding. Floats are rejected at numerical protocol boundaries.

Accounts and signing

Account reads require an account address, but no signer. Use get_balances(), get_position(market_id), get_positions(), get_open_orders(), get_order(order_id), get_order_history() and get_trade_history(). Paginated iter_positions(), iter_open_orders(), iter_order_history() and iter_trade_history() have explicit page bounds. get_account_snapshot() collects fresh REST balances, positions and open orders; the constituent requests are separate exchange reads.

For signed operations, pass the account identity and its registered delegated signer separately:

import os
from risex import LocalSigner, RiseXClient

signer = LocalSigner(os.environ["RISEX_SIGNER_PRIVATE_KEY"])
client = RiseXClient(mainnet=False, account=os.environ["RISEX_ACCOUNT"], signer=signer)

LocalSigner stores a supplied key in memory. Applications can inject another Signer implementing address and async sign_typed_data(data) -> bytes; signatures must be canonical 65-byte r + s + v, with v 27/28. Recovered signatures are checked against the injected signer's address.

initialize() fetches and cross-checks the signing domain, chain and contracts. Addresses and chain IDs are discovered from the selected environment. register_signer(account_signer, expiration=...) requires the account signer and delegated signer; expiration is Unix seconds. revoke_signer(account_signer) revokes the configured delegated signer. Registration is explicit, and get_signer_status().active recognizes only the provider's active status.

Orders and uncertain outcomes

from decimal import Decimal
from risex import OrderRequest, OrderSide, UnknownOutcomeError

# Inside an async function, with a configured client and registered signer:
request = OrderRequest(
    market_id=1,
    side=OrderSide.BUY,
    quantity=Decimal("0.001"),
    price=Decimal("60000"),
    post_only=True,
)
try:
    receipt = await client.place_order(request)
except UnknownOutcomeError as error:
    resolution = await client.reconcile_submission(error)
    # Persist error.context; inspect resolution and account state before deciding.
    raise
else:
    current = await client.get_order(receipt.order_id)
    await client.cancel_order(receipt.order_id, market_id=1)
    terminal = await client.wait_for_order(receipt.order_id)

Choose actual quantities/prices using current market metadata. A market order uses order_type=OrderType.MARKET and still requires an explicit price bound. Default time-in-force is IOC for market orders, GTC for limit orders. GTT requires nonzero protocol ttl_units; reduce-only, post-only and STP flags are validated. The SDK checks market activity, precision and protocol widths before reserving a nonce.

A submission receipt is not a completed fill. Query order state, account fills or private events for execution. filled_quantity on an IOC/FOK receipt is converted from the provider's WAD integer; unavailable values remain None. Partially filled orders retain provider status and a nonzero filled_size. wait_for_order() times out without cancelling.

Signed POSTs are attempted once. Timeouts, cancellation during transmission, server failures and unusable success responses raise UnknownOutcomeError with non-secret recovery identifiers and a request ID when available. reconcile_submission() queries order history/lookup and nonce state; absence or an unused nonce does not prove rejection. Client order IDs are recovery identifiers, not idempotency keys. Persist them in the consuming application. Signer registration/revocation uncertainty should additionally be checked with get_signer_status(); cancel-all needs a fresh open-order view.

Nonce reservations and signed mutations are coordinated within one client. Use one signing client per account, with exclusive ownership of that account's bitmap nonce range. Multiple clients, processes, manual trading tools or other signers sharing the account require external coordination. Reserved nonces are never recycled locally, including after errors.

WebSockets

The implementation is in src/risex/websocket.py; subscriptions are exposed by the public client:

Public streams Private streams (account + registered signer)
stream_orderbook() stream_orders()
stream_trades() stream_positions()
stream_oracle() stream_fills()

stream(channel, market_ids=[1]) is the general subscription API. Omit IDs to subscribe to all markets. Each stream owns a socket, starts when iterated, supports one reader, and must be closed when consumption ends.

from risex import ConnectionEvent, LocalOrderbook, RiseXClient

book = LocalOrderbook(1)
async with RiseXClient(mainnet=False) as client:
    async with client.stream_orderbook(market_ids=[1]) as stream:
        async for event in stream:
            book.apply(event)
            if isinstance(event, ConnectionEvent):
                print(event.state, "stale:", event.stale)
            else:
                print(book.snapshot.bids[:1], book.snapshot.asks[:1])

The stream checks acknowledgement status and filters, retains snapshots received before acknowledgements, handles protocol ping/pong, and applies bounded buffering and reconnect/resubscription. Private reconnects request a fresh server nonce and authenticate again; authorization rejection terminates.

Consume ConnectionEvent alongside data events. Disconnects mark prior state stale. Orders/positions supply snapshots; trades, oracle and fills have no replay guarantee. After reconnect, refresh REST account views and backfill fill history with an application checkpoint and ID deduplication.

LocalOrderbook replaces snapshots, applies/deletes levels, and validates full-book CRC32. It invalidates on connection events or checksum failure. After ChecksumMismatchError, close the stream and open a fresh one. A depth-limited REST book cannot seed full-book checksum validation.

Limits and errors

Read retries cover transient GET transport failures and HTTP 429/500/502/503/504, within configured bounds. Retry-After is honored or the request fails when it exceeds the delay budget. APIError preserves status, code and request ID; RateLimitError also exposes retry delay.

Default per-client pacing is 40 REST requests/second and 8 WebSocket JSON requests/second, shared across its streams. Applications must coordinate multiple clients sharing an IP. Timeouts, buffers, read retries, reconnect budgets and pacing are configured with RiseXConfig.

Parsing errors raise ProtocolError; authentication/subscription errors raise AuthenticationError/SubscriptionError. Stream transport exhaustion raises ReconnectExhaustedError. These errors do not silently replace stale state.

Examples and checks

python examples/public_rest.py --testnet
python examples/public_stream.py --testnet
python examples/account_reads.py --testnet
python examples/private_stream.py --testnet
# Mutating examples require explicit execution:
python examples/register_signer.py --testnet --execute
python examples/order_lifecycle.py --testnet --execute

python -m pytest
ruff check .
ruff format --check .
mypy
python -m build
RISEX_RUN_INTEGRATION=1 python -m pytest tests/integration/test_public_testnet.py -v

Examples read explicitly provided process environment variables; .env.example lists the names. Public examples require no credentials. See testnet handoff for wallet setup and funded gates and API contracts for encoding details.

The offline suite covers signing, exact units, pagination, order lifecycle, uncertain outcomes and local stream recovery. Live public data, testnet account reads and authenticated order/position snapshots have also passed. Live order execution, fills, signer administration and private reconnect/expiry scenarios remain unverified. Local checks used Python 3.12; CI is configured for 3.11–3.13.

Keep symbol mapping, normalized models, hedging, execution limits and persistence in the consuming application.

Metadata

Release files for risex-python-sdk 0.1.0a2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for risex-python-sdk 0.1.0a2
File Size Uploaded
risex_python_sdk-0.1.0a2.tar.gz 68.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for risex-python-sdk 0.1.0a2
File Interpreter ABI Platform
risex_python_sdk-0.1.0a2-py3-none-any.whl Python 3 none any Details

Total release size: 104.6 kB

Release files / risex_python_sdk-0.1.0a2.tar.gz

Download URL risex_python_sdk-0.1.0a2.tar.gz
Size 68.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b5ab957ac579538343a85a326e8987deb38f84c135010e2bc0d6db5e56b23065
BLAKE2b-256 checksum
How to use checksums
7308f51a986e9cf67ed30520a0ab5db25a518801b98c26957a116b5f31f6a4d0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / risex_python_sdk-0.1.0a2-py3-none-any.whl

Download URL risex_python_sdk-0.1.0a2-py3-none-any.whl
Size 36.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9c90cf09c8022bb1920308806a5a5d72eaf690d1e50538697ed494256e957db6
BLAKE2b-256 checksum
How to use checksums
efb6c902ebd7b6751a6ed5b545f267268502757d250d3fe01d2697931bcd43f9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0a2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page