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.1.0.tar.gz (353.8 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.1.0-py3-none-any.whl (572.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: rithmic_rapi-0.1.0.tar.gz
  • Upload date:
  • Size: 353.8 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.1.0.tar.gz
Algorithm Hash digest
SHA256 ff9f854678eecd606e0a26e11801666993593bc80a90d7bad76eaf3eaa7d8a42
MD5 ea15184b0912151e4a873eb4000f6fe9
BLAKE2b-256 ff12588b07d2ce5c77e253e2ef01d47e046d6355fa68861c7a7520461f4bc74c

See more details on using hashes here.

Provenance

The following attestation bundles were made for rithmic_rapi-0.1.0.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.1.0-py3-none-any.whl.

File metadata

  • Download URL: rithmic_rapi-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 572.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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b4c67109eb27807508929629f02e388d0838cb8ea8e4be87bd580e37ee4e8e40
MD5 c6ce40e930144899927eedd595c965b3
BLAKE2b-256 19bacff0e8b801de649812b296beabe04dbc8244caea4cf66e6cd871ddabf564

See more details on using hashes here.

Provenance

The following attestation bundles were made for rithmic_rapi-0.1.0-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