|
| 1 | +#include <cstdlib> |
| 2 | +#include <string> |
| 3 | +#include <limits> |
| 4 | +#include <tuple> |
| 5 | +#include <map> |
| 6 | +#include <unordered_map> |
| 7 | +#include <deque> |
| 8 | +#include <deque> |
| 9 | +#include <algorithm> |
| 10 | + |
| 11 | +using namespace std; |
| 12 | + |
| 13 | +#define MIN_NPRICE LLONG_MIN |
| 14 | +#define MAX_NPRICE LLONG_MAX |
| 15 | +#define MIN_QUANTITY 1e-9 |
| 16 | +#define MIN_TICK_SIZE 1e-9 |
| 17 | +#define EPILSON 5e-10 |
| 18 | +#define NORMALIZE_PRICE( x ) static_cast<int>( x / MIN_TICK_SIZE + EPILSON ) |
| 19 | +#define DENORMALIZE_PRICE( x ) ( x * MIN_TICK_SIZE ) |
| 20 | +#define nprice_t long long |
| 21 | +#define price_t double |
| 22 | +#define qty_t double |
| 23 | +#define id_t long long |
| 24 | + |
| 25 | +enum Side { |
| 26 | + BUY = 1, |
| 27 | + SELL = 2 |
| 28 | +}; |
| 29 | + |
| 30 | +struct Order { |
| 31 | + id_t order_id; |
| 32 | + const string& instmt; |
| 33 | + price_t price; |
| 34 | + qty_t qty; |
| 35 | + qty_t cum_qty; |
| 36 | + qty_t leaves_qty; |
| 37 | + Side side; |
| 38 | + |
| 39 | + Order(id_t order_id, const string& instmt, price_t price, qty_t qty, Side side): |
| 40 | + order_id(order_id), |
| 41 | + instmt(instmt), |
| 42 | + price(price), |
| 43 | + qty(qty), |
| 44 | + cum_qty(0), |
| 45 | + leaves_qty(qty), |
| 46 | + side(side) {} |
| 47 | +}; |
| 48 | + |
| 49 | +struct Trade { |
| 50 | + id_t order_id; |
| 51 | + const string& instmt; |
| 52 | + price_t trade_price; |
| 53 | + qty_t trade_qty; |
| 54 | + Side trade_side; |
| 55 | + id_t trade_id; |
| 56 | + |
| 57 | + Trade(id_t order_id, const string& instmt, price_t trade_price, qty_t trade_qty, |
| 58 | + Side trade_side, id_t trade_id): |
| 59 | + order_id(order_id), |
| 60 | + instmt(instmt), |
| 61 | + trade_price(trade_price), |
| 62 | + trade_qty(trade_qty), |
| 63 | + trade_side(trade_side), |
| 64 | + trade_id(trade_id) {} |
| 65 | +}; |
| 66 | + |
| 67 | +struct OrderBook { |
| 68 | + map<nprice_t, deque<Order>> bids; |
| 69 | + map<nprice_t, deque<Order>> asks; |
| 70 | + unordered_map<id_t, Order> order_id_map; |
| 71 | +}; |
| 72 | + |
| 73 | +class LightMatchingEngine { |
| 74 | + public: |
| 75 | + unordered_map<string, OrderBook>& order_books() { |
| 76 | + return __order_books; |
| 77 | + } |
| 78 | + |
| 79 | + int curr_order_id() { |
| 80 | + return __curr_order_id; |
| 81 | + } |
| 82 | + |
| 83 | + int curr_trade_id() { |
| 84 | + return __curr_trade_id; |
| 85 | + } |
| 86 | + |
| 87 | + tuple<Order, vector<Trade>> add_order( |
| 88 | + const string& instmt, price_t price, qty_t qty, Side side) |
| 89 | + { |
| 90 | + vector<Trade> trades; |
| 91 | + id_t order_id = (__curr_order_id += 1); |
| 92 | + Order order = Order(order_id, instmt, price, qty, side); |
| 93 | + nprice_t nprice = NORMALIZE_PRICE(price); |
| 94 | + |
| 95 | + // Find the order book |
| 96 | + auto order_book_it = __order_books.find(instmt); |
| 97 | + if (order_book_it == __order_books.end()) { |
| 98 | + order_book_it = __order_books.emplace(instmt, OrderBook()).first; |
| 99 | + } |
| 100 | + |
| 101 | + auto order_book = order_book_it->second; |
| 102 | + |
| 103 | + if (side == Side::BUY) { |
| 104 | + nprice_t best_nprice = MAX_NPRICE; |
| 105 | + if (order_book.asks.size() > 0) { |
| 106 | + best_nprice = order_book.asks.begin()->first; |
| 107 | + } |
| 108 | + |
| 109 | + while (nprice >= best_nprice && order.leaves_qty > MIN_QUANTITY) { |
| 110 | + auto nbbo = order_book.asks.begin()->second; |
| 111 | + auto original_leaves_qty = order.leaves_qty; |
| 112 | + |
| 113 | + // Matching the ask queue |
| 114 | + while (nbbo.size() > 0) { |
| 115 | + auto front_nbbo = nbbo.front(); |
| 116 | + qty_t matching_qty = min(order.leaves_qty, nbbo[0].leaves_qty); |
| 117 | + order.leaves_qty -= matching_qty; |
| 118 | + front_nbbo.leaves_qty -= matching_qty; |
| 119 | + |
| 120 | + // Trades on the passive order |
| 121 | + trades.push_back(Trade( |
| 122 | + front_nbbo.order_id, |
| 123 | + instmt, |
| 124 | + best_nprice, |
| 125 | + matching_qty, |
| 126 | + front_nbbo.side, |
| 127 | + ++__curr_trade_id)); |
| 128 | + |
| 129 | + // Remove the order if it is fully executed |
| 130 | + if (front_nbbo.leaves_qty < MIN_QUANTITY) { |
| 131 | + order_book.order_id_map.erase(front_nbbo.order_id); |
| 132 | + nbbo.pop_front(); |
| 133 | + } |
| 134 | + } |
| 135 | + |
| 136 | + // Trades from the original order |
| 137 | + trades.push_back(Trade( |
| 138 | + order.order_id, |
| 139 | + instmt, |
| 140 | + best_nprice, |
| 141 | + original_leaves_qty - order.leaves_qty, |
| 142 | + order.side, |
| 143 | + ++__curr_trade_id)); |
| 144 | + |
| 145 | + // Remove the ask queue if the size = 0 |
| 146 | + if (nbbo.size() == 0) { |
| 147 | + order_book.asks.erase(order_book.asks.begin()); |
| 148 | + } |
| 149 | + |
| 150 | + // Update the ask best prices |
| 151 | + if (order_book.asks.size() > 0) { |
| 152 | + best_nprice = order_book.asks.begin()->first; |
| 153 | + } else { |
| 154 | + best_nprice = MAX_NPRICE; |
| 155 | + } |
| 156 | + } |
| 157 | + |
| 158 | + // After matching the order, place the leaving order to the end |
| 159 | + // of the order book queue, and create the order id mapping |
| 160 | + if (order.leaves_qty > MIN_QUANTITY) { |
| 161 | + auto nbbo_it = order_book.bids.find(nprice); |
| 162 | + if (nbbo_it == order_book.bids.end()){ |
| 163 | + nbbo_it = order_book.bids.emplace(nprice, deque<Order>()).first; |
| 164 | + } |
| 165 | + |
| 166 | + auto nbbo = nbbo_it->second; |
| 167 | + nbbo.emplace_back(order); |
| 168 | + order_book.order_id_map.emplace(order.order_id, order); |
| 169 | + } |
| 170 | + } else { |
| 171 | + nprice_t best_nprice = MIN_NPRICE; |
| 172 | + if (order_book.bids.size() > 0) { |
| 173 | + best_nprice = order_book.bids.begin()->first; |
| 174 | + } |
| 175 | + |
| 176 | + while (nprice <= best_nprice && order.leaves_qty > MIN_QUANTITY) { |
| 177 | + auto nbbo = order_book.bids.begin()->second; |
| 178 | + auto original_leaves_qty = order.leaves_qty; |
| 179 | + |
| 180 | + // Matching the ask queue |
| 181 | + while (nbbo.size() > 0) { |
| 182 | + auto front_nbbo = nbbo.front(); |
| 183 | + qty_t matching_qty = min(order.leaves_qty, nbbo[0].leaves_qty); |
| 184 | + order.leaves_qty -= matching_qty; |
| 185 | + front_nbbo.leaves_qty -= matching_qty; |
| 186 | + |
| 187 | + // Trades on the passive order |
| 188 | + trades.push_back(Trade( |
| 189 | + front_nbbo.order_id, |
| 190 | + instmt, |
| 191 | + best_nprice, |
| 192 | + matching_qty, |
| 193 | + front_nbbo.side, |
| 194 | + ++__curr_trade_id)); |
| 195 | + |
| 196 | + // Remove the order if it is fully executed |
| 197 | + if (front_nbbo.leaves_qty < MIN_QUANTITY) { |
| 198 | + order_book.order_id_map.erase(front_nbbo.order_id); |
| 199 | + nbbo.pop_front(); |
| 200 | + } |
| 201 | + } |
| 202 | + |
| 203 | + // Trades from the original order |
| 204 | + trades.push_back(Trade( |
| 205 | + order.order_id, |
| 206 | + instmt, |
| 207 | + best_nprice, |
| 208 | + original_leaves_qty - order.leaves_qty, |
| 209 | + order.side, |
| 210 | + ++__curr_trade_id)); |
| 211 | + |
| 212 | + // Remove the bid queue if the size = 0 |
| 213 | + if (nbbo.size() == 0) { |
| 214 | + order_book.bids.erase(order_book.bids.begin()); |
| 215 | + } |
| 216 | + |
| 217 | + // Update the bid best prices |
| 218 | + if (order_book.bids.size() > 0) { |
| 219 | + best_nprice = order_book.bids.begin()->first; |
| 220 | + } else { |
| 221 | + best_nprice = MIN_NPRICE; |
| 222 | + } |
| 223 | + } |
| 224 | + |
| 225 | + // After matching the order, place the leaving order to the end |
| 226 | + // of the order book queue, and create the order id mapping |
| 227 | + if (order.leaves_qty > MIN_QUANTITY) { |
| 228 | + auto nbbo_it = order_book.asks.find(nprice); |
| 229 | + if (nbbo_it == order_book.asks.end()){ |
| 230 | + nbbo_it = order_book.asks.emplace(nprice, deque<Order>()).first; |
| 231 | + } |
| 232 | + |
| 233 | + auto nbbo = nbbo_it->second; |
| 234 | + nbbo.emplace_back(order); |
| 235 | + order_book.order_id_map.emplace(order.order_id, order); |
| 236 | + } |
| 237 | + } |
| 238 | + |
| 239 | + return make_tuple(order, trades); |
| 240 | + } |
| 241 | + |
| 242 | + private: |
| 243 | + unordered_map<string, OrderBook> __order_books; |
| 244 | + int __curr_order_id; |
| 245 | + int __curr_trade_id; |
| 246 | +}; |
0 commit comments