1use std::collections::HashMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::spot::{
6 event::{self, SpotEvent},
7 Order,
8};
9
10use super::{
11 orders::{L3Error, OrderId},
12 prices::L2Error,
13 L2, L3,
14};
15
16#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Default)]
22pub struct OrderBook {
23 pub l2: L2,
25 pub l3: L3,
27 pub fee_recipients: HashMap<Vec<u8>, Vec<u8>>,
29 pub dust: u64,
31}
32
33#[derive(Debug, thiserror::Error, PartialEq, Eq)]
34pub enum OrderBookError {
35 #[error("price is zero")]
36 PriceIsZero,
37 #[error("amount is zero")]
38 AmountIsZero,
39 #[error("public amount is zero")]
40 PublicAmountIsZero,
41 #[error("order is not owned by the sender")]
42 OrderNotOwnedBySender,
43 #[error("L3 error: {0}")]
44 L3(L3Error),
45 #[error("L2 error: {0}")]
46 L2(L2Error),
47 #[error("iceberg quantity is bigger than whole amount")]
48 IcebergQuantityIsBiggerThanWholeAmount,
49 #[error("order has expired")]
50 OrderExpired,
51 #[error("unsupported time in force is provided")]
52 UnsupportedTimeInForce,
53 #[error("order is not supported by the client id")]
54 OrderNotSupportedByClientId,
55 #[error("fill or kill order not fully filled")]
56 OrderNotFullyFilled,
57 #[error("no ask orders in the orderbook")]
58 NoAskOrdersInOrderbook,
59 #[error("no bid orders in the orderbook")]
60 NoBidOrdersInOrderbook,
61}
62
63impl From<L3Error> for OrderBookError {
64 fn from(err: L3Error) -> Self {
65 OrderBookError::L3(err)
66 }
67}
68
69impl From<L2Error> for OrderBookError {
70 fn from(err: L2Error) -> Self {
71 OrderBookError::L2(err)
72 }
73}
74
75impl OrderBook {
76 pub fn new() -> Self {
77 Self {
78 l2: L2::new(),
79 l3: L3::new(),
80 fee_recipients: HashMap::new(),
81 dust: 1000,
82 }
83 }
84
85 pub fn set_dust(&mut self, dust: u64) {
87 self.dust = dust;
88 }
89
90 pub fn get_required(
95 &self,
96 taker_order: Order,
97 price: u64,
98 amount: u64,
99 ) -> Result<u64, OrderBookError> {
100 if taker_order.is_bid {
101 Ok(amount.saturating_mul(price).saturating_div(1_0000_0000))
102 } else {
103 Ok(amount.saturating_mul(1_0000_0000).saturating_div(price))
104 }
105 }
106
107 pub fn clear_empty_head(&mut self, is_bid: bool) -> Result<u64, OrderBookError> {
109 let mut head = if is_bid {
111 self.l2.bid_head()
112 } else {
113 self.l2.ask_head()
114 };
115
116 while let Some(head_price) = head {
118 let order_id = self.l3.head(head_price);
120
121 if order_id.is_some() {
123 return Ok(head_price);
124 }
125
126 head = self.l2.clear_head(is_bid)?;
128 }
129
130 Err(OrderBookError::PriceIsZero)
132 }
133
134 pub fn clear_empty_head_or_zero(&mut self, is_bid: bool) -> u64 {
136 self.clear_empty_head(is_bid).unwrap_or(0)
137 }
138
139 pub fn pop_front(&mut self, is_bid: bool) -> Result<Order, OrderBookError> {
141 let now = std::time::SystemTime::now()
142 .duration_since(std::time::UNIX_EPOCH)
143 .unwrap()
144 .as_millis() as i64;
145 loop {
146 self.clear_empty_head(is_bid)?;
147 let head = if is_bid {
148 self.l2.bid_head()
149 } else {
150 self.l2.ask_head()
151 };
152
153 let head_price = match head {
154 Some(price) => price,
155 None => continue,
156 };
157 let head_id = self.l3.head(head_price);
158 if let Some(order_id) = head_id {
159 let order = self.l3.get_order(order_id)?;
160 if order.expires_at <= now {
162 self._expire_order(order_id, is_bid, Vec::new(), now)?;
163 continue;
164 }
165 if self.l3.is_empty(order.price) {
167 self.l2.remove_price(is_bid, order.price)?;
168 continue;
169 }
170 let (order, is_empty) = self.l3.pop_front(head_price)?;
172 if is_empty {
173 self.l2.clear_head(is_bid)?;
174 }
175 return Ok(order
176 .expect("head price must have at least one order")
177 .clone());
178 }
179 }
180 }
181
182 pub fn place_bid(
191 &mut self,
192 cid: impl Into<Vec<u8>>,
193 pair_id: impl Into<Vec<u8>>,
194 base_asset_id: impl Into<Vec<u8>>,
195 quote_asset_id: impl Into<Vec<u8>>,
196 owner: impl Into<Vec<u8>>,
197 price: u64,
198 amnt: u64,
199 iqty: u64,
200 timestamp: i64,
201 expires_at: i64,
202 maker_fee_bps: i16,
203 ) -> Result<Order, OrderBookError> {
204 let cid = cid.into();
205 let pair_id = pair_id.into();
206 let base_asset_id = base_asset_id.into();
207 let quote_asset_id = quote_asset_id.into();
208 let owner = owner.into();
209 let price = price;
210 if iqty > amnt {
211 return Err(OrderBookError::IcebergQuantityIsBiggerThanWholeAmount);
212 }
213 let pqty = amnt - iqty;
214
215 let order = self.l3.create_order(
216 cid.clone(),
217 owner.clone(),
218 true,
219 price,
220 amnt,
221 iqty,
222 timestamp,
223 expires_at,
224 maker_fee_bps,
225 )?;
226
227 event::emit_event(SpotEvent::SpotOrderPlaced {
229 cid: cid.clone(),
230 pair_id: pair_id.clone(),
231 base_asset_id: base_asset_id.clone(),
232 quote_asset_id: quote_asset_id.clone(),
233 order_id: order.id.to_bytes().to_vec(),
234 maker_account_id: owner.into(),
235 is_bid: true,
236 price: price,
237 amnt: amnt,
238 iqty: iqty,
239 pqty: pqty,
240 cqty: amnt,
241 timestamp: timestamp,
242 expires_at: expires_at,
243 });
244
245 self.update_price_level(pair_id, true, true, price, pqty, amnt, None)?;
247 Ok(order)
248 }
249
250 pub fn place_ask(
259 &mut self,
260 cid: impl Into<Vec<u8>>,
261 pair_id: impl Into<Vec<u8>>,
262 base_asset_id: impl Into<Vec<u8>>,
263 quote_asset_id: impl Into<Vec<u8>>,
264 owner: impl Into<Vec<u8>>,
265 price: u64,
266 amnt: u64,
267 iqty: u64,
268 timestamp: i64,
269 expires_at: i64,
270 maker_fee_bps: i16,
271 ) -> Result<Order, OrderBookError> {
272 let cid = cid.into();
273 let pair_id = pair_id.into();
274 let base_asset_id = base_asset_id.into();
275 let quote_asset_id = quote_asset_id.into();
276 let owner = owner.into();
277 let price = price.into();
278 let amnt = amnt.into();
279 let iqty = iqty.into();
280 if iqty > amnt {
281 return Err(OrderBookError::IcebergQuantityIsBiggerThanWholeAmount);
282 }
283 let pqty = amnt - iqty;
284
285 let order = self.l3.create_order(
286 cid.clone(),
287 owner.clone(),
288 false,
289 price,
290 amnt,
291 iqty,
292 timestamp,
293 expires_at,
294 maker_fee_bps,
295 )?;
296
297 event::emit_event(SpotEvent::SpotOrderPlaced {
299 cid: cid.clone(),
300 pair_id: pair_id.clone(),
301 base_asset_id: base_asset_id.clone(),
302 quote_asset_id: quote_asset_id.clone(),
303 order_id: order.id.to_bytes().to_vec(),
304 maker_account_id: owner,
305 is_bid: false,
306 price: price,
307 amnt: amnt,
308 iqty: iqty,
309 pqty: pqty,
310 cqty: amnt,
311 timestamp: timestamp,
312 expires_at: expires_at,
313 });
314
315 self.update_price_level(pair_id, true, false, price, pqty, amnt, None)?;
317 Ok(order)
318 }
319
320 pub fn execute(
332 &mut self,
333 taker_order: Order,
334 maker_order: Order,
335 pair_id: impl Into<Vec<u8>>,
336 base_asset_id: impl Into<Vec<u8>>,
337 quote_asset_id: impl Into<Vec<u8>>,
338 now: i64,
339 ) -> Result<(), OrderBookError> {
340 let pair_id_vec = pair_id.into();
342 let base_asset_id_vec = base_asset_id.into();
343 let quote_asset_id_vec = quote_asset_id.into();
344 let taker_is_bid = taker_order.is_bid;
345 let (matching_amount, taker_clear, maker_clear) = self._get_matching_amount(taker_order.clone(), maker_order.clone())?;
346 let matching_base_amount = if taker_is_bid {
348 matching_amount.saturating_mul(1_0000_0000).saturating_div(taker_order.price)
349 } else {
350 matching_amount
351 };
352 let matching_quote_amount = if taker_is_bid {
353 matching_amount
354 } else {
355 matching_amount.saturating_mul(taker_order.price).saturating_div(1_0000_0000)
356 };
357
358 let taker_matching_amount = if taker_is_bid { matching_quote_amount.clone() } else { matching_base_amount.clone() };
359 let maker_matching_amount = if taker_is_bid { matching_base_amount.clone() } else { matching_quote_amount.clone() };
360
361 if maker_order.expires_at <= now {
363 self._expire_order(maker_order.id, !taker_is_bid, pair_id_vec.clone(), now)?;
364 return Err(OrderBookError::OrderExpired);
366 }
367 let (_taker_delete_amount, taker_delete_price) =
368 self.l3
369 .decrease_order(taker_order.id, taker_matching_amount, self.dust, taker_clear)?;
370 let (_maker_delete_amount, maker_delete_price) =
371 self.l3
372 .decrease_order(maker_order.id, maker_matching_amount, self.dust, maker_clear)?;
373
374 let (base_fee, quote_fee) = self._calculate_fees(
376 taker_is_bid,
377 matching_base_amount,
378 matching_quote_amount,
379 maker_order.fee_bps,
380 taker_order.fee_bps,
381 );
382
383 let match_timestamp = now;
385
386 let (taker_remaining_cqty, taker_remaining_pqty) = match self.l3.get_order(taker_order.id) {
387 Ok(updated) => (updated.cqty, updated.pqty),
388 Err(_) => (0, 0),
389 };
390
391 let (maker_remaining_cqty, maker_remaining_pqty) = match self.l3.get_order(maker_order.id) {
393 Ok(updated) => (updated.cqty, updated.pqty),
394 Err(_) => (0, 0),
395 };
396 let taker_delta_cqty = taker_order.cqty.saturating_sub(taker_remaining_cqty);
397 let taker_delta_pqty = taker_order.pqty.saturating_sub(taker_remaining_pqty);
398 let maker_delta_cqty = maker_order.cqty.saturating_sub(maker_remaining_cqty);
399 let maker_delta_pqty = maker_order.pqty.saturating_sub(maker_remaining_pqty);
400 self._emit_taker_maker_match(
402 taker_order.clone(),
403 maker_order.clone(),
404 taker_remaining_cqty,
405 taker_remaining_pqty,
406 maker_remaining_cqty,
407 maker_remaining_pqty,
408 pair_id_vec.clone(),
409 base_asset_id_vec.clone(),
410 quote_asset_id_vec.clone(),
411 matching_base_amount,
412 matching_quote_amount,
413 base_fee,
414 quote_fee,
415 match_timestamp,
416 taker_order.expires_at,
417 maker_order.expires_at,
418 )?;
419
420 self.update_price_level(
424 pair_id_vec.clone(),
425 false,
426 taker_order.is_bid,
427 taker_order.price,
428 taker_delta_pqty,
429 taker_delta_cqty,
430 taker_delete_price,
431 )?;
432 self.update_price_level(
433 pair_id_vec.clone(),
434 false,
435 maker_order.is_bid,
436 maker_order.price,
437 maker_delta_pqty,
438 maker_delta_cqty,
439 maker_delete_price,
440 )?;
441
442 self._emit_order_rebates(pair_id_vec.clone(), base_asset_id_vec.clone(), quote_asset_id_vec.clone(), maker_order.clone(), taker_order.clone(), matching_base_amount, matching_quote_amount)?;
443
444 Ok(())
445 }
446
447 fn _emit_order_rebates(
448 &self,
449 pair_id: impl Into<Vec<u8>>,
450 base_asset_id: impl Into<Vec<u8>>,
451 quote_asset_id: impl Into<Vec<u8>>,
452 maker_order: Order,
453 taker_order: Order,
454 matching_base_amount: u64,
455 matching_quote_amount: u64,
456 ) -> Result<(), OrderBookError> {
457 let pair_id_vec = pair_id.into();
458 let base_asset_id_vec = base_asset_id.into();
459 let quote_asset_id_vec = quote_asset_id.into();
460
461 if maker_order.fee_bps < 0 {
463 let now = std::time::SystemTime::now()
464 .duration_since(std::time::UNIX_EPOCH)
465 .unwrap()
466 .as_millis() as i64;
467 event::emit_event(SpotEvent::SpotPairRebate {
468 pair_id: pair_id_vec.clone(),
469 base_asset_id: base_asset_id_vec.clone(),
470 quote_asset_id: quote_asset_id_vec.clone(),
471 price: maker_order.price,
472 is_bid: maker_order.is_bid,
473 account_id: maker_order.owner.clone(),
474 is_maker: true,
475 fee_bps: maker_order.fee_bps as i16,
476 base_volume: matching_base_amount,
477 quote_volume: matching_quote_amount,
478 timestamp: now,
479 });
480 }
481 if taker_order.fee_bps < 0 {
482 let now = std::time::SystemTime::now()
483 .duration_since(std::time::UNIX_EPOCH)
484 .unwrap()
485 .as_millis() as i64;
486 event::emit_event(SpotEvent::SpotPairRebate {
487 pair_id: pair_id_vec.clone(),
488 base_asset_id: base_asset_id_vec.clone(),
489 quote_asset_id: quote_asset_id_vec.clone(),
490 price: taker_order.price,
491 is_bid: taker_order.is_bid,
492 account_id: taker_order.owner.clone(),
493 is_maker: false,
494 fee_bps: taker_order.fee_bps as i16,
495 base_volume: matching_base_amount,
496 quote_volume: matching_quote_amount,
497 timestamp: now,
498 });
499 }
500 Ok(())
501 }
502
503 fn _get_matching_amount(
508 &mut self,
509 taker_order: Order,
510 maker_order: Order,
511 ) -> Result<(u64, bool, bool), OrderBookError> {
512 let taker_converted_matching_cqty = if taker_order.is_bid {
513 taker_order.cqty.saturating_mul(1_0000_0000).saturating_div(taker_order.price)
514 } else {
515 taker_order.cqty.saturating_mul(taker_order.price).saturating_div(1_0000_0000)
516 };
517 if taker_converted_matching_cqty >= maker_order.cqty {
520 let taker_matching_amount = self.get_required(maker_order.clone(), taker_order.price, maker_order.cqty)?;
522 return Ok((taker_matching_amount, false, true));
523 }
524 else if taker_converted_matching_cqty < maker_order.cqty {
526 let maker_matching_cqty = self.get_required(taker_order.clone(), maker_order.price, taker_order.cqty)?;
528 return Ok((maker_matching_cqty, true, false));
529 }
530 else {
532 return Ok((taker_order.cqty, true, true));
534 }
535 }
536
537 fn _calculate_fees(
538 &self,
539 is_bid: bool,
540 matching_base_amount: u64,
541 matching_quote_amount: u64,
542 mut maker_fee_bps: i16,
543 mut taker_fee_bps: i16,
544 ) -> (u64, u64) {
545 maker_fee_bps = if maker_fee_bps < 0 { 0 } else { maker_fee_bps };
548 taker_fee_bps = if taker_fee_bps < 0 { 0 } else { taker_fee_bps };
549 if is_bid {
550 (
551 matching_base_amount * maker_fee_bps as u64 / 10000,
552 matching_quote_amount * taker_fee_bps as u64 / 10000,
553 )
554 } else {
555 (
556 matching_base_amount * taker_fee_bps as u64 / 10000,
557 matching_quote_amount * maker_fee_bps as u64 / 10000,
558 )
559 }
560 }
561
562 fn _expire_order(
563 &mut self,
564 order_id: OrderId,
565 is_bid: bool,
566 pair_id: Vec<u8>,
567 now: i64,
568 ) -> Result<(), OrderBookError> {
569 let order = self.l3.get_order(order_id)?.clone();
570 let deleted_price_opt = self.l3.delete_order(order_id)?;
571 self.update_price_level(
573 pair_id,
574 false,
575 is_bid,
576 order.price,
577 order.pqty,
578 order.cqty,
579 deleted_price_opt,
580 )?;
581 event::emit_event(SpotEvent::SpotOrderExpired {
583 cid: order.cid.clone(),
584 order_id: order_id.to_bytes().to_vec(),
585 maker_account_id: order.owner.clone(),
586 is_bid,
587 price: order.price,
588 amnt: order.amnt,
589 iqty: order.iqty,
590 pqty: order.pqty,
591 cqty: order.cqty,
592 timestamp: now,
593 expires_at: order.expires_at,
594 });
595 Ok(())
596 }
597
598 fn _emit_taker_maker_match(
599 &self,
600 taker_order: Order,
601 maker_order: Order,
602 taker_remaining_cqty: u64,
603 taker_remaining_pqty: u64,
604 maker_remaining_cqty: u64,
605 maker_remaining_pqty: u64,
606 pair_id: impl Into<Vec<u8>>,
607 base_asset_id: impl Into<Vec<u8>>,
608 quote_asset_id: impl Into<Vec<u8>>,
609 matching_base_amount: u64,
610 matching_quote_amount: u64,
611 base_fee: u64,
612 quote_fee: u64,
613 timestamp: i64,
614 taker_expires_at: i64,
615 maker_expires_at: i64,
616 ) -> Result<(), OrderBookError> {
617 let pair_id_vec = pair_id.into();
618 let base_asset_id_vec = base_asset_id.into();
619 let quote_asset_id_vec = quote_asset_id.into();
620
621 if taker_remaining_cqty > 0 {
623 event::emit_event(SpotEvent::SpotOrderPartiallyFilled {
624 is_taker_event: true,
625 taker_cid: taker_order.cid.clone(),
626 maker_cid: maker_order.cid.clone(),
627 taker_order_id: taker_order.id.to_bytes().to_vec(),
628 maker_order_id: maker_order.id.to_bytes().to_vec(),
629 taker_account_id: taker_order.owner.clone(),
630 maker_account_id: maker_order.owner.clone(),
631 taker_order_is_bid: taker_order.is_bid,
632 maker_order_is_bid: maker_order.is_bid,
633 price: taker_order.price,
634 pair_id: pair_id_vec.clone(),
635 base_asset_id: base_asset_id_vec.clone(),
636 quote_asset_id: quote_asset_id_vec.clone(),
637 base_volume: matching_base_amount,
638 quote_volume: matching_quote_amount,
639 base_fee: base_fee,
640 quote_fee: quote_fee,
641 maker_fee_bps: maker_order.fee_bps,
642 taker_fee_bps: taker_order.fee_bps,
643 amnt: taker_order.amnt,
644 iqty: taker_order.iqty,
645 pqty: taker_remaining_pqty,
646 cqty: taker_remaining_cqty,
647 timestamp: timestamp,
648 expires_at: taker_expires_at,
649 });
650 } else {
651 event::emit_event(SpotEvent::SpotOrderFullyFilled {
652 is_taker_event: true,
653 taker_cid: taker_order.cid.clone(),
654 maker_cid: maker_order.cid.clone(),
655 taker_order_id: taker_order.id.to_bytes().to_vec(),
656 maker_order_id: maker_order.id.to_bytes().to_vec(),
657 taker_account_id: taker_order.owner.clone(),
658 maker_account_id: maker_order.owner.clone(),
659 taker_order_is_bid: taker_order.is_bid,
660 maker_order_is_bid: maker_order.is_bid,
661 price: taker_order.price,
662 pair_id: pair_id_vec.clone(),
663 base_asset_id: base_asset_id_vec.clone(),
664 quote_asset_id: quote_asset_id_vec.clone(),
665 base_volume: matching_base_amount,
666 quote_volume: matching_quote_amount,
667 base_fee: base_fee,
668 quote_fee: quote_fee,
669 maker_fee_bps: maker_order.fee_bps,
670 taker_fee_bps: taker_order.fee_bps,
671 amnt: taker_order.amnt,
672 iqty: taker_order.iqty,
673 pqty: taker_remaining_pqty,
674 cqty: taker_remaining_cqty,
675 timestamp: timestamp,
676 expires_at: taker_expires_at,
677 });
678 }
679
680 if maker_remaining_cqty > 0 {
682 event::emit_event(SpotEvent::SpotOrderPartiallyFilled {
683 is_taker_event: false,
684 taker_cid: taker_order.cid.clone(),
685 maker_cid: maker_order.cid.clone(),
686 taker_order_id: taker_order.id.to_bytes().to_vec(),
687 maker_order_id: maker_order.id.to_bytes().to_vec(),
688 taker_account_id: taker_order.owner.clone(),
689 maker_account_id: maker_order.owner.clone(),
690 taker_order_is_bid: taker_order.is_bid,
691 maker_order_is_bid: maker_order.is_bid,
692 pair_id: pair_id_vec.clone(),
693 base_asset_id: base_asset_id_vec.clone(),
694 quote_asset_id: quote_asset_id_vec.clone(),
695 price: maker_order.price,
696 base_volume: matching_base_amount,
697 quote_volume: matching_quote_amount,
698 base_fee: base_fee,
699 quote_fee: quote_fee,
700 maker_fee_bps: maker_order.fee_bps,
701 taker_fee_bps: taker_order.fee_bps,
702 amnt: maker_order.amnt,
703 iqty: maker_order.iqty,
704 pqty: maker_remaining_pqty,
705 cqty: maker_remaining_cqty,
706 timestamp: timestamp,
707 expires_at: maker_expires_at,
708 });
709 } else {
710 event::emit_event(SpotEvent::SpotOrderFullyFilled {
711 is_taker_event: false,
712 taker_cid: taker_order.cid.clone(),
713 maker_cid: maker_order.cid.clone(),
714 taker_order_id: taker_order.id.to_bytes().to_vec(),
715 maker_order_id: maker_order.id.to_bytes().to_vec(),
716 taker_account_id: taker_order.owner.clone(),
717 maker_account_id: maker_order.owner.clone(),
718 taker_order_is_bid: taker_order.is_bid,
719 maker_order_is_bid: maker_order.is_bid,
720 pair_id: pair_id_vec.clone(),
721 base_asset_id: base_asset_id_vec.clone(),
722 quote_asset_id: quote_asset_id_vec.clone(),
723 price: maker_order.price,
724 base_volume: matching_base_amount,
725 quote_volume: matching_quote_amount,
726 base_fee: base_fee,
727 quote_fee: quote_fee,
728 maker_fee_bps: maker_order.fee_bps,
729 taker_fee_bps: taker_order.fee_bps,
730 amnt: maker_order.amnt,
731 iqty: maker_order.iqty,
732 pqty: maker_remaining_pqty,
733 cqty: maker_remaining_cqty,
734 timestamp: timestamp,
735 expires_at: maker_expires_at,
736 });
737 }
738 Ok(())
739 }
740
741 pub fn update_price_level(
752 &mut self,
753 pair_id: Vec<u8>,
754 is_placed: bool,
755 is_bid: bool,
756 price: u64,
757 delta_pqty: u64,
758 delta_cqty: u64,
759 delete_price: Option<u64>,
760 ) -> Result<(), OrderBookError> {
761 if is_placed {
762 if !self.l2.price_exists(is_bid, price) {
764 self.l2.insert_price(is_bid, price)?;
765 if is_bid {
767 self.l2.set_public_bid_level(price, delta_pqty)?;
768 self.l2.set_current_bid_level(price, delta_cqty)?;
769 } else {
770 self.l2.set_public_ask_level(price, delta_pqty)?;
771 self.l2.set_current_ask_level(price, delta_cqty)?;
772 }
773 return Ok(());
774 }
775
776 let current_pqty;
778 let current_cqty;
779 if is_bid {
780 current_pqty = self.l2.public_bid_level(price).ok_or(OrderBookError::L2(
781 L2Error::PriceMissing {
782 price,
783 is_bid,
784 is_placed,
785 },
786 ))?;
787 current_cqty = self.l2.current_bid_level(price).ok_or(OrderBookError::L2(
788 L2Error::PriceMissing {
789 price,
790 is_bid,
791 is_placed,
792 },
793 ))?;
794 } else {
795 current_pqty = self.l2.public_ask_level(price).ok_or(OrderBookError::L2(
796 L2Error::PriceMissing {
797 price,
798 is_bid,
799 is_placed,
800 },
801 ))?;
802 current_cqty = self.l2.current_ask_level(price).ok_or(OrderBookError::L2(
803 L2Error::PriceMissing {
804 price,
805 is_bid,
806 is_placed,
807 },
808 ))?;
809 }
810
811 let new_pqty = current_pqty.saturating_add(delta_pqty);
813 let new_cqty = current_cqty.saturating_add(delta_cqty);
814
815 if is_bid {
816 self.l2.set_public_bid_level(price, new_pqty)?;
817 self.l2.set_current_bid_level(price, new_cqty)?;
818 } else {
819 self.l2.set_public_ask_level(price, new_pqty)?;
820 self.l2.set_current_ask_level(price, new_cqty)?;
821 }
822
823 event::emit_event(SpotEvent::SpotPriceLevelChanged {
825 pair_id,
826 is_bid,
827 price,
828 pqty: new_pqty,
829 cqty: new_cqty,
830 timestamp: std::time::SystemTime::now()
831 .duration_since(std::time::UNIX_EPOCH)
832 .unwrap()
833 .as_millis() as i64,
834 });
835 Ok(())
836 } else {
837 let current_cqty;
839 let public_cqty;
840 if is_bid {
841 public_cqty = self.l2.public_bid_level(price).ok_or(OrderBookError::L2(
842 L2Error::PriceMissing {
843 price,
844 is_bid,
845 is_placed,
846 },
847 ))?;
848 current_cqty = self.l2.current_bid_level(price).ok_or(OrderBookError::L2(
849 L2Error::PriceMissing {
850 price,
851 is_bid,
852 is_placed,
853 },
854 ))?;
855 } else {
856 public_cqty = self.l2.public_ask_level(price).ok_or(OrderBookError::L2(
857 L2Error::PriceMissing {
858 price,
859 is_bid,
860 is_placed,
861 },
862 ))?;
863 current_cqty = self.l2.current_ask_level(price).ok_or(OrderBookError::L2(
864 L2Error::PriceMissing {
865 price,
866 is_bid,
867 is_placed,
868 },
869 ))?;
870 };
871
872 let new_cqty = current_cqty.saturating_sub(delta_cqty);
875 let new_pqty = public_cqty.saturating_sub(delta_pqty);
876
877 if is_bid {
879 if new_cqty > 0 {
880 self.l2.set_public_bid_level(price, new_pqty)?;
881 self.l2.set_current_bid_level(price, new_cqty)?;
882 } else {
883 self.l2.set_public_bid_level(price, 0)?;
885 self.l2.set_current_bid_level(price, 0)?;
886 if self.l3.is_empty(price) {
888 self.l2.remove_price(is_bid, price)?;
889 }
890 }
891 } else {
892 if new_cqty > 0 {
893 self.l2.set_public_ask_level(price, new_pqty)?;
894 self.l2.set_current_ask_level(price, new_cqty)?;
895 } else {
896 self.l2.set_public_ask_level(price, 0)?;
898 self.l2.set_current_ask_level(price, 0)?;
899 if self.l3.is_empty(price) {
901 self.l2.remove_price(is_bid, price)?;
902 }
903 }
904 }
905
906 if let Some(delete_price) = delete_price {
909 self.l2.remove_price(is_bid, delete_price)?;
910 }
911
912 event::emit_event(SpotEvent::SpotPriceLevelChanged {
914 pair_id,
915 is_bid,
916 price,
917 pqty: new_pqty,
918 cqty: new_cqty,
919 timestamp: std::time::SystemTime::now()
920 .duration_since(std::time::UNIX_EPOCH)
921 .unwrap()
922 .as_millis() as i64,
923 });
924
925 Ok(())
926 }
927 }
928
929 pub fn cancel_order(
934 &mut self,
935 cid: impl Into<Vec<u8>>,
936 pair_id: impl Into<Vec<u8>>,
937 is_bid: bool,
938 order_id: OrderId,
939 owner: impl Into<Vec<u8>>,
940 ) -> Result<(), OrderBookError> {
941 let cid = cid.into();
942 let pair_id = pair_id.into();
943 let owner = owner.into();
944 let order = self.l3.get_order(order_id)?.clone();
946 if order.owner != owner {
948 return Err(OrderBookError::OrderNotOwnedBySender);
949 }
950 let deleted_price_opt = self.l3.delete_order(order_id)?;
951 if let Some(price) = deleted_price_opt {
952 self.l2.remove_price(is_bid, price)?;
953 }
954
955 event::emit_event(SpotEvent::SpotOrderCancelled {
957 cid: cid.clone(),
958 order_id: order_id.to_bytes().to_vec(),
959 maker_account_id: order.owner.clone(),
960 is_bid,
961 price: order.price,
962 amnt: order.amnt,
963 iqty: order.iqty,
964 pqty: order.pqty,
965 cqty: order.cqty,
966 timestamp: order.timestamp,
967 expires_at: order.expires_at,
968 });
969
970 self.update_price_level(
972 pair_id.clone(),
973 false,
974 is_bid,
975 order.price,
976 order.pqty,
977 order.cqty,
978 deleted_price_opt,
979 )?;
980 Ok(())
981 }
982
983 pub fn expire_orders(
984 &mut self,
985 is_bid: bool,
986 pair_id: impl Into<Vec<u8>>,
987 base_asset_id: impl Into<Vec<u8>>,
988 quote_asset_id: impl Into<Vec<u8>>,
989 managing_account_id: impl Into<Vec<u8>>,
990 now: i64,
991 ) -> Result<(), OrderBookError> {
992 let pair_id = pair_id.into();
993 let base_asset_id = base_asset_id.into();
994 let quote_asset_id = quote_asset_id.into();
995 let managing_account_id = managing_account_id.into();
996 let expired_orders = self.l3.remove_dormant_orders(now);
997 for (order_id, order) in expired_orders {
998 event::emit_event(SpotEvent::SpotOrderExpired {
1000 cid: order.cid.clone(),
1001 order_id: order_id.to_bytes().to_vec(),
1002 maker_account_id: order.owner.clone(),
1003 is_bid,
1004 price: order.price,
1005 amnt: order.amnt,
1006 iqty: order.iqty,
1007 pqty: order.pqty,
1008 cqty: order.cqty,
1009 timestamp: now,
1010 expires_at: order.expires_at,
1011 });
1012 let expired_asset_id = if is_bid {
1014 quote_asset_id.clone()
1015 } else {
1016 base_asset_id.clone()
1017 };
1018 event::emit_event(SpotEvent::Transfer {
1019 cid: order.cid.clone(),
1020 from: managing_account_id.clone(),
1021 to: order.owner.clone(),
1022 asset: expired_asset_id,
1023 amnt: order.amnt,
1024 timestamp: now,
1025 });
1026
1027 let delete_price = if self.l3.is_empty(order.price) {
1029 Some(order.price)
1030 } else {
1031 None
1032 };
1033 self.update_price_level(
1034 pair_id.clone(),
1035 false,
1036 is_bid,
1037 order.price,
1038 order.pqty,
1039 order.cqty,
1040 delete_price,
1041 )?;
1042 }
1043 Ok(())
1044 }
1045
1046 pub fn set_iceberg_quantity(
1047 &mut self,
1048 cid: impl Into<Vec<u8>>,
1049 pair_id: impl Into<Vec<u8>>,
1050 is_bid: bool,
1051 order_id: OrderId,
1052 iqty: u64,
1053 ) -> Result<(), OrderBookError> {
1054 let cid = cid.into();
1055 let pair_id = pair_id.into();
1056 let before = self.l3.get_order(order_id)?.clone();
1057 let order = self.l3.set_iceberg_quantity(order_id, iqty)?;
1058 let (is_placed, delta_pqty) = if order.pqty >= before.pqty {
1059 (true, order.pqty - before.pqty)
1060 } else {
1061 (false, before.pqty - order.pqty)
1062 };
1063 if delta_pqty > 0 {
1064 self.update_price_level(
1065 pair_id,
1066 is_placed,
1067 is_bid,
1068 order.price,
1069 delta_pqty,
1070 0,
1071 None,
1072 )?;
1073 }
1074 event::emit_event(SpotEvent::SpotOrderIcebergQuantityChanged {
1076 cid,
1077 order_id: order_id.to_bytes().to_vec(),
1078 amnt: order.amnt,
1079 iqty: iqty,
1080 pqty: order.pqty,
1081 cqty: order.cqty,
1082 timestamp: order.timestamp,
1083 expires_at: order.expires_at,
1084 });
1085 Ok(())
1086 }
1087}