Skip to content

Live symbol resolution (instrument_id → symbol)#44

Merged
tbeason merged 1 commit into
mainfrom
live-symbol-mapping
Jun 22, 2026
Merged

Live symbol resolution (instrument_id → symbol)#44
tbeason merged 1 commit into
mainfrom
live-symbol-mapping

Conversation

@tbeason

@tbeason tbeason commented Jun 22, 2026

Copy link
Copy Markdown
Owner

The live counterpart to the historical symbol join (#39). Live data records carry only the opaque numeric instrument_id; the gateway announces the human-readable symbol out-of-band via SymbolMappingMsg. This adds a running instrument_id → symbol map maintained by the Live reader so consumers can resolve a record's symbol without tracking control records themselves.

API

symbol_for(client, instrument_id; which=:in)  # -> String | nothing
symbol_for(client, record;        which=:in)  # uses record.hd.instrument_id
symbol_map(client; which=:in)                 # -> Dict{UInt32,String} snapshot
add_symbol_mapping_callback(client, cb)       # cb(instrument_id, msg) on each (re)mapping

which=:in (default) returns the subscribed symbol (stype_in_symbol, e.g. "AAPL" or the parent "ES.FUT"); which=:out returns stype_out_symbol. symbol_for/symbol_map extend the same-named DBN functions — the historical side keys on metadata + a timestamp (ids are reused across a date range), while the live map is current-state, so no timestamp.

live = Live(dataset="GLBX.MDP3")
connect!(live); subscribe!(live; schema=Schema.TRADES, symbols=["ES.FUT"], stype_in=SType.PARENT); start!(live)
for rec in live
    rec isa DBN.TradeMsg || continue
    println(symbol_for(live, rec), "  ", rec.price)
end

Design notes

  • Owned by the client, so it survives reconnects (gateway re-sends mappings on re-subscribe), like last_ts_event_by_id.
  • Typed mode: the mapping is captured in the reader before the control_channel overflow-drop, so it's reliable even when a parent/continuous subscription floods SymbolMappingMsg at startup and the user isn't draining control_channel.
  • Thread-safe: the reader task writes under symbols_lock; symbol_map returns a copy; callbacks are snapshotted under the lock and fired outside it, with exceptions logged-and-swallowed (mirrors add_reconnect_callback).
  • Tracking is always-on and effectively free (a dict write only on the rare SymbolMappingMsg).
  • The full SymbolMappingMsg is stored per id, so both :in/:out symbols (and the [start,end] window) remain available.

Tests / docs

  • test/test_live_symbols.jl (21 assertions): resolve in/out, record overload, remap-overwrites, snapshot-is-a-copy, callbacks (including map-already-updated and a raising-callback-swallowed case). Full suite 1900 pass.
  • Documented under "Symbol resolution" in the Live API reference; docs build clean.

🤖 Generated with Claude Code

Live data records carry only the opaque numeric hd.instrument_id; the gateway
announces the human-readable symbol out-of-band via SymbolMappingMsg. The Live
client now keeps a running instrument_id -> SymbolMappingMsg map, maintained by
the reader as those control records arrive.

- symbol_for(client, instrument_id_or_record; which=:in) -> resolve a record's
  symbol (which=:out for the output symbol); returns nothing if unseen.
- symbol_map(client; which=:in) -> thread-safe snapshot Dict{UInt32,String}.
- add_symbol_mapping_callback(client, cb) -> cb(instrument_id, msg) on each
  (re)mapping (lock-protected, errors logged-and-swallowed, mirrors
  add_reconnect_callback).

symbol_for/symbol_map extend the DBN functions of the same name (the historical
side keys on metadata + a timestamp; the live map is current-state, so no
timestamp). Works in both typed and untyped mode and survives reconnects. In
typed mode the mapping is captured before the control_channel overflow-drop, so
it stays correct even when the control channel isn't drained.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tbeason tbeason merged commit aded30d into main Jun 22, 2026
8 checks passed
@tbeason tbeason deleted the live-symbol-mapping branch June 22, 2026 20:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant