diff --git a/src/engine/engine.cc b/src/engine/engine.cc index 866d09b..71a9976 100644 --- a/src/engine/engine.cc +++ b/src/engine/engine.cc @@ -87,8 +87,9 @@ private: TopInfo top_info_{}; }; -Engine::Engine(std::shared_ptr listener) - : listener_(listener) {} +Engine::Engine(std::shared_ptr listener, + CrossBehaviour cross_behaviour) + : listener_(listener), cross_behaviour_(cross_behaviour) {} void Engine::process_orders(std::vector const& orders) { for (auto const& order : orders) { @@ -109,8 +110,31 @@ void Engine::operator()(TradeOrder const& trade_order) { auto& [_, bid_map] = *bid_map_it; if (bid_map.size() > 0 && bid_map.begin()->first >= trade_order.price) { - // FIXME: handle matching if enabled - listener_->on_rejection(trade_order.user, trade_order.id); + if (cross_behaviour_ == CrossBehaviour::REJECT) { + listener_->on_rejection(trade_order.user, trade_order.id); + } else { + // FIXME: assumes a single trade for the order + // FIXME: assumes no remaining orders on both sides + auto matching = bid_map.begin(); + auto const& bid_order = matching->second; + + auto const matching_quantity + = std::min(bid_order.quantity, trade_order.quantity); + // NOTE: Pick the asking price for matching + auto const matching_price = trade_order.price; + + listener_->on_acknowledgement(trade_order.user, + trade_order.id); + listener_->on_match(bid_order.user, bid_order.id, + trade_order.user, trade_order.id, + matching_price, matching_quantity); + assert(matching_quantity == bid_order.quantity + && "multiple matches not implemented"); + assert(matching_quantity == trade_order.quantity + && "multiple matches not implemented"); + + bid_map.erase(matching); + } return; } } @@ -124,8 +148,30 @@ void Engine::operator()(TradeOrder const& trade_order) { auto& [_, ask_map] = *ask_map_it; if (ask_map.size() > 0 && ask_map.begin()->first <= trade_order.price) { - // FIXME: handle matching if enabled - listener_->on_rejection(trade_order.user, trade_order.id); + if (cross_behaviour_ == CrossBehaviour::REJECT) { + listener_->on_rejection(trade_order.user, trade_order.id); + } else { + // FIXME: assumes a single trade for the order + // FIXME: assumes no remaining orders on both sides + auto matching = ask_map.begin(); + // NOTE: Pick the asking price for matching + auto const& [matching_price, ask_order] = *matching; + + auto const matching_quantity + = std::min(ask_order.quantity, trade_order.quantity); + + listener_->on_acknowledgement(trade_order.user, + trade_order.id); + listener_->on_match(trade_order.user, trade_order.id, + ask_order.user, ask_order.id, + matching_price, matching_quantity); + assert(matching_quantity == ask_order.quantity + && "multiple matches not implemented"); + assert(matching_quantity == trade_order.quantity + && "multiple matches not implemented"); + + ask_map.erase(matching); + } return; } } diff --git a/src/engine/engine.hh b/src/engine/engine.hh index 77c0b0a..e02fedb 100644 --- a/src/engine/engine.hh +++ b/src/engine/engine.hh @@ -9,12 +9,21 @@ namespace kraken::engine { +/// Which behaviour on cross orders. +enum class CrossBehaviour { + /// Reject the crossing order. + REJECT, + /// Make the trade with the matching order(s). + MATCH, +}; + struct CallbackOnTopOfBookChange; struct EngineListener; /// Matching engine which processes orders and keeps the book up-to-date. struct Engine { - Engine(std::shared_ptr listener); + Engine(std::shared_ptr listener, + CrossBehaviour cross_behaviour = CrossBehaviour::REJECT); /// Process orders, triggerring the listener on each event. void process_orders(std::vector const& orders); @@ -25,6 +34,7 @@ private: void operator()(FlushOrder const& flush_order); std::shared_ptr listener_; + CrossBehaviour cross_behaviour_; // Symbol, price, side are implicit given the way the book is represented struct OrderMetaData {