Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate match and account update messages #312

Merged
merged 1 commit into from
Oct 31, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -7,43 +7,33 @@
#include <glaze/core/common.hpp>
#include <glaze/core/meta.hpp>

#include <chrono>

namespace nutc::common {

struct start_time {
int64_t start_time_ns;
std::int64_t start_time_ns;

start_time() = default;

explicit start_time(int64_t stns) : start_time_ns(stns) {}
explicit start_time(std::chrono::high_resolution_clock::time_point stns) :
start_time_ns(stns.time_since_epoch().count())
{}
};

struct match {
common::position position;
std::string buyer_id;
std::string seller_id;
common::decimal_price buyer_capital;
common::decimal_price seller_capital;
std::string match_type{};

match() = default;

match(
const common::position& position, std::string bid, std::string sid,
common::decimal_price bcap, common::decimal_price scap
) :
position(position), buyer_id(std::move(bid)), seller_id(std::move(sid)),
buyer_capital(bcap), seller_capital(scap)
{}
struct account_update {
common::position trade;
common::decimal_price available_capital;
};

struct tick_update {
std::vector<common::position> ob_updates;
std::vector<match> matches;
std::vector<common::position> matches;

tick_update() = default;

explicit tick_update(
std::vector<common::position> ob_updates, std::vector<match> matches
std::vector<common::position> ob_updates, std::vector<common::position> matches
) : ob_updates(std::move(ob_updates)), matches(std::move(matches))
{}
};
@@ -67,12 +57,10 @@ struct glz::meta<nutc::common::tick_update> {
};

template <>
struct glz::meta<nutc::common::match> {
using t = nutc::common::match;
static constexpr auto value = object(
"match", &t::position, &t::buyer_id, &t::seller_id, &t::buyer_capital,
&t::seller_capital
);
struct glz::meta<nutc::common::account_update> {
using t = nutc::common::account_update;
static constexpr auto value =
object("account_update", &t::trade, &t::available_capital);
};

template <>
3 changes: 2 additions & 1 deletion exchange/src/exchange/algos/dev_mode/dev_mode.cpp
Original file line number Diff line number Diff line change
@@ -18,10 +18,11 @@ DevModeAlgoInitializer::initialize_trader_container(
traders.add_trader<AlgoTrader>(algo, start_capital);
}

int64_t start_time = get_start_time(WAIT_SECS);
auto start_time = get_start_time(WAIT_SECS);
std::for_each(traders.begin(), traders.end(), [start_time](auto& trader) {
send_start_time(trader, start_time);
});
std::this_thread::sleep_until(start_time);
}

void
3 changes: 2 additions & 1 deletion exchange/src/exchange/algos/normal_mode/normal_mode.cpp
Original file line number Diff line number Diff line change
@@ -48,10 +48,11 @@ NormalModeAlgoInitializer::initialize_trader_container(
}
}

int64_t start_time = get_start_time(WAIT_SECS);
auto start_time = get_start_time(WAIT_SECS);
std::for_each(traders.begin(), traders.end(), [start_time](auto& trader) {
send_start_time(trader, start_time);
});
std::this_thread::sleep_until(start_time);
}

