Skip to main content

Native async Python client for the Rithmic R|API.

Project description

pyrithmic

CI PyPI Python License Ruff Checked with pyright

Distribution: rithmic-rapi on PyPI · import name: pyrithmic.

Native async Python client for the Rithmic R|API. Fully typed, fail-loud, and built around a single consistent contract.

pyrithmic is an asyncio-only client for the Rithmic R|API protocol (WebSocket + Protocol Buffers). Every callback delivers a typed, frozen Pydantic model (never a raw dict or a bare protobuf message), so a consumer writes the same code for a fill, a tick, a PnL push, or a historical bar. The public surface is strict, explicit, and verified under pyright --strict.

import asyncio
from pyrithmic import RithmicClient
from pyrithmic.protocol.plants import Plant
from pyrithmic.models.enums import MarketDataType
from pyrithmic.models.market_data import LastTrade

async def main() -> None:
    # Reads RITHMIC_USER / RITHMIC_PASSWORD / RITHMIC_SYSTEM_NAME / RITHMIC_GATEWAY_URL
    client = RithmicClient.from_env(plants=frozenset({Plant.TICKER}))

    @client.on_last_trade
    async def _on_trade(trade: LastTrade) -> None:   # a typed model, with full IDE autocomplete
        print(trade.symbol, trade.trade_price, trade.trade_size)

    async with client:                                # connects, logs in, reconnects on drop
        contract = await client.get_front_month_contract("NQ", "CME")
        await client.subscribe_market_data(contract.trading_symbol, "CME", MarketDataType.LAST_TRADE)
        await asyncio.sleep(10)

asyncio.run(main())

Highlights

  • One consistent, typed contract. Every push and every response is decoded into a frozen Pydantic v2 model before it reaches your code. There is no dict in one place and a protobuf message in another: the same typed, immutable surface everywhere.
  • Strict, validated signatures. Orders are submitted as validated OrderRequest models (no opaque **kwargs); invalid input is rejected at construction, before it ever touches the wire.
  • Fail-loud error model. A PyrithmicError hierarchy maps server rejects to named exceptions (OrderRejected, RpcRejected, LoginRejected, and so on). No silent fallbacks, no swallowed errors.
  • Correct connection topology. One WebSocket connection per plant, the only topology the R|API actually supports, multiplexed transparently behind a single client.
  • Resilient by design. Per-plant reconnect with configurable backoff and a finite retry budget; active subscriptions are replayed automatically after a reconnect.
  • Race-free RPC layer. UUID-correlated request/response matching, explicit terminal-response detection, idempotent push de-duplication, and lock-protected shared state.
  • Broad protocol coverage. Orders (flat / bracket / OCO, modify with fill-race handling, cancel), PnL (snapshot + live stream), market data (quotes, statistics, front-month), and historical bars (replay + live stream).
  • Structured, opt-in observability. structlog-based logging that is silent by default and never logs secrets; your application stays in control of sinks and levels.
  • Strict typing and lean dependencies. Ships py.typed, passes pyright --strict, and depends only on protobuf, websockets, pydantic, and structlog.
  • Live-gated. Every feature is validated against a live Rithmic gateway across the order, PnL, market-data and historical workflows before it is shipped.

Installation

pip install rithmic-rapi        # or:  uv add rithmic-rapi

The PyPI distribution is rithmic-rapi; the import name stays pyrithmic (import pyrithmic).

Requires Python 3.11+.

An optional fast extra installs uvloop (a faster event loop) on non-Windows platforms:

pip install "rithmic-rapi[fast]"

pyrithmic stays event-loop-agnostic and never changes the global loop policy itself. Installing the extra only makes uvloop available on your platform; you enable it explicitly in your own entry point (see ADR-0006):

import asyncio
import uvloop

asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
# ...then run the pyrithmic client on the uvloop-backed loop

Usage

Connecting

Provide credentials explicitly, or load them from the environment with from_env. The client is an async context manager: entering it opens the connections, logs into every requested plant, and starts heartbeat and reconnect supervision; leaving it shuts everything down gracefully.

