1use crate::order_book::{
2 orderbook::OrderBook,
3 types::{
4 CancelOrder, CancelOutcome, GlobalOrderRegistry, ModifyOrder, ModifyOutcome, NewOrder, OrderLocation, OrderNode, OrderType
5 },
6};
7use anyhow::{Context};
8use std::collections::HashMap;
9use tracing::{Span, instrument};
10use uuid::Uuid;
11
12#[derive(Debug)]
13pub struct MatchingEngine {
14 _book: HashMap<Uuid, OrderBook>,
15 _global_registry: GlobalOrderRegistry,
16}
17
18impl MatchingEngine {
19
20 pub fn new() -> Self{
21 Self { _book: HashMap::new(), _global_registry: GlobalOrderRegistry::new() }
22 }
23
24 #[instrument(
25 name = "get_orderbook",
26 skip(self),
27 fields(
28 order_id = %global_order_id,
29 ),
30 )]
31 fn get_orderbook(
32 &mut self,
33 global_order_id: Uuid,
34 span: &Span
35 ) -> Option<(usize, bool,Uuid, &mut OrderBook)> {
36 let order_location = match self._global_registry.get_details(&global_order_id){
37 Some(location) => {
38 location
39 }
40 None => {
41 span.record("reason", "order not found in global registry");
42 return None;
43 }
44 };
45 let Some(book) = self._book.get_mut(&order_location.security_id) else {
46 span.record("reason", "orderbook doesn't exist");
47 return None;
48 };
49 Some((order_location.order_index, order_location.is_buy_side,order_location.security_id, book))
50 }
51
52 pub fn modify(
53 &mut self,
54 global_order_id: Uuid,
55 new_price: Option<u32>,
56 new_qty: Option<u32>,
57 span: &Span,
58 ) -> Result<(), anyhow::Error> {
59 let (order_index, is_buy_side,security_id, orderbook) = self
60 .get_orderbook(global_order_id, span)
61 .context("Could not find the orderbook")?;
62 if let Ok(potential_modfication) = orderbook.modify_order(
63 global_order_id,
64 ModifyOrder {
65 new_price,
66 order_index,
67 is_buy_side,
68 new_quantity: new_qty,
69 },
70 ) {
71 if let Some(modification_result) = potential_modfication {
72 match modification_result {
73 ModifyOutcome::Both {
74 new_price,
75 new_initial_qty,
76 old_current_qty,
77 } => {
78 span.record("modify_outcome", "price & qty");
79 if let Some(_) = self._global_registry.delete(&global_order_id){
80 let _ = self.match_order(
81 NewOrder {
82 engine_order_id: global_order_id,
83 price: Some(new_price),
84 initial_quantity: new_initial_qty,
85 current_quantity : old_current_qty,
86 is_buy_side,
87 security_id,
88 order_type: OrderType::Limit,
89 },
90 span);
91 return Ok(());
92 }
93 span.record("intermediate_error", "Failed to delete from global registry");
94 },
95 ModifyOutcome::Repriced { new_price, old_initial_qty, old_current_qty } =>
96 {
97 span.record("modify_outcome", "price");
98 if let Some(_) = self._global_registry.delete(&global_order_id){
99 let _ = self.match_order(
100 NewOrder {
101 engine_order_id: global_order_id,
102 price: Some(new_price),
103 initial_quantity: old_initial_qty,
104 current_quantity : old_current_qty,
105 is_buy_side,
106 security_id,
107 order_type: OrderType::Limit,
108 },
109 span);
110 return Ok(());
111 }
112 span.record("intermediate_error", "Failed to delete from global registry");
113 },
114 ModifyOutcome::Requantized { old_price, new_initial_qty, old_current_qty } => {
115 span.record("modify_outcome", "qty");
116 if let Some(_) = self._global_registry.delete(&global_order_id){
117 let _ = self.match_order(
118 NewOrder {
119 engine_order_id: global_order_id,
120 price: Some(old_price),
121 initial_quantity: new_initial_qty,
122 current_quantity : old_current_qty,
123 is_buy_side,
124 security_id,
125 order_type: OrderType::Limit,
126 }, span);
127 return Ok(());
128 }
129 span.record("intermediate_error", "Failed to delete from global registry");
130 },
131 ModifyOutcome::Inplace => {
132 span.record("modify_outcome", "qty reduction");
133 return Ok(());
134 }
135 }
136 }
137 } else {
138 return Ok(());
139 }
140 Ok(())
141 }
142
143 pub fn cancel(&mut self, global_order_id: Uuid, span: &Span) -> Result<CancelOutcome, anyhow::Error>{
144 let (order_index, is_buy_side,_, orderbook) = self
145 .get_orderbook(global_order_id, span)
146 .context("Could not find the orderbook")?;
147 if let Err(_) = orderbook.cancel_order(global_order_id, CancelOrder{is_buy_side, order_index}){
148 span.record("reason", "orderbook cancellation failed");
149 span.record("success_status", false);
150 return Ok(CancelOutcome::Failed);
151 };
152 if let Some(_) = self._global_registry.delete(&global_order_id){
153 span.record("success_status", true);
154 return Ok(CancelOutcome::Success)
155 };
156 span.record("reason", "Registry cancellation failed");
157 span.record("success_status", false);
158 Ok(CancelOutcome::Failed)
159 }
160
161 pub fn match_order(&mut self, order: NewOrder, span: &Span) -> Result<(), anyhow::Error> {
162
163 let _gaurd = span.enter();
164
165 let orderbook = match self._book.get_mut(&order.security_id){
166 Some(orderbook) => {
167 orderbook
168 }
169 None => {
170 self._book.entry(order.security_id).or_insert(OrderBook::new(1))
171 }
172 };
173
174 if !order.is_buy_side {
175 match order.order_type {
177 OrderType::Market(None) => {
178 let mut fill_quantity = order.initial_quantity;
180 let mut levels_touched = 0;
181 let mut orders_consumed = 0;
182 while fill_quantity > 0 {
183 let remove_node: bool;
184 {
185 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
186 break;
187 };
188 let price_level = price_node.get_mut();
189 while price_level.total_quantity > 0 && fill_quantity > 0 {
190 let head_idx = price_level.head;
191 let first_order_node =
192 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
193 if fill_quantity >= first_order_node.current_quantity {
194 fill_quantity -= first_order_node.current_quantity;
195 price_level.total_quantity -= first_order_node.current_quantity;
196 let next = first_order_node.next;
197 orderbook.bid.order_pool[head_idx] = None;
198 orderbook.bid.free_list.push(head_idx);
199 orders_consumed += 1;
200 if let Some(next_order_idx) = next {
201 price_level.head = next_order_idx;
202 } else {
203 span.record("reason", "exhausted");
204 break;
205 }
206 } else {
207 first_order_node.current_quantity -= fill_quantity;
208 price_level.total_quantity -= fill_quantity;
209 fill_quantity = 0;
210 span.record("filled", true);
211 }
212 }
213 span.record("reason", "orderbook is empty");
214 span.record("filled", false);
215 remove_node = price_level.total_quantity == 0;
216 }
217 if remove_node {
218 orderbook.bid.price_map.pop_first();
219 levels_touched += 1;
220 }
221 }
222 span.record("order_type", "market");
223 span.record("is_buy_side", false);
224 span.record("levels_touched", levels_touched);
225 span.record("orders_consumed", orders_consumed);
226 }
227 OrderType::Market(market_limit) => {
228 let mut fill_quantity = order.initial_quantity;
229 let mut levels_touched = 0;
230 let mut orders_consumed = 0;
231 while fill_quantity > 0 {
232 let remove_node: bool;
233 {
234 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
235 break;
236 };
237 if market_limit.unwrap() >= *price_node.key() {
238 break;
239 }
240 let price_level = price_node.get_mut();
241 while price_level.total_quantity > 0 && fill_quantity > 0 {
242 let head_idx = price_level.head;
243 let first_order_node =
244 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
245 if fill_quantity >= first_order_node.current_quantity {
246 fill_quantity -= first_order_node.current_quantity;
247 price_level.total_quantity -= first_order_node.current_quantity;
248 let next = first_order_node.next;
249 orderbook.bid.order_pool[head_idx] = None;
250 orderbook.bid.free_list.push(head_idx);
251 orders_consumed += 1;
252 if let Some(next_order_idx) = next {
253 price_level.head = next_order_idx;
254 } else {
255 span.record("reason", "exhausted");
256 break;
257 }
258 } else {
259 first_order_node.current_quantity -= fill_quantity;
260 price_level.total_quantity -= fill_quantity;
261 fill_quantity = 0;
262 span.record("filled", true);
263 }
264 }
265 span.record("reason", "orderbook is empty");
266 span.record("filled", false);
267 remove_node = price_level.total_quantity == 0;
268 }
269 if remove_node {
270 orderbook.bid.price_map.pop_first();
271 levels_touched += 1;
272 }
273 }
274 span.record("order_type", "market");
275 span.record("is_buy_side", false);
276 span.record("levels_touched", levels_touched);
277 span.record("orders_consumed", orders_consumed);
278 }
279 OrderType::Limit => {
280 let mut fill_quantity = order.initial_quantity;
281 let mut levels_touched = 0;
282 let mut orders_consumed = 0;
283 while fill_quantity > 0 {
284 let remove_node: bool;
285 {
286 let Some(mut price_node) = orderbook.bid.price_map.first_entry() else {
287 break;
288 };
289 if order.price >= Some(*price_node.key()) {
290 break;
291 }
292 let price_level = price_node.get_mut();
293 while price_level.total_quantity > 0 && fill_quantity > 0 {
294 let head_idx = price_level.head;
295 let first_order_node =
296 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
297 if fill_quantity >= first_order_node.current_quantity {
298 fill_quantity -= first_order_node.current_quantity;
299 price_level.total_quantity -= first_order_node.current_quantity;
300 let next = first_order_node.next;
301 orderbook.bid.order_pool[head_idx] = None;
302 orderbook.bid.free_list.push(head_idx);
303 orders_consumed += 1;
304 if let Some(next_order_idx) = next {
305 price_level.head = next_order_idx;
306 } else {
307 span.record("reason", "partially_filled");
308 break;
309 }
310 } else {
311 first_order_node.current_quantity -= fill_quantity;
312 price_level.total_quantity -= fill_quantity;
313 fill_quantity = 0;
314 }
315 }
316 span.record("reason", "orderbook is empty");
317 span.record("filled", false);
318 remove_node = price_level.total_quantity == 0;
319 }
320 if remove_node {
321 orderbook.bid.price_map.pop_first();
322 levels_touched += 1;
323 }
324 }
325 let alloted_index = orderbook.create_sell_order(
326 order.engine_order_id,
327 OrderNode {
328 initial_quantity: order.initial_quantity,
329 current_quantity: fill_quantity,
330 market_limit: order.price.unwrap(),
331 next: None,
332 prev: None,
333 },
334 )?;
335 let order_location = OrderLocation {
336 security_id : order.security_id,
337 is_buy_side : order.is_buy_side,
338 order_index : alloted_index
339 };
340 self._global_registry.insert(order.engine_order_id, order_location);
341 span.record("order_type", "limit");
342 span.record("is_buy_side", false);
343 span.record("levels_touched", levels_touched);
344 span.record("orders_consumed", orders_consumed);
345 }
346 }
347 } else {
348 match order.order_type {
349 OrderType::Market(None) => {
350 let mut fill_quantity = order.initial_quantity;
352 let mut levels_touched = 0;
353 let mut orders_consumed = 0;
354 while fill_quantity > 0 {
355 let remove_node: bool;
356 {
357 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
358 break;
359 };
360 let price_level = price_node.get_mut();
361 while price_level.total_quantity > 0 && fill_quantity > 0 {
362 let head_idx = price_level.head;
363 let first_order_node =
364 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
365 if fill_quantity >= first_order_node.current_quantity {
366 fill_quantity -= first_order_node.current_quantity;
367 price_level.total_quantity -= first_order_node.current_quantity;
368 let next = first_order_node.next;
369 orderbook.ask.order_pool[head_idx] = None;
370 orderbook.ask.free_list.push(head_idx);
371 orders_consumed += 1;
372 if let Some(next_order_idx) = next {
373 price_level.head = next_order_idx;
374 } else {
375 span.record("reason", "exhausted");
376 break;
377 }
378 } else {
379 first_order_node.current_quantity -= fill_quantity;
380 price_level.total_quantity -= fill_quantity;
381 fill_quantity = 0;
382 span.record("filled", true);
383 }
384 }
385 span.record("reason", "orderbook is empty");
386 span.record("filled", false);
387 remove_node = price_level.total_quantity == 0;
388 }
389 if remove_node {
390 orderbook.bid.price_map.pop_last();
391 levels_touched += 1;
392 }
393 }
394 span.record("order_type", "market");
395 span.record("is_buy_side", true);
396 span.record("levels_touched", levels_touched);
397 span.record("orders_consumed", orders_consumed);
398 }
399 OrderType::Market(market_limit) => {
400 let mut fill_quantity = order.initial_quantity;
401 let mut levels_touched = 0;
402 let mut orders_consumed = 0;
403 while fill_quantity > 0 {
404 let remove_node: bool;
405 {
406 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
407 break;
408 };
409 if market_limit.unwrap() <= *price_node.key() {
410 break;
411 }
412 let price_level = price_node.get_mut();
413 while price_level.total_quantity > 0 && fill_quantity > 0 {
414 let head_idx = price_level.head;
415 let first_order_node =
416 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
417 if fill_quantity >= first_order_node.current_quantity {
418 fill_quantity -= first_order_node.current_quantity;
419 price_level.total_quantity -= first_order_node.current_quantity;
420 let next = first_order_node.next;
421 orderbook.ask.order_pool[head_idx] = None;
422 orderbook.ask.free_list.push(head_idx);
423 orders_consumed += 1;
424 if let Some(next_order_idx) = next {
425 price_level.head = next_order_idx;
426 } else {
427 span.record("reason", "exhausted");
428 break;
429 }
430 } else {
431 first_order_node.current_quantity -= fill_quantity;
432 price_level.total_quantity -= fill_quantity;
433 fill_quantity = 0;
434 span.record("filled", true);
435 }
436 }
437 span.record("reason", "orderbook is empty");
438 span.record("filled", false);
439 remove_node = price_level.total_quantity == 0;
440 }
441 if remove_node {
442 orderbook.bid.price_map.pop_last();
443 levels_touched += 1;
444 }
445 }
446 span.record("order_type", "market");
447 span.record("is_buy_side", true);
448 span.record("levels_touched", levels_touched);
449 span.record("orders_consumed", orders_consumed);
450 }
451 OrderType::Limit => {
452 let mut fill_quantity = order.initial_quantity;
453 let mut levels_touched = 0;
454 let mut orders_consumed = 0;
455 while fill_quantity > 0 {
456 let remove_node: bool;
457 {
458 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
459 break;
460 };
461 if order.price <= Some(*price_node.key()) {
462 break;
463 }
464 let price_level = price_node.get_mut();
465 while price_level.total_quantity > 0 && fill_quantity > 0 {
466 let head_idx = price_level.head;
467 let first_order_node =
468 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
469 if fill_quantity >= first_order_node.current_quantity {
470 fill_quantity -= first_order_node.current_quantity;
471 price_level.total_quantity -= first_order_node.current_quantity;
472 let next = first_order_node.next;
473 orderbook.ask.order_pool[head_idx] = None;
474 orderbook.ask.free_list.push(head_idx);
475 orders_consumed += 1;
476 if let Some(next_order_idx) = next {
477 price_level.head = next_order_idx;
478 } else {
479 span.record("reason", "partially_filled");
480 break;
481 }
482 } else {
483 first_order_node.current_quantity -= fill_quantity;
484 price_level.total_quantity -= fill_quantity;
485 fill_quantity = 0;
486 span.record("filled", false);
487 }
488 }
489 span.record("reason", "orderbook is empty");
490 span.record("filled", false);
491 remove_node = price_level.total_quantity == 0;
492 }
493 if remove_node {
494 orderbook.bid.price_map.pop_last();
495 levels_touched += 1;
496 }
497 }
498 let alloted_index = orderbook.create_buy_order(
499 order.engine_order_id,
500 OrderNode {
501 initial_quantity: order.initial_quantity,
502 current_quantity: fill_quantity,
503 market_limit: order.price.unwrap(),
504 next: None,
505 prev: None,
506 },
507 )?;
508 let order_location = OrderLocation {
509 security_id : order.security_id,
510 is_buy_side : order.is_buy_side,
511 order_index : alloted_index
512 };
513 self._global_registry.insert(order.engine_order_id, order_location);
514 span.record("order_type", "limit");
515 span.record("is_buy_side", true);
516 span.record("levels_touched", levels_touched);
517 span.record("orders_consumed", orders_consumed);
518 }
519 }
520 }
521 Ok(())
522 }
523}