File size: 6,085 Bytes
8f4d2d0 44cddac 8f4d2d0 44cddac 8f4d2d0 44cddac 8f4d2d0 | 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 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// Integration test: full actor-based matching engine
//
// Tests the OEGateway β OrderBook β MarketData actor pipeline.
// Verifies that the actor topology produces correct trades,
// execution reports, and market data snapshots.
// ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
#include "actors/MECoreActor.hpp"
#include "actors/OEGActor.hpp"
#include "actors/MDGActor.hpp"
#include <iostream>
#include <cassert>
using namespace eunex;
static int testsPassed = 0;
static int testsFailed = 0;
#define TEST(name) \
std::cout << " " << #name << "... "; \
try { test_##name(); std::cout << "PASS\n"; ++testsPassed; } \
catch (const std::exception& e) { std::cout << "FAIL: " << e.what() << "\n"; ++testsFailed; }
#define ASSERT_EQ(a, b) \
if ((a) != (b)) throw std::runtime_error( \
std::string("Expected ") + std::to_string(static_cast<long long>(b)) + " got " + std::to_string(static_cast<long long>(a)))
#define ASSERT_TRUE(x) \
if (!(x)) throw std::runtime_error("Assertion failed: " #x)
// ββ Test fixtures ββββββββββββββββββββββββββββββββββββββββββββββββββ
struct Fixture {
std::unique_ptr<OEGActor> oeGateway;
std::unique_ptr<MDGActor> mdActor;
std::unique_ptr<MECoreActor> book;
static constexpr SymbolIndex_t SYM = 1;
static constexpr SessionId_t SESS = 1;
Fixture() {
oeGateway = std::make_unique<OEGActor>();
mdActor = std::make_unique<MDGActor>();
book = std::make_unique<MECoreActor>(
SYM, oeGateway->getActorId(), mdActor->getActorId());
oeGateway->mapSymbol(SYM, book->getActorId());
}
};
// ββ Tests ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
void test_order_routed_to_book() {
Fixture f;
f.oeGateway->submitNewOrder(1, f.SYM, Side::Buy, OrderType::Limit,
TimeInForce::Day, toFixedPrice(100.0), 50, f.SESS);
ASSERT_TRUE(f.oeGateway->getReports().size() > 0);
ASSERT_EQ(f.oeGateway->getReports().back().status, OrderStatus::New);
}
void test_trade_reaches_market_data() {
Fixture f;
f.oeGateway->submitNewOrder(1, f.SYM, Side::Sell, OrderType::Limit,
TimeInForce::Day, toFixedPrice(50.0), 100, f.SESS);
f.oeGateway->submitNewOrder(2, f.SYM, Side::Buy, OrderType::Limit,
TimeInForce::Day, toFixedPrice(50.0), 60, f.SESS);
ASSERT_EQ(f.mdActor->getRecentTrades().size(), 1UL);
ASSERT_EQ(f.mdActor->getRecentTrades()[0].quantity, 60UL);
}
void test_market_data_snapshot_updated() {
Fixture f;
f.oeGateway->submitNewOrder(1, f.SYM, Side::Sell, OrderType::Limit,
TimeInForce::Day, toFixedPrice(50.0), 100, f.SESS);
f.oeGateway->submitNewOrder(2, f.SYM, Side::Buy, OrderType::Limit,
TimeInForce::Day, toFixedPrice(49.0), 100, f.SESS);
auto* snap = f.mdActor->getSnapshot(f.SYM);
ASSERT_TRUE(snap != nullptr);
ASSERT_EQ(snap->bestBid, toFixedPrice(49.0));
ASSERT_EQ(snap->bestAsk, toFixedPrice(50.0));
}
void test_cancel_via_gateway() {
Fixture f;
f.oeGateway->submitNewOrder(1, f.SYM, Side::Sell, OrderType::Limit,
TimeInForce::Day, toFixedPrice(50.0), 100, f.SESS);
auto orderId = f.oeGateway->getReports().back().orderId;
f.oeGateway->clearReports();
f.oeGateway->submitCancel(orderId, 1, f.SYM, f.SESS);
ASSERT_TRUE(f.oeGateway->getReports().size() > 0);
ASSERT_EQ(f.oeGateway->getReports().back().status, OrderStatus::Cancelled);
}
void test_multiple_fills_generate_reports() {
Fixture f;
f.oeGateway->submitNewOrder(1, f.SYM, Side::Sell, OrderType::Limit,
TimeInForce::Day, toFixedPrice(50.0), 30, f.SESS);
f.oeGateway->submitNewOrder(2, f.SYM, Side::Sell, OrderType::Limit,
TimeInForce::Day, toFixedPrice(51.0), 30, f.SESS);
f.oeGateway->clearReports();
f.oeGateway->submitNewOrder(3, f.SYM, Side::Buy, OrderType::Limit,
TimeInForce::Day, toFixedPrice(51.0), 50, f.SESS);
// Should have reports for: resting sell fill(s) + incoming buy
ASSERT_TRUE(f.oeGateway->getReports().size() >= 2);
ASSERT_EQ(f.mdActor->getRecentTrades().size(), 2UL);
}
void test_unknown_symbol_ignored() {
Fixture f;
f.oeGateway->submitNewOrder(1, 999, Side::Buy, OrderType::Limit,
TimeInForce::Day, toFixedPrice(50.0), 50, f.SESS);
ASSERT_EQ(f.oeGateway->getReports().size(), 0UL);
}
// ββ Main βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
int main() {
std::cout << "Matching Engine Integration Tests\n";
std::cout << "βββββββββββββββββββββββββββββββββββββββββββ\n";
TEST(order_routed_to_book);
TEST(trade_reaches_market_data);
TEST(market_data_snapshot_updated);
TEST(cancel_via_gateway);
TEST(multiple_fills_generate_reports);
TEST(unknown_symbol_ignored);
std::cout << "βββββββββββββββββββββββββββββββββββββββββββ\n";
std::cout << testsPassed << " passed, " << testsFailed << " failed\n";
return testsFailed > 0 ? 1 : 0;
}
|