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