from pydantic import SecretStr
from pyrithmic import RithmicClient, RithmicCredentials
from pyrithmic.protocol.plants import Plant

creds = RithmicCredentials(
    user=SecretStr("my-user"),
    password=SecretStr("my-password"),
    system_name="Rithmic Paper Trading",
    gateway_url="rituz00100.rithmic.com:443",   # "wss://" is added for you; an explicit wss:// is also accepted
    app_name="my-app",
    app_version="1.0.0.0",
)

client = RithmicClient(creds, plants=frozenset({Plant.ORDER, Plant.PNL}))

Each plant is opt-in. A market-data-only client ({Plant.TICKER}) or a monitoring-only client ({Plant.PNL}) logs into just that plant, so you never pay for capabilities you don't use.

Submitting an order

Orders are validated OrderRequest models. You usually want to route to the current front-month contract, so resolve the canonical underlying first (for example NQ to NQM6) and feed the resolved symbol into the request. submit_and_wait then returns the terminal OrderUpdate (filled or cancelled), so the happy path is a single await:

from pyrithmic.models.order import OrderRequest
from pyrithmic.models.enums import TransactionType, OrderType, OrderPlacement

async with client:                          # client built with Plant.ORDER and Plant.TICKER
    # Resolve the underlying to the current front-month contract (e.g. "NQ" -> "NQM6").
    contract = await client.get_front_month_contract("NQ", "CME")

    req = OrderRequest(
        account_id="ACCOUNT-123",
        symbol=contract.trading_symbol,         # the resolved front-month symbol
        exchange=contract.trading_exchange,
        transaction_type=TransactionType.BUY,
        order_type=OrderType.LIMIT,
        quantity=1,
        price=21_000.0,
        placement=OrderPlacement.MANUAL,        # wire manual_or_auto; AUTO for automated routing
    )

    fill = await client.submit_and_wait(req, timeout=30.0)
    print(fill.status, fill.fill_price)         # OrderStatus.FILLED, 21000.0

Resolving the front-month contract requires Plant.TICKER in the client's plant set. A malformed request raises at construction; a gateway rejection raises OrderRejected with the reason, never a silent no-op.

Streaming PnL

from pyrithmic.models.pnl import AccountPnL

@client.on_account_pnl_update
async def _on_pnl(pnl: AccountPnL) -> None:
    print(pnl.account_id, pnl.day_pnl, pnl.account_balance)

async with client:
    await client.subscribe_pnl_updates("ACCOUNT-123")
    account, positions = await client.get_pnl_snapshot("ACCOUNT-123")
    await asyncio.sleep(60)

Historical bars

from datetime import datetime, timezone
from pyrithmic.models.enums import TimeBarType

async with client:                          # client built with Plant.HISTORY
    bars = await client.replay_time_bars(
        "NQM6", "CME",
        bar_type=TimeBarType.MINUTE_BAR, period=1,
        start=datetime(2026, 6, 1, 14, 0, tzinfo=timezone.utc),
        finish=datetime(2026, 6, 1, 15, 0, tzinfo=timezone.utc),
    )
    for bar in bars:                        # each is a typed TimeBar
        print(bar.bar_start, bar.open_price, bar.high_price, bar.low_price, bar.close_price)

Logging

Logging is silent by default. Opt in from your application with one call (console for humans, JSON for ingestion), and pyrithmic never logs credentials:

from pyrithmic.observability.logging import configure_logging

configure_logging(level="INFO")              # or: configure_logging(level="DEBUG", json=True)

Architecture

pyrithmic is layered, with a strict boundary between the wire and the public API:

Layer Responsibility
transport/ WebSocket connection, framing, heartbeat, reconnect
protocol/ Protobuf codec and the Template enum (wire layer)
session/ Login handshake, RPC correlation, response semantics
plants/ ORDER / PNL / TICKER / HISTORY / ADMIN domain logic
models/ Frozen Pydantic v2 models, the typed public surface
client.py RithmicClient, the high-level facade

