#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "Types.h" #include "Order.h" #include "Trade.h" class Orderbook { public: using OnTradeCallback = std::function; using OnOrderCallback = std::function; using OnSnapshotCallback = std::function; Orderbook(); ~Orderbook(); Orderbook(const Orderbook&) = delete; Orderbook& operator=(const Orderbook&) = delete; Orderbook(Orderbook&&) = delete; Orderbook& operator=(Orderbook&&) = delete; // ─── Core API ───────────────────────────────────────────────────────────── Trades AddOrder (OrderPointer order); void CancelOrder(OrderId orderId); Trades ModifyOrder(OrderModify order); // ─── Query API ──────────────────────────────────────────────────────────── std::size_t Size() const; bool HasOrder(OrderId) const; OrderbookSnapshot GetSnapshot() const; Price BestBid() const; Price BestAsk() const; Price MidPrice() const; Price Spread() const; // ─── Statistics ─────────────────────────────────────────────────────────── uint64_t GetTotalOrders() const noexcept { return stats_.totalOrders.load(); } uint64_t GetTotalTrades() const noexcept { return stats_.totalTrades.load(); } uint64_t GetTotalCancels() const noexcept { return stats_.totalCancels.load(); } uint64_t GetTotalVolume() const noexcept { return stats_.totalVolume.load(); } uint64_t GetSequenceNumber() const noexcept { return stats_.seqNum.load(); } // ─── Event Hooks ────────────────────────────────────────────────────────── void SetOnTrade(OnTradeCallback cb) { onTrade_ = std::move(cb); } void SetOnOrderAdded(OnOrderCallback cb) { onAdded_ = std::move(cb); } void SetOnOrderCancelled(OnOrderCallback cb) { onCancelled_ = std::move(cb); } // BUG FIX #3: No longer public — called internally only, no longer calls // AddOrderInternal directly to avoid recursive MatchOrders → stack overflow. void CheckAndTriggerStops(Price lastTrade); private: struct OrderEntry { OrderPointer order; OrderPointers::iterator location; }; struct LevelData { Quantity quantity{0}; uint32_t count{0}; enum class Action { Add, Remove, Match }; }; std::map> bids_; std::map> asks_; std::unordered_map orders_; std::unordered_map levelData_; std::multimap stopBuys_; std::multimap> stopSells_; // BUG FIX #3: Deferred stop injection buffer — stops collected here during // MatchOrders and injected AFTER matching loop exits, preventing recursive // MatchOrders calls that could cause stack overflow on stop chains. std::vector pendingStops_; mutable std::mutex ordersMutex_; std::thread pruneThread_; std::condition_variable shutdownCV_; std::atomic shutdown_{false}; struct Stats { std::atomic totalOrders{0}; std::atomic totalTrades{0}; std::atomic totalCancels{0}; std::atomic totalVolume{0}; std::atomic seqNum{0}; std::atomic lastTradePrice{0}; std::atomic lastTradeQty{0}; } stats_; OnTradeCallback onTrade_; OnOrderCallback onAdded_; OnOrderCallback onCancelled_; void PruneGoodForDayOrders(); void CancelOrdersInternal(const OrderIds& ids); void CancelOrderInternal (OrderId orderId); Trades AddOrderInternal (OrderPointer order); bool CanMatch (Side side, Price price) const; bool CanFullyFill (Side side, Price price, Quantity qty) const; Trades MatchOrders (); Trade MakeTrade (OrderPointer bid, OrderPointer ask, Quantity qty); void UpdateLevelData (Price price, Quantity qty, LevelData::Action action); void OnOrderAdded (OrderPointer order); void OnOrderCancelled (OrderPointer order); void OnOrderMatched (Price price, Quantity qty, bool fullyFilled); };