order_book 0.3.0
Exchange feed order book that prints the best bid and offer
Loading...
Searching...
No Matches
levels.hpp
1#pragma once
2
3#include <cstddef>
4#include <cstdint>
5#include <optional>
6#include <vector>
7
8#include <order_book/feed.hpp>
9
10namespace order_book {
11
13struct Level {
14 Price price;
15 Quantity quantity;
17 friend bool operator==(const Level&, const Level&) = default;
18};
19
21struct Bbo {
22 std::optional<Level> bid;
23 std::optional<Level> ask;
25 friend bool operator==(const Bbo&, const Bbo&) = default;
26};
27
30 Quantity total;
31 std::uint32_t orders;
32 std::uint16_t locate;
33 Side side;
34};
35
38struct LevelEntry {
39 Price price;
40 std::uint32_t level;
41};
42
45struct BookLevels {
46 std::vector<LevelEntry> bids;
47 std::vector<LevelEntry> asks;
48};
49
60class LevelPool {
61public:
63 struct Added {
64 std::uint32_t level;
65 bool changed;
66 };
67
70 Added add(BookLevels& book, const AddOrder& order, std::uint16_t locate = 0);
73 bool reduce(BookLevels& book, std::uint32_t level, Shares quantity, bool removed);
75 [[nodiscard]] Bbo bbo(const BookLevels& book) const;
76
78 [[nodiscard]] const LevelRecord& operator[](std::uint32_t level) const
79 {
80 return m_records[level];
81 }
82
83 [[nodiscard]] std::size_t size() const { return m_records.size() - 1 - m_free.size(); }
84
85private:
86 // Make a level for `order` and insert it into `levels` at `index`. Kept out of line, so that
87 // add() stays small enough to inline: most adds join a level that exists.
88 Added create(std::vector<LevelEntry>& levels, std::size_t index, const AddOrder& order,
89 std::uint16_t locate);
90 // Whether `lhs` is a worse price than `rhs` on `side`: lower for bids, higher for asks
91 [[nodiscard]] static bool worse(Side side, Price lhs, Price rhs)
92 {
93 return side == Side::Buy ? lhs < rhs : lhs > rhs;
94 }
95 // Where the level at `price` is in `levels`, worst first, or where it would be inserted:
96 // levels[result - 1] is the first from the best that is not better than `price`
97 [[nodiscard]] static std::size_t position(const std::vector<LevelEntry>& levels, Side side,
98 Price price);
99
100 std::vector<LevelRecord> m_records = std::vector<LevelRecord>(1);
101 std::vector<std::uint32_t> m_free;
102};
103
105 std::uint16_t locate)
106{
107 auto& levels = order.side == Side::Buy ? book.bids : book.asks;
108 const auto index = position(levels, order.side, order.price);
109 if (index == 0 || levels[index - 1].price != order.price) {
110 return create(levels, index, order, locate);
111 }
112 const auto handle = levels[index - 1].level;
113 auto& level = m_records[handle];
114 level.total += order.shares;
115 ++level.orders;
116 return {.level = handle, .changed = index == levels.size() && order.shares != 0};
117}
118
119// A handle and a quantity, which the check sees as swappable
120// NOLINTNEXTLINE(bugprone-easily-swappable-parameters)
121inline bool LevelPool::reduce(BookLevels& book, std::uint32_t level, Shares quantity, bool removed)
122{
123 auto& record = m_records[level];
124 auto& levels = record.side == Side::Buy ? book.bids : book.asks;
125 const auto best = levels.back().level == level;
126 // Only the best level shows in the BBO, and only if its total changes or it goes away
127 auto changed = best && quantity != 0;
128 record.total -= quantity;
129 if (removed && --record.orders == 0) {
130 changed = best;
131 if (best) {
132 levels.pop_back();
133 }
134 else {
135 // Found by handle from the end, which takes as many steps as position() would, so the
136 // record needs no copy of the price
137 auto index = levels.size() - 2;
138 while (levels[index].level != level) {
139 --index;
140 }
141 levels.erase(levels.begin() + static_cast<std::ptrdiff_t>(index));
142 }
143 m_free.push_back(level);
144 }
145 return changed;
146}
147
148inline Bbo LevelPool::bbo(const BookLevels& book) const
149{
150 Bbo bbo;
151 if (!book.bids.empty()) {
152 const auto& best = book.bids.back();
153 bbo.bid = Level{.price = best.price, .quantity = m_records[best.level].total};
154 }
155 if (!book.asks.empty()) {
156 const auto& best = book.asks.back();
157 bbo.ask = Level{.price = best.price, .quantity = m_records[best.level].total};
158 }
159 return bbo;
160}
161
162inline std::size_t LevelPool::position(const std::vector<LevelEntry>& levels, Side side,
163 Price price)
164{
165 // Most adds and removals are near the top of the book, which is the end of the vector
166 auto index = levels.size();
167 while (index > 0 && worse(side, price, levels[index - 1].price)) {
168 --index;
169 }
170 return index;
171}
172
173} // namespace order_book
Bbo bbo(const BookLevels &book) const
Best bid and offer of book.
Definition levels.hpp:148
Added add(BookLevels &book, const AddOrder &order, std::uint16_t locate=0)
Definition levels.hpp:104
const LevelRecord & operator[](std::uint32_t level) const
The record of level level.
Definition levels.hpp:78
bool reduce(BookLevels &book, std::uint32_t level, Shares quantity, bool removed)
Definition levels.hpp:121
std::size_t size() const
Number of levels in every book.
Definition levels.hpp:83
B or S: add a resting order.
Definition feed.hpp:26
Side side
Side the order rests on.
Definition feed.hpp:27
Price price
Limit price.
Definition feed.hpp:28
Best bid and offer; an empty side has no level.
Definition levels.hpp:21
std::optional< Level > bid
Highest bid level.
Definition levels.hpp:22
friend bool operator==(const Bbo &, const Bbo &)=default
Same levels on both sides.
std::optional< Level > ask
Definition levels.hpp:23
std::vector< LevelEntry > asks
Ask levels, lowest price last.
Definition levels.hpp:47
std::vector< LevelEntry > bids
Bid levels, highest price last.
Definition levels.hpp:46
std::uint32_t level
Handle of the level in its pool.
Definition levels.hpp:40
Price price
Level price.
Definition levels.hpp:39
The level an add went to, and whether it changed its book's best bid or offer.
Definition levels.hpp:63
bool changed
Whether the best bid or offer changed.
Definition levels.hpp:65
std::uint32_t level
Handle of the level.
Definition levels.hpp:64
The orders resting at one price: their total, their count, and the book and side they are in.
Definition levels.hpp:29
Side side
Side the level is on.
Definition levels.hpp:33
std::uint16_t locate
Book the level belongs to, where a pool serves several; 0 otherwise.
Definition levels.hpp:32
std::uint32_t orders
Number of orders at the level.
Definition levels.hpp:31
Quantity total
Sum of the remaining quantity of the level's orders.
Definition levels.hpp:30
Price and total resting quantity of one level.
Definition levels.hpp:13
Price price
Level price.
Definition levels.hpp:14
friend bool operator==(const Level &, const Level &)=default
Same price and quantity.
Quantity quantity
Definition levels.hpp:15