top of page

Get auto trading tips and tricks from our experts. Join our newsletter now

Thanks for submitting!

Building a High Frequency Trading Engine: A Deep Dive into .NET 8, Rithmic API, and Valkey Architecture

Rithmic Trading System - Comprehensive Coding Breakdown

 

Table of Contents

 

  1. Solution Architecture Overview

  2. Project 1: RithmicTradingSystem.Shared

  3. Project 2: RithmicGatewayServer

  4. Design Patterns and Principles

  5. Data Flow Analysis

  6. Technical Deep Dive

  7.  

1. Solution Architecture Overview

 

The Rithmic High Frequency Trading Engine System is a high-performance, distributed trading infrastructure designed to bridge the gap between the Rithmic RAPI (a C++ based API with a .NET wrapper used for futures trading) and modern, custom algorithmic strategies. The system is built on a microservices-inspired architecture, utilizing a Hub-and-Spoke topology where a central Gateway Server manages the connection to the exchange, while multiple Strategy Clients interact with the market via a high-speed message broker.


hft engine

 

Architectural Philosophy

 

The core philosophy of this architecture is decoupling. Direct connections to financial exchanges are often fragile, resource-intensive, and complex to manage. By introducing an abstraction layer, we isolate the complexity of the Rithmic API into a single component (the Gateway). Strategy clients, which contain the proprietary trading logic, are thereby shielded from connection drops, API idiosyncrasies, and protocol updates. They communicate using a standardized internal protocol over a message bus.

 

Core Components

 

  1. The Message Broker (Valkey):


    At the center of the infrastructure lies Valkey (a high-performance fork of Redis). It serves two critical roles:

    • Pub/Sub Messaging: It acts as the nervous system, broadcasting real-time market data (Ticks, Order Books) to all listening strategies and routing order requests from strategies to the gateway.

    • State Persistence: It acts as a high-speed cache, storing the "Last Known Value" for server status, open positions, and active orders. This allows a strategy to crash, restart, and immediately recover its state without querying the exchange.

  2. The Gateway Server (RithmicGatewayServer):


    This is a .NET 8 Console Application that acts as the "Hub." It holds the single, authoritative TCP/IP connection to the Rithmic infrastructure. Its responsibilities are:

    • Authenticating with Rithmic.

    • Normalizing Rithmic's proprietary data formats into clean C# Data Transfer Objects (DTOs).

    • Publishing normalized data to Valkey.

    • Listening for order instructions from Valkey, validating them, and executing them via Rithmic.

  3. The Shared Contract (RithmicTradingSystem.Shared):


    This is a Class Library referenced by both the Gateway and the Strategy Clients. It defines the "language" of the system. It contains the data models, channel naming constants, and serialization logic. This ensures that a TickData object sent by the Gateway is exactly what the Strategy Client expects to receive.

  4. The Strategy Clients (Spokes):


    These are independent applications (Console Apps, Worker Services, or UI apps) that consume data and generate orders. Because they connect to Valkey rather than Rithmic, multiple strategies can run simultaneously, even on different servers, all sharing a single Rithmic login session managed by the Gateway.

 

2. Project 1: RithmicTradingSystem.Shared

 

This project is the foundation of the solution. It has zero dependencies on external trading libraries, ensuring that strategy clients remain lightweight. It targets .NET 8.0 to utilize the latest performance enhancements in C#.

 

File Analysis: Models.cs

 

This file defines the Data Transfer Objects (DTOs). These classes are designed to be immutable (using record types or init-only properties) to ensure thread safety when passed between processing pipelines.

 

1. ServerStatusThis class broadcasts the health of the Gateway.

  • Status: An enum-string (DISCONNECTED, CONNECTING, READY, ERROR).

  • RithmicConnectionId: Useful for debugging logs.

  • Timestamp: UTC time of the status update.

  • Usage: Strategies subscribe to this to know if they should pause trading because the gateway has lost connection to the exchange.

 

2. TickDataThe atomic unit of market data.

 

  • Instrument: The symbol (e.g., "ESZ4").

  • Price: The last trade price (decimal).

  • Volume: The size of the last trade (int).

  • BidPrice / AskPrice: The top of book at the moment of the trade.

  • ExchangeTimestamp: When the trade happened at the matching engine.

  • ReceiptTimestamp: When the Gateway received it.

  • Design Note: We separate Exchange and Receipt timestamps to calculate latency.

 

 

