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 let head_idx = price_level.head;
208 let first_order_node =
209 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
210 if fill_quantity >= first_order_node.current_quantity {
211 fill_quantity -= first_order_node.current_quantity;
212 price_level.total_quantity -= first_order_node.current_quantity;
213 let next = first_order_node.next;
214 orderbook.bid.order_pool[head_idx] = None;
215 orderbook.bid.free_list.push(head_idx);
216 orders_consumed += 1;
217 if let Some(next_order_idx) = next {
218 price_level.head = next_order_idx;
219 } else {
220 span.record("reason", "exhausted");
221 break;
222 }
223 } else {
224 first_order_node.current_quantity -= fill_quantity;
225 price_level.total_quantity -= fill_quantity;
226 fill_quantity = 0;
227 span.record("filled", true);
228 }
229 }
230 span.record("reason", "orderbook is empty");
231 span.record("filled", false);
232 remove_node = price_level.total_quantity == 0;
233 }
234 if remove_node {
235 orderbook.bid.price_map.pop_first();
236 levels_touched += 1;
237 }
238 }
239 span.record("order_type", "market");
240 span.record("is_buy_side", false);
241 span.record("levels_touched", levels_touched);
242 span.record("orders_consumed", orders_consumed);
243 }
244 OrderType::Market(market_limit) => {
245 let mut fill_quantity = order.initial_quantity;
246 let mut levels_touched = 0;
247 let mut orders_consumed = 0;
248 while fill_quantity > 0 {
249 let remove_node: bool;
250 {
251 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
252 break;
253 };
254 if market_limit.unwrap() > *price_node.key() {
255 break;
256 }
257 let price_level = price_node.get_mut();
258 while price_level.total_quantity > 0 && fill_quantity > 0 {
259 let head_idx = price_level.head;
260 let first_order_node =
261 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
262 if fill_quantity >= first_order_node.current_quantity {
263 fill_quantity -= first_order_node.current_quantity;
264 price_level.total_quantity -= first_order_node.current_quantity;
265 let next = first_order_node.next;
266 orderbook.bid.order_pool[head_idx] = None;
267 orderbook.bid.free_list.push(head_idx);
268 orders_consumed += 1;
269 if let Some(next_order_idx) = next {
270 price_level.head = next_order_idx;
271 } else {
272 span.record("reason", "exhausted");
273 break;
274 }
275 } else {
276 first_order_node.current_quantity -= fill_quantity;
277 price_level.total_quantity -= fill_quantity;
278 fill_quantity = 0;
279 span.record("filled", true);
280 }
281 }
282 span.record("reason", "orderbook is empty");
283 span.record("filled", false);
284 remove_node = price_level.total_quantity == 0;
285 }
286 if remove_node {
287 orderbook.bid.price_map.pop_first();
288 levels_touched += 1;
289 }
290 }
291 span.record("order_type", "market");
292 span.record("is_buy_side", false);
293 span.record("levels_touched", levels_touched);
294 span.record("orders_consumed", orders_consumed);
295 }
296 OrderType::Limit => {
297 let mut fill_quantity = order.initial_quantity;
298 let mut levels_touched = 0;
299 let mut orders_consumed = 0;
300 while fill_quantity > 0 {
301 let remove_node: bool;
302 {
303 let Some(mut price_node) = orderbook.bid.price_map.last_entry() else {
304 break;
305 };
306 if order.price > Some(*price_node.key()) {
307 break;
308 }
309 let price_level = price_node.get_mut();
310 while price_level.total_quantity > 0 && fill_quantity > 0 {
311 let head_idx = price_level.head;
312 let first_order_node = orderbook.bid.order_pool[head_idx].as_mut().unwrap();
313 if fill_quantity >= first_order_node.current_quantity {
314 fill_quantity -= first_order_node.current_quantity;
315 price_level.total_quantity -= first_order_node.current_quantity;
316 let next = first_order_node.next;
317 orderbook.bid.order_pool[head_idx] = None;
318 orderbook.bid.free_list.push(head_idx);
319 orders_consumed += 1;
320 if let Some(next_order_idx) = next {
321 price_level.head = next_order_idx;
322 } else {
323 span.record("reason", "partially_filled");
324 break;
325 }
326 } else {
327 first_order_node.current_quantity -= fill_quantity;
328 price_level.total_quantity -= fill_quantity;
329 fill_quantity = 0;
330 }
331 }
332 span.record("reason", "orderbook is empty");
333 span.record("filled", false);
334 remove_node = price_level.total_quantity == 0;
335 }
336 if remove_node {
337 orderbook.bid.price_map.pop_first();
338 levels_touched += 1;
339 }
340 }
341 let alloted_index = orderbook.create_sell_order(
342 order.engine_order_id,
343 OrderNode {
344 initial_quantity: order.initial_quantity,
345 current_quantity: fill_quantity,
346 market_limit: order.price.unwrap(),
347 next: None,
348 prev: None,
349 },
350 )?;
351 let order_location = OrderLocation {
352 security_id : order.security_id,
353 is_buy_side : order.is_buy_side,
354 order_index : alloted_index
355 };
356 self._global_registry.insert(order.engine_order_id, order_location);
357 span.record("order_type", "limit");
358 span.record("is_buy_side", false);
359 span.record("levels_touched", levels_touched);
360 span.record("orders_consumed", orders_consumed);
361 }
362 }
363 } else {
364 match order.order_type {
365 OrderType::Market(None) => {
366 let mut fill_quantity = order.initial_quantity;
368 let mut levels_touched = 0;
369 let mut orders_consumed = 0;
370 while fill_quantity > 0 {
371 let remove_node: bool;
372 {
373 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
374 break;
375 };
376 let price_level = price_node.get_mut();
377 while price_level.total_quantity > 0 && fill_quantity > 0 {
378 let head_idx = price_level.head;
379 let first_order_node =
380 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
381 if fill_quantity >= first_order_node.current_quantity {
382 fill_quantity -= first_order_node.current_quantity;
383 price_level.total_quantity -= first_order_node.current_quantity;
384 let next = first_order_node.next;
385 orderbook.ask.order_pool[head_idx] = None;
386 orderbook.ask.free_list.push(head_idx);
387 orders_consumed += 1;
388 if let Some(next_order_idx) = next {
389 price_level.head = next_order_idx;
390 } else {
391 span.record("reason", "exhausted");
392 break;
393 }
394 } else {
395 first_order_node.current_quantity -= fill_quantity;
396 price_level.total_quantity -= fill_quantity;
397 fill_quantity = 0;
398 span.record("filled", true);
399 }
400 }
401 span.record("reason", "orderbook is empty");
402 span.record("filled", false);
403 remove_node = price_level.total_quantity == 0;
404 }
405 if remove_node {
406 orderbook.bid.price_map.pop_last();
407 levels_touched += 1;
408 }
409 }
410 span.record("order_type", "market");
411 span.record("is_buy_side", true);
412 span.record("levels_touched", levels_touched);
413 span.record("orders_consumed", orders_consumed);
414 }
415 OrderType::Market(market_limit) => {
416 let mut fill_quantity = order.initial_quantity;
417 let mut levels_touched = 0;
418 let mut orders_consumed = 0;
419 while fill_quantity > 0 {
420 let remove_node: bool;
421 {
422 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
423 break;
424 };
425 if market_limit.unwrap() < *price_node.key() {
426 break;
427 }
428 let price_level = price_node.get_mut();
429 while price_level.total_quantity > 0 && fill_quantity > 0 {
430 let head_idx = price_level.head;
431 let first_order_node =
432 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
433 if fill_quantity >= first_order_node.current_quantity {
434 fill_quantity -= first_order_node.current_quantity;
435 price_level.total_quantity -= first_order_node.current_quantity;
436 let next = first_order_node.next;
437 orderbook.ask.order_pool[head_idx] = None;
438 orderbook.ask.free_list.push(head_idx);
439 orders_consumed += 1;
440 if let Some(next_order_idx) = next {
441 price_level.head = next_order_idx;
442 } else {
443 span.record("reason", "exhausted");
444 break;
445 }
446 } else {
447 first_order_node.current_quantity -= fill_quantity;
448 price_level.total_quantity -= fill_quantity;
449 fill_quantity = 0;
450 span.record("filled", true);
451 }
452 }
453 span.record("reason", "orderbook is empty");
454 span.record("filled", false);
455 remove_node = price_level.total_quantity == 0;
456 }
457 if remove_node {
458 orderbook.bid.price_map.pop_last();
459 levels_touched += 1;
460 }
461 }
462 span.record("order_type", "market");
463 span.record("is_buy_side", true);
464 span.record("levels_touched", levels_touched);
465 span.record("orders_consumed", orders_consumed);
466 }
467 OrderType::Limit => {
468 let mut fill_quantity = order.initial_quantity;
469 let mut levels_touched = 0;
470 let mut orders_consumed = 0;
471 while fill_quantity > 0 {
472 let remove_node: bool;
473 {
474 let Some(mut price_node) = orderbook.ask.price_map.first_entry() else {
475 break;
476 };
477 if order.price < Some(*price_node.key()) {
478 break;
479 }
480 let price_level = price_node.get_mut();
481 while price_level.total_quantity > 0 && fill_quantity > 0 {
482 let head_idx = price_level.head;
483 let first_order_node =
484 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
485 if fill_quantity >= first_order_node.current_quantity {
486 fill_quantity -= first_order_node.current_quantity;
487 price_level.total_quantity -= first_order_node.current_quantity;
488 let next = first_order_node.next;
489 orderbook.ask.order_pool[head_idx] = None;
490 orderbook.ask.free_list.push(head_idx);
491 orders_consumed += 1;
492 if let Some(next_order_idx) = next {
493 price_level.head = next_order_idx;
494 } else {
495 span.record("reason", "partially_filled");
496 break;
497 }
498 } else {
499 first_order_node.current_quantity -= fill_quantity;
500 price_level.total_quantity -= fill_quantity;
501 fill_quantity = 0;
502 span.record("filled", false);
503 }
504 }
505 span.record("reason", "orderbook is empty");
506 span.record("filled", false);
507 remove_node = price_level.total_quantity == 0;
508 }
509 if remove_node {
510 orderbook.bid.price_map.pop_last();
511 levels_touched += 1;
512 }
513 }
514 let alloted_index = orderbook.create_buy_order(
515 order.engine_order_id,
516 OrderNode {
517 initial_quantity: order.initial_quantity,
518 current_quantity: fill_quantity,
519 market_limit: order.price.unwrap(),
520 next: None,
521 prev: None,
522 },
523 )?;
524 let order_location = OrderLocation {
525 security_id : order.security_id,
526 is_buy_side : order.is_buy_side,
527 order_index : alloted_index
528 };
529 self._global_registry.insert(order.engine_order_id, order_location);
530 span.record("order_type", "limit");
531 span.record("is_buy_side", true);
532 span.record("levels_touched", levels_touched);
533 span.record("orders_consumed", orders_consumed);
534 }
535 }
536 }
537 Ok(())
538 }
539}