of_adapters
of_adapters defines the provider boundary between venue-specific feeds and the normalized Orderflow runtime.
It standardizes lifecycle, subscription, polling, and health reporting while keeping provider protocol details isolated.
Core API
- Trait: [
MarketDataAdapter] - Factory: [
create_adapter] - Events: [
RawEvent] (Book,Trade) - Config: [
AdapterConfig], [ProviderKind], [CredentialsRef] - Health: [
AdapterHealth]
New In 0.2.0
Relative to the 0.1.x line, the adapter layer now has materially stronger
live-path supervision:
- reconnect with backoff
- subscription replay after reconnect
- richer
AdapterHealth::protocol_info - stronger degraded and timeout handling in the live providers
The trait surface stayed stable; the hardening happened behind the same public adapter interface.
Public API Inventory
Public types:
- [
SubscribeReq] - [
AdapterHealth] - [
RawEvent] - [
AdapterError] - [
AdapterResult<T>] - [
ProviderKind] - [
AdapterConfig] - [
CredentialsRef] - [
MockAdapter]
Public functions and methods:
- [
create_adapter] - [
MockAdapter::push_event]
[MarketDataAdapter] trait methods:
connect()subscribe(SubscribeReq)unsubscribe(SymbolId)poll(&mut Vec<RawEvent>)health() -> AdapterHealth
Provider Strategy
The crate is built around a feature-gated provider model:
- Always available:
Mockprovider - Optional:
Rithmic,CQG,Binance(enable via Cargo features)
This keeps the default build deterministic while allowing production adapters where needed.
Current provider notes:
Rithmic:- mock mode emits deterministic book and trade events for end-to-end testing
- live
ws:///wss://mode now performs websocket reachability validation before reporting connected - live mode tracks heartbeat/message activity, schedules reconnect with backoff, and replays subscriptions after reconnect
- live mode accepts normalized JSON
book/trade/heartbeatpayloads from bridge processes - health metadata includes mode, endpoint, app name, uptime, reconnect attempt, subscription count, and activity ages
CQG:- reconnect/resubscribe and sequencing logic are implemented
Binance:- live websocket transport parses trade and depth events
- live mode schedules reconnect with backoff on disconnect or market-data timeout
- reconnect replays active subscriptions automatically
- health metadata includes reconnect attempt, subscription count, and message/data ages
Trait Contract
[MarketDataAdapter] is intentionally small, but each method has a specific contract:
connect()establishes transport/session state and should be idempotent where practical.subscribe(SubscribeReq)starts or updates delivery for one symbol and depth.unsubscribe(SymbolId)stops delivery for that symbol.poll(&mut Vec<RawEvent>)appends zero or more normalized events into the caller-owned buffer and returns the number appended.health()returns the latest transport/supervision state without mutating adapter state.
Normalization rules:
- adapters emit only [
RawEvent::Book] and [RawEvent::Trade] - provider-native protocol details stay inside the adapter implementation
- all emitted symbols, sequences, timestamps, and sides should already be normalized for runtime consumption
AdapterConfig Reference
provider: selectsMock,Rithmic,Cqg, orBinance.credentials: optional env-var references for providers that need authenticated bootstrap.endpoint: websocket or provider endpoint URI for live adapters.app_name: optional client or bridge identifier used in health metadata where supported.
[CredentialsRef] contains env-var names, not secret values:
key_id_env: env var that stores the provider user/key idsecret_env: env var that stores the secret/password/token
Health Semantics
[AdapterHealth] is the bridge between provider supervision and runtime quality decisions.
connected = truemeans the transport/session is considered updegraded = truemeans the adapter is reconnecting, stale, or otherwise unhealthy enough for runtime quality gatinglast_erroris the latest human-readable adapter failure if knownprotocol_infois provider-specific diagnostic text intended for logging and dashboards
Factory Behavior
[create_adapter] is feature-gated.
ProviderKind::Mockis always available.- live providers require their Cargo feature to be enabled.
- requesting a provider without its feature returns [
AdapterError::FeatureDisabled]. - missing endpoint or credential references return [
AdapterError::NotConfigured].
Choosing an Adapter
- Use [
MockAdapter] for deterministic tests, replay, and CI. - Use
RithmicorCQGwhen an authenticated futures feed is needed. - Use
Binancewhen a public crypto depth/trade feed is sufficient. - Keep provider-specific auth, transport, and reconnect tuning out of runtime code and inside the adapter layer.
Create an Adapter
use ;
let cfg = AdapterConfig ;
let mut adapter = create_adapter.expect;
adapter.connect.expect;
assert!;
Mock Adapter for Tests and Replays
[MockAdapter] is useful for deterministic tests and simulation pipelines:
use ;
use ;
let symbol = SymbolId ;
let mut adapter = default;
adapter.connect.expect;
adapter.subscribe.expect;
adapter.push_event;
let mut out = Vecnew;
let n = adapter.poll.expect;
assert_eq!;
assert_eq!;
Error Handling
All adapter operations return [AdapterResult<T>] with [AdapterError], covering:
- disconnected state
- missing configuration
- provider feature not enabled at build time
- provider-specific operational errors
Subscription Semantics
- [
SubscribeReq::depth_levels] is advisory and provider-dependent; mock and depth-aware providers use it directly. - repeated subscribe calls for the same symbol should be treated as update-or-refresh, not as a duplicate stream request.
- unsubscribe should remove future delivery for that symbol, but does not retroactively clear already-polled events from the caller buffer.
Real-World Use Cases
1. Bridge a proprietary broker or exchange feed
If you already have SDK access for a venue that Orderflow does not support,
implement [MarketDataAdapter] and normalize that SDK’s messages into
[RawEvent].
2. Simulate live behavior in tests
Use [MockAdapter] to feed deterministic BookUpdate and TradePrint events
into of_runtime without network dependencies.
3. Build a gateway process
Run your provider-specific adapter in one process and expose normalized output to downstream strategy or runtime layers. This keeps transport complexity out of the strategy code.
How To Create Your Own Adapter
The minimum implementation steps are:
- define a struct that owns connection state, subscriptions, and any pending event queue
- implement [
MarketDataAdapter] for that struct - normalize provider payloads into [
RawEvent::Book] and [RawEvent::Trade] - expose meaningful health through [
AdapterHealth] - add factory/config wiring if you want the adapter selectable via [
create_adapter]
Important design rules:
- keep emitted prices and sizes integer-normalized
- preserve provider sequence numbers whenever available
- preserve both exchange and local receive timestamps
- keep reconnect/backoff logic in the adapter, not in strategy code
- emit only normalized events from the public surface
Detailed Example: Custom Adapter Skeleton
use ;
use ;
Adapter Authoring Checklist
- normalize every provider event into
BookUpdateorTradePrint - treat
poll()as the public emission point - make
health()useful for operators, not just for tests - decide how reconnect/backoff affects
degraded - ensure unsubscribe removes future delivery for the symbol