From ade363238aa20abbae7ecb321024d751c1f30af0 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 01:23:51 -0600 Subject: [PATCH 01/18] update extern API --- src/lib_lob.cpp | 104 ++++++++++++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/src/lib_lob.cpp b/src/lib_lob.cpp index 551498e..7e21647 100644 --- a/src/lib_lob.cpp +++ b/src/lib_lob.cpp @@ -30,45 +30,71 @@ extern "C" { /// Delete the limit order book, i.e., purge it from memory EXP void Delete(LimitOrderBook* book) { delete book; } - // /// Return a pointer to a controller on the machine - // EXP NES::NES_Byte* Controller(LimitOrderBook* book, int port) { - // return emu->get_controller(port); - // } - - // /// Return the pointer to the screen buffer - // EXP NES::NES_Pixel* Screen(LimitOrderBook* book) { - // return emu->get_screen_buffer(); - // } - - // /// Return the pointer to the memory buffer - // EXP NES::NES_Byte* Memory(LimitOrderBook* book) { - // return emu->get_memory_buffer(); - // } - - // /// Reset the emulator - // EXP void Reset(LimitOrderBook* book) { - // emu->reset(); - // } - - // /// Perform a discrete step in the emulator (i.e., 1 frame) - // EXP void Step(LimitOrderBook* book) { - // emu->step(); - // } - - // /// Create a deep copy (i.e., a clone) of the given emulator - // EXP void Backup(LimitOrderBook* book) { - // emu->backup(); - // } - - // /// Create a deep copy (i.e., a clone) of the given emulator - // EXP void Restore(LimitOrderBook* book) { - // emu->restore(); - // } - - // /// Close the emulator, i.e., purge it from memory - // EXP void Close(LimitOrderBook* book) { - // delete emu; - // } + // limit_sell + /// + + // limit_buy + /// + + // limit + /// + + // has + /// + + // get + /// + + // cancel + /// + + // market_sell + /// + + // market_buy + /// + + // market + /// + + // Price best_sell + /// + + // Price best_buy + /// + + // Price best + /// + + /// Return the total volume for the sell side of the book. + EXP Volume volume_sell_price(LimitOrderBook* book, Price price) { return book->volume_sell(price); } + + /// Return the total volume for the sell side of the book. + EXP Volume volume_sell(LimitOrderBook* book) { return book->volume_sell(); } + + /// Return the total volume for the buy side of the book. + EXP Volume volume_buy_price(LimitOrderBook* book, Price price) { return book->volume_buy(price); } + + /// Return the total volume for the buy side of the book. + EXP Volume volume_buy(LimitOrderBook* book) { return book->volume_buy(); } + + /// Return the volume at the given limit price. + EXP Volume volume_price(LimitOrderBook* book, Price price) { return book->volume(price); } + + /// Return the total volume for the book. + EXP Volume volume(LimitOrderBook* book) { return book->volume(); } + + /// Return the size at the given limit price. + EXP Size size_at(LimitOrderBook* book, Price price) { return book->size_at(price); } + + /// Return the total size of the sell side of the book (number of orders). + EXP Size size_sell(LimitOrderBook* book) { return book->size_sell(); } + + /// Return the total size of the buy side of the book (number of orders). + EXP Size size_buy(LimitOrderBook* book) { return book->size_buy(); } + + /// Return the total size of the book (number of orders). + EXP Size size(LimitOrderBook* book) { return book->size(); } } // un-define the macro From 34d31fabb332099393ae2b3cb769a51545018e4d Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 01:24:11 -0600 Subject: [PATCH 02/18] fix types --- include/limit_order_book.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/limit_order_book.hpp b/include/limit_order_book.hpp index e6c7903..893161a 100644 --- a/include/limit_order_book.hpp +++ b/include/limit_order_book.hpp @@ -187,20 +187,20 @@ class LimitOrderBook { /// @param price the limit price to get the volume for /// @return the volume for the given limit price /// - inline Size volume_sell(Price price) { return sells.volume_at(price); } + inline Volume volume_sell(Price price) { return sells.volume_at(price); } /// Return the total volume for the sell side of the book. - inline Size volume_sell() { return sells.volume; } + inline Volume volume_sell() { return sells.volume; } /// Return the total volume for the buy side of the book. /// /// @param price the limit price to get the volume for /// @return the volume for the given limit price /// - inline Size volume_buy(Price price) { return buys.volume_at(price); } + inline Volume volume_buy(Price price) { return buys.volume_at(price); } /// Return the total volume for the buy side of the book. - inline Size volume_buy() { return buys.volume; } + inline Volume volume_buy() { return buys.volume; } /// Return the volume at the given limit price. /// From 296cadd60faa81a7accb17749c2477283fc56913 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 01:28:22 -0600 Subject: [PATCH 03/18] cleanup --- src/lib_lob.cpp | 66 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/src/lib_lob.cpp b/src/lib_lob.cpp index 7e21647..897afd3 100644 --- a/src/lib_lob.cpp +++ b/src/lib_lob.cpp @@ -25,10 +25,14 @@ using namespace LOB; // definitions of functions for the Python interface to access extern "C" { /// Initialize a new limit order book and return a pointer to it - EXP LimitOrderBook* Initialize() { return new LimitOrderBook(); } + EXP LimitOrderBook* Initialize() { + return new LimitOrderBook(); + } /// Delete the limit order book, i.e., purge it from memory - EXP void Delete(LimitOrderBook* book) { delete book; } + EXP void Delete(LimitOrderBook* book) { + delete book; + } // limit_sell /// @@ -57,44 +61,70 @@ extern "C" { // market /// - // Price best_sell - /// + /// Return the best sell price. + EXP Price best_sell(LimitOrderBook* book) { + return book->best_sell(); + } - // Price best_buy - /// + /// Return the best buy price. + EXP Price best_buy(LimitOrderBook* book) { + return book->best_buy(); + } - // Price best - /// + /// Return the best price for the given side. + EXP Price best(LimitOrderBook* book, Side side) { + return book->best(side); + } /// Return the total volume for the sell side of the book. - EXP Volume volume_sell_price(LimitOrderBook* book, Price price) { return book->volume_sell(price); } + EXP Volume volume_sell_price(LimitOrderBook* book, Price price) { + return book->volume_sell(price); + } /// Return the total volume for the sell side of the book. - EXP Volume volume_sell(LimitOrderBook* book) { return book->volume_sell(); } + EXP Volume volume_sell(LimitOrderBook* book) { + return book->volume_sell(); + } /// Return the total volume for the buy side of the book. - EXP Volume volume_buy_price(LimitOrderBook* book, Price price) { return book->volume_buy(price); } + EXP Volume volume_buy_price(LimitOrderBook* book, Price price) { + return book->volume_buy(price); + } /// Return the total volume for the buy side of the book. - EXP Volume volume_buy(LimitOrderBook* book) { return book->volume_buy(); } + EXP Volume volume_buy(LimitOrderBook* book) { + return book->volume_buy(); + } /// Return the volume at the given limit price. - EXP Volume volume_price(LimitOrderBook* book, Price price) { return book->volume(price); } + EXP Volume volume_price(LimitOrderBook* book, Price price) { + return book->volume(price); + } /// Return the total volume for the book. - EXP Volume volume(LimitOrderBook* book) { return book->volume(); } + EXP Volume volume(LimitOrderBook* book) { + return book->volume(); + } /// Return the size at the given limit price. - EXP Size size_at(LimitOrderBook* book, Price price) { return book->size_at(price); } + EXP Size size_at(LimitOrderBook* book, Price price) { + return book->size_at(price); + } /// Return the total size of the sell side of the book (number of orders). - EXP Size size_sell(LimitOrderBook* book) { return book->size_sell(); } + EXP Size size_sell(LimitOrderBook* book) { + return book->size_sell(); + } /// Return the total size of the buy side of the book (number of orders). - EXP Size size_buy(LimitOrderBook* book) { return book->size_buy(); } + EXP Size size_buy(LimitOrderBook* book) { + return book->size_buy(); + } /// Return the total size of the book (number of orders). - EXP Size size(LimitOrderBook* book) { return book->size(); } + EXP Size size(LimitOrderBook* book) { + return book->size(); + } } // un-define the macro From 9fc3f62690f528c7db214f540b9d539b9d4bc83e Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 01:47:49 -0600 Subject: [PATCH 04/18] update ctypes --- lob/lob.py | 70 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 14 deletions(-) diff --git a/lob/lob.py b/lob/lob.py index 1339439..9576ae8 100644 --- a/lob/lob.py +++ b/lob/lob.py @@ -1,27 +1,69 @@ -"""A CTypes interface to a Limit Order Book (LOB).""" +"""The Limit Order Book (LOB).""" import ctypes import os import glob -# the path to the directory this file is in -_MODULE_PATH = os.path.dirname(__file__) -# the pattern to find the C++ shared object library -_SO_PATH = 'lib_lob*' -# the absolute path to the C++ shared object library -_LIB_PATH = os.path.join(_MODULE_PATH, _SO_PATH) +LIB_PATH = os.path.join(os.path.dirname(__file__), 'lib_lob*') try: # load the library from the shared object file - _LIB = ctypes.cdll.LoadLibrary(glob.glob(_LIB_PATH)[0]) + LIB = ctypes.cdll.LoadLibrary(glob.glob(LIB_PATH)[0]) except IndexError: raise OSError('missing static lib_lob*.so library!') +Size = ctypes.c_uint32 +Price = ctypes.c_uint64 +Volume = ctypes.c_uint32 # setup the argument and return types for Initialize -_LIB.Initialize.argtypes = None -_LIB.Initialize.restype = ctypes.c_void_p +LIB.new_.argtypes = None +LIB.new_.restype = ctypes.c_void_p # setup the argument and return types for Delete -_LIB.Delete.argtypes = [ctypes.c_void_p] -_LIB.Delete.restype = None +LIB.delete_.argtypes = [ctypes.c_void_p] +LIB.delete_.restype = None + + + +# setup the argument and return types for best_sell +LIB.best_sell.argtypes = [ctypes.c_void_p] +LIB.best_sell.restype = Price +# setup the argument and return types for best_buy +LIB.best_buy.argtypes = [ctypes.c_void_p] +LIB.best_buy.restype = Price +# setup the argument and return types for best +LIB.best.argtypes = [ctypes.c_void_p, ctypes.c_bool] +LIB.best.restype = Price + +# setup the argument and return types for volume_sell_price +LIB.volume_sell_price.argtypes = [ctypes.c_void_p, Price] +LIB.volume_sell_price.restype = Volume +# setup the argument and return types for volume_sell +LIB.volume_sell.argtypes = [ctypes.c_void_p] +LIB.volume_sell.restype = Volume +# setup the argument and return types for volume_buy_price +LIB.volume_buy_price.argtypes = [ctypes.c_void_p, Price] +LIB.volume_buy_price.restype = Volume +# setup the argument and return types for volume_buy +LIB.volume_buy.argtypes = [ctypes.c_void_p] +LIB.volume_buy.restype = Volume +# setup the argument and return types for volume_price +LIB.volume_price.argtypes = [ctypes.c_void_p, Price] +LIB.volume_price.restype = Volume +# setup the argument and return types for volume +LIB.volume.argtypes = [ctypes.c_void_p] +LIB.volume.restype = Volume + +# setup the argument and return types for size_at +LIB.size_at.argtypes = [ctypes.c_void_p, Price] +LIB.size_at.restype = Size +# setup the argument and return types for size_sell +LIB.size_sell.argtypes = [ctypes.c_void_p] +LIB.size_sell.restype = Size +# setup the argument and return types for size_buy +LIB.size_buy.argtypes = [ctypes.c_void_p] +LIB.size_buy.restype = Size +# setup the argument and return types for size +LIB.size.argtypes = [ctypes.c_void_p] +LIB.size.restype = Size class LimitOrderBook: @@ -29,11 +71,11 @@ class LimitOrderBook: def __init__(self): """Initialize a new limit order book.""" - self._book = _LIB.Initialize() + self._book = LIB.new_() def __del__(self): """Delete this limit order book.""" - _LIB.Delete(self._book) + LIB.delete_(self._book) def limit_sell(self, size, price, arrival): """ From 4fa4372caa38ad57afacdcf354e1e4257983d96d Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 01:47:57 -0600 Subject: [PATCH 05/18] rename --- src/lib_lob.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib_lob.cpp b/src/lib_lob.cpp index 897afd3..428f2bc 100644 --- a/src/lib_lob.cpp +++ b/src/lib_lob.cpp @@ -25,12 +25,12 @@ using namespace LOB; // definitions of functions for the Python interface to access extern "C" { /// Initialize a new limit order book and return a pointer to it - EXP LimitOrderBook* Initialize() { + EXP LimitOrderBook* new_() { return new LimitOrderBook(); } /// Delete the limit order book, i.e., purge it from memory - EXP void Delete(LimitOrderBook* book) { + EXP void delete_(LimitOrderBook* book) { delete book; } From b7e7042cc84be380d9f23dafb53195b3bd9a64a9 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 02:05:14 -0600 Subject: [PATCH 06/18] update python API --- lob/lob.py | 87 +++++++++++++++++++++++++++++++++++++------- lob/test/test_lob.py | 17 ++++++++- 2 files changed, 90 insertions(+), 14 deletions(-) diff --git a/lob/lob.py b/lob/lob.py index 9576ae8..f0ef3b8 100644 --- a/lob/lob.py +++ b/lob/lob.py @@ -114,43 +114,104 @@ def market(self, side, size, arrival): """ def best_sell(self): - """ - """ + """Return the best sell price in the book.""" + return LIB.best_sell(self._book) def best_buy(self): - """ - """ + """Return the best buy price in the book.""" + return LIB.best_buy(self._book) - def best(self): + def best(self, side): """ + Return the best price for the given side. + + Args: + side: the side of the book to get the best price for (False=sell) + + Returns: + the best price for the given side of the book + """ + return LIB.best(self._book, side) def volume_sell(self, price=None): """ + Return the volume of the sell side of the book at the given price. + + Args: + price: the price to get the volume at + + Returns: + the volume of orders at the given price + + Note: + returns the total sell-side volume if price is `None` + """ + if price is None: + return LIB.volume_sell(self._book) + return LIB.volume_sell_price(self._book, price) def volume_buy(self, price=None): """ + Return the volume of the buy side of the book at the given price. + + Args: + price: the price to get the volume at + + Returns: + the volume of orders at the given price + + Note: + returns the total buy-side volume if price is `None` + """ + if price is None: + return LIB.volume_buy(self._book) + return LIB.volume_buy_price(self._book, price) def volume(self, price=None): """ + Return the volume of the book at the given price. + + Args: + price: the price to get the volume at + + Returns: + the volume of orders at the given price + + Note: + returns the total volume if price is `None` + """ + if price is None: + return LIB.volume(self._book) + return LIB.volume_price(self._book, price) - def size_at(self): + def size_at(self, price): """ + Return the size at the given limit price. + + Args: + price: the price to get the size of the book for + + Returns: + the size of the book at the given price + """ + return LIB.size_at(self._book, price) def size_sell(self): - """ - """ + """Return the size of the book on the sell side.""" + return LIB.size_sell(self._book) + def size_buy(self): - """ - """ + """Return the size of the book on the buy side.""" + return LIB.size_buy(self._book) - def size(self, price=None): - """ - """ + def size(self): + """Return the total size of the book (number of orders).""" + return LIB.size(self._book) # explicitly define the outward facing API of this module diff --git a/lob/test/test_lob.py b/lob/test/test_lob.py index 8422865..141c483 100644 --- a/lob/test/test_lob.py +++ b/lob/test/test_lob.py @@ -5,4 +5,19 @@ class ShouldInitializeLimitOrderBook(TestCase): def test(self): - self.assertIsInstance(lob.LimitOrderBook(), lob.LimitOrderBook) + book = lob.LimitOrderBook() + self.assertIsInstance(book, lob.LimitOrderBook) + self.assertEqual(0, book.best_sell()) + self.assertEqual(0, book.best_buy()) + self.assertEqual(0, book.best(False)) + self.assertEqual(0, book.best(True)) + self.assertEqual(0, book.volume_sell()) + self.assertEqual(0, book.volume_sell(100)) + self.assertEqual(0, book.volume_buy()) + self.assertEqual(0, book.volume_buy(100)) + self.assertEqual(0, book.volume()) + self.assertEqual(0, book.volume(100)) + self.assertEqual(0, book.size_at(100)) + self.assertEqual(0, book.size_sell()) + self.assertEqual(0, book.size_buy()) + self.assertEqual(0, book.size()) From 7c826b6366fc85c5579cdca41754192e8ef1b8b9 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 03:17:53 -0600 Subject: [PATCH 07/18] pass order_id instead of maintaining sequence in LOB --- include/limit_order_book.hpp | 64 +++-- lob/test/test_lob.py | 40 +++ test/test_limit_order_book.cpp | 470 ++++++++++++++++----------------- 3 files changed, 295 insertions(+), 279 deletions(-) diff --git a/include/limit_order_book.hpp b/include/limit_order_book.hpp index 893161a..a2f3440 100644 --- a/include/limit_order_book.hpp +++ b/include/limit_order_book.hpp @@ -22,69 +22,64 @@ class LimitOrderBook { LimitTree buys; /// a mapping of order IDs to orders (for cancellation). UIDOrderMap orders; - /// the counter for assigning unique IDs to orders. - UID sequence; public: /// Initialize a new limit order book object. - LimitOrderBook() : sells(), buys(), orders(), sequence(1) { } + LimitOrderBook() : sells(), buys(), orders() { } /// Add a new sell limit order to the book. /// + /// @param order_id the ID for the order /// @param size the number of shares to sell /// @param price the limit price for the order /// @param arrival the time the order arrived at /// @return the order ID for the order added to the book /// - UID limit_sell(Size size, Price price, Timestamp arrival) { - // put the order into the sequence map - orders.insert({sequence, {sequence, Side::Sell, size, price, arrival}}); + void limit_sell(UID order_id, Size size, Price price, Timestamp arrival) { + // put the order into the map + orders.insert({order_id, {order_id, Side::Sell, size, price, arrival}}); if (buys.best != nullptr && price <= buys.best->key) { // crosses // place a market order with the limit price - buys.market(&orders.at(sequence), [&](UID uid) { orders.erase(uid); }); - if (orders.at(sequence).size == 0) { // order filled - orders.erase(sequence); - return 0; - } + buys.market(&orders.at(order_id), [&](UID uid) { orders.erase(uid); }); + if (orders.at(order_id).size == 0) // order filled + orders.erase(order_id); } - sells.limit(&orders.at(sequence)); - return sequence++; + sells.limit(&orders.at(order_id)); } /// Add a new buy limit order to the book. /// + /// @param order_id the ID for the order /// @param size the number of shares to buy /// @param price the limit price for the order /// @param arrival the time the order arrived at /// @return the order ID for the order added to the book /// - UID limit_buy(Size size, Price price, Timestamp arrival) { - // put the order into the sequence map - orders.insert({sequence, {sequence, Side::Buy, size, price, arrival}}); + void limit_buy(UID order_id, Size size, Price price, Timestamp arrival) { + // put the order into the map + orders.insert({order_id, {order_id, Side::Buy, size, price, arrival}}); if (sells.best != nullptr && price >= sells.best->key) { // crosses // place a market order with the limit price - sells.market(&orders.at(sequence), [&](UID uid) { orders.erase(uid); }); - if (orders.at(sequence).size == 0) { // order filled - orders.erase(sequence); - return 0; - } + sells.market(&orders.at(order_id), [&](UID uid) { orders.erase(uid); }); + if (orders.at(order_id).size == 0) // order filled + orders.erase(order_id); } - buys.limit(&orders.at(sequence)); - return sequence++; + buys.limit(&orders.at(order_id)); } /// Add a new order to the book. /// /// @param side whether the order is a buy (true) or sell (false) + /// @param order_id the ID for the order /// @param size the number of shares to buy /// @param price the limit/market price for the order /// @param arrival the time the order arrived at /// @return the order ID for the order added to the book /// - inline UID limit(Side side, Size size, Price price, Timestamp arrival) { + inline void limit(Side side, UID order_id, Size size, Price price, Timestamp arrival) { switch (side) { // send the order to the appropriate side - case Side::Sell: return limit_sell(size, price, arrival); - case Side::Buy: return limit_buy(size, price, arrival); + case Side::Sell: return limit_sell(order_id, size, price, arrival); + case Side::Buy: return limit_buy(order_id, size, price, arrival); } } @@ -117,22 +112,24 @@ class LimitOrderBook { /// Execute a sell market order. /// + /// @param order_id the ID for the order /// @param size the size of the market order /// @arrival the arrival of the market order /// - void market_sell(Size size, Timestamp arrival) { - auto order = Order(sequence, Side::Sell, size, 0, arrival); + void market_sell(UID order_id, Size size, Timestamp arrival) { + auto order = Order(order_id, Side::Sell, size, 0, arrival); order.execution = arrival; buys.market(&order, [&](UID uid) { orders.erase(uid); }); } /// Execute a buy market order. /// + /// @param order_id the ID for the order /// @param size the size of the market order /// @arrival the arrival of the market order /// - void market_buy(Size size, Timestamp arrival) { - auto order = Order(sequence, Side::Buy, size, 0, arrival); + void market_buy(UID order_id, Size size, Timestamp arrival) { + auto order = Order(order_id, Side::Buy, size, 0, arrival); order.execution = arrival; sells.market(&order, [&](UID uid) { orders.erase(uid); }); } @@ -140,13 +137,14 @@ class LimitOrderBook { /// Execute a market order. /// /// @param side whether the order is a sell or buy order + /// @param order_id the ID for the order /// @param size the size of the market order /// @arrival the arrival of the market order /// - inline void market(Side side, Size size, Timestamp arrival) { + inline void market(Side side, UID order_id, Size size, Timestamp arrival) { switch (side) { // send the market order to the appropriate side - case Side::Sell: { market_sell(size, arrival); break; } - case Side::Buy: { market_buy(size, arrival); break; } + case Side::Sell: { market_sell(order_id, size, arrival); break; } + case Side::Buy: { market_buy(order_id, size, arrival); break; } } } diff --git a/lob/test/test_lob.py b/lob/test/test_lob.py index 141c483..893a2b8 100644 --- a/lob/test/test_lob.py +++ b/lob/test/test_lob.py @@ -21,3 +21,43 @@ def test(self): self.assertEqual(0, book.size_sell()) self.assertEqual(0, book.size_buy()) self.assertEqual(0, book.size()) + + +class ShouldPlaceSellLimitOrder(TestCase): + def test(self): + book = lob.LimitOrderBook() + + +class ShouldCancelSellLimitOrder(TestCase): + def test(self): + book = lob.LimitOrderBook() + + +class ShouldPlaceBuyLimitOrder(TestCase): + def test(self): + book = lob.LimitOrderBook() + + +class ShouldCancelBuyLimitOrder(TestCase): + def test(self): + book = lob.LimitOrderBook() + + +class ShouldPlaceSellMarketOrderEmptyBook(TestCase): + def test(self): + book = lob.LimitOrderBook() + + +class ShouldPlaceBuyMarketOrderEmptyBook(TestCase): + def test(self): + book = lob.LimitOrderBook() + + +class ShouldPlaceSellMarketOrderAndMatch(TestCase): + def test(self): + book = lob.LimitOrderBook() + + +class ShouldPlaceBuyMarketOrderAndMatch(TestCase): + def test(self): + book = lob.LimitOrderBook() diff --git a/test/test_limit_order_book.cpp b/test/test_limit_order_book.cpp index 218c77e..567fd52 100644 --- a/test/test_limit_order_book.cpp +++ b/test/test_limit_order_book.cpp @@ -31,10 +31,8 @@ SCENARIO("send single order to LimitOrderBook") { uint64_t price = 0xFEDCBA9876543210; uint64_t arrival = 0x1122334455667788; WHEN("the order is sent") { - auto uid = book.limit(side, size, price, arrival); + book.limit(side, 1, size, price, arrival); THEN("order ID is returned and the order is recorded") { - // the first order should have ID 1 - REQUIRE(1 == uid); REQUIRE(size == book.volume(price)); REQUIRE(0 == book.volume(price - 1)); REQUIRE(0 == book.volume(price + 1)); @@ -50,10 +48,8 @@ SCENARIO("send single order to LimitOrderBook") { uint64_t price = 0xFEDCBA9876543210; uint64_t arrival = 0x1122334455667788; WHEN("the order is sent") { - auto uid = book.limit(side, size, price, arrival); + book.limit(side, 1, size, price, arrival); THEN("order ID is returned and the order is recorded") { - // the first order should have ID 1 - REQUIRE(1 == uid); REQUIRE(size == book.volume(price)); REQUIRE(0 == book.volume(price - 1)); REQUIRE(0 == book.volume(price + 1)); @@ -76,13 +72,10 @@ SCENARIO("send homogeneous orders to LimitOrderBook at same price") { uint64_t arrivalB = 1; uint64_t arrivalC = 2; WHEN("the orders are sent") { - auto uidA = book.limit(side, sizeA, price, arrivalA); - auto uidB = book.limit(side, sizeB, price, arrivalB); - auto uidC = book.limit(side, sizeC, price, arrivalC); + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); THEN("order ID is returned and the order is recorded") { - REQUIRE(1 == uidA); - REQUIRE(2 == uidB); - REQUIRE(3 == uidC); REQUIRE(sizeA + sizeB + sizeC == book.volume(price)); REQUIRE(0 == book.volume(price - 1)); REQUIRE(0 == book.volume(price + 1)); @@ -102,13 +95,10 @@ SCENARIO("send homogeneous orders to LimitOrderBook at same price") { uint64_t arrivalB = 1; uint64_t arrivalC = 2; WHEN("the orders are sent") { - auto uidA = book.limit(side, sizeA, price, arrivalA); - auto uidB = book.limit(side, sizeB, price, arrivalB); - auto uidC = book.limit(side, sizeC, price, arrivalC); + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); THEN("order ID is returned and the order is recorded") { - REQUIRE(1 == uidA); - REQUIRE(2 == uidB); - REQUIRE(3 == uidC); REQUIRE(sizeA + sizeB + sizeC == book.volume(price)); REQUIRE(0 == book.volume(price - 1)); REQUIRE(0 == book.volume(price + 1)); @@ -133,13 +123,10 @@ SCENARIO("send homogeneous orders to LimitOrderBook at different prices") { uint64_t arrivalB = 1; uint64_t arrivalC = 2; WHEN("the orders are sent") { - auto uidA = book.limit(side, sizeA, priceA, arrivalA); - auto uidB = book.limit(side, sizeB, priceB, arrivalB); - auto uidC = book.limit(side, sizeC, priceC, arrivalC); + book.limit(side, 1, sizeA, priceA, arrivalA); + book.limit(side, 2, sizeB, priceB, arrivalB); + book.limit(side, 3, sizeC, priceC, arrivalC); THEN("order ID is returned and the order is recorded") { - REQUIRE(1 == uidA); - REQUIRE(2 == uidB); - REQUIRE(3 == uidC); REQUIRE(sizeA == book.volume(priceA)); REQUIRE(sizeB == book.volume(priceB)); REQUIRE(sizeC == book.volume(priceC)); @@ -161,13 +148,10 @@ SCENARIO("send homogeneous orders to LimitOrderBook at different prices") { uint64_t arrivalB = 1; uint64_t arrivalC = 2; WHEN("the orders are sent") { - auto uidA = book.limit(side, sizeA, priceA, arrivalA); - auto uidB = book.limit(side, sizeB, priceB, arrivalB); - auto uidC = book.limit(side, sizeC, priceC, arrivalC); + book.limit(side, 1, sizeA, priceA, arrivalA); + book.limit(side, 2, sizeB, priceB, arrivalB); + book.limit(side, 3, sizeC, priceC, arrivalC); THEN("order ID is returned and the order is recorded") { - REQUIRE(1 == uidA); - REQUIRE(2 == uidB); - REQUIRE(3 == uidC); REQUIRE(sizeA == book.volume(priceA)); REQUIRE(sizeB == book.volume(priceB)); REQUIRE(sizeC == book.volume(priceC)); @@ -189,14 +173,11 @@ SCENARIO("a limit order is submitted that crosses") { uint64_t arrivalA = 0; uint64_t arrivalB = 1; uint64_t arrivalC = 2; - auto uidA = book.limit(side, size, priceA, arrivalA); - auto uidB = book.limit(side, size, priceB, arrivalB); + book.limit(side, 1, size, priceA, arrivalA); + book.limit(side, 2, size, priceB, arrivalB); WHEN("the sell limit order is sent") { - auto uidC = book.limit(!side, sizeMarket, priceB, arrivalC); + book.limit(!side, 3, sizeMarket, priceB, arrivalC); THEN("order ID is returned and the order is recorded") { - REQUIRE(1 == uidA); - REQUIRE(2 == uidB); - REQUIRE(3 == uidC); REQUIRE(size == book.volume(priceA)); REQUIRE(sizeMarket - size == book.volume(priceB)); REQUIRE(priceA == book.best_buy()); @@ -215,14 +196,11 @@ SCENARIO("a limit order is submitted that crosses") { uint64_t arrivalA = 0; uint64_t arrivalB = 1; uint64_t arrivalC = 2; - auto uidA = book.limit(side, size, priceA, arrivalA); - auto uidB = book.limit(side, size, priceB, arrivalB); + book.limit(side, 1, size, priceA, arrivalA); + book.limit(side, 2, size, priceB, arrivalB); WHEN("the sell limit order is sent") { - auto uidC = book.limit(!side, sizeMarket, priceB, arrivalC); + book.limit(!side, 3, sizeMarket, priceB, arrivalC); THEN("order ID is returned and the order is recorded") { - REQUIRE(1 == uidA); - REQUIRE(2 == uidB); - REQUIRE(3 == uidC); REQUIRE(size == book.volume(priceA)); REQUIRE(sizeMarket - size == book.volume(priceB)); REQUIRE(priceA == book.best_sell()); @@ -248,9 +226,9 @@ tree shape (single node): uint32_t size = 50; uint64_t price = 3253; uint64_t arrival = 0x1122334455667788; - auto uid = book.limit(side, size, price, arrival); + book.limit(side, 1, size, price, arrival); WHEN("the order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(0 == book.volume(price)); REQUIRE(0 == book.best_buy()); @@ -259,9 +237,9 @@ tree shape (single node): } // tree integrity check WHEN("the order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, price, arrival + 1); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 2, size, price, arrival + 1); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(0 == book.volume(price)); REQUIRE(0 == book.best_buy()); @@ -275,9 +253,9 @@ tree shape (single node): uint32_t size = 50; uint64_t price = 3253; uint64_t arrival = 0x1122334455667788; - auto uid = book.limit(side, size, price, arrival); + book.limit(side, 1, size, price, arrival); WHEN("the orders is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(0 == book.volume(price)); REQUIRE(0 == book.best_buy()); @@ -286,9 +264,9 @@ tree shape (single node): } // tree integrity check WHEN("the order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, price, arrival + 1); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 2, size, price, arrival + 1); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(0 == book.volume(price)); REQUIRE(0 == book.best_buy()); @@ -311,13 +289,13 @@ tree shape - left: auto book = LimitOrderBook(); auto side = Side::Sell; // submit the MIDDLE order first (price-wise) - book.limit(side, size, prices[1], arrivals[0]); + book.limit(side, 1, size, prices[1], arrivals[0]); // the lowest price will be the left child - auto uid = book.limit(side, size, prices[0], arrivals[1]); + book.limit(side, 2, size, prices[0], arrivals[1]); // the highest price will be the right child - book.limit(side, size, prices[2], arrivals[2]); + book.limit(side, 3, size, prices[2], arrivals[2]); WHEN("the left order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -327,9 +305,9 @@ tree shape - left: } // tree integrity check WHEN("the left order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[0], arrivals[3]); - book.cancel(uid1); + book.cancel(2); + book.limit(side, 4, size, prices[0], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -342,13 +320,13 @@ tree shape - left: auto book = LimitOrderBook(); auto side = Side::Buy; // submit the MIDDLE order first (price-wise) - book.limit(side, size, prices[1], arrivals[0]); + book.limit(side, 1, size, prices[1], arrivals[0]); // the lowest price will be the left child - auto uid = book.limit(side, size, prices[0], arrivals[1]); + book.limit(side, 2, size, prices[0], arrivals[1]); // the highest price will be the right child - book.limit(side, size, prices[2], arrivals[2]); + book.limit(side, 3, size, prices[2], arrivals[2]); WHEN("the left order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -358,9 +336,9 @@ tree shape - left: } // tree integrity check WHEN("the left order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[0], arrivals[3]); - book.cancel(uid1); + book.cancel(2); + book.limit(side, 4, size, prices[0], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -384,13 +362,13 @@ tree shape - right: auto book = LimitOrderBook(); auto side = Side::Sell; // submit the MIDDLE order first (price-wise) - book.limit(side, size, prices[1], arrivals[0]); + book.limit(side, 1, size, prices[1], arrivals[0]); // the lowest price will be the left child - book.limit(side, size, prices[0], arrivals[1]); + book.limit(side, 2, size, prices[0], arrivals[1]); // the highest price will be the right child - auto uid = book.limit(side, size, prices[2], arrivals[2]); + book.limit(side, 3, size, prices[2], arrivals[2]); WHEN("the right order is canceled") { - book.cancel(uid); + book.cancel(3); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -400,9 +378,9 @@ tree shape - right: } // tree integrity check WHEN("the right order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[2], arrivals[3]); - book.cancel(uid1); + book.cancel(3); + book.limit(side, 4, size, prices[2], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -415,13 +393,13 @@ tree shape - right: auto book = LimitOrderBook(); auto side = Side::Buy; // submit the MIDDLE order first (price-wise) - book.limit(side, size, prices[1], arrivals[0]); + book.limit(side, 1, size, prices[1], arrivals[0]); // the lowest price will be the left child - book.limit(side, size, prices[0], arrivals[1]); + book.limit(side, 2, size, prices[0], arrivals[1]); // the highest price will be the right child - auto uid = book.limit(side, size, prices[2], arrivals[2]); + book.limit(side, 3, size, prices[2], arrivals[2]); WHEN("the right order is canceled") { - book.cancel(uid); + book.cancel(3); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -431,9 +409,9 @@ tree shape - right: } // tree integrity check WHEN("the right order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[2], arrivals[3]); - book.cancel(uid1); + book.cancel(3); + book.limit(side, 4, size, prices[2], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -457,13 +435,13 @@ tree shape - root: auto book = LimitOrderBook(); auto side = Side::Sell; // submit the MIDDLE order first (price-wise) - auto uid = book.limit(side, size, prices[1], arrivals[0]); + book.limit(side, 1, size, prices[1], arrivals[0]); // the lowest price will be the left child - book.limit(side, size, prices[0], arrivals[1]); + book.limit(side, 2, size, prices[0], arrivals[1]); // the highest price will be the right child - book.limit(side, size, prices[2], arrivals[2]); + book.limit(side, 3, size, prices[2], arrivals[2]); WHEN("the root order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -473,9 +451,9 @@ tree shape - root: } // tree integrity check WHEN("the root order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[3]); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 4, size, prices[1], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -488,13 +466,13 @@ tree shape - root: auto book = LimitOrderBook(); auto side = Side::Buy; // submit the MIDDLE order first (price-wise) - auto uid = book.limit(side, size, prices[1], arrivals[0]); + book.limit(side, 1, size, prices[1], arrivals[0]); // the lowest price will be the left child - book.limit(side, size, prices[0], arrivals[1]); + book.limit(side, 2, size, prices[0], arrivals[1]); // the highest price will be the right child - book.limit(side, size, prices[2], arrivals[2]); + book.limit(side, 3, size, prices[2], arrivals[2]); WHEN("the root order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -504,9 +482,9 @@ tree shape - root: } // tree integrity check WHEN("the root order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[3]); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 4, size, prices[1], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -531,11 +509,11 @@ tree shape - root: GIVEN("an order book with right leg shaped limit tree (sell)") { auto book = LimitOrderBook(); auto side = Side::Sell; - auto uid = book.limit(side, size, prices[0], arrivals[0]); - book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[2], arrivals[2]); + 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 root order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -545,9 +523,9 @@ tree shape - root: } // tree integrity check WHEN("the root order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[0], arrivals[3]); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 4, size, prices[0], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -559,11 +537,11 @@ tree shape - root: GIVEN("an order book with right leg shaped limit tree (buy)") { auto book = LimitOrderBook(); auto side = Side::Buy; - auto uid = book.limit(side, size, prices[0], arrivals[0]); - book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[2], arrivals[2]); + 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 root order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -573,9 +551,9 @@ tree shape - root: } // tree integrity check WHEN("the root order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[0], arrivals[3]); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 4, size, prices[0], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -600,11 +578,11 @@ tree shape - middle: GIVEN("an order book with right leg shaped limit tree (sell)") { auto book = LimitOrderBook(); auto side = Side::Sell; - book.limit(side, size, prices[0], arrivals[0]); - auto uid = book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[2], arrivals[2]); + 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 middle order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -614,9 +592,9 @@ tree shape - middle: } // tree integrity check WHEN("the middle order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[3]); - book.cancel(uid1); + book.cancel(2); + book.limit(side, 4, size, prices[1], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -628,11 +606,11 @@ tree shape - middle: GIVEN("an order book with right leg shaped limit tree (buy)") { auto book = LimitOrderBook(); auto side = Side::Buy; - book.limit(side, size, prices[0], arrivals[0]); - auto uid = book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[2], arrivals[2]); + 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 middle order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -642,9 +620,9 @@ tree shape - middle: } // tree integrity check WHEN("the middle order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[3]); - book.cancel(uid1); + book.cancel(2); + book.limit(side, 4, size, prices[1], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -669,11 +647,11 @@ tree shape - leaf: GIVEN("an order book with right leg shaped limit tree (sell)") { auto book = LimitOrderBook(); auto side = Side::Sell; - book.limit(side, size, prices[0], arrivals[0]); - book.limit(side, size, prices[1], arrivals[1]); - auto uid = book.limit(side, size, prices[2], arrivals[2]); + 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 leaf order is canceled") { - book.cancel(uid); + book.cancel(3); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -683,9 +661,9 @@ tree shape - leaf: } // tree integrity check WHEN("the leaf order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[2], arrivals[3]); - book.cancel(uid1); + book.cancel(3); + book.limit(side, 4, size, prices[2], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -697,11 +675,11 @@ tree shape - leaf: GIVEN("an order book with right leg shaped limit tree (buy)") { auto book = LimitOrderBook(); auto side = Side::Buy; - book.limit(side, size, prices[0], arrivals[0]); - book.limit(side, size, prices[1], arrivals[1]); - auto uid = book.limit(side, size, prices[2], arrivals[2]); + 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 leaf order is canceled") { - book.cancel(uid); + book.cancel(3); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -711,9 +689,9 @@ tree shape - leaf: } // tree integrity check WHEN("the leaf order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[2], arrivals[3]); - book.cancel(uid1); + book.cancel(3); + book.limit(side, 4, size, prices[2], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -738,11 +716,11 @@ tree shape - root: GIVEN("an order book with left leg shaped limit tree (sell)") { auto book = LimitOrderBook(); auto side = Side::Sell; - auto uid = book.limit(side, size, prices[2], arrivals[0]); - book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[0], arrivals[2]); + 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 root order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -752,9 +730,9 @@ tree shape - root: } // tree integrity check WHEN("the root order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[2], arrivals[3]); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 4, size, prices[2], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -766,11 +744,11 @@ tree shape - root: GIVEN("an order book with left leg shaped limit tree (buy)") { auto book = LimitOrderBook(); auto side = Side::Buy; - auto uid = book.limit(side, size, prices[2], arrivals[0]); - book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[0], arrivals[2]); + 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 root order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -780,9 +758,9 @@ tree shape - root: } // tree integrity check WHEN("the root order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[2], arrivals[3]); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 4, size, prices[2], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -807,11 +785,11 @@ tree shape - middle: GIVEN("an order book with left leg shaped limit tree (sell)") { auto book = LimitOrderBook(); auto side = Side::Sell; - book.limit(side, size, prices[2], arrivals[0]); - auto uid = book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[0], arrivals[2]); + 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 middle order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -821,9 +799,9 @@ tree shape - middle: } // tree integrity check WHEN("the middle order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[3]); - book.cancel(uid1); + book.cancel(2); + book.limit(side, 4, size, prices[1], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -835,11 +813,11 @@ tree shape - middle: GIVEN("an order book with left leg shaped limit tree (buy)") { auto book = LimitOrderBook(); auto side = Side::Buy; - book.limit(side, size, prices[2], arrivals[0]); - auto uid = book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[0], arrivals[2]); + 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 middle order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -849,9 +827,9 @@ tree shape - middle: } // tree integrity check WHEN("the middle order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[3]); - book.cancel(uid1); + book.cancel(2); + book.limit(side, 4, size, prices[1], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -876,11 +854,11 @@ tree shape - leaf: GIVEN("an order book with left leg shaped limit tree (sell)") { auto book = LimitOrderBook(); auto side = Side::Sell; - book.limit(side, size, prices[2], arrivals[0]); - book.limit(side, size, prices[1], arrivals[1]); - auto uid = book.limit(side, size, prices[0], arrivals[2]); + 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 leaf order is canceled") { - book.cancel(uid); + book.cancel(3); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -890,9 +868,9 @@ tree shape - leaf: } // tree integrity check WHEN("the leaf order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[0], arrivals[3]); - book.cancel(uid1); + book.cancel(3); + book.limit(side, 4, size, prices[0], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -904,11 +882,11 @@ tree shape - leaf: GIVEN("an order book with left leg shaped limit tree (buy)") { auto book = LimitOrderBook(); auto side = Side::Buy; - book.limit(side, size, prices[2], arrivals[0]); - book.limit(side, size, prices[1], arrivals[1]); - auto uid = book.limit(side, size, prices[0], arrivals[2]); + 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 leaf order is canceled") { - book.cancel(uid); + book.cancel(3); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -918,9 +896,9 @@ tree shape - leaf: } // tree integrity check WHEN("the leaf order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[0], arrivals[3]); - book.cancel(uid1); + book.cancel(3); + book.limit(side, 4, size, prices[0], arrivals[3]); + book.cancel(4); THEN("the limit is cleared") { REQUIRE(0 == book.volume(prices[0])); REQUIRE(size == book.volume(prices[1])); @@ -946,15 +924,15 @@ tree shape - root: auto book = LimitOrderBook(); auto side = Side::Buy; // submit the MIDDLE order first (price-wise) - auto uid = book.limit(side, size, prices[1], arrivals[0]); + book.limit(side, 1, size, prices[1], arrivals[0]); // the lowest price will be the left child - book.limit(side, size, prices[0], arrivals[1]); + book.limit(side, 2, size, prices[0], arrivals[1]); // the highest price will be the right child - book.limit(side, size, prices[2], arrivals[2]); + book.limit(side, 3, size, prices[2], arrivals[2]); // the last price will be the left branch of the right child - book.limit(side, size, prices[3], arrivals[3]); + book.limit(side, 4, size, prices[3], arrivals[3]); WHEN("the root order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -965,9 +943,9 @@ tree shape - root: } // tree integrity check WHEN("the root order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[4]); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 5, size, prices[1], arrivals[4]); + book.cancel(5); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -996,17 +974,17 @@ tree shape - root: auto book = LimitOrderBook(); auto side = Side::Buy; // submit the MIDDLE order first (price-wise) - auto uid = book.limit(side, size, prices[1], arrivals[0]); + book.limit(side, 1, size, prices[1], arrivals[0]); // the lowest price will be the left child - book.limit(side, size, prices[0], arrivals[1]); + book.limit(side, 2, size, prices[0], arrivals[1]); // the highest price will be the right child - book.limit(side, size, prices[2], arrivals[2]); + book.limit(side, 3, size, prices[2], arrivals[2]); // the last price will be the left branch of the right child - book.limit(side, size, prices[3], arrivals[3]); + book.limit(side, 4, size, prices[3], arrivals[3]); // the last price will be the left branch of the right child - book.limit(side, size, prices[4], arrivals[4]); + book.limit(side, 5, size, prices[4], arrivals[4]); WHEN("the root order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -1018,9 +996,9 @@ tree shape - root: } // tree integrity check WHEN("the root order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[5]); - book.cancel(uid1); + book.cancel(1); + book.limit(side, 6, size, prices[1], arrivals[5]); + book.cancel(6); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -1049,12 +1027,12 @@ tree shape - node 4: GIVEN("an order book with right zigzag--shaped limit tree (buy)") { auto book = LimitOrderBook(); auto side = Side::Buy; - book.limit(side, size, prices[0], arrivals[0]); - auto uid = book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[2], arrivals[2]); - book.limit(side, size, prices[3], arrivals[3]); + 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 root-child order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -1065,9 +1043,9 @@ tree shape - node 4: } // tree integrity check WHEN("the root-child order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[4]); - book.cancel(uid1); + book.cancel(2); + book.limit(side, 5, size, prices[1], arrivals[4]); + book.cancel(5); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -1095,12 +1073,12 @@ tree shape - node 1: GIVEN("an order book with left zigzag--shaped limit tree (sell)") { auto book = LimitOrderBook(); auto side = Side::Sell; - book.limit(side, size, prices[0], arrivals[0]); - auto uid = book.limit(side, size, prices[1], arrivals[1]); - book.limit(side, size, prices[2], arrivals[2]); - book.limit(side, size, prices[3], arrivals[3]); + 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 root-child order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -1111,9 +1089,9 @@ tree shape - node 1: } // tree integrity check WHEN("the root-child order is duplicated, added, and canceled again") { - book.cancel(uid); - uint64_t uid1 = book.limit(side, size, prices[1], arrivals[4]); - book.cancel(uid1); + book.cancel(2); + book.limit(side, 5, size, prices[1], arrivals[4]); + book.cancel(5); THEN("the limit is cleared") { REQUIRE(size == book.volume(prices[0])); REQUIRE(0 == book.volume(prices[1])); @@ -1138,11 +1116,11 @@ SCENARIO("cancel first order in a Limit queue of orders") { uint64_t arrivalA = 0; uint64_t arrivalB = 1; uint64_t arrivalC = 2; - auto uid = book.limit(side, sizeA, price, arrivalA); - book.limit(side, sizeB, price, arrivalB); - book.limit(side, sizeC, price, arrivalC); + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); WHEN("the first order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("order ID is returned and the order is recorded") { REQUIRE(sizeB + sizeC == book.volume(price)); REQUIRE(0 == book.best_buy()); @@ -1160,11 +1138,11 @@ SCENARIO("cancel first order in a Limit queue of orders") { uint64_t arrivalA = 0; uint64_t arrivalB = 1; uint64_t arrivalC = 2; - auto uid = book.limit(side, sizeA, price, arrivalA); - book.limit(side, sizeB, price, arrivalB); - book.limit(side, sizeC, price, arrivalC); + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); WHEN("the first order is canceled") { - book.cancel(uid); + book.cancel(1); THEN("order ID is returned and the order is recorded") { REQUIRE(sizeB + sizeC == book.volume(price)); REQUIRE(price == book.best_buy()); @@ -1185,11 +1163,11 @@ SCENARIO("cancel middle order in a Limit queue of orders") { uint64_t arrivalA = 0; uint64_t arrivalB = 1; uint64_t arrivalC = 2; - book.limit(side, sizeA, price, arrivalA); - auto uid = book.limit(side, sizeB, price, arrivalB); - book.limit(side, sizeC, price, arrivalC); + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); WHEN("the middle order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("order ID is returned and the order is recorded") { REQUIRE(sizeA + sizeC == book.volume(price)); REQUIRE(0 == book.best_buy()); @@ -1207,11 +1185,11 @@ SCENARIO("cancel middle order in a Limit queue of orders") { uint64_t arrivalA = 0; uint64_t arrivalB = 1; uint64_t arrivalC = 2; - book.limit(side, sizeA, price, arrivalA); - auto uid = book.limit(side, sizeB, price, arrivalB); - book.limit(side, sizeC, price, arrivalC); + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); WHEN("the middle order is canceled") { - book.cancel(uid); + book.cancel(2); THEN("order ID is returned and the order is recorded") { REQUIRE(sizeA + sizeC == book.volume(price)); REQUIRE(price == book.best_buy()); @@ -1232,11 +1210,11 @@ SCENARIO("cancel last order in a Limit queue of orders") { uint64_t arrivalA = 0; uint64_t arrivalB = 1; uint64_t arrivalC = 2; - book.limit(side, sizeA, price, arrivalA); - book.limit(side, sizeB, price, arrivalB); - auto uid = book.limit(side, sizeC, price, arrivalC); + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); WHEN("the last order is canceled") { - book.cancel(uid); + book.cancel(3); THEN("order ID is returned and the order is recorded") { REQUIRE(sizeA + sizeB == book.volume(price)); REQUIRE(0 == book.best_buy()); @@ -1254,11 +1232,11 @@ SCENARIO("cancel last order in a Limit queue of orders") { uint64_t arrivalA = 0; uint64_t arrivalB = 1; uint64_t arrivalC = 2; - book.limit(side, sizeA, price, arrivalA); - book.limit(side, sizeB, price, arrivalB); - auto uid = book.limit(side, sizeC, price, arrivalC); + book.limit(side, 1, sizeA, price, arrivalA); + book.limit(side, 2, sizeB, price, arrivalB); + book.limit(side, 3, sizeC, price, arrivalC); WHEN("the last order is canceled") { - book.cancel(uid); + book.cancel(3); THEN("order ID is returned and the order is recorded") { REQUIRE(sizeA + sizeB == book.volume(price)); REQUIRE(price == book.best_buy()); @@ -1278,7 +1256,7 @@ SCENARIO("a market order is submitted with no order in the book") { auto size = 100; auto arrival = 1; WHEN("a buy market order is submitted") { - book.market(Side::Sell, size, arrival); + book.market(Side::Sell, 1, size, arrival); } } } @@ -1292,8 +1270,8 @@ SCENARIO("a market order is submitted with a perfect match") { auto book = LimitOrderBook(); WHEN("a sell market order is matched to a buy limit order") { - book.limit(Side::Buy, size, price, arrival1); - book.market(Side::Sell, size, arrival2); + book.limit(Side::Buy, 1, size, price, arrival1); + book.market(Side::Sell, 2, size, arrival2); THEN("the limit_fill and market_fill functions should fire") { REQUIRE(0 == book.best_buy()); REQUIRE(0 == book.volume(price)); @@ -1310,10 +1288,10 @@ SCENARIO("a market order is submitted that is partially filled by a limit") { auto arrival1 = 1; auto arrival2 = 2; auto book = LimitOrderBook(); - book.limit(Side::Buy, size_limit, price, arrival1); + book.limit(Side::Buy, 1, size_limit, price, arrival1); WHEN("a buy market order is submitted") { - book.market(Side::Sell, size_market, arrival2); + book.market(Side::Sell, 2, size_market, arrival2); THEN("the limit_fill and market_fill functions should fire") { REQUIRE(price == book.best_buy()); REQUIRE(size_limit - size_market == book.volume(price)); @@ -1332,11 +1310,11 @@ SCENARIO("a market order is submitted that spans several limits") { auto arrival2 = 2; auto arrival3 = 3; auto book = LimitOrderBook(); - book.limit(Side::Buy, size_limit1, price, arrival1); - book.limit(Side::Buy, size_limit2, price, arrival2); + book.limit(Side::Buy, 1, size_limit1, price, arrival1); + book.limit(Side::Buy, 2, size_limit2, price, arrival2); WHEN("a buy market order is submitted") { - book.market(Side::Sell, size_market, arrival3); + book.market(Side::Sell, 3, size_market, arrival3); THEN("the limit_fill and market_fill functions should fire") { REQUIRE(100 == book.best_buy()); REQUIRE(size_limit1 + size_limit2 - size_market == book.volume(price)); @@ -1355,11 +1333,11 @@ SCENARIO("a market order is submitted that spans several limits and depletes boo auto arrival2 = 2; auto arrival3 = 3; auto book = LimitOrderBook(); - book.limit(Side::Buy, size_limit1, price, arrival1); - book.limit(Side::Buy, size_limit2, price, arrival2); + book.limit(Side::Buy, 1, size_limit1, price, arrival1); + book.limit(Side::Buy, 2, size_limit2, price, arrival2); WHEN("a buy market order is submitted") { - book.market(Side::Sell, size_market, arrival3); + book.market(Side::Sell, 3, size_market, arrival3); THEN("the limit_fill and market_fill functions should fire") { REQUIRE(0 == book.best_buy()); REQUIRE(0 == book.volume(price)); From d48cf197b534c321f04a65a34bf460e78ad11263 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 03:20:30 -0600 Subject: [PATCH 08/18] update --- benchmark/benchmark_lob.cpp | 16 ++++++++-------- main/dummy_matching.cpp | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/benchmark/benchmark_lob.cpp b/benchmark/benchmark_lob.cpp index c3c8ea3..3a32723 100644 --- a/benchmark/benchmark_lob.cpp +++ b/benchmark/benchmark_lob.cpp @@ -21,7 +21,7 @@ using Catch::Benchmark::Chronometer; // inline void spam_limits(LimitOrderBook book, int count) { - for (int i = 0; i < count; i++) book.limit(Side::Buy, 50, i, i); + for (int i = 0; i < count; i++) book.limit(Side::Buy, i, 50, i, i); } TEST_CASE("Spam new Limits") { @@ -57,7 +57,7 @@ TEST_CASE("Spam new Limits") { inline void spam_orders(LimitOrderBook book, int count, int variance = 5) { for (int i = 0; i < count; i++) - book.limit(Side::Buy, 50, i % variance, i); + book.limit(Side::Buy, i, 50, i % variance, i); } TEST_CASE("Spam new Orders") { @@ -100,9 +100,9 @@ inline void spam_orders_random_cancels( ) { auto generator = std::default_random_engine(); auto price_distribution = std::normal_distribution(mean, variance); - book.limit(Side::Buy, 50, price_distribution(generator), 0); + book.limit(Side::Buy, 0, 50, price_distribution(generator), 0); for (int i = 1; i < count; i++) { - book.limit(Side::Buy, 50, price_distribution(generator), i); + book.limit(Side::Buy, i, 50, price_distribution(generator), i); if (i % cancel_every == 0) book.cancel(i - cancel_every); } @@ -154,9 +154,9 @@ inline void spam_limit_random_orders( for (int i = 1; i < count; i++) { auto price_ = static_cast(price(generator)); auto size_ = static_cast(size(generator)); - book.limit(Side::Buy, 100, price_, i); + book.limit(Side::Buy, i, 100, price_, i); if (i % order_every == 0) // random submit a market order - book.market(Side::Sell, size_, i); + book.market(Side::Sell, i, size_, i); } } @@ -189,8 +189,8 @@ inline void spam_limit_many_market_orders( for (int i = 1; i < count; i++) { auto price_ = static_cast(price(generator)); auto size_ = static_cast(size(generator)); - book.limit(Side::Buy, 100, price_, i); - book.market(Side::Sell, size_, i); + book.limit(Side::Buy, i, 100, price_, i); + book.market(Side::Sell, i, size_, i); } } diff --git a/main/dummy_matching.cpp b/main/dummy_matching.cpp index 889f81a..59b08b2 100644 --- a/main/dummy_matching.cpp +++ b/main/dummy_matching.cpp @@ -18,9 +18,9 @@ int main() { for (int i = 1; i < 10000000; i++) { auto price_ = static_cast(price(generator)); auto size_ = static_cast(size(generator)); - book.limit(Side::Buy, 100, price_, i); + book.limit(Side::Buy, 0, 100, price_, i); if (i % 11 == 0) { // randomly submit a market order - book.market(Side::Sell, size_, i); + book.market(Side::Sell, 1, size_, i); } } } From 9825845f91145b31db1633f79b722750769e9810 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 03:25:05 -0600 Subject: [PATCH 09/18] update API and stub --- src/lib_lob.cpp | 54 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/src/lib_lob.cpp b/src/lib_lob.cpp index 428f2bc..df0e81b 100644 --- a/src/lib_lob.cpp +++ b/src/lib_lob.cpp @@ -34,32 +34,50 @@ extern "C" { delete book; } - // limit_sell - /// + /// Add a new sell limit order to the book. + EXP void limit_sell(LimitOrderBook* book) { - // limit_buy - /// + } + + /// Add a new buy limit order to the book. + EXP void limit_buy(LimitOrderBook* book) { + + } - // limit - /// + /// Add a new limit order to the book. + EXP void limit(LimitOrderBook* book) { - // has - /// + } - // get - /// + /// Return true if the book has an order with given ID, false otherwise. + EXP bool has(LimitOrderBook* book, UID order_id) { + return book->has(order_id); + } - // cancel - /// + /// Get the order with given ID. + EXP const Order* get(LimitOrderBook* book, UID order_id) { + return &book->get(order_id); + } - // market_sell - /// + /// Cancel an existing order in the book. + EXP void cancel(LimitOrderBook* book, UID order_id) { + book->cancel(order_id); + } - // market_buy - /// + /// Execute a sell market order. + EXP void market_sell(LimitOrderBook* book) { - // market - /// + } + + /// Execute a buy market order. + EXP void market_buy(LimitOrderBook* book) { + + } + + /// Execute a market order. + EXP void market(LimitOrderBook* book) { + + } /// Return the best sell price. EXP Price best_sell(LimitOrderBook* book) { From a4b333d0846c8b19d27c0ea3cf8f77a0912151ee Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 03:27:45 -0600 Subject: [PATCH 10/18] update API --- src/lib_lob.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib_lob.cpp b/src/lib_lob.cpp index df0e81b..3959b23 100644 --- a/src/lib_lob.cpp +++ b/src/lib_lob.cpp @@ -35,18 +35,18 @@ extern "C" { } /// Add a new sell limit order to the book. - EXP void limit_sell(LimitOrderBook* book) { - + EXP void limit_sell(LimitOrderBook* book, UID order_id, Size size, Price price) { + book->limit_sell(order_id, size, price, 0); } /// Add a new buy limit order to the book. - EXP void limit_buy(LimitOrderBook* book) { - + EXP void limit_buy(LimitOrderBook* book, UID order_id, Size size, Price price) { + book->limit_buy(order_id, size, price, 0); } /// Add a new limit order to the book. - EXP void limit(LimitOrderBook* book) { - + EXP void limit(LimitOrderBook* book, Side side, UID order_id, Size size, Price price) { + book->limit(side, order_id, size, price, 0); } /// Return true if the book has an order with given ID, false otherwise. @@ -65,18 +65,18 @@ extern "C" { } /// Execute a sell market order. - EXP void market_sell(LimitOrderBook* book) { - + EXP void market_sell(LimitOrderBook* book, UID order_id, Size size) { + book->market_sell(order_id, size, 0); } /// Execute a buy market order. - EXP void market_buy(LimitOrderBook* book) { - + EXP void market_buy(LimitOrderBook* book, UID order_id, Size size) { + book->market_buy(order_id, size, 0); } /// Execute a market order. - EXP void market(LimitOrderBook* book) { - + EXP void market(LimitOrderBook* book, Side side, UID order_id, Size size) { + book->market(side, order_id, size, 0); } /// Return the best sell price. From 16b71873b207e07494b85f58ad071fccb238b9e8 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 03:58:50 -0600 Subject: [PATCH 11/18] comment --- lob/lob.py | 214 +++++++++++++++++++++++++++++++++++-------- lob/test/test_lob.py | 127 +++++++++++++++++++++++++ 2 files changed, 304 insertions(+), 37 deletions(-) diff --git a/lob/lob.py b/lob/lob.py index f0ef3b8..2c658dc 100644 --- a/lob/lob.py +++ b/lob/lob.py @@ -10,60 +10,107 @@ except IndexError: raise OSError('missing static lib_lob*.so library!') -Size = ctypes.c_uint32 -Price = ctypes.c_uint64 -Volume = ctypes.c_uint32 + +class Types: + """Types used in the C++ code.""" + # a type for pointers + Pointer = ctypes.c_void_p + # a type for the side of the order (i.e., sell or buy) + Side = ctypes.c_bool + # a type for the unique order ID + UID = ctypes.c_uint64 + # a type for the size of an order + Size = ctypes.c_uint32 + # a type for the price of an order + Price = ctypes.c_uint64 + # a type for the volume measures in the book + Volume = ctypes.c_uint32 + # setup the argument and return types for Initialize LIB.new_.argtypes = None -LIB.new_.restype = ctypes.c_void_p +LIB.new_.restype = Types.Pointer # setup the argument and return types for Delete -LIB.delete_.argtypes = [ctypes.c_void_p] +LIB.delete_.argtypes = [Types.Pointer] LIB.delete_.restype = None +# setup the argument and return types for limit_sell +LIB.limit_sell.argtypes = [Types.Pointer, Types.UID, Types.Size, Types.Price] +LIB.limit_sell.restype = None +# setup the argument and return types for limit_buy +LIB.limit_buy.argtypes = [Types.Pointer, Types.UID, Types.Size, Types.Price] +LIB.limit_buy.restype = None +# setup the argument and return types for limit +LIB.limit.argtypes = [Types.Pointer, Types.Side, Types.UID, Types.Size, Types.Price] +LIB.limit.restype = None + + +# setup the argument and return types for has +LIB.has.argtypes = [Types.Pointer, Types.UID] +LIB.has.restype = ctypes.c_bool +# setup the argument and return types for get +LIB.get.argtypes = [Types.Pointer, Types.UID] +LIB.get.restype = Types.Pointer +# setup the argument and return types for cancel +LIB.cancel.argtypes = [Types.Pointer, Types.UID] +LIB.cancel.restype = None + + +# setup the argument and return types for market_sell +LIB.market_sell.argtypes = [Types.Pointer, Types.UID, Types.Size] +LIB.market_sell.restype = None +# setup the argument and return types for market_buy +LIB.market_buy.argtypes = [Types.Pointer, Types.UID, Types.Size] +LIB.market_buy.restype = None +# setup the argument and return types for market +LIB.market.argtypes = [Types.Pointer, Types.Side, Types.UID, Types.Size] +LIB.market.restype = None + # setup the argument and return types for best_sell -LIB.best_sell.argtypes = [ctypes.c_void_p] -LIB.best_sell.restype = Price +LIB.best_sell.argtypes = [Types.Pointer] +LIB.best_sell.restype = Types.Price # setup the argument and return types for best_buy -LIB.best_buy.argtypes = [ctypes.c_void_p] -LIB.best_buy.restype = Price +LIB.best_buy.argtypes = [Types.Pointer] +LIB.best_buy.restype = Types.Price # setup the argument and return types for best -LIB.best.argtypes = [ctypes.c_void_p, ctypes.c_bool] -LIB.best.restype = Price +LIB.best.argtypes = [Types.Pointer, Types.Side] +LIB.best.restype = Types.Price + # setup the argument and return types for volume_sell_price -LIB.volume_sell_price.argtypes = [ctypes.c_void_p, Price] -LIB.volume_sell_price.restype = Volume +LIB.volume_sell_price.argtypes = [Types.Pointer, Types.Price] +LIB.volume_sell_price.restype = Types.Volume # setup the argument and return types for volume_sell -LIB.volume_sell.argtypes = [ctypes.c_void_p] -LIB.volume_sell.restype = Volume +LIB.volume_sell.argtypes = [Types.Pointer] +LIB.volume_sell.restype = Types.Volume # setup the argument and return types for volume_buy_price -LIB.volume_buy_price.argtypes = [ctypes.c_void_p, Price] -LIB.volume_buy_price.restype = Volume +LIB.volume_buy_price.argtypes = [Types.Pointer, Types.Price] +LIB.volume_buy_price.restype = Types.Volume # setup the argument and return types for volume_buy -LIB.volume_buy.argtypes = [ctypes.c_void_p] -LIB.volume_buy.restype = Volume +LIB.volume_buy.argtypes = [Types.Pointer] +LIB.volume_buy.restype = Types.Volume # setup the argument and return types for volume_price -LIB.volume_price.argtypes = [ctypes.c_void_p, Price] -LIB.volume_price.restype = Volume +LIB.volume_price.argtypes = [Types.Pointer, Types.Price] +LIB.volume_price.restype = Types.Volume # setup the argument and return types for volume -LIB.volume.argtypes = [ctypes.c_void_p] -LIB.volume.restype = Volume +LIB.volume.argtypes = [Types.Pointer] +LIB.volume.restype = Types.Volume + # setup the argument and return types for size_at -LIB.size_at.argtypes = [ctypes.c_void_p, Price] -LIB.size_at.restype = Size +LIB.size_at.argtypes = [Types.Pointer, Types.Price] +LIB.size_at.restype = Types.Size # setup the argument and return types for size_sell -LIB.size_sell.argtypes = [ctypes.c_void_p] -LIB.size_sell.restype = Size +LIB.size_sell.argtypes = [Types.Pointer] +LIB.size_sell.restype = Types.Size # setup the argument and return types for size_buy -LIB.size_buy.argtypes = [ctypes.c_void_p] -LIB.size_buy.restype = Size +LIB.size_buy.argtypes = [Types.Pointer] +LIB.size_buy.restype = Types.Size # setup the argument and return types for size -LIB.size.argtypes = [ctypes.c_void_p] -LIB.size.restype = Size +LIB.size.argtypes = [Types.Pointer] +LIB.size.restype = Types.Size class LimitOrderBook: @@ -77,41 +124,134 @@ def __del__(self): """Delete this limit order book.""" LIB.delete_(self._book) - def limit_sell(self, size, price, arrival): + def limit_sell(self, order_id, size, price): """ + Place a sell limit order with given size and price. + + Args: + order_id: the ID for the order to add + size: the size of the limit order to place + price: the price of the limit order to place + + Returns: + None + """ + LIB.limit_sell(self._book, order_id, size, price) - def limit_buy(self, size, price, arrival): + def limit_buy(self, order_id, size, price): """ + Place a sell limit order with given size and price. + + Args: + order_id: the ID for the order to add + size: the size of the limit order to place + price: the price of the limit order to place + + Returns: + None + """ + LIB.limit_buy(self._book, order_id, size, price) - def limit(self, side, size, price, arrival): + def limit(self, side, order_id, size, price): """ + Place a sell limit order with given size and price. + + Args: + side: the side of the order to place + order_id: the ID for the order to add + size: the size of the limit order to place + price: the price of the limit order to place + + Returns: + None + """ + LIB.limit_buy(self._book, side, order_id, size, price) def has(self, order_id): """ + Return true if the order with given ID is in the book, false otherwise. + + Args: + order_id: the ID of the order to check for existence of + + Returns: + True if the order is in the book, False otherwise + """ + return LIB.has(self._book, order_id) def get(self, order_id): """ + Return a pointer to the order with given ID. + + Args: + order_id: the ID of the order to get + + Returns: + an order object wrapped around the pointer + """ + # pointer = LIB.get(self._book, order_id) + raise NotImplementedError def cancel(self, order_id): """ + Cancel an order with given order ID. + + Args: + order_id: the ID of the order to cancel + + Returns: + None + """ + LIB.cancel(self._book, order_id) - def market_sell(self, size, arrival): + def market_sell(self, order_id, size): """ + Place a market sell order. + + Args: + order_id: the ID for the order to add + size: the size of the market order to place + + Returns: + None + """ + LIB.market_sell(self._book, order_id, size) - def market_buy(self, size, arrival): + def market_buy(self, order_id, size): """ + Place a market buy order. + + Args: + order_id: the ID for the order to add + size: the size of the market order to place + + Returns: + None + """ + LIB.market_buy(self._book, order_id, size) - def market(self, side, size, arrival): + def market(self, side, order_id, size): """ + Place a market order. + + Args: + side: the side of the order to place + order_id: the ID for the order to add + size: the size of the market order to place + + Returns: + None + """ + LIB.market(self._book, side, order_id, size) def best_sell(self): """Return the best sell price in the book.""" diff --git a/lob/test/test_lob.py b/lob/test/test_lob.py index 893a2b8..5e8b76f 100644 --- a/lob/test/test_lob.py +++ b/lob/test/test_lob.py @@ -26,38 +26,165 @@ def test(self): class ShouldPlaceSellLimitOrder(TestCase): def test(self): book = lob.LimitOrderBook() + book.limit_sell(1, 100, 50) + self.assertEqual(50, book.best_sell()) + self.assertEqual(0, book.best_buy()) + self.assertEqual(50, book.best(False)) + self.assertEqual(0, book.best(True)) + self.assertEqual(100, book.volume_sell()) + self.assertEqual(100, book.volume_sell(50)) + self.assertEqual(0, book.volume_buy()) + self.assertEqual(100, book.volume()) + self.assertEqual(100, book.volume(50)) + self.assertEqual(1, book.size_at(50)) + self.assertEqual(1, book.size_sell()) + self.assertEqual(0, book.size_buy()) + self.assertEqual(1, book.size()) class ShouldCancelSellLimitOrder(TestCase): def test(self): book = lob.LimitOrderBook() + book.limit_sell(1, 100, 50) + self.assertTrue(book.has(1)) + book.cancel(1) + self.assertFalse(book.has(1)) + self.assertEqual(0, book.best_sell()) + self.assertEqual(0, book.best_buy()) + self.assertEqual(0, book.best(False)) + self.assertEqual(0, book.best(True)) + self.assertEqual(0, book.volume_sell()) + self.assertEqual(0, book.volume_sell(100)) + self.assertEqual(0, book.volume_buy()) + self.assertEqual(0, book.volume_buy(100)) + self.assertEqual(0, book.volume()) + self.assertEqual(0, book.volume(100)) + self.assertEqual(0, book.size_at(100)) + self.assertEqual(0, book.size_sell()) + self.assertEqual(0, book.size_buy()) + self.assertEqual(0, book.size()) class ShouldPlaceBuyLimitOrder(TestCase): def test(self): book = lob.LimitOrderBook() + book.limit_buy(1, 100, 50) + self.assertEqual(0, book.best_sell()) + self.assertEqual(50, book.best_buy()) + self.assertEqual(0, book.best(False)) + self.assertEqual(50, book.best(True)) + self.assertEqual(0, book.volume_sell()) + self.assertEqual(0, book.volume_sell(50)) + self.assertEqual(100, book.volume_buy()) + self.assertEqual(100, book.volume_buy(50)) + self.assertEqual(100, book.volume()) + self.assertEqual(100, book.volume(50)) + self.assertEqual(1, book.size_at(50)) + self.assertEqual(0, book.size_sell()) + self.assertEqual(1, book.size_buy()) + self.assertEqual(1, book.size()) class ShouldCancelBuyLimitOrder(TestCase): def test(self): book = lob.LimitOrderBook() + book.limit_buy(1, 100, 50) + self.assertTrue(book.has(1)) + book.cancel(1) + self.assertFalse(book.has(1)) + self.assertEqual(0, book.best_sell()) + self.assertEqual(0, book.best_buy()) + self.assertEqual(0, book.best(False)) + self.assertEqual(0, book.best(True)) + self.assertEqual(0, book.volume_sell()) + self.assertEqual(0, book.volume_sell(100)) + self.assertEqual(0, book.volume_buy()) + self.assertEqual(0, book.volume_buy(100)) + self.assertEqual(0, book.volume()) + self.assertEqual(0, book.volume(100)) + self.assertEqual(0, book.size_at(100)) + self.assertEqual(0, book.size_sell()) + self.assertEqual(0, book.size_buy()) + self.assertEqual(0, book.size()) class ShouldPlaceSellMarketOrderEmptyBook(TestCase): def test(self): book = lob.LimitOrderBook() + book.market_sell(1, 100) + self.assertEqual(0, book.best_sell()) + self.assertEqual(0, book.best_buy()) + self.assertEqual(0, book.best(False)) + self.assertEqual(0, book.best(True)) + self.assertEqual(0, book.volume_sell()) + self.assertEqual(0, book.volume_sell(100)) + self.assertEqual(0, book.volume_buy()) + self.assertEqual(0, book.volume_buy(100)) + self.assertEqual(0, book.volume()) + self.assertEqual(0, book.volume(100)) + self.assertEqual(0, book.size_at(100)) + self.assertEqual(0, book.size_sell()) + self.assertEqual(0, book.size_buy()) + self.assertEqual(0, book.size()) class ShouldPlaceBuyMarketOrderEmptyBook(TestCase): def test(self): book = lob.LimitOrderBook() + book.market_buy(1, 100) + self.assertEqual(0, book.best_sell()) + self.assertEqual(0, book.best_buy()) + self.assertEqual(0, book.best(False)) + self.assertEqual(0, book.best(True)) + self.assertEqual(0, book.volume_sell()) + self.assertEqual(0, book.volume_sell(100)) + self.assertEqual(0, book.volume_buy()) + self.assertEqual(0, book.volume_buy(100)) + self.assertEqual(0, book.volume()) + self.assertEqual(0, book.volume(100)) + self.assertEqual(0, book.size_at(100)) + self.assertEqual(0, book.size_sell()) + self.assertEqual(0, book.size_buy()) + self.assertEqual(0, book.size()) class ShouldPlaceSellMarketOrderAndMatch(TestCase): def test(self): book = lob.LimitOrderBook() + book.limit_buy(1, 100, 50) + book.market_sell(1, 10) + self.assertEqual(0, book.best_sell()) + self.assertEqual(50, book.best_buy()) + self.assertEqual(0, book.best(False)) + self.assertEqual(50, book.best(True)) + self.assertEqual(0, book.volume_sell()) + self.assertEqual(0, book.volume_sell(100)) + self.assertEqual(90, book.volume_buy()) + self.assertEqual(90, book.volume_buy(50)) + self.assertEqual(90, book.volume()) + self.assertEqual(90, book.volume(50)) + self.assertEqual(1, book.size_at(50)) + self.assertEqual(0, book.size_sell()) + self.assertEqual(1, book.size_buy()) + self.assertEqual(1, book.size()) class ShouldPlaceBuyMarketOrderAndMatch(TestCase): def test(self): book = lob.LimitOrderBook() + book.limit_sell(1, 100, 50) + book.market_buy(1, 10) + self.assertEqual(50, book.best_sell()) + self.assertEqual(0, book.best_buy()) + self.assertEqual(50, book.best(False)) + self.assertEqual(0, book.best(True)) + self.assertEqual(90, book.volume_sell()) + self.assertEqual(90, book.volume_sell(50)) + self.assertEqual(0, book.volume_buy()) + self.assertEqual(0, book.volume_buy(50)) + self.assertEqual(90, book.volume()) + self.assertEqual(90, book.volume(50)) + self.assertEqual(1, book.size_at(50)) + self.assertEqual(1, book.size_sell()) + self.assertEqual(0, book.size_buy()) + self.assertEqual(1, book.size()) From 04bde889d045076d9d098223742d116af7ba5f74 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 04:03:40 -0600 Subject: [PATCH 12/18] update --- README.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/README.md b/README.md index b2a8536..adad405 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,23 @@ # Limit Order Book (LOB) [![build-status][]][build-server] +[![PackageVersion][pypi-version]][pypi-home] +[![PythonVersion][python-version]][python-home] +[![Stable][pypi-status]][pypi-home] +[![Format][pypi-format]][pypi-home] +[![License][pypi-license]](LICENSE) [build-status]: https://travis-ci.com/Kautenja/lob.svg [build-server]: https://travis-ci.com/Kautenja/lob +[pypi-version]: https://badge.fury.io/py/lob.svg +[pypi-license]: https://img.shields.io/pypi/l/lob.svg +[pypi-status]: https://img.shields.io/pypi/status/lob.svg +[pypi-format]: https://img.shields.io/pypi/format/lob.svg +[pypi-home]: https://badge.fury.io/py/lob +[python-version]: https://img.shields.io/pypi/pyversions/lob.svg +[python-home]: https://python.org + +This is an implementation of the limit order book structure and matching +algorithm for C++ (and Python through ctypes) for market data streaming. ![Limit order book](img/limit-order-book.svg) From e366faad5e40a9894acd2d00383617c36b0060d1 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 04:08:27 -0600 Subject: [PATCH 13/18] update --- README.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/README.md b/README.md index adad405..ffde7bc 100644 --- a/README.md +++ b/README.md @@ -21,3 +21,52 @@ This is an implementation of the limit order book structure and matching algorithm for C++ (and Python through ctypes) for market data streaming. ![Limit order book](img/limit-order-book.svg) + +## Usage + +### C++ + +Simply add [include/*.hpp](include) to your C++ project either by copying +directly or using git submodules. + +### Python + +The preferred Python installation of `lob` is from `pip`: + +```shell +pip install lob +``` + +### Windows + +You'll need to install the Visual-Studio 17.0 tools for Windows installation. +The [Visual Studio Community](https://visualstudio.microsoft.com/downloads/) +package provides these tools for free. + +## Testing + +### C++ + +To run the C++ unit-test suite, run: + +```shell +scons test +``` + +### Python + +To run the Python unit-test suite, run: + +```shell +python -m unittest discover . +``` + +## Benchmarking + +### C++ + +To run the C++ benchmark code, run: + +```shell +scons benchmark +``` From 241c90043b2e8a0f8d271108d0049dbe6d7c7a1b Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 04:13:27 -0600 Subject: [PATCH 14/18] rename --- README.md | 20 ++++++++--------- SConstruct | 2 +- {lob => limit_order_book}/__init__.py | 2 +- .../limit_order_book.py | 0 {lob => limit_order_book}/test/__init__.py | 0 .../test/test_limit_order_book.py | 22 +++++++++---------- setup.py | 4 ++-- 7 files changed, 25 insertions(+), 25 deletions(-) rename {lob => limit_order_book}/__init__.py (74%) rename lob/lob.py => limit_order_book/limit_order_book.py (100%) rename {lob => limit_order_book}/test/__init__.py (100%) rename lob/test/test_lob.py => limit_order_book/test/test_limit_order_book.py (92%) diff --git a/README.md b/README.md index ffde7bc..ba00726 100644 --- a/README.md +++ b/README.md @@ -7,14 +7,14 @@ [![Format][pypi-format]][pypi-home] [![License][pypi-license]](LICENSE) -[build-status]: https://travis-ci.com/Kautenja/lob.svg -[build-server]: https://travis-ci.com/Kautenja/lob -[pypi-version]: https://badge.fury.io/py/lob.svg -[pypi-license]: https://img.shields.io/pypi/l/lob.svg -[pypi-status]: https://img.shields.io/pypi/status/lob.svg -[pypi-format]: https://img.shields.io/pypi/format/lob.svg -[pypi-home]: https://badge.fury.io/py/lob -[python-version]: https://img.shields.io/pypi/pyversions/lob.svg +[build-status]: https://travis-ci.com/Kautenja/limit-order-book.svg +[build-server]: https://travis-ci.com/Kautenja/limit-order-book +[pypi-version]: https://badge.fury.io/py/limit-order-book.svg +[pypi-license]: https://img.shields.io/pypi/l/limit-order-book.svg +[pypi-status]: https://img.shields.io/pypi/status/limit-order-book.svg +[pypi-format]: https://img.shields.io/pypi/format/limit-order-book.svg +[pypi-home]: https://badge.fury.io/py/limit-order-book +[python-version]: https://img.shields.io/pypi/pyversions/limit-order-book.svg [python-home]: https://python.org This is an implementation of the limit order book structure and matching @@ -31,10 +31,10 @@ directly or using git submodules. ### Python -The preferred Python installation of `lob` is from `pip`: +The preferred Python installation of `limit-order-book` is from `pip`: ```shell -pip install lob +pip install limit-order-book ``` ### Windows diff --git a/SConstruct b/SConstruct index 67e5035..6c8e365 100644 --- a/SConstruct +++ b/SConstruct @@ -173,4 +173,4 @@ for main in find_source_files('main', 'build_main'): lib = PRODUCTION_ENV.SharedLibrary('_lob.so', SRC) AlwaysBuild(lib) # copy the so file to the lob package -Command(target="lob", source=lib, action=Copy("$TARGET", "$SOURCE")) +Command(target="limit_order_book", source=lib, action=Copy("$TARGET", "$SOURCE")) diff --git a/lob/__init__.py b/limit_order_book/__init__.py similarity index 74% rename from lob/__init__.py rename to limit_order_book/__init__.py index 3f76fd8..46da93a 100644 --- a/lob/__init__.py +++ b/limit_order_book/__init__.py @@ -1,5 +1,5 @@ """A Limit Order Book (LOB).""" -from .lob import LimitOrderBook +from .limit_order_book import LimitOrderBook # explicitly define the outward facing API of this package diff --git a/lob/lob.py b/limit_order_book/limit_order_book.py similarity index 100% rename from lob/lob.py rename to limit_order_book/limit_order_book.py diff --git a/lob/test/__init__.py b/limit_order_book/test/__init__.py similarity index 100% rename from lob/test/__init__.py rename to limit_order_book/test/__init__.py diff --git a/lob/test/test_lob.py b/limit_order_book/test/test_limit_order_book.py similarity index 92% rename from lob/test/test_lob.py rename to limit_order_book/test/test_limit_order_book.py index 5e8b76f..42c1f59 100644 --- a/lob/test/test_lob.py +++ b/limit_order_book/test/test_limit_order_book.py @@ -1,12 +1,12 @@ """Test cases for the lob module.""" from unittest import TestCase -from .. import lob +from .. import limit_order_book class ShouldInitializeLimitOrderBook(TestCase): def test(self): - book = lob.LimitOrderBook() - self.assertIsInstance(book, lob.LimitOrderBook) + book = limit_order_book.LimitOrderBook() + self.assertIsInstance(book, limit_order_book.LimitOrderBook) self.assertEqual(0, book.best_sell()) self.assertEqual(0, book.best_buy()) self.assertEqual(0, book.best(False)) @@ -25,7 +25,7 @@ def test(self): class ShouldPlaceSellLimitOrder(TestCase): def test(self): - book = lob.LimitOrderBook() + book = limit_order_book.LimitOrderBook() book.limit_sell(1, 100, 50) self.assertEqual(50, book.best_sell()) self.assertEqual(0, book.best_buy()) @@ -44,7 +44,7 @@ def test(self): class ShouldCancelSellLimitOrder(TestCase): def test(self): - book = lob.LimitOrderBook() + book = limit_order_book.LimitOrderBook() book.limit_sell(1, 100, 50) self.assertTrue(book.has(1)) book.cancel(1) @@ -67,7 +67,7 @@ def test(self): class ShouldPlaceBuyLimitOrder(TestCase): def test(self): - book = lob.LimitOrderBook() + book = limit_order_book.LimitOrderBook() book.limit_buy(1, 100, 50) self.assertEqual(0, book.best_sell()) self.assertEqual(50, book.best_buy()) @@ -87,7 +87,7 @@ def test(self): class ShouldCancelBuyLimitOrder(TestCase): def test(self): - book = lob.LimitOrderBook() + book = limit_order_book.LimitOrderBook() book.limit_buy(1, 100, 50) self.assertTrue(book.has(1)) book.cancel(1) @@ -110,7 +110,7 @@ def test(self): class ShouldPlaceSellMarketOrderEmptyBook(TestCase): def test(self): - book = lob.LimitOrderBook() + book = limit_order_book.LimitOrderBook() book.market_sell(1, 100) self.assertEqual(0, book.best_sell()) self.assertEqual(0, book.best_buy()) @@ -130,7 +130,7 @@ def test(self): class ShouldPlaceBuyMarketOrderEmptyBook(TestCase): def test(self): - book = lob.LimitOrderBook() + book = limit_order_book.LimitOrderBook() book.market_buy(1, 100) self.assertEqual(0, book.best_sell()) self.assertEqual(0, book.best_buy()) @@ -150,7 +150,7 @@ def test(self): class ShouldPlaceSellMarketOrderAndMatch(TestCase): def test(self): - book = lob.LimitOrderBook() + book = limit_order_book.LimitOrderBook() book.limit_buy(1, 100, 50) book.market_sell(1, 10) self.assertEqual(0, book.best_sell()) @@ -171,7 +171,7 @@ def test(self): class ShouldPlaceBuyMarketOrderAndMatch(TestCase): def test(self): - book = lob.LimitOrderBook() + book = limit_order_book.LimitOrderBook() book.limit_sell(1, 100, 50) book.market_buy(1, 10) self.assertEqual(50, book.best_sell()) diff --git a/setup.py b/setup.py index 480765c..5bd8fed 100644 --- a/setup.py +++ b/setup.py @@ -44,7 +44,7 @@ setup( - name='lob', + name='limit_order_book', version='1.0.0', description='A Limit Order Book (LOB) implementation', long_description=README, @@ -66,7 +66,7 @@ 'Topic :: Office/Business :: Financial', 'Topic :: Office/Business :: Financial :: Investment' ], - url='https://github.com/Kautenja/lob', + url='https://github.com/Kautenja/limit-order-book', author='Christian Kauten', author_email='kautencreations@gmail.com', license='MIT', From 889ffab6c2bdd26b085239793c965c523d4be018 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 04:16:14 -0600 Subject: [PATCH 15/18] rename library --- SConstruct | 2 +- limit_order_book/limit_order_book.py | 4 ++-- setup.py | 2 +- src/lib_lob.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/SConstruct b/SConstruct index 6c8e365..536ec87 100644 --- a/SConstruct +++ b/SConstruct @@ -170,7 +170,7 @@ for main in find_source_files('main', 'build_main'): # Create a shared library (it will add "lib" to the front automatically) -lib = PRODUCTION_ENV.SharedLibrary('_lob.so', SRC) +lib = PRODUCTION_ENV.SharedLibrary('_limit_order_book.so', SRC) AlwaysBuild(lib) # copy the so file to the lob package Command(target="limit_order_book", source=lib, action=Copy("$TARGET", "$SOURCE")) diff --git a/limit_order_book/limit_order_book.py b/limit_order_book/limit_order_book.py index 2c658dc..208ec42 100644 --- a/limit_order_book/limit_order_book.py +++ b/limit_order_book/limit_order_book.py @@ -4,11 +4,11 @@ import glob -LIB_PATH = os.path.join(os.path.dirname(__file__), 'lib_lob*') +LIB_PATH = os.path.join(os.path.dirname(__file__), 'lib_limit_order_book*') try: # load the library from the shared object file LIB = ctypes.cdll.LoadLibrary(glob.glob(LIB_PATH)[0]) except IndexError: - raise OSError('missing static lib_lob*.so library!') + raise OSError('missing static lib_limit_order_book*.so library!') class Types: diff --git a/setup.py b/setup.py index 5bd8fed..f763006 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ # The prefix name for the .so library to build. It will follow the format # lib_lob.*.so where the * changes depending on the build system -LIB_NAME = 'lob.lib_lob' +LIB_NAME = 'limit_order_book.lib_limit_order_book' # The source files for building the extension. Globs locate all the cpp files # used by the LaiNES subproject. MANIFEST.in has to include the blanket # "cpp" directory to ensure that the .inc file gets included too diff --git a/src/lib_lob.cpp b/src/lib_lob.cpp index 3959b23..3363bc3 100644 --- a/src/lib_lob.cpp +++ b/src/lib_lob.cpp @@ -11,7 +11,7 @@ // Windows-base systems #if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || defined(__MINGW32__) || defined(__BORLANDC__) // setup the module initializer. required to link visual studio C++ ctypes - void PyInit_lib_lob() { } + void PyInit_lib_limit_order_book() { } // setup the function modifier to export in the DLL #define EXP __declspec(dllexport) // Unix-like systems From 4c356de2b69eec696500528897cc506b3ac8ed99 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 04:19:55 -0600 Subject: [PATCH 16/18] make makefile --- makefile | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 makefile diff --git a/makefile b/makefile new file mode 100644 index 0000000..17cdd8f --- /dev/null +++ b/makefile @@ -0,0 +1,49 @@ +# an alias to the python command +PYTHON=python3 + +# build the LaiNES code, test the Python interface, and build +# the deployment package +all: test deployment + +# +# MARK: Development +# + +# test the CPP code +test_lib_limit_order_book: + scons test + +# build the CPP code +lib_limit_order_book: test_lib_limit_order_book + scons + +# run the Python test suite +test: lib_limit_order_book + ${PYTHON} -m unittest discover . + +# +# MARK: Deployment +# + +clean_dist: + rm -rf build/ dist/ .eggs/ *.egg-info/ || true + +clean_python_build: + find . -name "*.pyc" -delete + find . -name "__pycache__" -delete + +clean_cpp_build: + find . -name ".sconsign.dblite" -delete + find . -name "build" | rm -rf + find . -name "lib_limit_order_book.so" -delete + +# clean the build directory +clean: clean_dist clean_python_build clean_cpp_build + +# build the deployment package +deployment: clean + ${PYTHON} setup.py sdist bdist_wheel + +# ship the deployment package to PyPi +ship: test deployment + twine upload dist/* From 97f0326a05c9088e5b5b0f22975ad2c90556b484 Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 04:20:01 -0600 Subject: [PATCH 17/18] update --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index ba00726..637e98c 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,12 @@ package provides these tools for free. ## Testing +To run all the unit-test suites, run: + +```shell +make test +``` + ### C++ To run the C++ unit-test suite, run: From 99016ff4c7e8b9643edfbcf43f12c7eb9b2efb7b Mon Sep 17 00:00:00 2001 From: Kautenja Date: Tue, 17 Dec 2019 04:24:29 -0600 Subject: [PATCH 18/18] disable binary dist --- makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/makefile b/makefile index 17cdd8f..ac94623 100644 --- a/makefile +++ b/makefile @@ -42,7 +42,7 @@ clean: clean_dist clean_python_build clean_cpp_build # build the deployment package deployment: clean - ${PYTHON} setup.py sdist bdist_wheel + ${PYTHON} setup.py sdist #bdist_wheel # ship the deployment package to PyPi ship: test deployment