File size: 5,206 Bytes
d982819
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c997ee9
 
 
d982819
 
 
 
 
 
 
 
137eca0
d982819
 
 
 
137eca0
c997ee9
 
 
 
 
 
 
d982819
137eca0
c997ee9
 
 
 
 
d982819
137eca0
 
 
d982819
 
c997ee9
 
d982819
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee1dfbc
 
d982819
c997ee9
 
 
 
 
 
 
 
 
d982819
 
 
 
 
 
 
 
 
 
 
c997ee9
 
 
d982819
c997ee9
 
 
 
137eca0
c997ee9
 
d982819
c997ee9
 
d982819
c997ee9
 
 
 
137eca0
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
#pragma once

#include <map>
#include <unordered_map>
#include <list>
#include <thread>
#include <condition_variable>
#include <mutex>
#include <atomic>
#include <functional>
#include <optional>
#include <chrono>
#include <vector>

#include "Types.h"
#include "Order.h"
#include "Trade.h"

class Orderbook {
public:
    using OnTradeCallback    = std::function<void(const Trade&)>;
    using OnOrderCallback    = std::function<void(const Order&)>;
    using OnSnapshotCallback = std::function<void(const OrderbookSnapshot&)>;

    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<Price, OrderPointers, std::greater<Price>> bids_;
    std::map<Price, OrderPointers, std::less<Price>>    asks_;
    std::unordered_map<OrderId, OrderEntry>             orders_;
    std::unordered_map<Price, LevelData>                levelData_;
    std::multimap<Price, OrderPointer>                  stopBuys_;
    std::multimap<Price, OrderPointer, std::greater<Price>> 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<OrderPointer> pendingStops_;

    mutable std::mutex         ordersMutex_;
    std::thread                pruneThread_;
    std::condition_variable    shutdownCV_;
    std::atomic<bool>          shutdown_{false};

    struct Stats {
        std::atomic<uint64_t> totalOrders{0};
        std::atomic<uint64_t> totalTrades{0};
        std::atomic<uint64_t> totalCancels{0};
        std::atomic<uint64_t> totalVolume{0};
        std::atomic<uint64_t> seqNum{0};
        std::atomic<Price>    lastTradePrice{0};
        std::atomic<Quantity> 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);
};