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