Skip to content

Commit

Permalink
Correct sort of bids and asks
Browse files Browse the repository at this point in the history
  • Loading branch information
jmjatlanta committed Dec 9, 2019
1 parent 2403936 commit ffe2a36
Show file tree
Hide file tree
Showing 7 changed files with 154 additions and 27 deletions.
2 changes: 0 additions & 2 deletions .gitignore
@@ -1,3 +1 @@
.vscode/*
build/*
Makefile
18 changes: 18 additions & 0 deletions .vscode/c_cpp_properties.json
@@ -0,0 +1,18 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"${workspaceFolder}/**",
"/usr/include/c++/7",
"/usr/include/x86_64-linux-gnu/c++/7"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c11",
"cppStandard": "c++17",
"intelliSenseMode": "clang-x64"
}
],
"version": 4
}
27 changes: 27 additions & 0 deletions .vscode/launch.json
@@ -0,0 +1,27 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "functional_test",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/test/functional",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
5 changes: 5 additions & 0 deletions .vscode/settings.json
@@ -0,0 +1,5 @@
{
"files.associations": {
"set": "cpp"
}
}
46 changes: 45 additions & 1 deletion include/asset.hpp
Expand Up @@ -12,6 +12,50 @@ class Asset
}
};

template<class Price>
class AssetKey
{
public:
AssetKey(int id, Price price) : id(id), price(price) {}
int id;
Price price;
};

/***
* A way to sort orders first by high price, then by low id
*/
template<class Price>
class AssetBidKey : public AssetKey<Price>
{
public:
using AssetKey<Price>::price;
using AssetKey<Price>::id;
AssetBidKey(int id, Price price) : AssetKey<Price>(id, price) {}
inline bool operator<(const AssetKey<Price>& in) const
{
if (in.price == price)
return id < in.id;
return price > in.price;
}
};

template<class Price>
class AssetAskKey : public AssetKey<Price>
{
public:
using AssetKey<Price>::price;
using AssetKey<Price>::id;
AssetAskKey(int id, Price price) : AssetKey<Price>(id, price) {}
inline bool operator<(const AssetKey<Price>& in) const
{
if (in.price == price)
return id < in.id;
return price < in.price;
}
};



/***
* An order that will be possibly stored on the order book.
* Therefore, it must be sortable by price and ID
Expand All @@ -37,4 +81,4 @@ class AssetOrder
Asset assetToSell;
Quantity quantity;
Price price;
};
};
49 changes: 25 additions & 24 deletions include/book.hpp
Expand Up @@ -9,17 +9,10 @@ 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();
}
// bids are sorted highest price first, then lowest id
std::map<AssetBidKey<Price>, AssetOrder<Size, Price> > bids;
// asks are sorted lowest price first, then lowest id
std::map<AssetAskKey<Price>, AssetOrder<Size, Price>> asks;
public:
/***
* Add an order to the book
Expand All @@ -33,21 +26,21 @@ class Book
bool buying = (order.assetToBuy == _wantToSell);
if (buying)
{
auto bestAskItr = getBestAsk();
auto bestAskItr = asks.begin();
while (bestAskItr != asks.end() && (*bestAskItr).second.price <= currOrder.price && currOrder.quantity > 0)
{
take(asks, (*bestAskItr).second, currOrder);
bestAskItr = getBestAsk();
takeAsk( (*bestAskItr).second, currOrder);
bestAskItr = asks.begin();
}
placeOnBook(currOrder);
}
else
{
auto bestOfferItr = getBestOffer();
while ( bestOfferItr != bids.rend() && (*bestOfferItr).second.price >= currOrder.price && currOrder.quantity > 0)
auto bestOfferItr = bids.begin();
while ( bestOfferItr != bids.end() && (*bestOfferItr).second.price >= currOrder.price && currOrder.quantity > 0)
{
take(bids, (*bestOfferItr).second, currOrder);
bestOfferItr = getBestOffer();
takeBid( (*bestOfferItr).second, currOrder);
bestOfferItr = bids.begin();
}
// now deal with the leftover
placeOnBook(currOrder);
Expand All @@ -56,7 +49,7 @@ class Book
private:
Asset _wantToBuy;
Asset _wantToSell;
void take(std::map<Price, AssetOrder<Size, Price>>& collection, AssetOrder<Size, Price>& bookItem, AssetOrder<Size, Price>& incomingOrder)
void adjustQuantities(AssetOrder<Size, Price>& bookItem, AssetOrder<Size, Price>& incomingOrder)
{
if (bookItem.quantity > incomingOrder.quantity)
{
Expand All @@ -68,10 +61,18 @@ class Book
incomingOrder.quantity -= bookItem.quantity;
bookItem.quantity = 0;
}
}
void takeBid(AssetOrder<Size, Price>& bookItem, AssetOrder<Size, Price>& incomingOrder)
{
adjustQuantities(bookItem, incomingOrder);
if (bookItem.quantity == 0)
{
collection.erase(bookItem.price);
}
bids.erase(AssetBidKey<Price>(bookItem.id, bookItem.price));
}
void takeAsk(AssetOrder<Size, Price>& bookItem, AssetOrder<Size, Price>& incomingOrder)
{
adjustQuantities(bookItem, incomingOrder);
if (bookItem.quantity == 0)
asks.erase(AssetAskKey<Price>(bookItem.id, bookItem.price));
}
void placeOnBook(AssetOrder<Size, Price>& order)
{
Expand All @@ -80,11 +81,11 @@ class Book
bool buying = (order.assetToBuy == _wantToSell);
if (buying)
{
bids.insert({order.price, order});
bids.insert({AssetBidKey<Price>(order.id, order.price), order});
}
else
{
asks.insert({order.price, order});
asks.insert({AssetAskKey<Price>(order.id, order.price), order});
}
}
}
Expand Down
34 changes: 34 additions & 0 deletions test/functional.cpp
Expand Up @@ -37,4 +37,38 @@ BOOST_AUTO_TEST_CASE( simple_tests )
book.AddOrder(order3);
BOOST_TEST( book.bids.size() == 0);
BOOST_TEST( book.asks.size() == 0);
}

BOOST_AUTO_TEST_CASE( multiple_order_same_price )
{
// Two assets
Asset a1(1);
Asset a2(2);

Book<int, int> book(a1, a2);
AssetOrder<int, int> order1(1, a2, a1, 10, 10);
AssetOrder<int, int> order2(2, a2, a1, 10, 10);
book.AddOrder(order2);
book.AddOrder(order1);
BOOST_TEST( book.bids.size() == 2);
// Even though I added them in the wrong order, they should come out in the correct order
auto bidItr = book.bids.begin();
auto bidKey = (*bidItr).first;
BOOST_TEST(bidKey.id == 1);
// If I add one at a higher price, it should become the first
AssetOrder<int, int> order3(3, a2, a1, 10, 11);
book.AddOrder(order3);
BOOST_TEST( (*book.bids.begin()).first.id == 3 );

// now check the asks
order1 = AssetOrder<int, int>(1, a1, a2, 10, 20);
order2 = AssetOrder<int, int>(2, a1, a2, 10, 20);
order3 = AssetOrder<int, int>(3, a1, a2, 10, 19);
book.AddOrder(order2);
book.AddOrder(order1);
BOOST_TEST( book.asks.size() == 2);
BOOST_TEST( (*book.asks.begin()).first.id == 1);
book.AddOrder(order3);
BOOST_TEST( (*book.asks.begin()).first.id == 3);

}

0 comments on commit ffe2a36

Please sign in to comment.