diff --git a/Orderbook.h b/Orderbook.h index a7ead67..de98e07 100644 --- a/Orderbook.h +++ b/Orderbook.h @@ -412,14 +412,12 @@ class Orderbook { ? asks_.at(price) : bids_.at(price); - // Swap-and-pop: move the last element into the cancelled slot, then - // pop the back. This is O(1) and keeps the vector contiguous. - // We must update the swapped order's stored index to reflect its new position. - if (idx != orders.size() - 1) { - orders[idx] = std::move(orders.back()); - orders_.at(orders[idx]->GetOrderId()).location_ = idx; + // Erase in place to preserve FIFO time priority within the price level. + // The shifted suffix needs fresh indices in the lookup map. + orders.erase(orders.begin() + static_cast(idx)); + for (std::size_t i = idx; i < orders.size(); ++i) { + orders_.at(orders[i]->GetOrderId()).location_ = i; } - orders.pop_back(); if (side == Side::Sell) { if (orders.empty()) asks_.erase(price); @@ -500,4 +498,4 @@ class Orderbook { void ResetMarketDataStats() { stats_.Reset(); } bool IsInitialized() const { return isInitialized_; } uint64_t GetLastSequenceNumber() const { return lastSequenceNumber_; } -}; \ No newline at end of file +}; diff --git a/tests.cpp b/tests.cpp index aa44314..6784467 100644 --- a/tests.cpp +++ b/tests.cpp @@ -114,6 +114,21 @@ TEST(TestTimePriority_FIFO) { ASSERT_EQ(trades[0].GetBidTrade().orderId_, 1); } +TEST(TestCancelPreservesFIFO) { + Orderbook orderbook; + orderbook.AddOrder(std::make_shared(OrderType::GoodTillCancel, 1, Side::Buy, 100, 10)); + orderbook.AddOrder(std::make_shared(OrderType::GoodTillCancel, 2, Side::Buy, 100, 10)); + orderbook.AddOrder(std::make_shared(OrderType::GoodTillCancel, 3, Side::Buy, 100, 10)); + + orderbook.CancelOrder(1); + auto trades = orderbook.AddOrder( + std::make_shared(OrderType::GoodTillCancel, 4, Side::Sell, 100, 10) + ); + + ASSERT_EQ(trades.size(), 1); + ASSERT_EQ(trades[0].GetBidTrade().orderId_, 2); +} + TEST(TestMarketOrderBuy) { Orderbook orderbook; orderbook.AddOrder(std::make_shared(OrderType::GoodTillCancel, 1, Side::Sell, 100, 10)); @@ -589,6 +604,7 @@ int main() { RUN_TEST(TestMultipleMatchesAtSamePrice); RUN_TEST(TestPricePriority); RUN_TEST(TestTimePriority_FIFO); + RUN_TEST(TestCancelPreservesFIFO); RUN_TEST(TestMarketOrderBuy); RUN_TEST(TestMarketOrderSell); RUN_TEST(TestMarketOrderEmptyBook); @@ -603,7 +619,7 @@ int main() { RUN_TEST(TestMinNotionalValidation); RUN_TEST(TestMarketOrderValidation); - std::cout << "\nAll " << 21 << " functionality tests passed!\n"; + std::cout << "\nAll " << 22 << " functionality tests passed!\n"; PrintPerformanceHeader();