From 0e57c9e9dd4e33199c68dfac687176cce8b3fdd6 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Fri, 24 Jan 2020 22:05:57 -0600 Subject: [PATCH] clear function to remove all orders --- include/limit_order_book.hpp | 7 + include/limit_tree.hpp | 14 ++ test/test_limit_order_book.cpp | 396 +++++++++++++++++++++++++++++++++ test/test_limit_tree.cpp | 103 +++++++++ 4 files changed, 520 insertions(+) diff --git a/include/limit_order_book.hpp b/include/limit_order_book.hpp index a2f3440..27f75d5 100644 --- a/include/limit_order_book.hpp +++ b/include/limit_order_book.hpp @@ -27,6 +27,13 @@ class LimitOrderBook { /// Initialize a new limit order book object. LimitOrderBook() : sells(), buys(), orders() { } + /// Clear all the orders in the book. + void clear() { + sells.clear(); + buys.clear(); + orders.clear(); + } + /// Add a new sell limit order to the book. /// /// @param order_id the ID for the order diff --git a/include/limit_tree.hpp b/include/limit_tree.hpp index ad10de6..6e4233d 100644 --- a/include/limit_tree.hpp +++ b/include/limit_tree.hpp @@ -108,6 +108,20 @@ struct LimitTree { size(0), volume(0) { } + /// Clear all the limits in the tree + void clear() { + // delete all the limits + for(auto item = limits.begin(); item != limits.end(); item++) + delete item->second; + // clear the pairs from the map + limits.clear(); + // set remaining tracers to 0 + root = nullptr; + best = nullptr; + size = 0; + volume = 0; + } + /// Place a limit order on the limit tree. /// /// @param order an order that matches the side of this tree diff --git a/test/test_limit_order_book.cpp b/test/test_limit_order_book.cpp index 567fd52..4ab6c7c 100644 --- a/test/test_limit_order_book.cpp +++ b/test/test_limit_order_book.cpp @@ -1345,3 +1345,399 @@ SCENARIO("a market order is submitted that spans several limits and depletes boo } } } + +// --------------------------------------------------------------------------- +// MARK: clear +// --------------------------------------------------------------------------- + +SCENARIO(R"(LOB +clear() +tree shape (single node): +* +)") { + GIVEN("an order book and an ID for a single sell order") { + auto book = LimitOrderBook(); + auto side = Side::Sell; + uint32_t size = 50; + uint64_t price = 3253; + uint64_t arrival = 0x1122334455667788; + book.limit(side, 1, size, price, arrival); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume(price)); + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } + GIVEN("an order book and an ID for a single buy order") { + auto book = LimitOrderBook(); + auto side = Side::Buy; + uint32_t size = 50; + uint64_t price = 3253; + uint64_t arrival = 0x1122334455667788; + book.limit(side, 1, size, price, arrival); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume(price)); + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} + +SCENARIO(R"(LOB +clear() +tree shape: + 1 + / \ +0 2 +)") { + uint32_t size = 50; + uint64_t prices[3] = {1, 2, 3}; + uint64_t arrivals[4] = {1, 2, 3, 4}; + GIVEN("an order book with V shaped limit tree (sell)") { + auto book = LimitOrderBook(); + auto side = Side::Sell; + // submit the MIDDLE order first (price-wise) + book.limit(side, 1, size, prices[1], arrivals[0]); + // the lowest price will be the left child + book.limit(side, 2, size, prices[0], arrivals[1]); + // the highest price will be the right child + book.limit(side, 3, size, prices[2], arrivals[2]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } + GIVEN("an order book with V shaped limit tree (buy)") { + auto book = LimitOrderBook(); + auto side = Side::Buy; + // submit the MIDDLE order first (price-wise) + book.limit(side, 1, size, prices[1], arrivals[0]); + // the lowest price will be the left child + book.limit(side, 2, size, prices[0], arrivals[1]); + // the highest price will be the right child + book.limit(side, 3, size, prices[2], arrivals[2]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} + + +SCENARIO(R"(LOB +clear() +tree shape: +0 + \ + 1 + \ + 2 +)") { + uint32_t size = 50; + uint64_t prices[3] = {1, 2, 3}; + uint64_t arrivals[4] = {1, 2, 3, 4}; + GIVEN("an order book with right leg shaped limit tree (sell)") { + auto book = LimitOrderBook(); + auto side = Side::Sell; + book.limit(side, 1, size, prices[0], arrivals[0]); + book.limit(side, 2, size, prices[1], arrivals[1]); + book.limit(side, 3, size, prices[2], arrivals[2]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } + GIVEN("an order book with right leg shaped limit tree (buy)") { + auto book = LimitOrderBook(); + auto side = Side::Buy; + book.limit(side, 1, size, prices[0], arrivals[0]); + book.limit(side, 2, size, prices[1], arrivals[1]); + book.limit(side, 3, size, prices[2], arrivals[2]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} + +SCENARIO(R"(LOB +clear() +tree shape: + 2 + / + 1 + / +0 +)") { + uint32_t size = 50; + uint64_t prices[3] = {1, 2, 3}; + uint64_t arrivals[4] = {1, 2, 3, 4}; + GIVEN("an order book with left leg shaped limit tree (sell)") { + auto book = LimitOrderBook(); + auto side = Side::Sell; + book.limit(side, 1, size, prices[2], arrivals[0]); + book.limit(side, 2, size, prices[1], arrivals[1]); + book.limit(side, 3, size, prices[0], arrivals[2]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } + GIVEN("an order book with left leg shaped limit tree (buy)") { + auto book = LimitOrderBook(); + auto side = Side::Buy; + book.limit(side, 1, size, prices[2], arrivals[0]); + book.limit(side, 2, size, prices[1], arrivals[1]); + book.limit(side, 3, size, prices[0], arrivals[2]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} + +SCENARIO(R"(LOB +clear() +tree shape: + 2 + / \ +1 4 + / + 3 +)") { + uint32_t size = 50; + uint64_t prices[4] = {1, 2, 4, 3}; + uint64_t arrivals[5] = {1, 2, 3, 4, 5}; + GIVEN("an order book with right subtree with left branch--shaped limit tree (buy)") { + auto book = LimitOrderBook(); + auto side = Side::Buy; + // submit the MIDDLE order first (price-wise) + book.limit(side, 1, size, prices[1], arrivals[0]); + // the lowest price will be the left child + book.limit(side, 2, size, prices[0], arrivals[1]); + // the highest price will be the right child + book.limit(side, 3, size, prices[2], arrivals[2]); + // the last price will be the left branch of the right child + book.limit(side, 4, size, prices[3], arrivals[3]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} + +SCENARIO(R"(LOB +clear() +tree shape: + 2 + / \ +1 5 + / + 3 + \ + 4 +)") { + uint32_t size = 50; + uint64_t prices[5] = {1, 2, 5, 3, 4}; + uint64_t arrivals[6] = {1, 2, 3, 4, 5, 6}; + GIVEN("an order book with right subtree with left branch and terminal right child--shaped limit tree (buy)") { + auto book = LimitOrderBook(); + auto side = Side::Buy; + // submit the MIDDLE order first (price-wise) + book.limit(side, 1, size, prices[1], arrivals[0]); + // the lowest price will be the left child + book.limit(side, 2, size, prices[0], arrivals[1]); + // the highest price will be the right child + book.limit(side, 3, size, prices[2], arrivals[2]); + // the last price will be the left branch of the right child + book.limit(side, 4, size, prices[3], arrivals[3]); + // the last price will be the left branch of the right child + book.limit(side, 5, size, prices[4], arrivals[4]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} + +SCENARIO(R"(LOB +clear() +tree shape: +1 + \ + 4 + / +2 + \ + 3 +)") { + uint32_t size = 50; + uint64_t prices[4] = {1, 4, 2, 3}; + uint64_t arrivals[5] = {1, 2, 3, 4, 5}; + GIVEN("an order book with right zigzag--shaped limit tree (buy)") { + auto book = LimitOrderBook(); + auto side = Side::Buy; + book.limit(side, 1, size, prices[0], arrivals[0]); + book.limit(side, 2, size, prices[1], arrivals[1]); + book.limit(side, 3, size, prices[2], arrivals[2]); + book.limit(side, 4, size, prices[3], arrivals[3]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} + +SCENARIO(R"(LOB +clear() +tree shape: + 4 + / +1 + \ + 3 + / +2 +)") { + uint32_t size = 50; + uint64_t prices[4] = {4, 1, 3, 2}; + uint64_t arrivals[5] = {1, 2, 3, 4, 5}; + GIVEN("an order book with left zigzag--shaped limit tree (sell)") { + auto book = LimitOrderBook(); + auto side = Side::Sell; + book.limit(side, 1, size, prices[0], arrivals[0]); + book.limit(side, 2, size, prices[1], arrivals[1]); + book.limit(side, 3, size, prices[2], arrivals[2]); + book.limit(side, 4, size, prices[3], arrivals[3]); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} + + +// order removal + +SCENARIO("clear book with queue of orders at limit") { + GIVEN("an order book and a Limit queue of sell orders") { + auto book = LimitOrderBook(); + auto side = Side::Sell; + uint32_t sizeA = 50; + uint32_t sizeB = 40; + uint32_t sizeC = 30; + uint64_t price = 3253; + uint64_t arrivalA = 0; + uint64_t arrivalB = 1; + uint64_t arrivalC = 2; + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } + GIVEN("an order book and a Limit queue of buy orders") { + auto book = LimitOrderBook(); + auto side = Side::Buy; + uint32_t sizeA = 50; + uint32_t sizeB = 40; + uint32_t sizeC = 30; + uint64_t price = 3253; + uint64_t arrivalA = 0; + uint64_t arrivalB = 1; + uint64_t arrivalC = 2; + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); + WHEN("the book is cleared") { + book.clear(); + THEN("the book is cleared") { + REQUIRE(0 == book.volume_buy()); + REQUIRE(0 == book.volume_sell()); + REQUIRE(0 == book.volume()); + REQUIRE(0 == book.best_buy()); + REQUIRE(0 == book.best_sell()); + } + } + } +} diff --git a/test/test_limit_tree.cpp b/test/test_limit_tree.cpp index 56a10b9..45cb987 100644 --- a/test/test_limit_tree.cpp +++ b/test/test_limit_tree.cpp @@ -670,3 +670,106 @@ SCENARIO("a market order is submitted with a limit price that spans") { } } } + +// --------------------------------------------------------------------------- +// MARK: clear +// --------------------------------------------------------------------------- + +SCENARIO("clear a single limit from LimitTree") { + Size size = 0x4545; + Price price = 0xAABBCCDD00112233; + Timestamp arrival = 0xABABCDCDDEDEF0F0; + GIVEN("a LimitTree with a single buy order") { + auto tree = LimitTree(); + Order node = {1, Side::Buy, size, price, arrival}; + tree.limit(&node); + WHEN("the tree is cleared") { + tree.clear(); + THEN("the data structures are updated") { + REQUIRE(0 == tree.limits.size()); + REQUIRE(0 == tree.volume_at(price)); + REQUIRE(0 == tree.size_at(price)); + REQUIRE(nullptr == tree.root); + REQUIRE(nullptr == tree.best); + } + } + } + GIVEN("a LimitTree and with a single sell order") { + auto tree = LimitTree(); + Order node = {1, Side::Sell, size, price, arrival}; + tree.limit(&node); + WHEN("the tree is cleared") { + tree.clear(); + THEN("the data structures are updated") { + REQUIRE(0 == tree.limits.size()); + REQUIRE(0 == tree.volume_at(price)); + REQUIRE(0 == tree.size_at(price)); + REQUIRE(nullptr == tree.root); + REQUIRE(nullptr == tree.best); + } + } + } + GIVEN("a LimitTree and with 2 single sell orders of the same price") { + // just need to test sell because logic is side independent + auto tree = LimitTree(); + Order node1 = {1, Side::Sell, size, price, arrival}; + tree.limit(&node1); + Order node2 = {2, Side::Sell, size, price, arrival}; + tree.limit(&node2); + WHEN("the tree is cleared") { + tree.clear(); + THEN("the data structures are updated") { + REQUIRE(0 == tree.limits.size()); + REQUIRE(0 == tree.volume_at(price)); + REQUIRE(0 == tree.size_at(price)); + REQUIRE(nullptr == tree.root); + REQUIRE(nullptr == tree.best); + } + } + } +} + +SCENARIO("clear multiple limits from the tree") { + Size size = 0x4545; + Price price = 0xAABBCCDD00112233; + auto priceHigher = price + 1; + Timestamp arrival = 0xABABCDCDDEDEF0F0; + GIVEN("a LimitTree with 2 buy orders") { + auto tree = LimitTree(); + Order node1 = {1, Side::Buy, size, priceHigher, arrival}; + tree.limit(&node1); + Order node2 = {2, Side::Buy, size, price, arrival}; + tree.limit(&node2); + WHEN("the tree is cleared") { + tree.clear(); + THEN("the data structures are updated") { + REQUIRE(0 == tree.limits.size()); + REQUIRE(0 == tree.volume_at(priceHigher)); + REQUIRE(0 == tree.volume_at(price)); + REQUIRE(0 == tree.size_at(price)); + REQUIRE(0 == tree.size_at(priceHigher)); + REQUIRE(nullptr == tree.root); + REQUIRE(nullptr == tree.best); + } + } + } + GIVEN("a LimitTree with 2 sell orders") { + auto tree = LimitTree(); + Order node1 = {1, Side::Sell, size, price, arrival}; + tree.limit(&node1); + Order node2 = {2, Side::Buy, size, priceHigher, arrival}; + tree.limit(&node2); + WHEN("the tree is cleared") { + tree.clear(); + THEN("the data structures are updated") { + REQUIRE(0 == tree.limits.size()); + REQUIRE(0 == tree.volume_at(priceHigher)); + REQUIRE(0 == tree.volume_at(price)); + REQUIRE(0 == tree.size_at(price)); + REQUIRE(0 == tree.size_at(priceHigher)); + REQUIRE(nullptr == tree.root); + REQUIRE(nullptr == tree.best); + } + } + } +}