3. OrderRequestThe instruction sent from a Strategy to the Gateway.

 

 

  • ClientOrderId: A GUID generated by the strategy to track this specific request.

  • Instrument: Target symbol.

  • Side: Buy or Sell.

  • OrderType: Market, Limit, Stop, StopLimit.

  • Quantity: Number of contracts.

  • Price: (Optional) Limit price.

  • TriggerPrice: (Optional) Stop price.

  • StrategyId: Identifies which strategy sent the order (for P&L tracking).

 

4. OrderUpdateThe feedback loop from Gateway to Strategy.

 

  • ClientOrderId: Matches the request.

  • ExchangeOrderId: The ID assigned by the Rithmic/Exchange system.

  • Status: Pending, New, PartiallyFilled, Filled, Canceled, Rejected.

  • FillPrice: Average price of fills.

  • FilledQuantity: Cumulative filled amount.

  • Message: Error text if rejected.

 

5. PositionUpdateRepresents the current holding.

 

  • Instrument: Symbol.

  • Account: The Rithmic Account ID.

  • NetPosition: Positive for Long, Negative for Short, Zero for Flat.

  • AveragePrice: The breakeven price.

  • OpenPnL: Unrealized profit/loss based on current market price.

 

File Analysis: ValkeyChannels.cs

 

This static class eliminates "Magic Strings" from the codebase. It defines the hierarchical channel structure used in Valkey.

 

  • Structure: Root : Category : SubCategory : [Variable]

  • Examples: 

    • rithmic:md:tick:{instrument} -> Used for publishing ticks.

    • rithmic:md:bar:{instrument} -> Used for publishing OHLCV bars.

    • rithmic:orders:request -> The channel the Gateway listens to.

    • rithmic:orders:update -> The channel the Gateway publishes updates to.

    • rithmic:sys:heartbeat -> System health pulses.

 

By using string.Format or interpolation methods within this class, we ensure that a Strategy trying to subscribe to "ESZ4" ticks generates the exact same channel string as the Gateway publishing them.

 

File Analysis: ValkeyMessage.cs

 

This is a generic wrapper class (ValkeyMessage<T>) that encapsulates all payloads sent over the wire. It implements the Envelope Pattern.

 

Properties:

 

  • Id: A unique GUID for the message itself (traceability).

  • CorrelationId: Used to link a Response back to a Request.

  • Timestamp: Creation time.

  • Source: The name of the component sending the message (e.g., "Gateway", "ScalperBot1").

  • Type: A string descriptor of the payload type (e.g., "TickData").

  • Data: The generic payload T.

 

Serialization Logic:This class includes helper methods ToJson() and FromJson() using System.Text.Json.

 

  • Optimization: It uses a static JsonSerializerOptions instance configured for speed and camelCase formatting (to be compatible with non-.NET tools like Node.js dashboards).

  • Polymorphism: The deserializer uses the Type string to determine how to deserialize the Data property on the receiving end.

 

3. Project 2: RithmicGatewayServer

 

This Console Application is the engine of the system. It manages the lifecycle of the Rithmic API connection and the Valkey connection.

 

File Analysis: Program.cs

 

While a production system might split this into services, we will analyze the logical flow within the main entry point.

 

1. Configuration & StartupThe application starts by loading configuration from Environment Variables (supporting Docker deployments).

 

  • RITHMIC_USER, RITHMIC_PASS, RITHMIC_SYSTEM, RITHMIC_SERVER.

  • VALKEY_CONNECTION_STRING.

  • TARGET_ACCOUNTS (Comma separated list of accounts to monitor).

  •  

2. Connection Initialization

 

  • Valkey: It creates a ConnectionMultiplexer. It obtains an ISubscriber interface for Pub/Sub and an IDatabase interface for setting key-values.

  • Rithmic: It instantiates the Rithmic API wrapper. It hooks into the OnEngineChanged event to monitor connection states. It calls Login() and waits for the callback indicating success.

 

