File size: 2,992 Bytes
f86fb3b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | #include <gtest/gtest.h>
#include "../include/order_book/matching_engine.hpp"
using namespace quant::core;
using namespace quant::order_book;
using namespace quant::protocol;
TEST(OrderBookMatchingTest, BasicLimitOrderMatching) {
std::vector<ExecutionReportPayload> reports;
MatchingEngine engine(Symbol("TSLA"), [&](const ExecutionReportPayload& r) {
reports.push_back(r);
});
// Maker: Sell 100 @ 250.00
engine.process_new_order(1, 101, Side::SELL, OrderType::LIMIT, Price::from_double(250.0), 100, 1000);
ASSERT_EQ(reports.size(), 1);
EXPECT_EQ(reports[0].exec_type, static_cast<uint8_t>(ExecType::NEW));
EXPECT_EQ(reports[0].leaves_qty, 100);
// Taker: Buy 60 @ 250.00
engine.process_new_order(2, 201, Side::BUY, OrderType::LIMIT, Price::from_double(250.0), 60, 2000);
// Should trigger 2 reports: maker partial fill, taker fill
ASSERT_EQ(reports.size(), 3);
EXPECT_EQ(reports[1].client_id, 1);
EXPECT_EQ(reports[1].exec_type, static_cast<uint8_t>(ExecType::PARTIAL_FILL));
EXPECT_EQ(reports[1].fill_qty, 60);
EXPECT_EQ(reports[1].leaves_qty, 40);
EXPECT_EQ(reports[2].client_id, 2);
EXPECT_EQ(reports[2].exec_type, static_cast<uint8_t>(ExecType::FILL));
EXPECT_EQ(reports[2].fill_qty, 60);
EXPECT_EQ(reports[2].leaves_qty, 0);
EXPECT_TRUE(engine.check_invariants());
}
TEST(OrderBookMatchingTest, IOCImmediateOrCancel) {
std::vector<ExecutionReportPayload> reports;
MatchingEngine engine(Symbol("AAPL"), [&](const ExecutionReportPayload& r) {
reports.push_back(r);
});
// Maker: Sell 50 @ 180.00
engine.process_new_order(1, 1, Side::SELL, OrderType::LIMIT, Price::from_double(180.0), 50, 1000);
// Taker: IOC Buy 100 @ 180.00 (Fills 50, remaining 50 canceled)
engine.process_new_order(2, 2, Side::BUY, OrderType::IOC, Price::from_double(180.0), 100, 2000);
// Reports:
// 0: Maker NEW
// 1: Maker FILL 50
// 2: Taker PARTIAL_FILL 50
// 3: Taker CANCELED 50 leaves
ASSERT_EQ(reports.size(), 4);
EXPECT_EQ(reports[3].client_id, 2);
EXPECT_EQ(reports[3].exec_type, static_cast<uint8_t>(ExecType::CANCELED));
EXPECT_EQ(reports[3].leaves_qty, 50);
EXPECT_TRUE(engine.book().bids().empty());
EXPECT_TRUE(engine.book().asks().empty());
}
TEST(OrderBookMatchingTest, CancelOrder) {
std::vector<ExecutionReportPayload> reports;
MatchingEngine engine(Symbol("MSFT"), [&](const ExecutionReportPayload& r) {
reports.push_back(r);
});
engine.process_new_order(1, 999, Side::BUY, OrderType::LIMIT, Price::from_double(400.0), 100, 1000);
ASSERT_EQ(reports.size(), 1);
uint64_t engine_order_id = reports[0].engine_order_id;
bool canceled = engine.cancel_order(1, 999, engine_order_id, 2000);
EXPECT_TRUE(canceled);
ASSERT_EQ(reports.size(), 2);
EXPECT_EQ(reports[1].exec_type, static_cast<uint8_t>(ExecType::CANCELED));
EXPECT_TRUE(engine.book().bids().empty());
}
|