From 87e6e4b0770f28f202ef2a25924d2390a6f9cb28 Mon Sep 17 00:00:00 2001 From: Stephane Janel Date: Fri, 8 Mar 2024 18:18:57 +0100 Subject: [PATCH] [Feature] - Merge Closed Order method --- src/api-objects/CMakeLists.txt | 14 +++++-- src/api-objects/include/closed-order.hpp | 4 ++ src/api-objects/src/closed-order.cpp | 19 ++++++++++ src/api-objects/test/closed-order_test.cpp | 44 ++++++++++++++++++++++ 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 src/api-objects/test/closed-order_test.cpp diff --git a/src/api-objects/CMakeLists.txt b/src/api-objects/CMakeLists.txt index 3b63a4df..d0dd972d 100644 --- a/src/api-objects/CMakeLists.txt +++ b/src/api-objects/CMakeLists.txt @@ -1,8 +1,8 @@ aux_source_directory(src API_OBJECTS_SRC) add_library(coincenter_api-objects STATIC ${API_OBJECTS_SRC}) -target_link_libraries(coincenter_api-objects PRIVATE coincenter_tech) -target_link_libraries(coincenter_api-objects PRIVATE coincenter_objects) +target_link_libraries(coincenter_api-objects PUBLIC coincenter_tech) +target_link_libraries(coincenter_api-objects PUBLIC coincenter_objects) target_include_directories(coincenter_api-objects PUBLIC include) @@ -11,6 +11,13 @@ add_unit_test( test/baseconstraints_test.cpp ) +add_unit_test( + closed-order_test + test/closed-order_test.cpp + LIBRARIES + coincenter_api-objects +) + add_unit_test( deposit_test test/deposit_test.cpp @@ -20,10 +27,9 @@ add_unit_test( add_unit_test( withdrawsordepositsconstraints_test - src/withdrawsordepositsconstraints.cpp test/withdrawsordepositsconstraints_test.cpp LIBRARIES - coincenter_objects + coincenter_api-objects DEFINITIONS CCT_DISABLE_SPDLOG ) diff --git a/src/api-objects/include/closed-order.hpp b/src/api-objects/include/closed-order.hpp index 147179a3..2dbd4b06 100644 --- a/src/api-objects/include/closed-order.hpp +++ b/src/api-objects/include/closed-order.hpp @@ -18,6 +18,10 @@ class ClosedOrder : public Order { string matchedTimeStr() const; + /// Compute the resulting merged closed order from *this and given one. + /// Given closed order should be of same ID, TradeSide and Market. + [[nodiscard]] ClosedOrder mergeWith(const ClosedOrder &closedOrder) const; + private: TimePoint _matchedTime; }; diff --git a/src/api-objects/src/closed-order.cpp b/src/api-objects/src/closed-order.cpp index 3b3b0344..5b5e13e9 100644 --- a/src/api-objects/src/closed-order.cpp +++ b/src/api-objects/src/closed-order.cpp @@ -16,4 +16,23 @@ ClosedOrder::ClosedOrder(OrderId id, MonetaryAmount matchedVolume, MonetaryAmoun : Order(std::move(id), matchedVolume, price, placedTime, side), _matchedTime(matchedTime) {} string ClosedOrder::matchedTimeStr() const { return ToString(_matchedTime); } + +ClosedOrder ClosedOrder::mergeWith(const ClosedOrder &closedOrder) const { + const MonetaryAmount totalMatchedVolume = closedOrder.matchedVolume() + matchedVolume(); + const auto previousMatchedTs = TimestampToMs(matchedTime()); + const auto currentMatchedTs = TimestampToMs(closedOrder.matchedTime()); + const auto avgMatchedTs = (((previousMatchedTs * matchedVolume().toNeutral()) + + (currentMatchedTs * closedOrder.matchedVolume().toNeutral())) / + totalMatchedVolume.toNeutral()) + .integerPart(); + const TimePoint avgMatchedTime{TimeInMs{avgMatchedTs}}; + + MonetaryAmount avgPrice = price(); + if (closedOrder.price() != price()) { + avgPrice = + ((matchedVolume().toNeutral() * price()) + (closedOrder.matchedVolume().toNeutral() * closedOrder.price())) / + totalMatchedVolume.toNeutral(); + } + return ClosedOrder(id(), totalMatchedVolume, avgPrice, placedTime(), avgMatchedTime, side()); +} } // namespace cct \ No newline at end of file diff --git a/src/api-objects/test/closed-order_test.cpp b/src/api-objects/test/closed-order_test.cpp new file mode 100644 index 00000000..0901f061 --- /dev/null +++ b/src/api-objects/test/closed-order_test.cpp @@ -0,0 +1,44 @@ +#include "closed-order.hpp" + +#include + +#include + +#include "monetaryamount.hpp" +#include "timedef.hpp" +#include "tradeside.hpp" + +namespace cct { + +class ClosedOrderTest : public ::testing::Test { + protected: + TimePoint tp1{TimeInMs{std::numeric_limits::max() / 10000000}}; + TimePoint tp2{TimeInMs{std::numeric_limits::max() / 9900000}}; + TimePoint tp3{TimeInMs{std::numeric_limits::max() / 9800000}}; + + ClosedOrder closedOrder1{"1", MonetaryAmount(15, "BTC", 1), MonetaryAmount(35000, "USDT"), tp1, tp1, TradeSide::kBuy}; + ClosedOrder closedOrder2{"2", MonetaryAmount(25, "BTC", 1), MonetaryAmount(45000, "USDT"), tp2, tp3, TradeSide::kBuy}; +}; + +TEST_F(ClosedOrderTest, SelfMerge) { + ClosedOrder mergedClosedOrder = closedOrder1.mergeWith(closedOrder1); + + EXPECT_EQ(mergedClosedOrder.id(), closedOrder1.id()); + EXPECT_EQ(mergedClosedOrder.placedTime(), closedOrder1.placedTime()); + EXPECT_EQ(mergedClosedOrder.matchedVolume(), closedOrder1.matchedVolume() * 2); + EXPECT_EQ(mergedClosedOrder.price(), closedOrder1.price()); + EXPECT_EQ(mergedClosedOrder.market(), closedOrder1.market()); + EXPECT_EQ(mergedClosedOrder.matchedTime(), closedOrder1.matchedTime()); +} + +TEST_F(ClosedOrderTest, Merge) { + ClosedOrder mergedClosedOrder = closedOrder1.mergeWith(closedOrder2); + + EXPECT_EQ(mergedClosedOrder.id(), closedOrder1.id()); + EXPECT_EQ(mergedClosedOrder.placedTime(), closedOrder1.placedTime()); + EXPECT_EQ(mergedClosedOrder.matchedVolume(), closedOrder1.matchedVolume() + closedOrder2.matchedVolume()); + EXPECT_EQ(mergedClosedOrder.price(), MonetaryAmount(41250, closedOrder1.price().currencyCode())); + EXPECT_EQ(mergedClosedOrder.market(), closedOrder1.market()); + EXPECT_EQ(mergedClosedOrder.matchedTime(), TimePoint{TimeInMs{934101708833}}); +} +} // namespace cct \ No newline at end of file