diff --git a/CHANGELOG.md b/CHANGELOG.md index 63dbac9..27fc3e2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.44.0 - 2025-11-18 + +### Enhancements +- Added logic to set `code` when upgrading version 1 `SystemMsg` to newer versions + +### Bug fixes +- Added missing `` include to `constants.hpp` (credit: @ognian-) + ## 0.43.0 - 2025-10-21 ### Enhancements diff --git a/CMakeLists.txt b/CMakeLists.txt index 7cf3697..fdf5864 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.24..4.0) project( databento - VERSION 0.43.0 + VERSION 0.44.0 LANGUAGES CXX DESCRIPTION "Official Databento client library" ) diff --git a/include/databento/constants.hpp b/include/databento/constants.hpp index aa4eb3c..79220e7 100644 --- a/include/databento/constants.hpp +++ b/include/databento/constants.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include diff --git a/include/databento/enums.hpp b/include/databento/enums.hpp index dcba208..7b7bad9 100644 --- a/include/databento/enums.hpp +++ b/include/databento/enums.hpp @@ -555,7 +555,7 @@ namespace trading_event { enum TradingEvent : std::uint16_t { // No additional information given. None = 0, - // Order entry and modification are not allowed. + // Order entry is allowed. Modification and cancellation are not allowed. NoCancel = 1, // A change of trading session occurred. Daily statistics are reset. ChangeTradingSession = 2, @@ -633,7 +633,8 @@ enum SystemCode : std::uint8_t { // given timestamp. EndOfInterval = 4, // No system code was specified or this record was upgraded from a version 1 struct - // where the code field didn't exist. + // where + // the code field didn't exist. Unset = 255, }; } // namespace system_code diff --git a/pkg/PKGBUILD b/pkg/PKGBUILD index a35bf8a..7178466 100644 --- a/pkg/PKGBUILD +++ b/pkg/PKGBUILD @@ -1,7 +1,7 @@ # Maintainer: Databento _pkgname=databento-cpp pkgname=databento-cpp-git -pkgver=0.43.0 +pkgver=0.44.0 pkgrel=1 pkgdesc="Official C++ client for Databento" arch=('any') diff --git a/src/v1.cpp b/src/v1.cpp index 40d0304..2fdc57a 100644 --- a/src/v1.cpp +++ b/src/v1.cpp @@ -1,8 +1,10 @@ #include "databento/v1.hpp" #include // copy +#include // size_t #include -#include // numeric_limits +#include // strlen, strncmp +#include // numeric_limits #include "databento/enums.hpp" #include "databento/pretty.hpp" // Px @@ -239,8 +241,35 @@ v2::SystemMsg SystemMsg::ToV2() const { RecordHeader{sizeof(v2::SystemMsg) / RecordHeader::kLengthMultiplier, RType::System, hd.publisher_id, hd.instrument_id, hd.ts_event}, {}, - IsHeartbeat() ? SystemCode::Heartbeat : SystemCode::Unset}; + SystemCode::Unset}; std::copy(msg.begin(), msg.end(), ret.msg.begin()); + // No standardized strnlen + const auto null_it = std::find(msg.begin(), msg.end(), '\0'); + if (null_it != msg.end()) { + constexpr auto kEndOfInterval = "End of interval for "; + constexpr auto kSubAckStart = "Subscription request "; + constexpr auto kSubAckEnd = " succeeded"; + constexpr auto kSlowReader = "Warning: slow reading"; + constexpr auto kFinishedStart = "Finished "; + constexpr auto kFinishedEnd = " replay"; + + const auto msg_len = static_cast(null_it - msg.begin()); + if (IsHeartbeat()) { + ret.code = SystemCode::Heartbeat; + } else if (std::strncmp(Msg(), kEndOfInterval, std::strlen(kEndOfInterval)) == 0) { + ret.code = SystemCode::EndOfInterval; + } else if (std::strncmp(Msg(), kSubAckStart, std::strlen(kSubAckStart)) == 0 && + std::strncmp(&Msg()[msg_len - std::strlen(kSubAckEnd)], kSubAckEnd, + std::strlen(kSubAckEnd)) == 0) { + ret.code = SystemCode::SubscriptionAck; + } else if (std::strncmp(Msg(), kSlowReader, std::strlen(kSlowReader)) == 0) { + ret.code = SystemCode::SlowReaderWarning; + } else if (std::strncmp(Msg(), kFinishedStart, std::strlen(kFinishedStart)) == 0 && + std::strncmp(&Msg()[msg_len - std::strlen(kFinishedEnd)], kFinishedEnd, + std::strlen(kFinishedEnd)) == 0) { + ret.code = SystemCode::ReplayCompleted; + } + } return ret; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 344906d..5f6bb50 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -52,6 +52,7 @@ set( src/symbol_map_tests.cpp src/symbology_tests.cpp src/tcp_client_tests.cpp + src/v1_tests.cpp src/zstd_stream_tests.cpp ) add_executable(${PROJECT_NAME} ${test_headers} ${test_sources}) diff --git a/tests/src/v1_tests.cpp b/tests/src/v1_tests.cpp new file mode 100644 index 0000000..8063e39 --- /dev/null +++ b/tests/src/v1_tests.cpp @@ -0,0 +1,42 @@ +#include + +#include // strcpy + +#include "databento/enums.hpp" +#include "databento/record.hpp" +#include "databento/v1.hpp" + +namespace databento::v1::tests { +TEST(V1Tests, TestSystemMsgCodeUpgrade) { + v1::SystemMsg target{ + RecordHeader{sizeof(v1::SystemMsg) / RecordHeader::kLengthMultiplier, + RType::System, + 0, + 0, + {}}}; + std::strcpy(target.msg.data(), "Heartbeat"); + const auto res1 = target.ToV2(); + EXPECT_EQ(res1.code, SystemCode::Heartbeat); + + target.msg = {}; + std::strcpy(target.msg.data(), "End of interval for bbo-1s"); + const auto res2 = target.ToV2(); + EXPECT_EQ(res2.code, SystemCode::EndOfInterval); + + target.msg = {}; + std::strcpy(target.msg.data(), "Subscription request 5 for mbo data succeeded"); + const auto res3 = target.ToV2(); + EXPECT_EQ(res3.code, SystemCode::SubscriptionAck); + + target.msg = {}; + std::strcpy(target.msg.data(), + "Warning: slow reading, not keeping pace with cbbo-1s data"); + const auto res4 = target.ToV2(); + EXPECT_EQ(res4.code, SystemCode::SlowReaderWarning); + + target.msg = {}; + std::strcpy(target.msg.data(), "Finished ohlcv-1s replay"); + const auto res5 = target.ToV2(); + EXPECT_EQ(res5.code, SystemCode::ReplayCompleted); +} +} // namespace databento::v1::tests