Table of Contents

Class TsSymbolMap

Namespace
DatabentoDotNet.Dbn
Assembly
DatabentoDotNet.Dbn.dll

A timeseries symbol map: resolves an instrument ID to its symbol on a specific date. Useful for a historical request spanning multiple days, where the same instrument ID can mean a different symbol on different dates (a continuous contract rolling to a new front-month instrument, for example).

public sealed class TsSymbolMap : ISymbolIndex
Inheritance
TsSymbolMap
Implements
Inherited Members

Examples

using var decoder = new DbnDecoder(File.OpenRead("january.dbn.zst"));
TsSymbolMap symbols = TsSymbolMap.FromMetadata(decoder.Metadata!);

while (decoder.TryNextRecord(out RecordRef record))
{
    // This overload takes the record itself, and reads both keys off it: the instrument id, and
    // the date of the record's own IndexTs. Keying on Header.TsEvent instead would silently
    // return the previous day's symbol for any schema that indexes on ts_recv.
    if (symbols.TryGetSymbol(record, out string? symbol))
    {
        Console.WriteLine($"{record.Header.InstrumentId} is {symbol}");
    }
}

// Or by date and id directly, where there is no record in hand.
symbols.TryGetSymbol(new LocalDate(2024, 1, 2), 12345u, out string? onThatDay);

Remarks

Port of upstream's TsSymbolMap (symbol_map.rs:34-211). Build one with FromMetadata(Metadata) from a decoded stream's Metadata, then resolve with TryGetSymbol(LocalDate, uint, out string?) for each record's date and instrument ID.

Storage is one entry per instrument-day, not per interval. Insert(uint, LocalDate, LocalDate, string) expands a [startDate, endDate) interval into one dictionary entry for every calendar day in it, trading memory for an O(1) exact-date lookup with no range search — the same trade-off upstream makes. A query spanning many instruments over a wide date range can therefore build a large map; that is expected, not a bug to "optimize away".

The same symbol string is reused across every day of an interval, deliberately. Upstream stores Arc<String> so that expanding one interval into many day-keys costs a reference-count bump per day, not a string copy. A .NET string is already a reference type, so Insert(uint, LocalDate, LocalDate, string) gets the same sharing for free simply by storing the same string instance in every day-key it writes — never by re-slicing or re-allocating per day. Do not "simplify" this into a fresh string per day.

No date-range validation against Start/End happens here. FromMetadata(Metadata) inserts every interval of every mapping regardless of whether it falls inside the metadata's own query range — that is a property of well-formed input, not something this type enforces. Contrast FromMetadata(Metadata, LocalDate), which does validate its single date against the range.

Constructors

TsSymbolMap()

Creates a new, empty timeseries symbol map.

public TsSymbolMap()

Properties

Count

The number of instrument-day entries in the map.

public int Count { get; }

Property Value

int

Remarks

One entry per calendar day a mapping is valid for, not one per requested symbol or per mapping interval — see the remarks on TsSymbolMap.

IsEmpty

true when there are no mappings.

public bool IsEmpty { get; }

Property Value

bool

Methods

FromMetadata(Metadata)

Builds a timeseries symbol map from a decoded stream's metadata, covering every mapping interval it carries.

public static TsSymbolMap FromMetadata(Metadata metadata)

Parameters

metadata Metadata

The metadata to build the map from.

Returns

TsSymbolMap

The resulting map.

Remarks

Port of upstream's TsSymbolMap::from_metadata / TryFrom<&Metadata> (symbol_map.rs:107-109, 173-211), reached via Metadata::symbol_map() (metadata.rs:126-128).

Exceptions

ArgumentNullException

metadata is null.

DbnDecodeException

Neither StypeIn nor StypeOut is InstrumentId, so metadata cannot yield a symbol map; or a mapping's instrument-ID string does not parse as a uint.

Insert(uint, LocalDate, LocalDate, string)

Inserts a mapping for instrumentId, valid on every calendar day from startDate (inclusive) up to but not including endDate (exclusive).

public void Insert(uint instrumentId, LocalDate startDate, LocalDate endDate, string symbol)

Parameters

instrumentId uint

The instrument ID the mapping is for.

startDate LocalDate

The first day the mapping is valid for, inclusive.

endDate LocalDate

The day the mapping stops being valid, exclusive.

symbol string

The symbol to map instrumentId to.

Remarks

Port of upstream's TsSymbolMap::insert (symbol_map.rs:117-146). Writes one dictionary entry per calendar day in the half-open range [startDate, endDate), overwriting any mapping already present for a given day/instrument pair.

startDate equal to endDate is a silent no-op, matching upstream's own comment on the degenerate case ("Shouldn't happen but better to just ignore") — it is not an error, and it does not insert a single-day entry either.

Exceptions

ArgumentNullException

symbol is null.

DbnDecodeException

startDate comes after endDate.

TryGetSymbol(RecordRef, out string?)

Looks up the symbol for a decoded record.

public bool TryGetSymbol(RecordRef record, out string? symbol)

Parameters

record RecordRef

The record to resolve.

symbol string

Receives the resolved symbol, or null when the map has no mapping for this record.

Returns

bool

true if a mapping was found.

Remarks

This is the overload a decoder loop uses, since RecordRef is what TryNextRecord hands out and no downcast is needed to resolve a symbol.

TryGetSymbol(LocalDate, uint, out string?)

Looks up the symbol for an instrument ID on a specific date.

public bool TryGetSymbol(LocalDate date, uint instrumentId, out string? symbol)

Parameters

date LocalDate

The date to resolve the symbol for.

instrumentId uint

The instrument ID to resolve.

symbol string

Receives the resolved symbol, or null when there is no mapping for instrumentId on date.

Returns

bool

true if a mapping was found.

Remarks

Port of upstream's TsSymbolMap::get (symbol_map.rs:150-152), shaped as a Try* member because a miss — an unmapped instrument ID, or a date the map has no entry for — is an expected outcome, not an exceptional one.

TryGetSymbol<TRecord>(in TRecord, out string?)

Looks up the symbol for a decoded record of a known type.

public bool TryGetSymbol<TRecord>(in TRecord record, out string? symbol) where TRecord : unmanaged, IRecord<TRecord>

Parameters

record TRecord

The record to resolve.

symbol string

Receives the resolved symbol, or null when the map has no mapping for this record.

Returns

bool

true if a mapping was found.

Type Parameters

TRecord

The record struct.

Remarks

For a record that has been downcast or copied out of the read buffer — one held in a collection, say, where no RecordRef survives to call the other overload with. Takes the record by in so a 520-byte InstrumentDefMsg is read in place rather than copied to be asked its symbol.