glz::json_t::object_t
17 changes: 8 additions & 9 deletions exchange/src/exchange/matching/engine.cpp
Original file line number Diff line number Diff line change
@@ -9,10 +9,9 @@
namespace nutc::exchange {

enum class MatchFailure { buyer_failure, seller_failure, done_matching };
using match = nutc::common::match;

template <common::Side AggressiveSide, typename OrderPairT>
glz::expected<match, bool>
glz::expected<tagged_match, bool>
match_orders_(
OrderPairT& orders, CompositeOrderBook& orderbook, common::decimal_price order_fee
)
@@ -38,7 +37,7 @@ match_orders_(
}

template <common::Side AggressiveSide, TaggedOrder OrderT>
glz::expected<match, bool>
glz::expected<tagged_match, bool>
match_incoming_order_(
OrderT& aggressive_order, LimitOrderBook::stored_limit_order passive_order,
CompositeOrderBook& orderbook, common::decimal_price order_fee
@@ -73,7 +72,7 @@ total_order_cost_(
}

template <common::Side AggressiveSide, typename OrderPairT>
glz::expected<match, MatchFailure>
glz::expected<tagged_match, MatchFailure>
attempt_match_(OrderPairT& orders, common::decimal_price order_fee)
{
auto price_opt = orders.potential_match_price();
@@ -106,7 +105,7 @@ attempt_match_(OrderPairT& orders, common::decimal_price order_fee)
}

template <TaggedOrder OrderT>
glz::expected<match, bool>
glz::expected<tagged_match, bool>
match_incoming_order_(
OrderT& aggressive_order, CompositeOrderBook& orderbook,
common::decimal_price order_fee
@@ -130,12 +129,12 @@ match_incoming_order_(
}

template <TaggedOrder OrderT>
std::vector<match>
std::vector<tagged_match>
match_order(
OrderT order, CompositeOrderBook& orderbook, common::decimal_price order_fee
)
{
std::vector<match> matches;
std::vector<tagged_match> matches;

while (order.quantity != 0.0) {
auto match_opt = match_incoming_order_(order, orderbook, order_fee);
@@ -154,9 +153,9 @@ match_order(
return matches;
}

template std::vector<match>
template std::vector<tagged_match>
match_order<>(tagged_limit_order, CompositeOrderBook&, common::decimal_price);

template std::vector<match>
template std::vector<tagged_match>
match_order<>(tagged_market_order, CompositeOrderBook&, common::decimal_price);
} // namespace nutc::exchange
3 changes: 1 addition & 2 deletions exchange/src/exchange/matching/engine.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#pragma once

#include "common/types/messages/messages_exchange_to_wrapper.hpp"
#include "exchange/orders/orderbook/composite_orderbook.hpp"

#include <glaze/util/expected.hpp>
@@ -10,7 +9,7 @@
namespace nutc::exchange {

template <TaggedOrder OrderT>
std::vector<common::match> match_order(
std::vector<tagged_match> match_order(
OrderT order, CompositeOrderBook& orderbook, common::decimal_price order_fee = 0.0
);

29 changes: 9 additions & 20 deletions exchange/src/exchange/matching/order_pair.hpp
Original file line number Diff line number Diff line change
@@ -101,24 +101,13 @@ class OrderPair {
}

template <common::Side AggressiveSide>
common::match
tagged_match
create_match(common::decimal_quantity quantity, common::decimal_price price) const
{
auto& buyer = get_underlying_order<side::buy>();
auto& seller = get_underlying_order<side::sell>();
common::position position{buyer.ticker, AggressiveSide, quantity, price};
// TODO: match_type is pretty bad, we should have a better way of tracking this.
// It's only used for metrics
std::string match_type =
fmt::format("{}->{}", seller.trader->get_type(), buyer.trader->get_type());
// TODO: can just use TraderPortfolio instead of entire trader
common::match match{
position, buyer.trader->get_id(), seller.trader->get_id(),
buyer.trader->get_portfolio().get_capital(),
seller.trader->get_portfolio().get_capital()
};
match.match_type = match_type;
return match;
return {buyer.trader, seller.trader, position};
}

common::decimal_quantity
@@ -131,21 +120,21 @@ class OrderPair {

void
handle_match(
const common::match& match, common::decimal_price order_fee,
const tagged_match& match, common::decimal_price order_fee,
CompositeOrderBook& orderbook
)
{
get_underlying_order<side::buy>().trader->get_portfolio().notify_match(
{match.position.ticker, common::Side::buy, match.position.quantity,
match.position.price * (common::decimal_price{1.0} + order_fee)}
{match.ticker, common::Side::buy, match.quantity,
match.price * (common::decimal_price{1.0} + order_fee)}
);
get_underlying_order<side::sell>().trader->get_portfolio().notify_match(
{match.position.ticker, common::Side::sell, match.position.quantity,
match.position.price * (common::decimal_price{1.0} - order_fee)}
{match.ticker, common::Side::sell, match.quantity,
match.price * (common::decimal_price{1.0} - order_fee)}
);

change_order_quantity(seller, -match.position.quantity, orderbook);
change_order_quantity(buyer, -match.position.quantity, orderbook);
change_order_quantity(seller, -match.quantity, orderbook);
change_order_quantity(buyer, -match.quantity, orderbook);
}

private:
64 changes: 57 additions & 7 deletions exchange/src/exchange/matching_cycle/base/base_cycle.cpp
Original file line number Diff line number Diff line change
@@ -52,10 +52,10 @@ BaseMatchingCycle::collect_orders(uint64_t) -> std::vector<OrderVariant>
return orders;
}

std::vector<common::match>
std::vector<tagged_match>
BaseMatchingCycle::match_orders_(std::vector<OrderVariant> orders)
{
std::vector<common::match> matches;
std::vector<tagged_match> matches;

auto match_incoming_order = [&]<typename OrderT>(OrderT& order) {
auto& ticker_data = tickers_[order.ticker];
@@ -83,20 +83,63 @@ BaseMatchingCycle::match_orders_(std::vector<OrderVariant> orders)
return matches;
}

void
BaseMatchingCycle::handle_matches_(std::vector<common::match> matches)
std::vector<common::position>
BaseMatchingCycle::get_orderbook_updates_()
{
std::vector<common::position> ob_updates{};
std::vector<common::position> ob_updates;

for (auto [symbol, info] : tickers_) {
auto tmp = info.get_orderbook().get_and_reset_updates();
std::ranges::copy(tmp, std::back_inserter(ob_updates));
}
return ob_updates;
}

std::vector<common::position>
BaseMatchingCycle::tagged_matches_to_positions(const std::vector<tagged_match>& matches)
{
std::vector<common::position> untagged_matches(matches.size());
std::transform(
matches.begin(), matches.end(), untagged_matches.begin(),
[](const tagged_match& match) { return match; }
);
return untagged_matches;
}

if (ob_updates.empty() && matches.empty())
void
BaseMatchingCycle::send_account_updates(const std::vector<tagged_match>& matches)
{
std::for_each(matches.begin(), matches.end(), [](const tagged_match& match) {
common::position trade{match.ticker, match.side, match.quantity, match.price};
common::account_update buyer_update{
trade, match.buyer->get_portfolio().get_capital()
};
common::account_update seller_update{
trade, match.seller->get_portfolio().get_capital()
};
auto buyer_update_str = glz::write_json(buyer_update);
if (!buyer_update_str.has_value()) [[unlikely]] {
throw std::runtime_error(glz::format_error(buyer_update_str.error()));
}
auto seller_update_str = glz::write_json(seller_update);
if (!seller_update_str.has_value()) [[unlikely]] {
throw std::runtime_error(glz::format_error(seller_update_str.error()));
}
match.buyer->send_message(buyer_update_str.value());
match.seller->send_message(seller_update_str.value());
});
}

void
BaseMatchingCycle::send_market_updates_(const std::vector<tagged_match>& matches)
{
std::vector<common::position> orderbook_updates = get_orderbook_updates_();
std::vector<common::position> trade_updates = tagged_matches_to_positions(matches);

if (orderbook_updates.empty() && trade_updates.empty()) [[unlikely]]
return;

common::tick_update updates{ob_updates, matches};
common::tick_update updates{orderbook_updates, trade_updates};
auto update = glz::write_json(updates);
if (!update.has_value()) [[unlikely]] {
throw std::runtime_error(glz::format_error(update.error()));
@@ -108,4 +151,11 @@ BaseMatchingCycle::handle_matches_(std::vector<common::match> matches)
);
}

void
BaseMatchingCycle::handle_matches_(std::vector<tagged_match> matches)
{
send_market_updates_(matches);
send_account_updates(matches);
}

} // namespace nutc::exchange
13 changes: 11 additions & 2 deletions exchange/src/exchange/matching_cycle/base/base_cycle.hpp
Original file line number Diff line number Diff line change
@@ -41,13 +41,22 @@ class BaseMatchingCycle : public MatchingCycleInterface {

std::vector<OrderVariant> collect_orders(uint64_t) override;

std::vector<common::match> match_orders_(std::vector<OrderVariant> orders) override;
std::vector<tagged_match> match_orders_(std::vector<OrderVariant> orders) override;

void handle_matches_(std::vector<common::match> matches) override;
void handle_matches_(std::vector<tagged_match> matches) override;

void
post_cycle_(uint64_t) override
{}

private:
std::vector<common::position> get_orderbook_updates_();

static std::vector<common::position>
tagged_matches_to_positions(const std::vector<tagged_match>& matches);

static void send_account_updates(const std::vector<tagged_match>& matches);
void send_market_updates_(const std::vector<tagged_match>& matches);
};

} // namespace nutc::exchange
4 changes: 2 additions & 2 deletions exchange/src/exchange/matching_cycle/cycle_interface.hpp
Original file line number Diff line number Diff line change
@@ -30,10 +30,10 @@ class MatchingCycleInterface {

virtual std::vector<OrderVariant> collect_orders(uint64_t new_tick) = 0;

virtual std::vector<common::match> match_orders_(std::vector<OrderVariant> orders
virtual std::vector<tagged_match> match_orders_(std::vector<OrderVariant> orders
) = 0;

virtual void handle_matches_(std::vector<common::match> matches) = 0;
virtual void handle_matches_(std::vector<tagged_match> matches) = 0;

virtual void post_cycle_(uint64_t new_tick) = 0;

4 changes: 2 additions & 2 deletions exchange/src/exchange/matching_cycle/dev/dev_cycle.hpp
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@ class DevMatchingCycle : public BaseMatchingCycle {
{}

protected:
std::vector<common::match>
std::vector<tagged_match>
match_orders_(std::vector<OrderVariant> orders) override
{
// TODO: add back
@@ -31,7 +31,7 @@ class DevMatchingCycle : public BaseMatchingCycle {
}

void
handle_matches_(std::vector<common::match> matches) override
handle_matches_(std::vector<tagged_match> matches) override
{
pusher.report_matches(matches);
BaseMatchingCycle::handle_matches_(std::move(matches));
Loading