From 601767d64a1cf6f86a670002fd4acca521414c0f Mon Sep 17 00:00:00 2001 From: Carter Green Date: Wed, 3 Jul 2024 13:19:28 -0500 Subject: [PATCH] ADD: Add separate `BboMsg` to C++ client --- CHANGELOG.md | 2 ++ include/databento/record.hpp | 61 +++++++++++++++++++++++++++++------- src/record.cpp | 16 ++++++++++ 3 files changed, 67 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba071a5..9d672cb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ for date fields were changed from strings or ints to `date::year_month_day`. - Added `DbnEncoder` class for encoding DBN data - Added blocking API similar to `LiveBlocking` to `DbnFileStore` with new `GetMetadata` and `NextRecord` methods +- Added `BboMsg` record struct for future `bbo-1m` and `bbo-1s` schemas - Added `PitSymbol` map constructor from `Metadata` and a `date::year_month_day` - Added `Metadata::CreateSymbolMap` and `Metadata::CreateSymbolMapForDate` methods for creating symbology maps from historical metadata @@ -25,6 +26,7 @@ for date fields were changed from strings or ints to `date::year_month_day`. - Added new dependency on [Howard Hinnant's date library](https://howardhinnant.github.io/date/date.html) - Added `ILogReceiver*` parameter to all `DbnDecoder` constructors and one `DbnFileStore` constructor - Removed type `StrMappingInterval`. `MappingInterval` is now also used in `SymbologyResolution`. +- Changed `Bbo1sMsg` and `Bbo1mMsg` to be aliases for `BboMsg` - Changed type of `start_date` and `end_date` in `MappingInterval` to `date::year_month_day` - Added `stype_in` and `stype_out` fields to `SymbologyResolution` to support creating a `TsSymbolMap` diff --git a/include/databento/record.hpp b/include/databento/record.hpp index 982917f..5a3eab0 100644 --- a/include/databento/record.hpp +++ b/include/databento/record.hpp @@ -177,9 +177,7 @@ static_assert(alignof(TradeMsg) == 8, "Must have 8-byte alignment"); struct Mbp1Msg { static bool HasRType(RType rtype) { switch (rtype) { - case RType::Mbp1: // fallthrough - case RType::Bbo1M: // fallthrough - case RType::Bbo1S: + case RType::Mbp1: return true; default: return false; @@ -202,8 +200,9 @@ struct Mbp1Msg { std::array levels; }; using TbboMsg = Mbp1Msg; -using Bbo1SMsg = Mbp1Msg; -using Bbo1MMsg = Mbp1Msg; +static_assert(alignof(Mbp1Msg) == 8, "Must have 8-byte alignment"); +static_assert(sizeof(Mbp1Msg) == sizeof(TradeMsg) + sizeof(BidAskPair), + "Mbp1Msg size must match Rust"); struct Mbp10Msg { static bool HasRType(RType rtype) { return rtype == rtype::Mbp10; } @@ -223,14 +222,40 @@ struct Mbp10Msg { std::uint32_t sequence; std::array levels; }; - -static_assert(alignof(Mbp1Msg) == 8, "Must have 8-byte alignment"); static_assert(alignof(Mbp10Msg) == 8, "Must have 8-byte alignment"); -static_assert(sizeof(Mbp1Msg) == sizeof(TradeMsg) + sizeof(BidAskPair), - "Mbp1Msg size must match Rust"); static_assert(sizeof(Mbp10Msg) == sizeof(TradeMsg) + sizeof(BidAskPair) * 10, "Mbp10Msg size must match Rust"); +struct BboMsg { + static bool HasRType(RType rtype) { + switch (rtype) { + case RType::Bbo1S: // fallthrough + case RType::Bbo1M: + return true; + default: + return false; + }; + } + + UnixNanos IndexTs() const { return ts_recv; } + + RecordHeader hd; + std::int64_t price; + std::uint32_t size; + char reserved1; + Side side; + FlagSet flags; + char reserved2; + UnixNanos ts_recv; + std::array reserved3; + std::uint32_t sequence; + std::array levels; +}; +using Bbo1SMsg = BboMsg; +using Bbo1MMsg = BboMsg; +static_assert(alignof(BboMsg) == 8, "Must have 8-byte alignment"); +static_assert(sizeof(BboMsg) == sizeof(Mbp1Msg), "BboMsg size must match Rust"); + struct CbboMsg { static bool HasRType(RType rtype) { switch (rtype) { @@ -252,7 +277,7 @@ struct CbboMsg { Action action; Side side; FlagSet flags; - std::array reserved; + char reserved; UnixNanos ts_recv; TimeDeltaNanos ts_in_delta; std::uint32_t sequence; @@ -571,6 +596,16 @@ inline bool operator!=(const Mbp10Msg& lhs, const Mbp10Msg& rhs) { return !(lhs == rhs); } +inline bool operator==(const BboMsg& lhs, const BboMsg& rhs) { + return std::tie(lhs.hd, lhs.price, lhs.size, lhs.side, lhs.flags, lhs.ts_recv, + lhs.sequence, lhs.levels) == + std::tie(rhs.hd, rhs.price, rhs.size, rhs.side, rhs.flags, rhs.ts_recv, + rhs.sequence, rhs.levels); +} +inline bool operator!=(const BboMsg& lhs, const BboMsg& rhs) { + return !(lhs == rhs); +} + inline bool operator==(const CbboMsg& lhs, const CbboMsg& rhs) { return lhs.hd == rhs.hd && lhs.price == rhs.price && lhs.size == rhs.size && lhs.action == rhs.action && lhs.side == rhs.side && @@ -680,8 +715,10 @@ std::string ToString(const Mbp1Msg& mbp_msg); std::ostream& operator<<(std::ostream& stream, const Mbp1Msg& mbp_msg); std::string ToString(const Mbp10Msg& mbp_msg); std::ostream& operator<<(std::ostream& stream, const Mbp10Msg& mbp_msg); -std::string ToString(const CbboMsg& mbp_msg); -std::ostream& operator<<(std::ostream& stream, const CbboMsg& mbp_msg); +std::string ToString(const BboMsg& bbo_msg); +std::ostream& operator<<(std::ostream& stream, const BboMsg& bbo_msg); +std::string ToString(const CbboMsg& cbbo_msg); +std::ostream& operator<<(std::ostream& stream, const CbboMsg& cbbo_msg); std::string ToString(const TradeMsg& trade_msg); std::ostream& operator<<(std::ostream& stream, const TradeMsg& trade_msg); std::string ToString(const OhlcvMsg& ohlcv_msg); diff --git a/src/record.cpp b/src/record.cpp index 60fddd6..3d41b03 100644 --- a/src/record.cpp +++ b/src/record.cpp @@ -306,6 +306,22 @@ std::ostream& operator<<(std::ostream& stream, const Mbp10Msg& mbp_msg) { static_cast(levels_helper.Finish())) .Finish(); } +std::string ToString(const BboMsg& bbo_msg) { return MakeString(bbo_msg); } +std::ostream& operator<<(std::ostream& stream, const BboMsg& bbo_msg) { + return StreamOpBuilder{stream} + .SetTypeName("BboMsg") + .SetSpacer("\n ") + .Build() + .AddField("hd", bbo_msg.hd) + .AddField("price", FixPx{bbo_msg.price}) + .AddField("size", bbo_msg.size) + .AddField("side", bbo_msg.side) + .AddField("flags", bbo_msg.flags) + .AddField("ts_recv", bbo_msg.ts_recv) + .AddField("sequence", bbo_msg.sequence) + .AddField("levels", std::get<0>(bbo_msg.levels)) + .Finish(); +} std::string ToString(const CbboMsg& cbbo_msg) { return MakeString(cbbo_msg); } std::ostream& operator<<(std::ostream& stream, const CbboMsg& cbbo_msg) { return StreamOpBuilder{stream}