Skip to content

Commit

Permalink
improvements to bid/ask processing (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
geseq committed Dec 27, 2023
1 parent 7f1e82d commit b2c960e
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 32 deletions.
9 changes: 8 additions & 1 deletion include/order.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,14 @@ struct Order : public boost::intrusive::set_base_hook<boost::intrusive::optimize
Order(OrderID id, Type type, Side side, Decimal qty, Decimal price, Decimal trig_price, Flag flag)
: id(id), qty(qty), price(price), trig_price(trig_price), type(type), side(side), flag(flag){};

Decimal getPrice(PriceType pt);
template <PriceType pt>
Decimal getPrice() {
if constexpr (pt == PriceType::TriggerOver || pt == PriceType::TriggerUnder) {
return trig_price;
}

return price;
}

friend bool operator<(const Order &a, const Order &b) { return a.id < b.id; }
friend bool operator>(const Order &a, const Order &b) { return a.id > b.id; }
Expand Down
10 changes: 5 additions & 5 deletions include/orderbook.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,16 +64,16 @@ class OrderBook {
template <class Notification>
void OrderBook<Notification>::addOrder(uint64_t tok, OrderID id, Type type, Side side, Decimal qty, Decimal price, Decimal trigPrice, Flag flag) {
uint64_t exp = tok - 1; // technically this should always be single threaded, but just in case.
if (!last_token_.compare_exchange_strong(exp, tok, std::memory_order_acq_rel, std::memory_order_acquire)) {
if (!last_token_.compare_exchange_strong(exp, tok, std::memory_order_acq_rel, std::memory_order_acquire)) [[unlikely]] {
throw std::invalid_argument("invalid token received: cannot maintain determinism");
}

if (qty.is_zero()) {
if (qty.is_zero()) [[unlikely]] {
notification_.putOrder(MsgType::CreateOrder, OrderStatus::Rejected, id, qty, Error::InvalidQty);
return;
}

if (!matching_.load(std::memory_order_acquire)) {
if (!matching_.load(std::memory_order_acquire)) [[unlikely]] {
if (type == Type::Market) {
notification_.putOrder(MsgType::CreateOrder, OrderStatus::Rejected, id, qty, Error::NoMatching);
}
Expand All @@ -93,7 +93,7 @@ void OrderBook<Notification>::addOrder(uint64_t tok, OrderID id, Type type, Side
}
}

if ((flag & (Flag::StopLoss | Flag::TakeProfit)) != 0) {
if ((flag & (Flag::StopLoss | Flag::TakeProfit)) != 0) [[unlikely]] {
if (trigPrice.is_zero()) {
notification_.putOrder(MsgType::CreateOrder, OrderStatus::Rejected, id, qty, Error::InvalidTriggerPrice);
return;
Expand Down Expand Up @@ -254,7 +254,7 @@ void OrderBook<Notification>::putTradeNotification(OrderID mOrderID, OrderID tOr
template <class Notification>
void OrderBook<Notification>::cancelOrder(uint64_t tok, OrderID id) {
uint64_t exp = tok - 1;
if (!last_token_.compare_exchange_strong(exp, tok)) {
if (!last_token_.compare_exchange_strong(exp, tok, std::memory_order_acq_rel, std::memory_order_acquire)) [[unlikely]] {
throw std::invalid_argument("invalid token received: cannot maintain determinism");
}

Expand Down
10 changes: 8 additions & 2 deletions include/pricelevel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "boost/intrusive/rbtree.hpp"
#include "orderqueue.hpp"
#include "pool.hpp"
#include "types.hpp"

namespace orderbook {

Expand Down Expand Up @@ -39,15 +40,20 @@ class PriceLevel {
uint64_t depth();
Decimal volume();
[[nodiscard]] OrderQueue* getQueue();
[[nodiscard]] OrderQueue* getNextQueue(const Decimal& price);
[[nodiscard]] OrderQueue* largestLessThan(const Decimal& price);
[[nodiscard]] OrderQueue* smallestGreaterThan(const Decimal& price);

void append(Order* order);
void remove(Order* order);

template <PriceType Q = P>
[[nodiscard]] OrderQueue* getNextQueue(const Decimal& price);

template <PriceType Q = P>
Decimal processMarketOrder(const TradeNotification& tn, const PostOrderFill& pf, OrderID takerOrderID, Decimal qty, Flag flag);
Decimal processLimitOrder(const TradeNotification& tn, const PostOrderFill& pf, OrderID& takerOrderID, Decimal& price, Decimal qty, Flag& flag);

template <PriceType Q = P>
Decimal processLimitOrder(const TradeNotification& tn, const PostOrderFill& pf, OrderID takerOrderID, Decimal price, Decimal qty, Flag flag);

PriceTree& price_tree() { return price_tree_; };
};
Expand Down
58 changes: 54 additions & 4 deletions main.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,60 @@
#include <algorithm>
#include <chrono>
#include <iostream>
#include <random>
#include <sstream>
#include <string>
#include <thread>
#include <vector>

#include "include/orderbook.hpp"
#include "include/types.hpp"

int main() {
auto n = orderbook::EmptyNotification();
auto ob = orderbook::OrderBook<orderbook::EmptyNotification>(n);
std::cout << "test";
// Helper function to parse command-line arguments and provide default values.
std::string getCmdOption(char **begin, char **end, const std::string &option, const std::string &default_value = "") {
char **itr = std::find(begin, end, option);
if (itr != end && ++itr != end) {
return *itr;
}
return default_value;
}

bool cmdOptionExists(char **begin, char **end, const std::string &option) { return std::find(begin, end, option) != end; }

void latency(int64_t seed, int duration, int pd, orderbook::Decimal lowerBound, orderbook::Decimal upperBound, orderbook::Decimal minSpread, bool sched) {
std::cout << "Latency function running...\n";
// Implement the latency benchmark logic.
}

void throughput(int64_t seed, int duration, orderbook::Decimal lowerBound, orderbook::Decimal upperBound, orderbook::Decimal minSpread) {
std::cout << "Throughput function running...\n";
// Implement the throughput benchmark logic.
}

void run(int64_t seed, int duration, int pd, const std::string &lb, const std::string &ub, const std::string &ms, const std::string &n, bool sched) {
orderbook::Decimal lowerBound(lb);
orderbook::Decimal upperBound(ub);
orderbook::Decimal minSpread(ms);

if (n == "latency") {
latency(seed, duration, pd, lowerBound, upperBound, minSpread, sched);
} else if (n == "throughput") {
throughput(seed, duration, lowerBound, upperBound, minSpread);
}
}

int main(int argc, char *argv[]) {
int64_t seed = std::stoll(getCmdOption(argv, argv + argc, "-seed", std::to_string(std::chrono::system_clock::now().time_since_epoch().count())));
int duration = std::stoi(getCmdOption(argv, argv + argc, "-duration", "0"));
std::string lb = getCmdOption(argv, argv + argc, "-l", "50.0");
std::string ub = getCmdOption(argv, argv + argc, "-u", "100.0");
std::string ms = getCmdOption(argv, argv + argc, "-m", "0.25");
int pd = std::stoi(getCmdOption(argv, argv + argc, "-p", "10"));
bool sched = cmdOptionExists(argv, argv + argc, "-sched");
std::string n = getCmdOption(argv, argv + argc, "-n", "latency");

std::cout << "PID: " << getpid() << std::endl;
run(seed, duration, pd, lb, ub, ms, n, sched);
return 0;
}

12 changes: 2 additions & 10 deletions src/order.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@

#include <iostream>

namespace orderbook {
#include "types.hpp"

Decimal Order::getPrice(PriceType pt) {
if (pt == PriceType::TriggerOver || pt == PriceType::TriggerUnder) [[unlikely]] {
return trig_price;
}

return price;
}

} // namespace orderbook
namespace orderbook {} // namespace orderbook
37 changes: 27 additions & 10 deletions src/pricelevel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <map>
#include <memory>
#include <string>
#include <type_traits>

namespace orderbook {

Expand All @@ -29,7 +30,7 @@ Decimal PriceLevel<P>::volume() {

template <PriceType P>
void PriceLevel<P>::append(Order* order) {
auto price = order->getPrice(price_type_);
auto price = order->getPrice<P>();

auto it = price_tree_.find(price);
auto q = &*it;
Expand All @@ -47,7 +48,7 @@ void PriceLevel<P>::append(Order* order) {

template <PriceType P>
void PriceLevel<P>::remove(Order* order) {
auto price = order->getPrice(price_type_);
auto price = order->getPrice<P>();

auto q = order->queue;
if (q != nullptr) {
Expand Down Expand Up @@ -75,7 +76,9 @@ OrderQueue* PriceLevel<P>::getQueue() {
}

template <PriceType P>
template <PriceType Q>
Decimal PriceLevel<P>::processMarketOrder(const TradeNotification& tn, const PostOrderFill& pf, OrderID takerOrderID, Decimal qty, Flag flag) {
static_assert(Q == PriceType::Bid || Q == PriceType::Ask, "Unsupported PriceType");
// TODO: this won't work as pricelevel volumes aren't accounted for correctly
if ((flag & (AoN | FoK)) != 0 && qty > volume_) {
return uint64_t(0);
Expand All @@ -93,7 +96,9 @@ Decimal PriceLevel<P>::processMarketOrder(const TradeNotification& tn, const Pos
};

template <PriceType P>
Decimal PriceLevel<P>::processLimitOrder(const TradeNotification& tn, const PostOrderFill& pf, OrderID& takerOrderID, Decimal& price, Decimal qty, Flag& flag) {
template <PriceType Q>
Decimal PriceLevel<P>::processLimitOrder(const TradeNotification& tn, const PostOrderFill& pf, OrderID takerOrderID, Decimal price, Decimal qty, Flag flag) {
static_assert(Q == PriceType::Bid || Q == PriceType::Ask, "Unsupported PriceType");
Decimal qtyProcessed = {};
auto orderQueue = getQueue();

Expand Down Expand Up @@ -176,14 +181,14 @@ OrderQueue* PriceLevel<P>::smallestGreaterThan(const Decimal& price) {
}

template <PriceType P>
template <PriceType Q>
OrderQueue* PriceLevel<P>::getNextQueue(const Decimal& price) {
switch (price_type_) {
case PriceType::Bid:
return largestLessThan(price);
case PriceType::Ask:
return smallestGreaterThan(price);
default:
throw std::runtime_error("invalid call to GetQueue");
if constexpr (Q == PriceType::Bid) {
return largestLessThan(price);
} else if constexpr (Q == PriceType::Ask) {
return smallestGreaterThan(price);
} else {
static_assert(Q == PriceType::Bid || Q == PriceType::Ask, "Unsupported PriceType");
}
}

Expand All @@ -192,4 +197,16 @@ template class PriceLevel<PriceType::Ask>;
template class PriceLevel<PriceType::TriggerOver>;
template class PriceLevel<PriceType::TriggerUnder>;

template Decimal PriceLevel<PriceType::Bid>::processMarketOrder<PriceType::Bid>(const TradeNotification& tn, const PostOrderFill& pf, OrderID takerOrderID,
Decimal qty, Flag flag);

template Decimal PriceLevel<PriceType::Ask>::processMarketOrder<PriceType::Ask>(const TradeNotification& tn, const PostOrderFill& pf, OrderID takerOrderID,
Decimal qty, Flag flag);

template Decimal PriceLevel<PriceType::Bid>::processLimitOrder<PriceType::Bid>(const TradeNotification& tn, const PostOrderFill& pf, OrderID takerOrderID,
Decimal price, Decimal qty, Flag flag);

template Decimal PriceLevel<PriceType::Ask>::processLimitOrder<PriceType::Ask>(const TradeNotification& tn, const PostOrderFill& pf, OrderID takerOrderID,
Decimal price, Decimal qty, Flag flag);

} // namespace orderbook

0 comments on commit b2c960e

Please sign in to comment.