1use crate::order_book::{
2 orderbook::OrderBook, types::{
3 BookDepth, CancelOrder, CancelOutcome, GlobalOrderRegistry, ModifyOrder, ModifyOutcome, NewOrder, OrderLocation, OrderNode, OrderType
4 }
5};
6use anyhow::{Context, anyhow};
7use std::collections::HashMap;
8use tracing::{Span, instrument};
9use uuid::Uuid;
10
11#[derive(Debug)]
12pub struct MatchingEngine {
13 _book: HashMap<Uuid, OrderBook>,
14 _global_registry: GlobalOrderRegistry,
15}
16
17impl MatchingEngine {
18
19 pub fn new() -> Self{
20 Self { _book: HashMap::new(), _global_registry: GlobalOrderRegistry::new() }
21 }
22
23 #[instrument(
24 name = "get_orderbook",
25 skip(self),
26 fields(
27 order_id = %global_order_id,
28 ),
29 )]
30 fn get_orderbook(
31 &mut self,
32 global_order_id: Uuid,
33 span: &Span
34 ) -> Option<(usize, bool,Uuid, &mut OrderBook)> {
35 let order_location = match self._global_registry.get_details(&global_order_id){
36 Some(location) => {
37 location
38 }
39 None => {
40 span.record("reason", "order not found in global registry");
41 return None;
42 }
43 };
44 let Some(book) = self._book.get_mut(&order_location.security_id) else {
45 span.record("reason", "orderbook doesn't exist");
46 return None;
47 };
48 Some((order_location.order_index, order_location.is_buy_side,order_location.security_id, book))
49 }
50
51 pub fn modify(
52 &mut self,
53 global_order_id: Uuid,
54 new_price: Option<u32>,
55 new_qty: Option<u32>,
56 span: &Span,
57 ) -> Result<(), anyhow::Error> {
58 let _gaurd = span.enter();
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 depth(&self, security_id : Uuid, levels_count :Option<u32>, span: &Span ) -> Result<BookDepth, anyhow::Error>{
162 let _gaurd = span.enter();
163 span.record("security_id", security_id.to_string());
164 let Some(order_book) = self._book.get(&security_id) else {
165 span.record("status", "failed");
166 span.record("reason", "orderbook doesn't exist");
167 return Err(anyhow!(""))
168 };
169 match order_book.depth(levels_count){
170 Ok(book_depth) => {
171 span.record("status", "success");
172 span.record("reason", "None");
173 Ok(book_depth)
174 },
175 Err(e) => Err(anyhow!("{}", e))
176 }
177 }
178
179 pub fn match_order(&mut self, order: NewOrder, span: &Span) -> Result<Option<usize>, anyhow::Error> {
180
181 let _gaurd = span.enter();
182
183 let orderbook = match self._book.get_mut(&order.security_id){
184 Some(orderbook) => {
185 orderbook
186 }
187 None => {
188 self._book.entry(order.security_id).or_insert(OrderBook::new(1))
189 }
190 };
191
192 if !order.is_buy_side {
193 match order.order_type {
195 OrderType::Market(None) => {
196 let mut fill_quantity = order.initial_quantity;
198 let mut levels_consumed = 0;
199 let mut orders_touched = 0;
200 while fill_quantity > 0 {
201 let remove_node: bool;
202 {
203 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
204 break;
205 };
206 let price_level = price_node.get_mut();
207 while price_level.total_quantity > 0 && fill_quantity > 0 {
208 if let Some(head_idx) = price_level.head{
209
210 let first_order_node =
211 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
212 if fill_quantity >= first_order_node.current_quantity {
213 fill_quantity -= first_order_node.current_quantity;
214 price_level.total_quantity -= first_order_node.current_quantity;
215 let next = first_order_node.next;
216 orderbook.bid.order_pool[head_idx] = None;
217 orderbook.bid.free_list.push(head_idx);
218 orders_touched += 1;
219 if let Some(next_order_idx) = next {
220 price_level.head = Some(next_order_idx);
221 } else {
222 span.record("reason", "exhausted");
223 price_level.total_quantity = 0;
224 break;
225 }
226 } else {
227 first_order_node.current_quantity -= fill_quantity;
228 price_level.total_quantity -= fill_quantity;
229 fill_quantity = 0;
230 orders_touched += 1;
231 span.record("filled", true);
232 }
233 }else {
234 break;
235 }
236 }
237 remove_node = price_level.total_quantity == 0;
238 }
239 if remove_node {
240 orderbook.bid.price_map.pop_last();
241 levels_consumed += 1;
242 }
243 }
244 span.record("order_type", "market");
245 span.record("is_buy_side", false);
246 span.record("levels_consumed", levels_consumed);
247 span.record("orders_touched", orders_touched);
248 Ok(None)
249 }
250 OrderType::Market(market_limit) => {
251 let mut fill_quantity = order.initial_quantity;
252 let mut levels_consumed = 0;
253 let mut orders_touched = 0;
254 while fill_quantity > 0 {
255 let remove_node: bool;
256 {
257 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
258 break;
259 };
260 if market_limit.unwrap() > *price_node.key() {
261 break;
262 }
263 let price_level = price_node.get_mut();
264 while price_level.total_quantity > 0 && fill_quantity > 0 {
265 if let Some(head_idx) = price_level.head{
266
267 let first_order_node =
268 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
269 if fill_quantity >= first_order_node.current_quantity {
270 fill_quantity -= first_order_node.current_quantity;
271 price_level.total_quantity -= first_order_node.current_quantity;
272 let next = first_order_node.next;
273 orderbook.bid.order_pool[head_idx] = None;
274 orderbook.bid.free_list.push(head_idx);
275 orders_touched += 1;
276 if let Some(next_order_idx) = next {
277 price_level.head = Some(next_order_idx);
278 } else {
279 span.record("reason", "exhausted");
280 price_level.total_quantity = 0;
281 break;
282 }
283 } else {
284 first_order_node.current_quantity -= fill_quantity;
285 price_level.total_quantity -= fill_quantity;
286 fill_quantity = 0;
287 orders_touched += 1;
288 span.record("filled", true);
289 }
290 }else {
291 break;
292 }
293 }
294 remove_node = price_level.total_quantity == 0;
295 }
296 if remove_node {
297 orderbook.bid.price_map.pop_last();
298 levels_consumed += 1;
299 }
300 }
301 span.record("order_type", "market");
302 span.record("is_buy_side", false);
303 span.record("levels_consumed", levels_consumed);
304 span.record("orders_touched", orders_touched);
305 Ok(None)
306 }
307 OrderType::Limit => {
308 let mut fill_quantity = order.initial_quantity;
309 let mut levels_consumed = 0;
310 let mut orders_touched = 0;
311 while fill_quantity > 0 {
312 let remove_node: bool;
313 {
314 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
315 break;
316 };
317 if order.price > Some(*price_node.key()) {
318 break;
319 }
320 let price_level = price_node.get_mut();
321 while price_level.total_quantity > 0 && fill_quantity > 0 {
322 if let Some(head_idx) = price_level.head{
323 let first_order_node = orderbook.bid.order_pool[head_idx].as_mut().unwrap();
324 if fill_quantity >= first_order_node.current_quantity {
325 fill_quantity -= first_order_node.current_quantity;
326 price_level.total_quantity -= first_order_node.current_quantity;
327 let next = first_order_node.next;
328 orderbook.bid.order_pool[head_idx] = None;
329 orderbook.bid.free_list.push(head_idx);
330 orders_touched += 1;
331 if let Some(next_order_idx) = next {
332 price_level.head = Some(next_order_idx);
333 } else {
334 span.record("reason", "partially_filled");
335 price_level.total_quantity = 0;
336 break;
337 }
338 } else {
339 first_order_node.current_quantity -= fill_quantity;
340 price_level.total_quantity -= fill_quantity;
341 fill_quantity = 0;
342 orders_touched += 1;
343 span.record("filled", true);
344 }
345 }else {
346 break;
347 }
348 }
349 remove_node = price_level.total_quantity == 0;
350 }
351 if remove_node {
352 orderbook.bid.price_map.pop_last();
353 levels_consumed += 1;
354 }
355 }
356 if fill_quantity > 0 {
357 let alloted_index = orderbook.create_sell_order(
358 order.engine_order_id,
359 OrderNode {
360 initial_quantity: order.initial_quantity,
361 current_quantity: fill_quantity,
362 market_limit: order.price.unwrap(),
363 next: None,
364 prev: None,
365 },
366 )?;
367 let order_location = OrderLocation {
368 security_id : order.security_id,
369 is_buy_side : order.is_buy_side,
370 order_index : alloted_index
371 };
372 self._global_registry.insert(order.engine_order_id, order_location);
373 span.record("order_type", "limit");
374 span.record("is_buy_side", false);
375 span.record("levels_consumed", levels_consumed);
376 span.record("orders_touched", orders_touched);
377 return Ok(Some(alloted_index))
378 }
379 Ok(None)
380 }
381 }
382 } else {
383 match order.order_type {
384 OrderType::Market(None) => {
385 let mut fill_quantity = order.initial_quantity;
387 let mut levels_consumed = 0;
388 let mut orders_touched = 0;
389 while fill_quantity > 0 {
390 let remove_node: bool;
391 {
392 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
393 break;
394 };
395 let price_level = price_node.get_mut();
396 while price_level.total_quantity > 0 && fill_quantity > 0 {
397 if let Some(head_idx) = price_level.head{
398 let first_order_node =
399 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
400 if fill_quantity >= first_order_node.current_quantity {
401 fill_quantity -= first_order_node.current_quantity;
402 price_level.total_quantity -= first_order_node.current_quantity;
403 let next = first_order_node.next;
404 orderbook.ask.order_pool[head_idx] = None;
405 orderbook.ask.free_list.push(head_idx);
406 orders_touched += 1;
407 if let Some(next_order_idx) = next {
408 price_level.head = Some(next_order_idx);
409 } else {
410 span.record("reason", "exhausted");
411 price_level.total_quantity = 0;
412 break;
413 }
414 } else {
415 first_order_node.current_quantity -= fill_quantity;
416 price_level.total_quantity -= fill_quantity;
417 fill_quantity = 0;
418 orders_touched += 1;
419 span.record("filled", true);
420 }
421 }
422 else {
423 break;
424 }
425 }
426 remove_node = price_level.total_quantity == 0;
427 }
428 if remove_node {
429 orderbook.bid.price_map.pop_first();
430 levels_consumed += 1;
431 }
432 }
433 span.record("order_type", "market");
434 span.record("is_buy_side", true);
435 span.record("levels_consumed", levels_consumed);
436 span.record("orders_touched", orders_touched);
437 Ok(None)
438 }
439 OrderType::Market(market_limit) => {
440 let mut fill_quantity = order.initial_quantity;
441 let mut levels_consumed = 0;
442 let mut orders_touched = 0;
443 while fill_quantity > 0 {
444 let remove_node: bool;
445 {
446 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
447 break;
448 };
449 if market_limit.unwrap() < *price_node.key() {
450 break;
451 }
452 let price_level = price_node.get_mut();
453 while price_level.total_quantity > 0 && fill_quantity > 0 {
454 let head_pointer = price_level.head;
455 if let Some(head_idx) = head_pointer{
456 let first_order_node =
457 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
458 if fill_quantity >= first_order_node.current_quantity {
459 fill_quantity -= first_order_node.current_quantity;
460 price_level.total_quantity -= first_order_node.current_quantity;
461 let next = first_order_node.next;
462 orderbook.ask.order_pool[head_idx] = None;
463 orderbook.ask.free_list.push(head_idx);
464 orders_touched += 1;
465 if let Some(next_order_idx) = next {
466 price_level.head = Some(next_order_idx);
467 } else {
468 span.record("reason", "exhausted");
469 price_level.head = None;
470 price_level.total_quantity = 0;
471 break;
472 }
473 } else {
474 first_order_node.current_quantity -= fill_quantity;
475 price_level.total_quantity -= fill_quantity;
476 fill_quantity = 0;
477 orders_touched += 1;
478 span.record("filled", true);
479 }
480 }
481 else {
482 break;
483 }
484 }
485 remove_node = price_level.total_quantity == 0;
486 }
487 if remove_node {
488 orderbook.bid.price_map.pop_first();
489 levels_consumed += 1;
490 }
491 }
492 span.record("order_type", "market");
493 span.record("is_buy_side", true);
494 span.record("levels_consumed", levels_consumed);
495 span.record("orders_touched", orders_touched);
496 Ok(None)
497 }
498 OrderType::Limit => {
499 let mut fill_quantity = order.initial_quantity;
500 let mut levels_consumed = 0;
501 let mut orders_touched = 0;
502 while fill_quantity > 0 {
503 let remove_node: bool;
504 {
505 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
506 break;
507 };
508 if order.price < Some(*price_node.key()) {
509 break;
510 }
511 let price_level = price_node.get_mut();
512 while price_level.total_quantity > 0 && fill_quantity > 0 {
513 if let Some(head_idx) = price_level.head{
514
515 let first_order_node = orderbook.ask.order_pool[head_idx].as_mut().unwrap();
516 if fill_quantity >= first_order_node.current_quantity {
517 fill_quantity -= first_order_node.current_quantity;
518 price_level.total_quantity -= first_order_node.current_quantity;
519 let next = first_order_node.next;
520 orderbook.ask.order_pool[head_idx] = None;
521 orderbook.ask.free_list.push(head_idx);
522 orders_touched += 1;
523 if let Some(next_order_idx) = next {
524 price_level.head = Some(next_order_idx);
525 } else {
526 span.record("reason", "partially_filled");
527 price_level.total_quantity = 0;
528 break;
529 }
530 } else {
531 first_order_node.current_quantity -= fill_quantity;
532 price_level.total_quantity -= fill_quantity;
533 fill_quantity = 0;
534 orders_touched += 1;
535 span.record("filled", true);
536 }
537 }else {
538 break;
539 }
540 }
541 remove_node = price_level.total_quantity == 0;
542 }
543 if remove_node {
544 orderbook.bid.price_map.pop_first();
545 levels_consumed += 1;
546 }
547 }
548 if fill_quantity > 0{
549 let alloted_index = orderbook.create_buy_order(
550 order.engine_order_id,
551 OrderNode {
552 initial_quantity: order.initial_quantity,
553 current_quantity: fill_quantity,
554 market_limit: order.price.unwrap(),
555 next: None,
556 prev: None,
557 },
558 )?;
559 let order_location = OrderLocation {
560 security_id : order.security_id,
561 is_buy_side : order.is_buy_side,
562 order_index : alloted_index
563 };
564 self._global_registry.insert(order.engine_order_id, order_location);
565 span.record("order_type", "limit");
566 span.record("is_buy_side", true);
567 span.record("levels_consumed", levels_consumed);
568 span.record("orders_touched", orders_touched);
569 return Ok(Some(alloted_index))
570 }
571 Ok(None)
572 }
573 }
574 }
575 }
576}