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.first_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.first_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.first_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 =
313 orderbook.bid.order_pool[head_idx].as_mut().unwrap();
314 if fill_quantity >= first_order_node.current_quantity {
315 fill_quantity -= first_order_node.current_quantity;
316 price_level.total_quantity -= first_order_node.current_quantity;
317 let next = first_order_node.next;
318 orderbook.bid.order_pool[head_idx] = None;
319 orderbook.bid.free_list.push(head_idx);
320 orders_consumed += 1;
321 if let Some(next_order_idx) = next {
322 price_level.head = next_order_idx;
323 } else {
324 span.record("reason", "partially_filled");
325 break;
326 }
327 } else {
328 first_order_node.current_quantity -= fill_quantity;
329 price_level.total_quantity -= fill_quantity;
330 fill_quantity = 0;
331 }
332 }
333 span.record("reason", "orderbook is empty");
334 span.record("filled", false);
335 remove_node = price_level.total_quantity == 0;
336 }
337 if remove_node {
338 orderbook.bid.price_map.pop_first();
339 levels_touched += 1;
340 }
341 }
342 let alloted_index = orderbook.create_sell_order(
343 order.engine_order_id,
344 OrderNode {
345 initial_quantity: order.initial_quantity,
346 current_quantity: fill_quantity,
347 market_limit: order.price.unwrap(),
348 next: None,
349 prev: None,
350 },
351 )?;
352 let order_location = OrderLocation {
353 security_id : order.security_id,
354 is_buy_side : order.is_buy_side,
355 order_index : alloted_index
356 };
357 self._global_registry.insert(order.engine_order_id, order_location);
358 span.record("order_type", "limit");
359 span.record("is_buy_side", false);
360 span.record("levels_touched", levels_touched);
361 span.record("orders_consumed", orders_consumed);
362 }
363 }
364 } else {
365 match order.order_type {
366 OrderType::Market(None) => {
367 let mut fill_quantity = order.initial_quantity;
369 let mut levels_touched = 0;
370 let mut orders_consumed = 0;
371 while fill_quantity > 0 {
372 let remove_node: bool;
373 {
374 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
375 break;
376 };
377 let price_level = price_node.get_mut();
378 while price_level.total_quantity > 0 && fill_quantity > 0 {
379 let head_idx = price_level.head;
380 let first_order_node =
381 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
382 if fill_quantity >= first_order_node.current_quantity {
383 fill_quantity -= first_order_node.current_quantity;
384 price_level.total_quantity -= first_order_node.current_quantity;
385 let next = first_order_node.next;
386 orderbook.ask.order_pool[head_idx] = None;
387 orderbook.ask.free_list.push(head_idx);
388 orders_consumed += 1;
389 if let Some(next_order_idx) = next {
390 price_level.head = next_order_idx;
391 } else {
392 span.record("reason", "exhausted");
393 break;
394 }
395 } else {
396 first_order_node.current_quantity -= fill_quantity;
397 price_level.total_quantity -= fill_quantity;
398 fill_quantity = 0;
399 span.record("filled", true);
400 }
401 }
402 span.record("reason", "orderbook is empty");
403 span.record("filled", false);
404 remove_node = price_level.total_quantity == 0;
405 }
406 if remove_node {
407 orderbook.bid.price_map.pop_last();
408 levels_touched += 1;
409 }
410 }
411 span.record("order_type", "market");
412 span.record("is_buy_side", true);
413 span.record("levels_touched", levels_touched);
414 span.record("orders_consumed", orders_consumed);
415 }
416 OrderType::Market(market_limit) => {
417 let mut fill_quantity = order.initial_quantity;
418 let mut levels_touched = 0;
419 let mut orders_consumed = 0;
420 while fill_quantity > 0 {
421 let remove_node: bool;
422 {
423 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
424 break;
425 };
426 if market_limit.unwrap() <= *price_node.key() {
427 break;
428 }
429 let price_level = price_node.get_mut();
430 while price_level.total_quantity > 0 && fill_quantity > 0 {
431 let head_idx = price_level.head;
432 let first_order_node =
433 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
434 if fill_quantity >= first_order_node.current_quantity {
435 fill_quantity -= first_order_node.current_quantity;
436 price_level.total_quantity -= first_order_node.current_quantity;
437 let next = first_order_node.next;
438 orderbook.ask.order_pool[head_idx] = None;
439 orderbook.ask.free_list.push(head_idx);
440 orders_consumed += 1;
441 if let Some(next_order_idx) = next {
442 price_level.head = next_order_idx;
443 } else {
444 span.record("reason", "exhausted");
445 break;
446 }
447 } else {
448 first_order_node.current_quantity -= fill_quantity;
449 price_level.total_quantity -= fill_quantity;
450 fill_quantity = 0;
451 span.record("filled", true);
452 }
453 }
454 span.record("reason", "orderbook is empty");
455 span.record("filled", false);
456 remove_node = price_level.total_quantity == 0;
457 }
458 if remove_node {
459 orderbook.bid.price_map.pop_last();
460 levels_touched += 1;
461 }
462 }
463 span.record("order_type", "market");
464 span.record("is_buy_side", true);
465 span.record("levels_touched", levels_touched);
466 span.record("orders_consumed", orders_consumed);
467 }
468 OrderType::Limit => {
469 let mut fill_quantity = order.initial_quantity;
470 let mut levels_touched = 0;
471 let mut orders_consumed = 0;
472 while fill_quantity > 0 {
473 let remove_node: bool;
474 {
475 let Some(mut price_node) = orderbook.ask.price_map.last_entry() else {
476 break;
477 };
478 if order.price <= Some(*price_node.key()) {
479 break;
480 }
481 let price_level = price_node.get_mut();
482 while price_level.total_quantity > 0 && fill_quantity > 0 {
483 let head_idx = price_level.head;
484 let first_order_node =
485 orderbook.ask.order_pool[head_idx].as_mut().unwrap();
486 if fill_quantity >= first_order_node.current_quantity {
487 fill_quantity -= first_order_node.current_quantity;
488 price_level.total_quantity -= first_order_node.current_quantity;
489 let next = first_order_node.next;
490 orderbook.ask.order_pool[head_idx] = None;
491 orderbook.ask.free_list.push(head_idx);
492 orders_consumed += 1;
493 if let Some(next_order_idx) = next {
494 price_level.head = next_order_idx;
495 } else {
496 span.record("reason", "partially_filled");
497 break;
498 }
499 } else {
500 first_order_node.current_quantity -= fill_quantity;
501 price_level.total_quantity -= fill_quantity;
502 fill_quantity = 0;
503 span.record("filled", false);
504 }
505 }
506 span.record("reason", "orderbook is empty");
507 span.record("filled", false);
508 remove_node = price_level.total_quantity == 0;
509 }
510 if remove_node {
511 orderbook.bid.price_map.pop_last();
512 levels_touched += 1;
513 }
514 }
515 let alloted_index = orderbook.create_buy_order(
516 order.engine_order_id,
517 OrderNode {
518 initial_quantity: order.initial_quantity,
519 current_quantity: fill_quantity,
520 market_limit: order.price.unwrap(),
521 next: None,
522 prev: None,
523 },
524 )?;
525 let order_location = OrderLocation {
526 security_id : order.security_id,
527 is_buy_side : order.is_buy_side,
528 order_index : alloted_index
529 };
530 self._global_registry.insert(order.engine_order_id, order_location);
531 span.record("order_type", "limit");
532 span.record("is_buy_side", true);
533 span.record("levels_touched", levels_touched);
534 span.record("orders_consumed", orders_consumed);
535 }
536 }
537 }
538 Ok(())
539 }
540}