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