FastMM documentation
FastMM C++ API 0.6.0
Public headers, tier 1 and tier 2
Loading...
Searching...
No Matches
symbology.hpp
Go to the documentation of this file.
1#pragma once
2// SymbolTable (6.2): venue symbol <-> InstrumentId. Built once at startup from the
3// InstrumentTable, read-only afterwards, so the net threads can share it without locks.
4//
5// Lookup is an open-addressing table keyed by (VenueId, FNV-1a of the upper-cased symbol);
6// venues differ in case (Binance streams are lowercase, REST/user-data payloads uppercase,
7// Bybit always uppercase) so the canonical key is uppercase and lookups fold case on the fly
8// without allocating.
11
12#include <array>
13#include <cstddef>
14#include <cstdint>
15#include <string_view>
16
17namespace fastmm::venues {
18
19[[nodiscard]] constexpr char ascii_upper(char c) noexcept {
20 return (c >= 'a' && c <= 'z') ? static_cast<char>(c - 32) : c;
22[[nodiscard]] constexpr char ascii_lower(char c) noexcept {
23 return (c >= 'A' && c <= 'Z') ? static_cast<char>(c + 32) : c;
25
26// FNV-1a over the upper-cased bytes.
27[[nodiscard]] constexpr std::uint64_t symbol_hash(std::string_view s) noexcept {
28 std::uint64_t h = 14695981039346656037ULL;
29 for (char c : s) {
30 h ^= static_cast<std::uint8_t>(ascii_upper(c));
31 h *= 1099511628211ULL;
32 }
33 return h;
34}
35
36[[nodiscard]] constexpr bool iequals_symbol(std::string_view a, std::string_view b) noexcept {
37 if (a.size() != b.size()) return false;
38 for (std::size_t i = 0; i < a.size(); ++i) {
39 if (ascii_upper(a[i]) != ascii_upper(b[i])) return false;
40 }
41 return true;
42}
43
44class SymbolTable {
45 public:
46 static constexpr std::size_t kSlots = 1024; // >= 4 * kMaxInstruments: load factor <= 0.25
47 static_assert((kSlots & (kSlots - 1)) == 0);
49 SymbolTable() noexcept { clear(); }
50
51 void clear() noexcept {
52 for (Slot& s : slots_) s = Slot{};
53 for (Entry& e : by_id_) e = Entry{};
54 count_ = 0;
55 }
56
57 // Registers every instrument of the table. Returns false on a duplicate (venue, symbol)
58 // pair or if the symbol is empty; the table is then partially filled and must be cleared.
59 bool build(const InstrumentTable& instruments) noexcept {
60 clear();
61 for (const Instrument& inst : instruments) {
62 if (!add(inst.id, inst.venue, inst.symbol.view())) return false;
63 }
64 return true;
65 }
66
67 bool add(InstrumentId id, VenueId venue, std::string_view symbol) noexcept {
68 if (symbol.empty() || symbol.size() > Symbol::kCapacity || id.value >= kMaxInstruments)
69 return false;
70 if (find(venue, symbol).valid()) return false;
71 if (count_ >= kMaxInstruments) return false;
72 const std::uint64_t h = symbol_hash(symbol);
73 std::size_t i = static_cast<std::size_t>(h) & (kSlots - 1);
74 while (slots_[i].used) i = (i + 1) & (kSlots - 1);
75 slots_[i] = Slot{h, id, venue, true};
76 Entry& e = by_id_[id.value];
77 e.venue = venue;
78 for (std::size_t k = 0; k < symbol.size(); ++k) {
79 e.upper.push_back(ascii_upper(symbol[k]));
80 e.lower.push_back(ascii_lower(symbol[k]));
81 }
82 e.valid = true;
83 ++count_;
84 return true;
85 }
86
87 // Case-insensitive lookup; invalid id when unknown. Hot path: no allocation.
88 [[nodiscard]] InstrumentId find(VenueId venue, std::string_view symbol) const noexcept {
89 const std::uint64_t h = symbol_hash(symbol);
90 std::size_t i = static_cast<std::size_t>(h) & (kSlots - 1);
91 for (std::size_t probes = 0; probes < kSlots; ++probes) {
92 const Slot& s = slots_[i];
93 if (!s.used) return InstrumentId::invalid();
94 if (s.hash == h && s.venue == venue &&
95 iequals_symbol(by_id_[s.id.value].upper.view(), symbol))
96 return s.id;
97 i = (i + 1) & (kSlots - 1);
98 }
99 return InstrumentId::invalid();
100 }
101
102 // Canonical (uppercase) venue symbol, e.g. "BTCUSDT"; empty if unknown.
103 [[nodiscard]] std::string_view venue_symbol(InstrumentId id) const noexcept {
104 return id.value < kMaxInstruments && by_id_[id.value].valid ? by_id_[id.value].upper.view()
105 : std::string_view{};
106 }
107 // Lowercase form used by Binance stream names ("btcusdt@depth@100ms").
108 [[nodiscard]] std::string_view lower_symbol(InstrumentId id) const noexcept {
109 return id.value < kMaxInstruments && by_id_[id.value].valid ? by_id_[id.value].lower.view()
110 : std::string_view{};
111 }
112 [[nodiscard]] VenueId venue_of(InstrumentId id) const noexcept {
113 return id.value < kMaxInstruments && by_id_[id.value].valid ? by_id_[id.value].venue
115 }
116 [[nodiscard]] bool contains(InstrumentId id) const noexcept {
117 return id.value < kMaxInstruments && by_id_[id.value].valid;
119 [[nodiscard]] std::size_t size() const noexcept { return count_; }
120
121 private:
122 struct Slot {
123 std::uint64_t hash = 0;
124 InstrumentId id{};
125 VenueId venue{};
126 bool used = false;
127 };
128 struct Entry {
129 Symbol upper{};
130 Symbol lower{};
131 VenueId venue{};
132 bool valid = false;
133 };
134 std::array<Slot, kSlots> slots_;
135 std::array<Entry, kMaxInstruments> by_id_;
136 std::size_t count_ = 0;
137};
138
139} // namespace fastmm::venues
Dense-by-id table built at startup; symbol lookup is a sorted FlatMap keyed by (venue,...
Definition instrument.hpp:141
bool contains(InstrumentId id) const noexcept
Definition symbology.hpp:118
std::string_view lower_symbol(InstrumentId id) const noexcept
Lowercase form used by Binance stream names ("btcusdt\@depth\@100ms").
Definition symbology.hpp:110
InstrumentId find(VenueId venue, std::string_view symbol) const noexcept
Case-insensitive lookup; invalid id when unknown. Hot path: no allocation.
Definition symbology.hpp:90
bool add(InstrumentId id, VenueId venue, std::string_view symbol) noexcept
Definition symbology.hpp:69
std::string_view venue_symbol(InstrumentId id) const noexcept
Canonical (uppercase) venue symbol, e.g. "BTCUSDT"; empty if unknown.
Definition symbology.hpp:105
VenueId venue_of(InstrumentId id) const noexcept
Definition symbology.hpp:114
std::size_t size() const noexcept
Definition symbology.hpp:121
bool build(const InstrumentTable &instruments) noexcept
Registers every instrument of the table.
Definition symbology.hpp:61
static constexpr std::size_t kSlots
>= 4 * kMaxInstruments: load factor <= 0.25
Definition symbology.hpp:48
SymbolTable() noexcept
Definition symbology.hpp:51
void clear() noexcept
Definition symbology.hpp:53
Instrument: the single definition of a tradable contract (5.3 / 5.15).
Definition venue.hpp:38
constexpr bool iequals_symbol(std::string_view a, std::string_view b) noexcept
Definition symbology.hpp:38
constexpr char ascii_upper(char c) noexcept
Definition symbology.hpp:21
constexpr std::uint64_t symbol_hash(std::string_view s) noexcept
FNV-1a over the upper-cased bytes.
Definition symbology.hpp:29
constexpr char ascii_lower(char c) noexcept
Definition symbology.hpp:24
StrongId< struct InstrumentTag, std::uint32_t > InstrumentId
Dense index into the engine's instrument table.
Definition strong_id.hpp:38
constexpr std::size_t kMaxInstruments
Definition instrument.hpp:26
FixedString< 20 > Symbol
Definition instrument.hpp:27
StrongId< struct VenueTag, std::uint8_t > VenueId
Definition strong_id.hpp:39
Strongly typed identifiers.
Definition instrument.hpp:29
static constexpr StrongId invalid() noexcept
Definition strong_id.hpp:30
Rep value
Definition strong_id.hpp:26