Objective-Z showcase project

ObzMarketLab

A clean C++20 lab for exploring order books, price-time priority, deterministic events, versioned market-data packets, UDP multicast, benchmarks, and profiling.

ETH-USD Feed seq 42
Ask1054
Ask1036
Ask1005
top-of-book changes become one packet per UDP datagram
Bid993
Bid978
Bid952

A Matching Engine You Can Read

ObzMarketLab is not just a toy example that submits one order and stops. It models the core shape of a small exchange-style system: incoming commands, per-symbol books, active resting orders, emitted events, snapshots, and a market-data feed that can publish top-of-book changes.

The most important design choice is determinism. The engine implements price-time priority, so the best price wins first, and older orders at the same price are filled before newer ones.

submit limit 1 10 ETH-USD sell 100 5
submit limit 2 20 ETH-USD buy 99 3
snapshot ETH-USD 5

submit limit 3 30 ETH-USD buy 101 4
snapshot ETH-USD 5

The Core Domain Stays Single-Threaded

The engine owns the active order index and routes commands to one order book per symbol. Each order book owns its bid and ask price levels. That keeps the matching rules local and makes the behaviour easier to test.

class engine {
public:
    std::vector<event> submit(const submit_order& order);
    std::vector<event> update(const update_order& order);
    std::vector<event> cancel(const cancel_order& order);
    std::optional<book_snapshot> snapshot(
        const symbol& instrument,
        std::size_t depth
    ) const;
};

Why that matters

The order book can stay focused on matching. Concurrency and socket transport are handled at the system boundary instead of being mixed into every domain operation.

A Queued Boundary Around The Engine

The project still demonstrates a concurrent shape. The runtime owns a runner that serialises submit, update, cancel, and snapshot commands onto a dedicated engine thread using an ObzLib bounded blocking queue.

class runner {
public:
    explicit runner(std::size_t command_capacity);

    void start();
    void stop();
    bool running() const noexcept;

    event_future submit(submit_command command);
    event_future update(update_command command);
    event_future cancel(cancel_command command);
    snapshot_future snapshot(snapshot_command command);
};

That separation is the showcase piece: the matching rules remain deterministic and easy to reason about, while the runner demonstrates how a real application might feed commands into the engine from another thread.

Market Data Leaves As Versioned Packets

The newer part of ObzMarketLab is the market-data layer. Matching events are translated at the feed boundary, so the wire protocol is not tied to the engine's internal C++ types. Version 1 carries one top-of-book update per packet, with a fixed common header and network-byte-order integers.

struct packet {
    std::uint64_t sequence{};
    message payload;
};

struct book_update {
    std::string symbol;
    side direction{};
    std::optional<std::uint64_t> best_price;
    std::uint64_t aggregate_size{};
};

The feed assigns publication sequence numbers, encodes one packet per UDP datagram, and keeps decode errors as ordinary data. That makes malformed network input testable without turning every bad packet into an exception path.

UDP Multicast With Ordered Delivery

The demo runs a publisher and listener as separate processes. The publisher sends encoded top-of-book updates to a multicast group. The listener joins the group, decodes incoming datagrams, and buffers packets that arrive ahead of a bounded sequence gap.

sequenced_buffer<packet> pending{
    {.maximum_pending_values = 64, .maximum_sequence_gap = 128},
    1
};

const auto result = pending.push(sequence, [&] {
    return decoded_packet;
});

Why that matters

The project now shows more than matching. It shows the next boundary a trading-style system needs: turning deterministic book changes into a small external feed with explicit sequencing.

Replay, Tests, Benchmarks, And Profiling

The repo includes a replay app, editable scenario files, deterministic event output, JSONL output, focused tests, Google Benchmark coverage, and optional Tracy instrumentation. The multicast demo adds a practical cross-process path for the feed, while the core tests keep matching and packet behaviour inspectable.

./build/apps/market_data_listener/obz_market_data_listener
./build/apps/exchange_feed_simulator/obz_market_exchange_feed_simulator

./build/apps/market_replay/obz_market_replay --directory scenarios --format jsonl