The protobuf bindings are an internal detail: application code never imports them directly and never sees a raw protobuf message. Design decisions are recorded as Architecture Decision Records and the full layout lives in docs/architecture.md.

Capabilities

Plant Coverage
Order submit · bracket · OCO · modify (fill-race aware) · cancel · fills · RMS · order history
PnL account & instrument snapshot · live position/PnL stream
Ticker front-month resolution · last-trade / best-bid-offer · trade & quote statistics
History time / tick / volume-profile bar replay · live bar stream
Admin account discovery · pre-login system & gateway discovery · reference data

Development

uv sync                  # install dependencies + create .venv
just test                # unit tests
just test-integration    # live Rithmic tests (require RITHMIC_* env vars)
just lint                # ruff check
just typecheck           # pyright --strict
just proto               # regenerate protobuf bindings
just build               # build wheel + sdist
just ci                  # lint + format-check + typecheck + tests

The public API is unstable before 0.1.0: breaking changes may land between pre-releases and are recorded in CHANGELOG.md.


License

pyrithmic (transport, codec wrappers, plant abstractions, models, and the client facade) is released under the Apache License 2.0.

Disclaimer & trademark notice

This project is not affiliated with, endorsed by, or sponsored by Rithmic, LLC or Trading Technologies International. "Rithmic" and "R|API" are trademarks of their respective owners.

Protocol bindings

The Python protobuf bindings shipped in the PyPI wheel (pyrithmic.protocol._generated) are compiled from Rithmic's R|API protocol distribution. The Rithmic .proto source files themselves are not redistributed in this repository or in the wheel; they remain proprietary to Rithmic, LLC and must be obtained directly through your own Rithmic developer relationship. If you build from source, place the RProtocolAPI distribution at proto/source/ and run just proto (the build hook does this automatically during uv build).

Takedown contact

If you are an authorized representative of Rithmic, LLC or Trading Technologies International and object to the distribution of compiled protocol bindings derived from your .proto definitions, please open an issue at https://github.com/alexandre-meline/pyrithmic/issues or contact the maintainer directly. Such requests will be honored promptly with PyPI takedown and repository archival.

Use at your own risk

This software interfaces with a live trading protocol. Bugs can lead to incorrect order routing, missed fills, or unintended position state. Always test extensively against a paper account before deploying to live trading. The authors and contributors assume no liability for financial losses arising from the use of this software.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

rithmic_rapi-0.2.2.tar.gz (359.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

rithmic_rapi-0.2.2-py3-none-any.whl (576.0 kB view details)

Uploaded Python 3

File details

Details for the file rithmic_rapi-0.2.2.tar.gz.

File metadata

  • Download URL: rithmic_rapi-0.2.2.tar.gz
  • Upload date:
  • Size: 359.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rithmic_rapi-0.2.2.tar.gz
Algorithm Hash digest
SHA256 767d9a09d16639235b8f388a5b0b74ecbf0931c5d629633a91610595a71ee6b5
MD5 3d1314e363a80c7c1f4babaf9a9ccc6c
BLAKE2b-256 59e45455b98a99dbfa393b594d75b30bd1ea348ed9db122528d325278c89b94d

See more details on using hashes here.

Provenance

The following attestation bundles were made for rithmic_rapi-0.2.2.tar.gz:

Publisher: release.yml on alexandre-meline/pyrithmic

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file rithmic_rapi-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: rithmic_rapi-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 576.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for rithmic_rapi-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 36e2ece998f34cc9ca69258a441f1e03401bcaa83e473baf0e408b99bd1e1e8a
MD5 79270cb48845f9aa02a2a256be351527
BLAKE2b-256 5c352ce331b907830f474a9cc294b04963444afe954f047dce6f5bb858bfad31

See more details on using hashes here.

Provenance

The following attestation bundles were made for rithmic_rapi-0.2.2-py3-none-any.whl:

Publisher: release.yml on alexandre-meline/pyrithmic

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page