Skip to content

Instantly share code, notes, and snippets.

@nlyan
Last active April 16, 2020 14:03
Show Gist options
  • Save nlyan/bb55b6054b89821a63a11a3d8307ec2a to your computer and use it in GitHub Desktop.
Save nlyan/bb55b6054b89821a63a11a3d8307ec2a to your computer and use it in GitHub Desktop.
/* Outputs:
Serialized Bytes: 464
Bids: [
{3, 0, 100},
{1, 1.13, 1000},
{2, 1.12, 500},
{4, 1.12, 250}
]
Asks: [
{6, 1.135, 90},
{7, 1.135, 1020},
{5, 1.14, 15}
]
Order 6: {6, 1.135, 90}
Asks: [
{6, 1.135, 80},
{7, 1.135, 1020},
{5, 1.14, 15}
]
Ask depth at 1.135: 1100
*/
#include "cista.h"
namespace data = cista::offset;
struct Order {
CISTA_PRINTABLE (Order);
uint32_t order_id = 0;
float price = 0;
uint32_t quantity = 0;
};
struct OrderBook {
data::indexed_vector<Order> bids;
data::indexed_vector<Order> asks;
data::hash_map<uint32_t, data::ptr<Order>> id_index;
};
#include <algorithm>
#include <iostream>
// Needed for equal_range
struct OrderPriceCompare {
bool
operator() (Order const& order, float price) const {
return order.price < price;
}
bool
operator() (float price, Order const& order) const {
return price < order.price;
}
};
// Need for accumulate
struct OrderQuantityAccumulator {
auto
operator() (Order const& order, int quantity) const {
return order.quantity + quantity;
}
auto
operator() (int quantity, Order const& order) const {
return order.quantity + quantity;
}
};
int
main () {
constexpr auto mode =
cista::mode::WITH_VERSION | cista::mode::WITH_INTEGRITY;
std::vector<uint8_t> buffer;
{
OrderBook book;
book.bids = {{1, 1.13, 1000},
{2, 1.12, 500},
{3, 0, 100}, // market order
{4, 1.12, 250}};
book.asks = {{5, 1.14, 15},
{6, 1.135, 90},
{7, 1.135, 1020}};
// Sort asks in ascending order by price
std::stable_sort (book.asks.begin (),
book.asks.end (),
[] (Order const& a, Order const& b) -> bool {
return a.price < b.price;
});
// Sort bids in descending order by price, making sure market orders
// are at the top
std::stable_sort (book.bids.begin (),
book.bids.end (),
[] (Order const& a, Order const& b) -> bool {
if (a.price == 0.0f && b.price != 0.0f)
return true;
else if (b.price == 0.0f)
return false;
else
return (a.price > b.price);
});
// Index all the orders by order id. This must be done last
// because all the hash_map values are offsets in memory to the
// orders stored in the bid and ask vectors, so re-ordering them
// afterward will screw things up.
for (auto& bid : book.bids) {
book.id_index.emplace (bid.order_id, &bid);
}
for (auto& ask : book.asks) {
book.id_index.emplace (ask.order_id, &ask);
}
buffer = cista::serialize<mode> (book);
}
std::cout << "Serialized Bytes: " << buffer.size () << "\n\n";
// Reload everything and do some modification
{
OrderBook* const book = cista::deserialize<OrderBook, mode> (buffer);
// Also works - Demonstrates that deserialization is trivial/zero-copy
// 8 bytes are used for the type id, and 8 bytes for the
// integrity hash
// Should only be done with trusted buffers
// OrderBook* const book = reinterpret_cast<OrderBook*> (buffer.data () + 16);
std::cout << "Bids: " << book->bids << "\n";
std::cout << "Asks: " << book->asks << "\n\n";
// O(1) lookup of orders by order id
auto order6 = book->id_index.find (6u);
if (order6 != book->id_index.end ()) {
std::cout << "Order 6: ";
std::cout << *order6->second << "\n\n";
order6->second->quantity -= 10;
std::cout << "Asks: " << book->asks << "\n";
}
// O(log N) lookup of orders by price
auto range = std::equal_range (book->asks.begin (),
book->asks.end (),
1.135f,
OrderPriceCompare ());
std::cout << "Ask depth at 1.135: "
<< std::accumulate (range.first,
range.second,
0,
OrderQuantityAccumulator ())
<< "\n";
// No need to reserialize as changes are in place
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment