Skip to main content

Crate rithmic_rs

Crate rithmic_rs 

Source
Expand description

§rithmic-rs

rithmic-rs is a Rust client library for the Rithmic R | Protocol API.

§Features

  • Stream real-time market data (trades, quotes, order book depth)
  • Submit and manage orders (bracket orders, modifications, cancellations)
  • Access historical market data (ticks and time bars)
  • Manage risk and track positions and P&L
  • Connection health monitoring with heartbeat and forced logout handling

§Quick Start

use rithmic_rs::{
    RithmicConfig, RithmicEnv, ConnectStrategy, RithmicTickerPlant,
    rti::messages::RithmicMessage,
};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Load configuration from environment variables
    let config = RithmicConfig::from_env(RithmicEnv::Demo)?;

    // Connect with Retry strategy (recommended default)
    let ticker_plant = RithmicTickerPlant::connect(&config, ConnectStrategy::Retry).await?;
    let mut handle = ticker_plant.get_handle();

    // Login and subscribe to market data
    handle.login().await?;
    handle.subscribe("ESM6", "CME").await?;

    // Process real-time updates
    loop {
        match handle.subscription_receiver.recv().await {
            Ok(update) => {
                // Check for connection health issues
                if let Some(err) = &update.error {
                    eprintln!("Error: {}", err);
                    if err.is_connection_issue() { break; }
                    continue;
                }

                // Process market data
                match update.message {
                    RithmicMessage::LastTrade(trade) => {
                        println!("Trade: {:?}", trade);
                    }
                    RithmicMessage::BestBidOffer(bbo) => {
                        println!("BBO: {:?}", bbo);
                    }
                    _ => {}
                }
            }
            Err(e) => {
                eprintln!("Channel error: {}", e);
                break;
            }
        }
    }

    Ok(())
}

§Connection Strategies

The library provides three connection strategies:

A graceful disconnect().await logs out first and then closes the WebSocket. See Error Handling for how that differs from an unexpected drop.

§Configuration

Use RithmicConfig for modern, ergonomic configuration:

use rithmic_rs::{RithmicAccount, RithmicConfig, RithmicEnv};

fn example() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    // From environment variables
    let config = RithmicConfig::from_env(RithmicEnv::Demo)?;
    let account = RithmicAccount::from_env(RithmicEnv::Demo)?;

    // Or using builder pattern
    let config = RithmicConfig::builder(RithmicEnv::Demo)
        .user("your_user".to_string())
        .password("your_password".to_string())
        .system_name("Rithmic Paper Trading".to_string())
        .app_name("your_app_name".to_string())
        .app_version("1".to_string())
        .build()?;

    let account = RithmicAccount::new("your_fcm", "your_ib", "your_account");
    let _ = (config, account);
    Ok(())
}

§Error Handling

An error reaches you in one of two places: the call you made, or the subscription channel. Which one it is tells you what to do about it.

examples/error_handling.rs in the repository is this section as one runnable file, if you would rather read code.

§From a call

Handle methods return Result<_, RithmicError>, but Ok does not mean success. A request the server turned down still comes back as Ok, with the reason in resp.error. Code that checks only for Err will read it as having worked. login is the exception — a rejected login is an Err.

use rithmic_rs::RithmicError;

match handle.subscribe("ESM6", "CME").await {
    Ok(resp) => match &resp.error {
        Some(err) => eprintln!("Server rejected: {err}"),
        None => { /* success */ }
    },
    Err(RithmicError::ConnectionClosed | RithmicError::SendFailed) => {
        handle.abort();
        // reconnect — see examples/reconnect.rs
    }
    Err(e) => eprintln!("{e}"),
}

if let Err(RithmicError::RequestRejected(err)) = handle.login().await {
    eprintln!(
        "Login rejected: code={} msg={}",
        err.code.as_deref().unwrap_or("?"),
        err.message.as_deref().unwrap_or(""),
    );
}

Two errors turn up in resp.error. RequestRejected is the server saying no, with its code and message split out so you can branch on the code. ProtocolError means the response arrived but would not decode — usually Rithmic’s schema has moved ahead of this crate, so retrying will not help and it is worth filing.

An Err means you never got an answer at all:

  • InvalidArgument — your arguments. Nothing was sent. Fix them and call again.
  • NoTradeRoute — no route for the order’s exchange. Nothing was sent. Set the order’s trade_route, or check the exchange with trade_route_for before you trade.
  • SendFailed — the send failed. Only this request fails and the plant is still up, but the connection is usually on its way out; expect a ConnectionError to follow. Treat it as a connection problem rather than retrying in a loop.
  • ConnectionClosed — the plant is gone. Reconnect; calling again will not work.

When a connection drops, everything in flight fails with ConnectionClosed whatever the real cause was. The cause goes out on the subscription channel, so look there if you need to tell a heartbeat timeout from a dead socket.

The crate does not time out requests. A request finishes when Rithmic answers it or the connection drops. If you need timeout handling, see examples/request_timeout.rs. Timing out on your side does not cancel anything on Rithmic’s, so reconcile an order rather than re-sending it.

(ConnectionFailed comes from connect() rather than a handle method, and only under ConnectStrategy::Simple — the retrying strategies keep trying instead of handing you an error. EmptyResponse is a defensive case you should not see.)

§From the subscription channel

Updates normally arrive with error: None. Five messages want a decision from you:

MessageWhat to do
ConnectionErrorReconnect. The plant is stopping or already stopped.
HeartbeatTimeoutReconnect — unless error holds a RequestRejected, which means the server rejected a heartbeat and the connection is fine.
ForcedLogoutThe server ended your session. A ConnectionError follows, so expect two events.
UnknownTemplateNothing, unless you want to. A template this crate has no mapping for, raw payload attached. Not an error.
UnknownA frame that would not decode. Log it and carry on.

