The provider
The provider INTERFACE
The provider is the live data source: it advertises what it can do, hands out the matching sub-interfaces, owns the connection lifecycle, and optionally opts into a family of extra feeds.
On this page
The IDataProvider contract
IDataProvider is the object the factory hands back once credentials are in hand. It is a
single end-to-end source that bundles every facet of a venue — history, live ticks, instrument
metadata, depth — behind one type. Rather than implementing everything, you declare a
Capabilities bitmask and expose only the sub-interfaces that correspond to the flags you
set; the rest stay null.
IDataProvider.csusing System;
using System.Threading;
using System.Threading.Tasks;
using TradeStrike.Pipeline.Bars;
using TradeStrike.Pipeline.MarketDepth;
using TradeStrike.Pipeline.Runtime;
namespace TradeStrike.Pipeline.Providers;
public interface IDataProvider : IDisposable
{
string Key { get; } // matches IDataProviderFactory.Key
string DisplayName { get; }
ProviderCapabilities Capabilities { get; } // [Flags]
// Core sub-interfaces — non-null exactly when the matching flag is set.
IBackfillProvider? Backfill { get; } // Capabilities has Backfill
ITickSource? LiveTicks { get; } // Capabilities has LiveTicks
IInstrumentMetadata? Instruments { get; } // Capabilities has InstrumentMetadata
// Depth — non-null when OrderbookDepth is set. Both default to null.
IMarketDepthFeed? Depth { get; } // aggregated Level-2
IMarketByOrderFeed? Mbo { get; } // Level-3 market-by-order
// Optional feeds — default null; no capability flag, probed by the host.
IOpenInterestFeed? OpenInterest { get; }
IMarketSummaryFeed? MarketSummary { get; }
IFundamentalsFeed? Fundamentals { get; }
// Gate session construction before hitting a NotSupportedException at backfill time.
bool SupportsBarSpec(BarSpecification spec);
ProviderConnectionStatus Status { get; }
event Action<ProviderConnectionStatus>? ConnectionStatusChanged;
Task ConnectAsync(CancellationToken cancellationToken = default);
Task DisconnectAsync();
}
The capability rule
The contract is strict: a core sub-interface property must be non-null exactly when its flag is
present in Capabilities, and null otherwise. That is the rule the host relies on when it
decides whether to wire your provider into a chart, a backtest, or a live strategy. It inspects the
bitmask before wiring so it never discovers incompatibility through a runtime exception.
ProviderCapabilities.csnamespace TradeStrike.Pipeline.Providers;
[Flags]
public enum ProviderCapabilities
{
None = 0,
Backfill = 1 << 0, // historical bars via IBackfillProvider
LiveTicks = 1 << 1, // live tick stream via ITickSource
InstrumentMetadata = 1 << 2, // tick size, point value, ... via IInstrumentMetadata
OrderRouting = 1 << 3, // order routing + execution (trading side)
OrderbookDepth = 1 << 4, // level-2 / market-by-order depth
}
SupportsBarSpec lets you reject bar types you cannot serve before any session is built —
e.g. a venue that serves only time bars returns spec is TimeBarSpec. The
Status property plus the ConnectionStatusChanged event keep the host's
connection banner honest across heterogeneous venues.
Connection lifecycle
ConnectAsync and DisconnectAsync bracket the live session. Connecting should be
idempotent — calling it when already Connected must not open two
sessions — and should throw only on a genuine credential or transport failure (on a transport error
it leaves the provider Disconnected and surfaces the exception). Disconnecting must
always be safe to call, including when already disconnected: it is a no-throw contract,
so callers do not need a try/finally around teardown.
ProviderConnectionStatus.csnamespace TradeStrike.Pipeline.Providers;
public enum ProviderConnectionStatus
{
Disconnected = 0, // initial, or after a clean disconnect
Connecting = 1, // login + handshake in progress
Connected = 2, // ready to serve backfill / live data
ConnectionLost = 3, // was connected; lost the link unexpectedly
}
ConnectionStatusChanged for
each state change — Connecting, Connected, ConnectionLost,
Disconnected. The host's status banner and the data layer's "offline, serve cache" fallback
both depend on it.Optional feeds
Beyond the flagged core surfaces, a provider can expose live scalar feeds that are neither trade
ticks nor order-book events. Each property defaults to null (an interface default member), so
you implement only the ones your venue supplies and existing providers keep compiling. All three share
the same shape: per-instrument Subscribe returning an IDisposable, any-thread
callbacks, a "latest value wins" semantic, and a SubscriptionReset event for when a
connection drop makes the last value stale.
| Property | Feed | Carries |
|---|---|---|
OpenInterest |
IOpenInterestFeed |
OpenInterestUpdate — the venue's outstanding-contract count (an absolute level). The chart stamps it onto the forming bar's Open-Interest channel. |
MarketSummary |
IMarketSummaryFeed |
MarketSummaryUpdate — level-1 daily statistics (settlement, previous close, opening, daily high/low/volume). Every field optional; "latest non-null wins" per field. |
Fundamentals |
IFundamentalsFeed |
FundamentalUpdate — Beta / EPS / dividends / market cap / 52-week stats (see FundamentalDataType). Futures & crypto venues typically leave this null. |
IOpenInterestFeed.csusing System;
namespace TradeStrike.Pipeline.Providers;
public readonly record struct OpenInterestUpdate(
string InstrumentId,
double OpenInterest, // absolute level (not a delta) — latest wins
DateTime ExchangeTimestampUtc);
public interface IOpenInterestFeed : IDisposable
{
// Dispose the returned token to unsubscribe; the upstream subscription is torn
// down when the last subscriber for the instrument drops.
IDisposable Subscribe(string instrumentId, Action<OpenInterestUpdate> onUpdate);
// Fired on a connection loss so consumers reset to "unknown" (NaN). Default no-op.
event Action? SubscriptionReset;
}
MarketSummaryReplayCache — a thread-safe, per-instrument "latest non-null wins" cache —
so every venue feed reuses one correct implementation instead of duplicating it. Call
Observe(update) for every raw update and Replay(instrumentId) when a new
subscriber joins.Opt-in capability interfaces
Some advanced host features are exposed not as IDataProvider members but as separate
interfaces your provider class also implements. The host probes for them with a cast (see
decorator-aware probing below). Implementing one is the per-venue opt-in; not
implementing it means the host falls back to a simpler path.
| Interface | Lets the host… |
|---|---|
IInstrumentDirectory |
enumerate the venue's tradeable universe (ListInstruments()) to populate a searchable symbol catalog. See Metadata & discovery. |
IContinuousBarsCapableProvider |
route a continuous-futures chart through a multi-contract stitcher by exposing the bare IHistoricalTimeBarSource. |
IContinuousTicksCapableProvider |
the tick-domain twin: expose the venue's cached IRawTickRangeProvider for continuous-futures tick replay. |
IProviderClock |
define the venue's own notion of "now" (real time for a live feed, replay position for a market-replay feed) instead of wall-clock. |
IProviderClock.csusing System;
namespace TradeStrike.Pipeline.Providers;
// Optional: a provider that defines its OWN "now" (live = real time;
// market-replay = replay position; simulation = warped). Providers that don't
// implement this are treated as real-time. Read live — a replay clock advances
// as playback runs, so callers re-read rather than cache.
public interface IProviderClock
{
DateTime NowUtc { get; }
}
Decorators & capability probing
The host sometimes wraps a venue provider in a transparent decorator — for example, a
timezone-normalising feed wrapper. A bare provider is IContinuousBarsCapableProvider check
would then fail, because the decorator does not re-implement every optional interface its inner provider
has. The SDK solves this with IDataProviderDecorator and the
DataProviderCapabilities.AsCapability<T>() helper, which looks through the
whole decorator chain.
IDataProviderDecorator.csnamespace TradeStrike.Pipeline.Providers;
// Marks an IDataProvider that wraps another. Lets capability probes look THROUGH
// the decorator to the venue provider underneath.
public interface IDataProviderDecorator
{
IDataProvider Inner { get; } // may itself be another decorator
}
public static class DataProviderCapabilities
{
// Returns `provider` (or the first provider beneath it through any decorator
// chain) as capability T, or null when neither it nor anything it wraps
// supports T. The decorator-aware replacement for `provider is T`.
public static T? AsCapability<T>(this IDataProvider? provider) where T : class;
}
When you write a decorator, implement IDataProviderDecorator.Inner and forward the core
members; consumers then use provider.AsCapability<IContinuousBarsCapableProvider>()
rather than a direct cast, and your inner venue's capabilities still surface.
The active-provider registry
Once a session has connected a venue, the host keeps the live IDataProvider in a process-wide
map keyed by venue (e.g. "rithmic", "coinbase"). Framework services — most
importantly the unified historical-data service — resolve the live provider through
IActiveDataProviderRegistry, a read-only lookup defined in the contracts assembly so they
depend on the lookup without dragging in the host's connection stack. There is one
IDataProvider instance per venue (even when a venue holds multiple trading sessions, the
data plane stays venue-keyed), and lookup is case-insensitive on the venue key.
IActiveDataProviderRegistry.csusing System.Collections.Generic;
namespace TradeStrike.Pipeline.Providers;
public interface IActiveDataProviderRegistry
{
// The active provider for venueKey, or null when no session is connected for it
// (including the empty/whitespace key — never throws on lookup).
IDataProvider? GetActive(string venueKey);
// Snapshot of every currently-active provider, keyed by venue.
IReadOnlyDictionary<string, IDataProvider> ActiveProviders { get; }
}