diff --git a/src/objects/src/marketorderbook.cpp b/src/objects/src/marketorderbook.cpp index df454256..eb3f5b4b 100644 --- a/src/objects/src/marketorderbook.cpp +++ b/src/objects/src/marketorderbook.cpp @@ -66,10 +66,21 @@ MarketOrderBook::MarketOrderBook(TimePoint timeStamp, Market market, const Marke } std::ranges::sort(_orders, [](auto lhs, auto rhs) { return lhs.price < rhs.price; }); - const auto adjacentFindIt = - std::ranges::adjacent_find(_orders, [](auto lhs, auto rhs) { return lhs.price == rhs.price; }); - if (adjacentFindIt != _orders.end()) { - throw exception("Forbidden duplicate price {} in the order book for market {}", adjacentFindIt->price, market); + + for (auto it = _orders.begin(); it != _orders.end();) { + it = std::adjacent_find(it, _orders.end(), [](auto lhs, auto rhs) { return lhs.price == rhs.price; }); + if (it != _orders.end()) { + auto nextIt = std::next(it); + log::warn("Forbidden duplicate price {} at amounts {} & {} in the order book for market {}, summing them", + it->price, it->amount, nextIt->amount, market); + nextIt->amount += it->amount; + // Remove the first duplicated price line (we summed the amounts on the next line) + it = _orders.erase(it); + if (it->amount == 0) { + // If the sum has 0 amount, remove the next one as well + it = _orders.erase(it); + } + } } const auto highestBidPriceIt = diff --git a/src/objects/test/marketorderbook_test.cpp b/src/objects/test/marketorderbook_test.cpp index 5096757f..3f39025c 100644 --- a/src/objects/test/marketorderbook_test.cpp +++ b/src/objects/test/marketorderbook_test.cpp @@ -153,6 +153,37 @@ TEST_F(MarketOrderBookTestCase1, Convert) { EXPECT_EQ(marketOrderBook.convert(MonetaryAmount("800", "EUR")), MonetaryAmount("0.61443932411674347", "ETH")); } +class MarketOrderBookTestDuplicatedLines : public ::testing::Test { + protected: + MarketOrderBook marketOrderBook{ + TimePoint{}, Market("ETH", "EUR"), + CreateMarketOrderBookLines( + {OrderBookLine(MonetaryAmount("0.65", "ETH"), MonetaryAmount("1300.50", "EUR"), OrderBookLine::Type::kBid), + OrderBookLine(MonetaryAmount("0.24", "ETH"), MonetaryAmount("1301", "EUR"), OrderBookLine::Type::kBid), + OrderBookLine(MonetaryAmount("0.11", "ETH"), MonetaryAmount("1301.50", "EUR"), OrderBookLine::Type::kBid), + OrderBookLine(MonetaryAmount("0.11", "ETH"), MonetaryAmount("1301.50", "EUR"), OrderBookLine::Type::kAsk), + OrderBookLine(MonetaryAmount("1.4009", "ETH"), MonetaryAmount("1302", "EUR"), OrderBookLine::Type::kAsk), + OrderBookLine(MonetaryAmount("3.78", "ETH"), MonetaryAmount("1302.50", "EUR"), OrderBookLine::Type::kAsk), + OrderBookLine(MonetaryAmount("0.24", "ETH"), MonetaryAmount("1302.50", "EUR"), OrderBookLine::Type::kAsk), + OrderBookLine(MonetaryAmount("56.10001267", "ETH"), MonetaryAmount("1303", "EUR"), + OrderBookLine::Type::kAsk)})}; +}; + +TEST_F(MarketOrderBookTestDuplicatedLines, NumberOfElements) { + EXPECT_EQ(marketOrderBook.size(), 5); + EXPECT_EQ(marketOrderBook.nbAskPrices(), 3); + EXPECT_EQ(marketOrderBook.nbBidPrices(), 2); +} + +TEST_F(MarketOrderBookTestDuplicatedLines, MiddleElements) { + EXPECT_EQ(marketOrderBook.lowestAskPrice(), MonetaryAmount("1302", "EUR")); + EXPECT_EQ(marketOrderBook.highestBidPrice(), MonetaryAmount("1301", "EUR")); +} + +TEST_F(MarketOrderBookTestDuplicatedLines, SummedAmountAsk) { + EXPECT_EQ(marketOrderBook[2].amount, MonetaryAmount("4.02", "ETH")); +} + class MarketOrderBookTestCase2 : public ::testing::Test { protected: TimePoint time;