3. The Market Data Loop (Inbound)The Gateway subscribes to market data on the Rithmic API for a configured list of instruments.

 

  • Event: OnTick (from Rithmic).

  • Transformation: The handler converts the Rithmic tick struct into Shared.Models.TickData.

  • Enveloping: It wraps the tick in a ValkeyMessage<TickData>.

  • Publishing: It determines the channel (e.g., rithmic:md:tick:ESZ4) and calls subscriber.PublishAsync().

  • Performance Note: This happens asynchronously. The Rithmic callback is released immediately to prevent blocking the API thread.

 

4. The Order Execution Loop (Outbound)The Gateway subscribes to the rithmic:orders:request channel on Valkey.

 

  • Event: Message Received on Valkey channel.

  • Deserialization: The JSON is parsed into ValkeyMessage<OrderRequest>.

  • Validation: The Gateway checks if the Instrument is valid, if the Quantity is positive, and if the Gateway is currently connected to the exchange.

  • Mapping: It creates a Rithmic OrderParams object. Crucially, it stores the OrderRequest.ClientOrderId in a concurrent dictionary mapping it to the internal tracking ID.

  • Execution: It calls RithmicApi.SendOrder().

 

5. Order Updates (Feedback)The Gateway listens to OnOrderChanged events from Rithmic.

 

  • Event: Rithmic reports an order status change (e.g., "Filled").

  • Correlation: The Gateway looks up the original ClientOrderId using the Rithmic Order ID.

  • Publication: It creates an OrderUpdate object, serializes it, and publishes it to rithmic:orders:update. This allows the specific strategy that placed the order to know it was filled.

 

6. Position TrackingRithmic provides P&L updates, but the Gateway also maintains a local state.

 

  • When a fill is received, the Gateway updates an internal Position object.

  • It publishes this to rithmic:pos:{account}.

  • Persistence: It also writes this object to a Valkey Key (not a channel). This allows a dashboard to open and immediately read the current position via a standard GET command, without waiting for the next update.

 

7. Heartbeat & Health CheckA background Task runs every 1 second.

 

  • It checks the Rithmic connection status.

  • It sends a Heartbeat message to rithmic:sys:heartbeat.

  • If the Rithmic connection drops, it updates the ServerStatus to "DISCONNECTED" and attempts a reconnection loop with exponential backoff.

 

4. Design Patterns and Principles

 

The architecture relies heavily on established software engineering patterns to ensure maintainability and scalability.

 

1. The Gateway Pattern

 

The RithmicGatewayServer encapsulates the external system (Rithmic). The rest of the system does not know Rithmic exists; they only know the "Internal Trading Protocol" defined in the Shared library. This allows us to swap Rithmic for CQG or Interactive Brokers in the future by simply rewriting the Gateway, without touching the strategies.

 

2. Publisher-Subscriber (Pub/Sub) Pattern

 

This is the core communication model.

 

  • Decoupling: The Gateway publishes ticks. It doesn't know if 1 strategy is listening or 100. This allows for horizontal scaling of strategies.

  • Asynchronous: The Gateway doesn't wait for strategies to acknowledge receipt of a tick. It fires and forgets, ensuring the Gateway remains responsive to the exchange.

 

3. The Envelope Pattern

 

By wrapping every message in ValkeyMessage<T>, we standardize metadata.

 

  • Tracing: If an error occurs, we can log the MessageId.

  • Routing: The Type field allows generic consumers to route messages to specific handlers without inspecting the payload first.

 

4. The Repository Pattern (via Valkey)

 

Valkey is treated not just as a message bus, but as a repository for state.

 

  • State Recovery: When the Gateway starts, it can check Valkey for existing active orders (in case of a crash/restart).

  • Last Value Caching: Strategies don't need to calculate the current position from a history of trades; they simply fetch the current Position object stored in Valkey.

 

5. Single Responsibility Principle (SRP)

 

  • Shared Project: Responsible only for definitions.

  • Gateway: Responsible only for translation and connectivity. It contains no trading logic.

  • Strategy: Responsible only for decision making. It contains no connection logic.

 

5. Data Flow Analysis

 

Understanding the lifecycle of data is crucial for debugging and performance tuning.

 

