All documentation

Rithmic transport

How the Rithmic connector's wire-protocol layer is isolated, why, and exactly what replacing it with a real implementation involves.

Why this document exists

Every other broker/payment integration in this codebase was built and verified against a real, checkable source: MT5 against the installed metaapi.cloud-sdk package's own TypeScript types, TradeLocker and Tradovate against their official public REST reference clients on GitHub, Stripe against the installed stripe npm package's own types. Rithmic has none of that available to this project: its real API, R|Protocol, is a proprietary Protocol Buffers-over-WebSocket wire format (confirmed via Rithmic's own public developer materials — see rithmic.com/apis and rithmic.com/products/api-suite), split across "plants" (Ticker Plant for market data, Order Plant for session/orders/account, History Plant for fills, PnL Plant for real-time position/PnL pushes). There is no official Node.js SDK, no installable npm package, and no .proto schema file this project has legitimate access to.

Per explicit instruction: this codebase does not reverse-engineer or guess Rithmic's actual wire protocol. Every protocol-specific detail is isolated behind one interface, `RithmicTransport` — a port describing the *capabilities* a real implementation must provide (ordinary, publicly-documented futures-trading operations: login, account/position/order queries, place/modify/cancel/close, push subscriptions, quote streaming), never the wire format itself. Every other piece of this connector — the four services (connection, market data, position sync, order execution), reconnect/heartbeat/retry logic, Copy Engine integration, UI wiring, metrics, tests — is built completely and for real against that port, and is exactly as production-ready as MT5/TradeLocker/Tradovate's equivalents regardless of which RithmicTransport implementation is behind it.

Two implementations exist today — know which is which

  • `RithmicUnimplementedTransport` (src/lib/connectors/rithmic/transport.ts)

— the real, production placeholder. register.ts wires this up by default. Every method throws RithmicTransportNotConfiguredError immediately. This is what a user hits today if they try to connect a Rithmic account through the Add Broker wizard — a clear, honest error, not a silent failure or a fabricated success.

  • `FakeRithmicTransport` (tests/helpers/rithmic-fakes.ts) — the

test double. Drives all ~116 Rithmic-related automated tests (connector, service, and Copy Engine validation tests). This stays forever, regardless of what real transport eventually gets wired up — it's what lets every layer above the transport be tested without a live Rithmic connection, the same role every other connector's fake session/API plays in this codebase's test suite.

What's required to replace RithmicUnimplementedTransport

1. Official Rithmic developer access. Rithmic's API access is not self-serve the way Tradovate's demo signup or TradeLocker's login is — it requires an approved developer relationship, typically arranged through a broker that resells Rithmic infrastructure (e.g. the parties named in this session's research: AMP Futures, Ironbeam, EdgeClear, Optimus Futures) or directly with Rithmic, LLC. This yields:

  • A system name (which Rithmic gateway/environment a login

authenticates against — already modeled as RithmicCredentials.systemName).

  • A registered application identity (app name/version — already

modeled as RithmicCredentials.appName/appVersion).

  • Test/demo gateway access for development, separate from the live

gateway (already modeled as RithmicEnvironment). 2. Rithmic's own R|Protocol schema files (.proto definitions for every message the Order/Ticker/History/PnL Plants exchange) — distributed to approved developers as part of Rithmic's own SDK package, not published in a form this project has legitimate access to. A real RithmicTransport implementation needs these to encode/ decode the actual wire messages. 3. A protobuf runtime for Node.js — `protobufjs` (confirmed via this session's research as the standard, general- purpose choice; not Rithmic-specific) to compile/consume the schema files from step 2 and handle WebSocket framing.

The actual swap-in steps

Because every protocol detail is isolated behind RithmicTransport, wiring in a real implementation is mechanical and touches exactly one call site:

1. Implement the RithmicTransport interface (src/lib/connectors/rithmic/transport.ts) against the real R|Protocol schema — e.g. src/lib/connectors/rithmic/real-transport.ts (a new file, analogous in role to tradelocker/sdk-adapter.ts / tradovate/sdk-adapter.ts — deliberately excluded from rithmic/index.ts's barrel export, the same "only register.ts touches the real network" convention those two files already follow). 2. In src/lib/connectors/rithmic/register.ts, change the one createTransport factory from () => new RithmicUnimplementedTransport() to () => new RealRithmicTransport(...). 3. Nothing else changes. Not rithmic-connector.ts, not any of the four services, not the Add Broker wizard, not the Copy Engine integration, not the tests (FakeRithmicTransport continues driving those exactly as before) — that's the entire point of the port.

Validation checklist for the transport swap specifically

Run this once a real RithmicTransport implementation exists, before the full docs/LIVE_TEST_CHECKLIST.md checklist:

1. login() against the demo/test gateway succeeds and returns real account info (fcmId/ibId populated correctly, not placeholders). 2. heartbeat() succeeds repeatedly over an extended idle period (no silent disconnect from Rithmic's own protocol-level timeout). 3. getPositions()/getOpenOrders()/getAccountBalance() against a demo account with known state return values that match what the Rithmic-provided trading platform (e.g. R | Trader) shows for the same account. 4. onPositionUpdate/onOrderUpdate/onFillUpdate actually fire when a trade happens on that account (placed through this connector, and — importantly — placed through some OTHER means, to confirm pushes aren't scoped only to self-placed activity). 5. placeOrder/modifyOrder/cancelOrder/closePosition against a demo account for all 4 order types, cross-checked against the Rithmic-provided platform's own order book. 6. subscribeQuote() delivers live bid/ask for an actively-traded contract within a reasonable interval.

Remaining production tasks (beyond the transport itself)

  • Exchange routing. RithmicOrderService currently hardcodes

DEFAULT_EXCHANGE = 'CME' (see that file's own doc comment) because this codebase has no symbol→exchange mapping data. A real deployment trading products outside CME (ICE, CBOT as a distinct routing target, etc.) needs that mapping resolved properly — either from Rithmic's own instrument-lookup capability (once the real transport exists) or a local catalog, the same kind of addition TradeLocker's/Tradovate's own instrument-resolution layers already make.

  • Reconciliation interval tuning. RithmicPositionService's

reconciliation poll (a safety net alongside real push updates — see that file's own doc) defaults to 15s; once live push behavior is observable, this may warrant tuning either direction.

  • Live load/latency characterization. The connector_order_latency_ms,

connector_market_data_latency_ms, and connector_position_sync_latency_ms metrics (see docs/BROKER_INTEGRATION_REPORT.md) are recorded and ready, but have no live data yet to establish what "healthy" looks like for a real Rithmic connection specifically.