Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
57b17d2
commit 2403936
Showing
7 changed files
with
182 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.vscode/* | ||
build/* | ||
Makefile |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
cmake_minimum_required(VERSION 3.4) | ||
project (matching_engine) | ||
add_subdirectory(test) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# A C++ Matching Engine | ||
|
||
An academic exercise in code profiling. For more details, see my website at https://www.jmjatlanta.com/index.php/2019/12/09/matching-engine-requirements/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/*** | ||
* An overly-simplified representation of an asset | ||
*/ | ||
class Asset | ||
{ | ||
public: | ||
Asset(int id) : id(id) {} | ||
int id; | ||
inline bool operator==(const Asset& in) const | ||
{ | ||
return in.id == id; | ||
} | ||
}; | ||
|
||
/*** | ||
* An order that will be possibly stored on the order book. | ||
* Therefore, it must be sortable by price and ID | ||
*/ | ||
template<class Quantity, class Price> | ||
class AssetOrder | ||
{ | ||
public: | ||
AssetOrder() : id(0), assetToBuy(Asset(0)), assetToSell(Asset(0)), quantity(0), price(0) {} | ||
AssetOrder(int id, const Asset& toBuy, const Asset& toSell, Quantity qty, Price price) | ||
: id(id), assetToBuy(toBuy), assetToSell(toSell), quantity(qty), price(price) {} | ||
AssetOrder& operator=(const AssetOrder& in) | ||
{ | ||
id = in.id; | ||
assetToBuy = in.assetToBuy; | ||
assetToSell = in.assetToSell; | ||
quantity = in.quantity; | ||
price = in.price; | ||
return *this; | ||
} | ||
int id; | ||
Asset assetToBuy; | ||
Asset assetToSell; | ||
Quantity quantity; | ||
Price price; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
#include <set> | ||
#include <asset.hpp> | ||
|
||
/*** | ||
* A representation of an order book | ||
*/ | ||
template<class Size, class Price> | ||
class Book | ||
{ | ||
public: | ||
Book(const Asset buying, const Asset selling) : _wantToBuy(buying), _wantToSell(selling) {} | ||
std::map<Price, AssetOrder<Size, Price>> bids; | ||
std::map<Price, AssetOrder<Size, Price>> asks; | ||
private: | ||
auto getBestOffer() | ||
{ | ||
return bids.rbegin(); | ||
} | ||
auto getBestAsk() | ||
{ | ||
return asks.begin(); | ||
} | ||
public: | ||
/*** | ||
* Add an order to the book | ||
* @param order the order | ||
* @returns true if there was at least one fill | ||
*/ | ||
bool AddOrder(const AssetOrder<Size, Price>& order) | ||
{ | ||
AssetOrder<Size, Price> currOrder = order; | ||
// From the book's point of view, is this order buying what we're selling? | ||
bool buying = (order.assetToBuy == _wantToSell); | ||
if (buying) | ||
{ | ||
auto bestAskItr = getBestAsk(); | ||
while (bestAskItr != asks.end() && (*bestAskItr).second.price <= currOrder.price && currOrder.quantity > 0) | ||
{ | ||
take(asks, (*bestAskItr).second, currOrder); | ||
bestAskItr = getBestAsk(); | ||
} | ||
placeOnBook(currOrder); | ||
} | ||
else | ||
{ | ||
auto bestOfferItr = getBestOffer(); | ||
while ( bestOfferItr != bids.rend() && (*bestOfferItr).second.price >= currOrder.price && currOrder.quantity > 0) | ||
{ | ||
take(bids, (*bestOfferItr).second, currOrder); | ||
bestOfferItr = getBestOffer(); | ||
} | ||
// now deal with the leftover | ||
placeOnBook(currOrder); | ||
} | ||
} | ||
private: | ||
Asset _wantToBuy; | ||
Asset _wantToSell; | ||
void take(std::map<Price, AssetOrder<Size, Price>>& collection, AssetOrder<Size, Price>& bookItem, AssetOrder<Size, Price>& incomingOrder) | ||
{ | ||
if (bookItem.quantity > incomingOrder.quantity) | ||
{ | ||
bookItem.quantity -= incomingOrder.quantity; | ||
incomingOrder.quantity = 0; | ||
} | ||
else | ||
{ | ||
incomingOrder.quantity -= bookItem.quantity; | ||
bookItem.quantity = 0; | ||
} | ||
if (bookItem.quantity == 0) | ||
{ | ||
collection.erase(bookItem.price); | ||
} | ||
} | ||
void placeOnBook(AssetOrder<Size, Price>& order) | ||
{ | ||
if (order.quantity > 0) | ||
{ | ||
bool buying = (order.assetToBuy == _wantToSell); | ||
if (buying) | ||
{ | ||
bids.insert({order.price, order}); | ||
} | ||
else | ||
{ | ||
asks.insert({order.price, order}); | ||
} | ||
} | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
add_executable(functional functional.cpp) | ||
target_include_directories(functional PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../include ) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
/*** | ||
* Functional tests for the order book | ||
*/ | ||
#define BOOST_TEST_MODULE orderbook | ||
#include <boost/test/included/unit_test.hpp> | ||
|
||
#include <book.hpp> | ||
|
||
BOOST_AUTO_TEST_CASE( simple_tests ) | ||
{ | ||
// two assets | ||
Asset asset1(1); | ||
Asset asset2(2); | ||
|
||
Book<int, double> book(asset1, asset2); | ||
// add something to the order book | ||
AssetOrder<int, double> order1(1, asset2, asset1, 10, 10); | ||
book.AddOrder(order1); | ||
BOOST_TEST( book.bids.size() == 1); | ||
BOOST_TEST( book.asks.size() == 0); | ||
// take half of the order | ||
AssetOrder<int, double> order2(2, asset1, asset2, 5, 10); | ||
book.AddOrder(order2); | ||
BOOST_TEST( book.bids.size() == 1); | ||
BOOST_TEST( book.asks.size() == 0); | ||
// take the rest of the order | ||
AssetOrder<int, double> order3(2, asset1, asset2, 5, 10); | ||
book.AddOrder(order3); | ||
BOOST_TEST( book.bids.size() == 0); | ||
BOOST_TEST( book.asks.size() == 0); | ||
// now let 2 bids sit on the books and 1 big ask swallow them up | ||
order1 = AssetOrder<int, double>(1, asset2, asset1, 10, 10); | ||
book.AddOrder(order1); | ||
order2 = AssetOrder<int, double>(2, asset2, asset1, 10, 7.5); | ||
book.AddOrder(order2); | ||
order3 = AssetOrder<int, double>(3, asset1, asset2, 20, 7.5); | ||
book.AddOrder(order3); | ||
BOOST_TEST( book.bids.size() == 0); | ||
BOOST_TEST( book.asks.size() == 0); | ||
} |