Metadata & discovery

Market data connections

Metadata & discovery

Instrument metadata lets the platform turn price moves into money; the instrument directory populates a symbol catalog; and discovery is how your finished plugin becomes a first-class data source with no registration code.

Instrument metadata

Bars and ticks alone do not tell the platform what a one-tick move is worth. To compute P&L, size positions, build range / Renko bars, and render a price axis correctly, the host needs the instrument's tick size and (where it exists) point value. You supply those by advertising the InstrumentMetadata capability and pointing IDataProvider.Instruments at an IInstrumentMetadata (namespace TradeStrike.Pipeline.Bars).

The interface is synchronous because these lookups happen constantly. Only the first three members are required — the rest are interface defaults that return null (or QuantityUnit.Contract), so you implement only what your venue actually knows. GetTickSize throws for an unknown instrument (better to fail loudly than build range bars on a fabricated increment); IsRegistered lets callers probe first.

IInstrumentMetadata.csnamespace TradeStrike.Pipeline.Bars;

public interface IInstrumentMetadata
{
    // Required:
    double GetTickSize(string instrumentId);     // e.g. MNQ = 0.25 (throws if unknown)
    bool   IsRegistered(string instrumentId);

    // Optional — interface defaults return null / Contract:
    double? TryGetPointValue(string instrumentId);          // currency per point (ES = 50)
    QuantityUnit GetQuantityUnit(string instrumentId);      // Contract | Share | Lot | Unit | Coin
    double? TryGetLotSize(string instrumentId);             // base units per lot (forex)
    double? TryGetQuantityStep(string instrumentId);        // smallest tradable increment
    double? TryGetMinQuantity(string instrumentId);         // venue minimum order size
    string? TryGetQuoteCurrency(string instrumentId);       // ISO-4217, e.g. "USD"
    string? TryGetAdjustmentVersion(string instrumentId);   // corporate-action stamp (equities)
    InstrumentCategory? TryGetCategory(string instrumentId);// Forex | Equity | Crypto | ...
}
A ready-made fixture. The SDK ships InMemoryInstrumentMetadata — a thread-safe registry with a fluent Register(instrumentId, tickSize, pointValue: …, quantityUnit: …, …). It is ideal for a venue whose security master you load once at connect, and for tests. The typical lifecycle is "host populates at startup, many readers thereafter".
populating metadata on connectprivate readonly InMemoryInstrumentMetadata _meta = new();

public IInstrumentMetadata? Instruments => _meta;   // with ProviderCapabilities.InstrumentMetadata set

public async Task ConnectAsync(CancellationToken ct = default)
{
    foreach (var def in await FetchSecurityMasterAsync(ct))
        _meta.Register(def.Symbol, def.TickSize,
                       pointValue: def.PointValue,
                       quoteCurrency: def.Currency);
    Status = ProviderConnectionStatus.Connected;
    ConnectionStatusChanged?.Invoke(Status);
}

The instrument directory

A venue that can enumerate its tradeable universe implements the separate IInstrumentDirectory interface (namespace TradeStrike.Pipeline.Providers). The host uses it to populate a searchable symbol catalog — connect once and get every symbol the venue lists, instead of typing each ticker by hand. It is a separate interface, not a core IDataProvider member, because not every provider has a discoverable universe; consumers probe with provider is IInstrumentDirectory.

IInstrumentDirectory.csusing System.Collections.Generic;

namespace TradeStrike.Pipeline.Providers;

public interface IInstrumentDirectory
{
    // The instrument ids this provider offers, in its canonical id form (e.g. "BTC/USDT").
    // Empty — never null — until the universe has loaded; read it after the provider
    // reports Connected if you need it complete.
    IReadOnlyList<string> ListInstruments();
}

The unified historical-data service

Consumers do not call your backfill chain directly. They ask one venue-neutral entry point — IHistoricalDataService (namespace TradeStrike.Pipeline.Services) — for bars between two UTC timestamps, regardless of which venue owns the symbol. The service resolves the active provider for a venue key through the active-provider registry, requires its Backfill to be range-aware, and delegates the load. Caching, pagination, and retry live inside your provider's chain; the service is thin composition + validation. Knowing this clarifies why implementing IRangeAwareBackfillProvider matters: the service requires it.

IHistoricalDataService.csnamespace TradeStrike.Pipeline.Services;

public interface IHistoricalDataService
{
    // venueKey matched case-insensitively against the active-provider registry;
    // bars carry UTC timestamps. Unknown venue throws InvalidOperationException.
    Task<LoadResult> LoadAsync(
        string venueKey, BarSpecification spec,
        DateTime fromUtc, DateTime toUtcExclusive,
        IProgress<LoadProgress>? progress = null, CancellationToken cancellationToken = default);

    // Raw recorded ticks for the EveryTickReal backtest path. Default impl throws
    // NotSupportedException, so a bar-only service need not implement it.
    Task<IReadOnlyList<Tick>> LoadTicksAsync(
        string venueKey, string instrumentId,
        DateTime fromUtc, DateTime toUtcExclusive,
        IProgress<LoadProgress>? progress = null, CancellationToken cancellationToken = default);
}

Discovery

There is no manifest to edit and no registration call to make. The platform discovers connection plugins the same way it discovers indicators and drawing tools — by scanning assemblies for the contracts they implement. Once your factory is on disk it becomes selectable from the host's provider list automatically.

Drop your plugin DLL into the Plugins folder and the loader puts your IDataProviderFactory into the data-provider-factory bucket. The host lists your provider, renders a credentials form from RequiredCredentials, and calls Create with the collected values. From there it is a first-class data source: charts, backtests and strategies use it like any built-in venue.