Scenario A: Market Data Flow (The "Tick" Lifecycle)

 

  1. Source: The Chicago Mercantile Exchange (CME) matches a trade.

  2. Transmission: Rithmic servers receive the data and push it over TCP to the RithmicGatewayServer.

  3. Ingestion: The OnTick callback fires in the Gateway.

  4. Normalization: Gateway converts Rithmic struct to TickData DTO.

  5. Serialization: TickData is wrapped in ValkeyMessage and serialized to a JSON string.

  6. Brokerage: The JSON string is sent to Valkey channel rithmic:md:tick:ESZ4.

  7. Distribution: Valkey pushes this string to all subscribed Strategy Clients.

  8. Consumption: Strategy Client receives JSON, deserializes it back to TickData, and triggers its OnTick logic to calculate indicators.

 

Total Latency Goal: < 5ms from Gateway Ingestion to Strategy Logic.

 

Scenario B: Order Execution Flow (The "Trade" Lifecycle)

 

  1. Decision: Strategy Client logic decides to buy.

  2. Generation: Client creates OrderRequest with a unique ClientOrderId.

  3. Transmission: Client serializes request and publishes to rithmic:orders:request.

  4. Ingestion: Gateway receives the message.

  5. Validation: Gateway checks connection status and request validity.

  6. Translation: Gateway converts OrderRequest to Rithmic API calls.

  7. Submission: Gateway calls RithmicApi.SendOrder().

  8. Acknowledgment: Rithmic returns an initial "Pending" status.

  9. Feedback: Gateway publishes OrderUpdate (Status: Pending) to rithmic:orders:update.

  10. Execution: CME fills the order. Rithmic sends "Filled" callback.

  11. Confirmation: Gateway publishes OrderUpdate (Status: Filled) to rithmic:orders:update.

  12. Update: Strategy Client receives update and marks its internal state as "In Position".

 

 

6. Technical Deep Dive

 

Serialization Strategy: System.Text.Json

 

We utilize System.Text.Json over Newtonsoft.Json for performance reasons. In high-frequency trading, memory allocation is the enemy (causing Garbage Collection pauses). System.Text.Json is optimized for low allocation using Span<T> and Utf8JsonWriter.

 

  • Optimization: We use source generation (available in .NET 8) where possible to avoid reflection overhead during serialization.

  •  

Concurrency and Thread Safety

 

The Rithmic API is multi-threaded. Callbacks can arrive on any thread from the thread pool.

 

  • Concurrent Collections: The Gateway uses ConcurrentDictionary to map Client IDs to Rithmic IDs. This allows the Order Handler (Outbound) and the Order Update Handler (Inbound) to access the mapping simultaneously without locking.

  • Channels: Inside the Gateway, we utilize System.Threading.Channels to decouple the Rithmic Callback thread from the Valkey Publish thread. When a tick arrives, it is written to a bounded Channel. A separate background worker reads from this Channel and handles the JSON serialization and Valkey publishing. This ensures that a slow network connection to Valkey does not block the Rithmic API callback.

 

Handling Disconnections

 

Robustness is key. The system handles three types of failure:

 

  1. Valkey Down: The Gateway catches exceptions when publishing. It buffers messages internally (up to a limit) and retries. If the buffer fills, it drops the oldest ticks (Market Data is ephemeral; old ticks are useless).

  2. Rithmic Down: The Gateway transitions ServerStatus to "CONNECTING". It rejects incoming OrderRequests with a specific error message ("Gateway not ready"). It enters a reconnection loop.

  3. Strategy Crash: Because the Gateway manages the Rithmic connection, a strategy crash does not cancel open orders at the exchange (unless specifically programmed to do so). When the strategy restarts, it reads the "Open Orders" key from Valkey to re-sync its internal state with reality.

 

Latency Considerations

 

To minimize latency:

 

  • Nagle's Algorithm: We disable Nagle's algorithm on the TCP sockets (NoDelay = true) for both Rithmic and Valkey connections.

  • Garbage Collection: We use structs for high-frequency data where possible, or object pooling for ValkeyMessage wrappers to reduce GC pressure.

  • Valkey Configuration: We configure Valkey to disable disk persistence (RDB/AOF) for the market data channels to maximize throughput, as historical tick data does not need to be saved in the broker.

 

Security

  • Environment Variables: Credentials are never hardcoded.

  • Network Isolation: In a production environment, Valkey is configured to bind only to the internal network interface (localhost or private LAN), preventing external access to the trading bus.

 

This architecture provides a professional-grade foundation for algorithmic trading, balancing the need for speed with the requirements of stability, scalability, and maintainability.

 

 

Comments


bottom of page