RithmicError::is_connection_issue is the shortcut: true means reconnect, false means the connection is fine and something about the data or the request was not. Do not reconnect on ProtocolError or RequestRejected — neither says anything about connection health, and you will only churn.

This is a broadcast channel, so anything sent while you hold no receiver is gone. Keep it for as long as the plant lives.

§When a plant stops

Only transport failure takes one down: a broken socket, a keep-alive timeout, a forced logout, or your own abort(). You get the matching connection-health event and every pending call fails with ConnectionClosed.

Bad data never does. An undecodable frame, an unmapped template, a rejected request — the plant keeps running and your other in-flight requests are untouched. A decode failure usually comes back from the call it belongs to, and arrives as Unknown when the frame names no request. A frame too damaged to carry a template id at all is logged and dropped.

disconnect().await is the clean shutdown and emits none of those events. Pending calls still fail with ConnectionClosed.

RithmicError implements std::error::Error, so ? works in functions returning Box<dyn Error>.

§Feature Flags

FlagDefaultDescription
serdeoffAdds Serialize/Deserialize derives on the config types (RithmicEnv, RithmicAccount), the trading enums (OrderSide, OrderType, TimeInForce, ManualOrAutoEntry, OrderCondition, OrderPriceField, BracketType, BracketOperationType, FillHistoryRange, EasyToBorrowRequest, RmsUpdateBits, OrderStatus), every order command type (RithmicOrder, RithmicBracketOrder, RithmicOcoOrder and its legs, RithmicModifyOrder, the cancel/exit/link/retag/adjustment commands), the triggers (TrailingStop, RithmicIfTouchedTrigger) and the history request types (VolumeProfileMinuteBarsRequest, TickBarReplayRequest)

TLS backend: The crate uses native-tls (via tokio-tungstenite) for all WebSocket connections. There is currently no rustls option.

§Module Organization

  • plants: Specialized clients for different data types (ticker, order, P&L, history)
  • config: Configuration API for connecting to Rithmic
  • error: Typed error enum for plant handle methods
  • api: Low-level API interfaces for sending and receiving messages
  • types: High-level trading enums (order side, type, time-in-force, …)
  • rti: Protocol message definitions
  • util: Utility types and helpers (timestamps, order status, instrument info)

Re-exports§

pub use plants::history_plant::RithmicHistoryPlant;
pub use plants::history_plant::RithmicHistoryPlantHandle;
pub use plants::order_plant::RithmicOrderPlant;
pub use plants::order_plant::RithmicOrderPlantHandle;
pub use plants::pnl_plant::RithmicPnlPlant;
pub use plants::pnl_plant::RithmicPnlPlantHandle;
pub use plants::subscription::SubscriptionFilter;
pub use plants::ticker_plant::RithmicTickerPlant;
pub use plants::ticker_plant::RithmicTickerPlantHandle;
pub use config::ConfigError;
pub use config::RithmicAccount;
pub use config::RithmicConfig;
pub use config::RithmicConfigBuilder;
pub use config::RithmicEnv;
pub use error::RithmicError;
pub use error::RithmicRequestError;
pub use api::LoginConfig;
pub use api::RithmicBracketLevelAdjustment;
pub use api::RithmicBracketOrder;
pub use api::RithmicCancelAllOrders;
pub use api::RithmicCancelOrder;
pub use api::RithmicExitPosition;
pub use api::RithmicIfTouchedTrigger;
pub use api::RithmicLinkOrders;
pub use api::RithmicModifyOrder;
pub use api::RithmicModifyOrderReferenceData;
pub use api::RithmicOcoOrder;
pub use api::RithmicOcoOrderLeg;
pub use api::RithmicOrder;
pub use api::RithmicResponse;
pub use api::TrailingStop;
pub use util::InstrumentInfo;
pub use util::InstrumentInfoError;
pub use util::OrderStatus;
pub use util::UnknownTemplateMessage;
pub use util::rithmic_to_unix_nanos;
pub use util::rithmic_to_unix_nanos_precise;
pub use types::BracketOperationType;
pub use types::BracketType;
pub use types::EasyToBorrowRequest;
pub use types::FillHistoryRange;
pub use types::ManualOrAutoEntry;
pub use types::OrderCondition;
pub use types::OrderPriceField;
pub use types::OrderSide;
pub use types::OrderType;
pub use types::ParseOrderSideError;
pub use types::ParseOrderTypeError;
pub use types::ParseTimeInForceError;
pub use types::RmsUpdateBits;
pub use types::TickBarReplayRequest;
pub use types::TimeBarReplayRequest;
pub use types::TimeBarType;
pub use types::TimeInForce;
pub use types::VolumeProfileMinuteBarsRequest;
pub use prost;

Modules§

api
Low-level API types for Rithmic communication.
config
Configuration API for connecting to Rithmic Configuration for Rithmic connections.
error
Error types for plant handle methods.
plants
Specialized clients (“plants”) for different Rithmic services.
rti
Rithmic protocol message definitions (protobuf-generated).
types
High-level trading types with optional serde support. Order enums with serde support and protobuf conversions.
util
Utility types for working with Rithmic data. Utility types for working with Rithmic data.

Enums§

ConnectStrategy
Connection strategy for connecting to Rithmic servers.

Constants§

DEFAULT_REQUEST_TIMEOUTDeprecated
No longer used. The library does not time out requests; wrap the call in tokio::time::timeout to set a deadline of your own. Removed in 4.0.0.