1use super::FeedError;
4use super::depth::{Lost, MAX_LEVELS, key};
5use super::footprint::meets_threshold;
6use crate::model::{OrderBook, OrderBookLevel, OrderSide, Tick};
7use chrono::{DateTime, TimeDelta, Utc};
8use serde::{Deserialize, Serialize};
9use std::collections::{BTreeMap, HashSet, VecDeque};
10
11#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
13#[non_exhaustive]
14pub struct RestingOrder {
15 pub id: u64,
17 pub side: OrderSide,
19 pub price: f64,
21 pub size: f64,
23 #[serde(default)]
26 pub participant: Option<u64>,
27 #[serde(default)]
30 pub placed: Option<DateTime<Utc>>,
31}
32
33impl RestingOrder {
34 pub fn new(id: u64, side: OrderSide, price: f64, size: f64) -> Self {
36 Self {
37 id,
38 side,
39 price,
40 size,
41 participant: None,
42 placed: None,
43 }
44 }
45
46 pub fn with_participant(mut self, participant: u64) -> Self {
48 self.participant = Some(participant);
49 self
50 }
51
52 pub fn with_placed(mut self, time: DateTime<Utc>) -> Self {
54 self.placed = Some(time);
55 self
56 }
57}
58
59#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
64#[serde(default)]
65pub struct OrderFilter {
66 pub min_size: f64,
69 pub min_age: Option<TimeDelta>,
73 pub max_age: Option<TimeDelta>,
79 pub exclude_participants: Vec<u64>,
81}
82
83impl OrderFilter {
84 pub fn accepts(&self, order: &RestingOrder, now: DateTime<Utc>) -> bool {
86 self.min_size.is_finite()
87 && self.min_size >= 0.0
88 && order.size >= self.min_size
89 && self.min_age.is_none_or(|age| {
90 order
91 .placed
92 .is_some_and(|placed| now - placed >= age.max(TimeDelta::zero()))
93 })
94 && self.max_age.is_none_or(|age| {
95 order
96 .placed
97 .is_some_and(|placed| now - placed < age.max(TimeDelta::zero()))
98 })
99 && order
100 .participant
101 .is_none_or(|p| !self.exclude_participants.contains(&p))
102 }
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq)]
107#[non_exhaustive]
108pub enum OrderFilterUnavailable {
109 AggregateDepth,
111 Participants,
113 PlacementTimes,
115}
116
117impl std::fmt::Display for OrderFilterUnavailable {
118 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119 f.write_str(match self {
120 Self::AggregateDepth => "the feed sends aggregate depth only",
121 Self::Participants => "no order names its participant",
122 Self::PlacementTimes => "no order has a placement time",
123 })
124 }
125}
126
127impl std::error::Error for OrderFilterUnavailable {}
128
129#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
131pub struct OrderSizeTiers {
132 pub notable: f64,
134 pub large: f64,
136}
137
138#[derive(Clone, Copy, Debug, PartialEq, Eq)]
140pub enum OrderSizeTier {
141 Notable,
143 Large,
145}
146
147impl OrderSizeTiers {
148 pub fn tier(&self, size: f64) -> Option<OrderSizeTier> {
150 if size >= self.large {
151 Some(OrderSizeTier::Large)
152 } else if size >= self.notable {
153 Some(OrderSizeTier::Notable)
154 } else {
155 None
156 }
157 }
158}
159
160#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
162#[non_exhaustive]
163pub enum OrderEvent {
164 Add(RestingOrder),
166 Modify {
169 id: u64,
171 price: Option<f64>,
173 size: Option<f64>,
175 keep_priority: bool,
177 },
178 Cancel {
180 id: u64,
182 },
183 Execute {
188 id: u64,
190 size: f64,
192 trade: Option<Tick>,
194 },
195}
196
197#[derive(Clone, Debug)]
200pub struct OrderQueue {
201 tick: Option<f64>,
202 sequence: Option<u64>,
203 requested: bool,
204 time: DateTime<Utc>,
205 orders: Vec<RestingOrder>,
206 held: VecDeque<(u64, DateTime<Utc>, OrderEvent)>,
207 executions: VecDeque<Tick>,
208}
209
210impl OrderQueue {
211 pub fn new(tick_size: Option<f64>) -> Self {
213 Self {
214 tick: tick_size.filter(|t| t.is_finite() && *t > 0.0),
215 sequence: None,
216 requested: false,
217 time: DateTime::default(),
218 orders: Vec::new(),
219 held: VecDeque::new(),
220 executions: VecDeque::new(),
221 }
222 }
223
224 pub fn sequence(&self) -> Option<u64> {
226 self.sequence
227 }
228
229 pub fn orders(&self) -> impl Iterator<Item = &RestingOrder> {
231 self.orders.iter()
232 }
233
234 pub fn queue(&self, side: OrderSide, price: f64) -> impl Iterator<Item = &RestingOrder> {
236 let price = key(self.tick, price);
237 self.orders
238 .iter()
239 .filter(move |o| o.side == side && key(self.tick, o.price) == price)
240 }
241
242 pub fn largest_order(&self, side: OrderSide, price: f64) -> Option<&RestingOrder> {
244 self.queue(side, price)
245 .reduce(|a, b| if b.size > a.size { b } else { a })
246 }
247
248 pub fn take_executions(&mut self) -> Vec<Tick> {
251 self.executions.drain(..).collect()
252 }
253
254 pub fn aggregate(&self, symbol: impl Into<String>, minimum_size: f64) -> OrderBook {
257 let filter = OrderFilter {
258 min_size: minimum_size,
259 ..OrderFilter::default()
260 };
261 self.sum(symbol.into(), &filter)
262 }
263
264 pub fn filtered(
269 &self,
270 symbol: impl Into<String>,
271 filter: &OrderFilter,
272 ) -> Result<OrderBook, OrderFilterUnavailable> {
273 if !self.orders.is_empty() {
274 if !filter.exclude_participants.is_empty()
275 && self.orders.iter().all(|o| o.participant.is_none())
276 {
277 return Err(OrderFilterUnavailable::Participants);
278 }
279 if (filter.min_age.is_some() || filter.max_age.is_some())
280 && self.orders.iter().all(|o| o.placed.is_none())
281 {
282 return Err(OrderFilterUnavailable::PlacementTimes);
283 }
284 }
285 Ok(self.sum(symbol.into(), filter))
286 }
287
288 pub fn largest_orders(&self, filter: &OrderFilter) -> Vec<&RestingOrder> {
291 let mut largest: BTreeMap<(bool, u64), &RestingOrder> = BTreeMap::new();
292 for order in self.orders.iter().filter(|o| filter.accepts(o, self.time)) {
293 let bid = order.side == OrderSide::Buy;
294 let level = largest.entry((!bid, key(self.tick, order.price)));
295 level
296 .and_modify(|held| {
297 if order.size > held.size {
298 *held = order;
299 }
300 })
301 .or_insert(order);
302 }
303 let (mut bids, asks): (Vec<_>, Vec<_>) =
304 largest.into_iter().partition(|((ask, _), _)| !ask);
305 bids.reverse();
306 bids.into_iter()
307 .chain(asks)
308 .map(|(_, order)| order)
309 .collect()
310 }
311
312 pub(crate) fn largest_levels(&self, filter: &OrderFilter) -> OrderBook {
315 let largest = self.largest_orders(filter);
316 let levels = |side| {
317 largest
318 .iter()
319 .filter(|o| o.side == side)
320 .map(|o| OrderBookLevel {
321 price: o.price,
322 size: o.size,
323 order_cnt: Some(1),
324 })
325 .collect()
326 };
327 OrderBook {
328 ts: self.time,
329 symbol: String::new(),
330 bids: levels(OrderSide::Buy),
331 asks: levels(OrderSide::Sell),
332 }
333 }
334
335 fn sum(&self, symbol: String, filter: &OrderFilter) -> OrderBook {
336 let mut bids: BTreeMap<u64, OrderBookLevel> = BTreeMap::new();
337 let mut asks: BTreeMap<u64, OrderBookLevel> = BTreeMap::new();
338 for order in self.orders.iter().filter(|o| filter.accepts(o, self.time)) {
339 let side = if order.side == OrderSide::Buy {
340 &mut bids
341 } else {
342 &mut asks
343 };
344 let level = side
345 .entry(key(self.tick, order.price))
346 .or_insert(OrderBookLevel {
347 price: order.price,
348 size: 0.0,
349 order_cnt: Some(0),
350 });
351 level.size = (level.size + order.size).min(f64::MAX);
352 level.order_cnt = level.order_cnt.map(|n| n + 1);
353 }
354 OrderBook {
355 ts: self.time,
356 symbol,
357 bids: bids.into_values().rev().collect(),
358 asks: asks.into_values().collect(),
359 }
360 }
361
362 pub fn snapshot(
365 &mut self,
366 sequence: u64,
367 time: DateTime<Utc>,
368 orders: Vec<RestingOrder>,
369 ) -> Result<(), FeedError> {
370 self.load(sequence, time, orders)
371 .map_err(|(_, error)| error)
372 }
373
374 pub fn apply(
377 &mut self,
378 sequence: u64,
379 time: DateTime<Utc>,
380 event: OrderEvent,
381 ) -> Result<(), FeedError> {
382 self.step(sequence, time, event).map_err(|(_, error)| error)
383 }
384
385 pub(crate) fn set_snapshot(
386 &mut self,
387 sequence: u64,
388 time: DateTime<Utc>,
389 orders: Vec<RestingOrder>,
390 ) -> Result<(), Lost> {
391 self.load(sequence, time, orders).map_err(|(lost, _)| lost)
392 }
393
394 pub(crate) fn event(
395 &mut self,
396 sequence: u64,
397 time: DateTime<Utc>,
398 event: OrderEvent,
399 ) -> Result<(), Lost> {
400 self.step(sequence, time, event).map_err(|(lost, _)| lost)
401 }
402
403 fn load(
404 &mut self,
405 sequence: u64,
406 time: DateTime<Utc>,
407 orders: Vec<RestingOrder>,
408 ) -> Result<(), (Lost, FeedError)> {
409 let mut ids = HashSet::new();
410 let bids = orders.iter().filter(|o| o.side == OrderSide::Buy).count();
411 let asks = orders.len() - bids;
412 let error = if bids > MAX_LEVELS {
413 Some(format!(
414 "order book snapshot has {bids} bids, more than {MAX_LEVELS}"
415 ))
416 } else if asks > MAX_LEVELS {
417 Some(format!(
418 "order book snapshot has {asks} asks, more than {MAX_LEVELS}"
419 ))
420 } else {
421 orders.iter().find_map(|order| {
422 if !order.price.is_finite() {
423 Some(format!(
424 "snapshot order {} price must be finite, got {:?}",
425 order.id, order.price
426 ))
427 } else if !order.size.is_finite() || order.size <= 0.0 {
428 Some(format!(
429 "snapshot order {} size must be finite and above zero, got {:?}",
430 order.id, order.size
431 ))
432 } else if !ids.insert(order.id) {
433 Some(format!("snapshot holds order {} twice", order.id))
434 } else {
435 None
436 }
437 })
438 };
439 if let Some(error) = error {
440 return Err((self.lose(true), FeedError::InvalidData(error)));
441 }
442 self.orders = orders;
443 self.sequence = Some(sequence);
444 self.requested = false;
445 self.time = time;
446 let held = std::mem::take(&mut self.held);
447 let mut result = Ok(());
448 for (sequence, time, event) in held {
449 result = result.and(self.step(sequence, time, event));
450 }
451 result
452 }
453
454 fn step(
455 &mut self,
456 sequence: u64,
457 time: DateTime<Utc>,
458 event: OrderEvent,
459 ) -> Result<(), (Lost, FeedError)> {
460 if self.sequence.is_some_and(|last| sequence <= last) {
461 return Ok(());
462 }
463 if self.sequence.and_then(|s| s.checked_add(1)) != Some(sequence) {
464 if self.held.iter().any(|(held, _, _)| *held == sequence) {
465 return Err((
466 Lost {
467 invalid: false,
468 request: false,
469 },
470 FeedError::InvalidData(format!(
471 "order book sequence {sequence} is already held"
472 )),
473 ));
474 }
475 let reason = match self.sequence {
476 Some(last) => format!("order book sequence {sequence} does not follow {last}"),
477 None => format!(
478 "order book has no snapshot; sequence {sequence} is held for the next one"
479 ),
480 };
481 let lost = self.lose(false);
482 if self
483 .held
484 .back()
485 .is_some_and(|(s, _, _)| s.checked_add(1) != Some(sequence))
486 {
487 self.held.clear();
488 }
489 self.held.push_back((sequence, time, event));
490 if self.held.len() > MAX_LEVELS {
491 self.held.pop_front();
492 }
493 return Err((lost, FeedError::InvalidData(reason)));
494 }
495 if let Err(error) = self.change(event) {
496 return Err((self.lose(true), error));
497 }
498 self.sequence = Some(sequence);
499 self.time = time;
500 Ok(())
501 }
502
503 fn change(&mut self, event: OrderEvent) -> Result<(), FeedError> {
504 match event {
505 OrderEvent::Add(order) => {
506 if !order.price.is_finite() {
507 return Err(FeedError::InvalidData(format!(
508 "added order {} price must be finite, got {:?}",
509 order.id, order.price
510 )));
511 }
512 if !order.size.is_finite() || order.size <= 0.0 {
513 return Err(FeedError::InvalidData(format!(
514 "added order {} size must be finite and above zero, got {:?}",
515 order.id, order.size
516 )));
517 }
518 if self.orders.iter().any(|o| o.id == order.id) {
519 return Err(FeedError::InvalidData(format!(
520 "added order {} is already in the book",
521 order.id
522 )));
523 }
524 if self.orders.iter().filter(|o| o.side == order.side).count() == MAX_LEVELS {
525 let side = if order.side == OrderSide::Buy {
526 "bid"
527 } else {
528 "ask"
529 };
530 return Err(FeedError::InvalidData(format!(
531 "added order {} exceeds the limit of {MAX_LEVELS} {side} orders",
532 order.id
533 )));
534 }
535 self.orders.push(order);
536 }
537 OrderEvent::Modify {
538 id,
539 price,
540 size,
541 keep_priority,
542 } => {
543 let at = self.orders.iter().position(|o| o.id == id).ok_or_else(|| {
544 FeedError::InvalidData(format!("modified order {id} is not in the book"))
545 })?;
546 let mut order = self.orders[at].clone();
547 let old_price = key(self.tick, order.price);
548 if let Some(price) = price {
549 order.price = price;
550 }
551 if let Some(size) = size {
552 order.size = size;
553 }
554 if !order.price.is_finite() {
555 return Err(FeedError::InvalidData(format!(
556 "modified order {id} price must be finite, got {:?}",
557 order.price
558 )));
559 }
560 if !order.size.is_finite() || order.size < 0.0 {
561 return Err(FeedError::InvalidData(format!(
562 "modified order {id} size must be finite and at least 0, got {:?}",
563 order.size
564 )));
565 }
566 if order.size == 0.0 {
567 self.orders.remove(at);
568 } else if !keep_priority || old_price != key(self.tick, order.price) {
569 self.orders.remove(at);
570 self.orders.push(order);
571 } else {
572 self.orders[at] = order;
573 }
574 }
575 OrderEvent::Cancel { id } => {
576 let at = self.orders.iter().position(|o| o.id == id).ok_or_else(|| {
577 FeedError::InvalidData(format!("cancelled order {id} is not in the book"))
578 })?;
579 self.orders.remove(at);
580 }
581 OrderEvent::Execute { id, size, trade } => {
582 let at = self.orders.iter().position(|o| o.id == id).ok_or_else(|| {
583 FeedError::InvalidData(format!("executed order {id} is not in the book"))
584 })?;
585 let order = &mut self.orders[at];
586 if !size.is_finite() || size <= 0.0 {
587 return Err(FeedError::InvalidData(format!(
588 "execution of order {id} size must be finite and above zero, got {size:?}"
589 )));
590 }
591 if !meets_threshold(order.size, size) {
592 return Err(FeedError::InvalidData(format!(
593 "execution of order {id} needs a size above zero and at most the resting {:?}, got {size:?}",
594 order.size
595 )));
596 }
597 if let Some(trade) = &trade {
598 if !trade.price.is_finite() {
599 return Err(FeedError::InvalidData(format!(
600 "trade of order {id} price must be finite, got {:?}",
601 trade.price
602 )));
603 }
604 if !trade.size.is_finite() || trade.size < 0.0 {
605 return Err(FeedError::InvalidData(format!(
606 "trade of order {id} size must be finite and at least 0, got {:?}",
607 trade.size
608 )));
609 }
610 if trade.size != size {
611 return Err(FeedError::InvalidData(format!(
612 "trade of order {id} size must equal execution size {size:?}, got {:?}",
613 trade.size
614 )));
615 }
616 if key(self.tick, trade.price) != key(self.tick, order.price) {
617 return Err(FeedError::InvalidData(format!(
618 "trade of order {id} price must share the grid level of {:?}, got {:?}",
619 order.price, trade.price
620 )));
621 }
622 }
623 if let Some(mut trade) = trade {
624 trade.resting_order_id = Some(id);
625 if self.executions.len() == MAX_LEVELS {
626 self.executions.pop_front();
627 }
628 self.executions.push_back(trade);
629 }
630 order.size = if meets_threshold(size, order.size) {
631 0.0
632 } else {
633 order.size - size
634 };
635 if order.size == 0.0 {
636 self.orders.remove(at);
637 }
638 }
639 }
640 Ok(())
641 }
642
643 fn lose(&mut self, invalid: bool) -> Lost {
644 self.sequence = None;
645 self.orders.clear();
646 Lost {
647 invalid,
648 request: !std::mem::replace(&mut self.requested, true),
649 }
650 }
651}
652
653#[cfg(test)]
654mod tests {
655 use super::*;
656 use OrderSide::{Buy, Sell};
657
658 #[test]
659 fn orders_preserve_priority_and_derive_levels() {
660 let orders = vec![
661 RestingOrder::new(1, Buy, 100.0, 3.0),
662 RestingOrder::new(2, Buy, 100.0, 5.0),
663 RestingOrder::new(3, Sell, 101.0, 7.0),
664 ];
665 #[rustfmt::skip]
666 let cases = [
667 ("partial execution", 8, OrderEvent::Execute { id: 2, size: 2.0, trade: None }, vec![(100.0, 6.0, 2)], vec![1, 2], None),
668 ("price change", 8, OrderEvent::Modify { id: 1, price: Some(99.0), size: None, keep_priority: true }, vec![(100.0, 5.0, 1), (99.0, 3.0, 1)], vec![2], None),
669 ("keeps priority", 8, OrderEvent::Modify { id: 1, price: None, size: Some(2.0), keep_priority: true }, vec![(100.0, 7.0, 2)], vec![1, 2], None),
670 ("loses priority", 8, OrderEvent::Modify { id: 1, price: None, size: Some(4.0), keep_priority: false }, vec![(100.0, 9.0, 2)], vec![2, 1], None),
671 ("invalid zero-size modify", 8, OrderEvent::Modify { id: 1, price: Some(f64::NAN), size: Some(0.0), keep_priority: true }, vec![], vec![], Some("modified order 1 price must be finite, got NaN")),
672 ("invalid add price", 8, OrderEvent::Add(RestingOrder::new(4, Buy, f64::NAN, 1.0)), vec![], vec![], Some("added order 4 price must be finite, got NaN")),
673 ("invalid add size", 8, OrderEvent::Add(RestingOrder::new(4, Buy, 100.0, 0.0)), vec![], vec![], Some("added order 4 size must be finite and above zero, got 0.0")),
674 ("duplicate add", 8, OrderEvent::Add(RestingOrder::new(1, Buy, 100.0, 1.0)), vec![], vec![], Some("added order 1 is already in the book")),
675 ("unknown modify", 8, OrderEvent::Modify { id: 99, price: None, size: Some(1.0), keep_priority: true }, vec![], vec![], Some("modified order 99 is not in the book")),
676 ("invalid modify size", 8, OrderEvent::Modify { id: 1, price: None, size: Some(-1.0), keep_priority: true }, vec![], vec![], Some("modified order 1 size must be finite and at least 0, got -1.0")),
677 ("unknown execution", 8, OrderEvent::Execute { id: 99, size: 1.0, trade: None }, vec![], vec![], Some("executed order 99 is not in the book")),
678 ("invalid execution size", 8, OrderEvent::Execute { id: 1, size: f64::NAN, trade: None }, vec![], vec![], Some("execution of order 1 size must be finite and above zero, got NaN")),
679 ("invalid trade price", 8, OrderEvent::Execute { id: 1, size: 1.0, trade: Some(Tick::new(DateTime::default(), f64::NAN, 1.0, None)) }, vec![], vec![], Some("trade of order 1 price must be finite, got NaN")),
680 ("invalid trade size", 8, OrderEvent::Execute { id: 1, size: 1.0, trade: Some(Tick::new(DateTime::default(), 100.0, -1.0, None)) }, vec![], vec![], Some("trade of order 1 size must be finite and at least 0, got -1.0")),
681 ("mismatched trade size", 8, OrderEvent::Execute { id: 1, size: 1.0, trade: Some(Tick::new(DateTime::default(), 100.0, 2.0, None)) }, vec![], vec![], Some("trade of order 1 size must equal execution size 1.0, got 2.0")),
682 ("mismatched trade price", 8, OrderEvent::Execute { id: 1, size: 1.0, trade: Some(Tick::new(DateTime::default(), 101.0, 1.0, None)) }, vec![], vec![], Some("trade of order 1 price must share the grid level of 100.0, got 101.0")),
683 ("gap", 9, OrderEvent::Cancel { id: 1 }, vec![], vec![], Some("order book sequence 9 does not follow 7")),
684 ("unknown cancel", 8, OrderEvent::Cancel { id: 99 }, vec![], vec![], Some("cancelled order 99 is not in the book")),
685 ("oversized execution", 8, OrderEvent::Execute { id: 1, size: 4.0, trade: None }, vec![], vec![], Some("execution of order 1 needs a size above zero and at most the resting 3.0, got 4.0")),
686 ];
687 for (name, seq, event, expected, queue, error) in cases {
688 let mut book = OrderQueue::new(Some(0.25));
689 book.snapshot(7, DateTime::default(), orders.clone())
690 .unwrap();
691 let result = book
692 .apply(seq, DateTime::default(), event)
693 .err()
694 .map(|e| e.to_string());
695 assert_eq!(
696 result,
697 error.map(|e| format!("invalid data: {e}")),
698 "{name}"
699 );
700 let lost = error.is_some();
701 let levels = book.aggregate("A", 0.0);
702 let bids: Vec<_> = levels
703 .bids
704 .iter()
705 .map(|l| (l.price, l.size, l.order_cnt.unwrap()))
706 .collect();
707 assert_eq!(bids, expected, "{name}");
708 assert_eq!(
709 book.queue(Buy, 100.0).map(|o| o.id).collect::<Vec<_>>(),
710 queue,
711 "{name}"
712 );
713 if !lost {
714 assert_eq!(levels.asks[0].size, 7.0);
715 }
716 }
717 for (snapshot, message) in [
718 (
719 vec![RestingOrder::new(1, Buy, f64::NAN, 1.0)],
720 "snapshot order 1 price must be finite, got NaN",
721 ),
722 (
723 vec![RestingOrder::new(1, Buy, 100.0, 0.0)],
724 "snapshot order 1 size must be finite and above zero, got 0.0",
725 ),
726 (
727 vec![orders[0].clone(), orders[0].clone()],
728 "snapshot holds order 1 twice",
729 ),
730 (
731 vec![RestingOrder::new(1, Buy, 100.0, 1.0); MAX_LEVELS + 1],
732 "order book snapshot has 10001 bids, more than 10000",
733 ),
734 (
735 vec![RestingOrder::new(1, Sell, 100.0, 1.0); MAX_LEVELS + 1],
736 "order book snapshot has 10001 asks, more than 10000",
737 ),
738 ] {
739 let mut book = OrderQueue::new(None);
740 book.snapshot(7, DateTime::default(), orders.clone())
741 .unwrap();
742 assert_eq!(
743 book.snapshot(8, DateTime::default(), snapshot)
744 .unwrap_err()
745 .to_string(),
746 format!("invalid data: {message}")
747 );
748 assert_eq!(book.sequence(), None);
749 assert_eq!(book.orders().count(), 0);
750 }
751 let mut book = OrderQueue::new(None);
752 book.snapshot(7, DateTime::default(), orders.clone())
753 .unwrap();
754 assert_eq!(book.aggregate("A", 4.0).bids[0].size, 5.0);
755 assert_eq!(book.largest_order(Buy, 100.0).unwrap().id, 2);
756 assert!(
757 book.apply(9, DateTime::default(), OrderEvent::Cancel { id: 1 })
758 .is_err()
759 );
760 book.snapshot(8, DateTime::default(), orders).unwrap();
761 assert_eq!(
762 book.queue(Buy, 100.0).map(|o| o.id).collect::<Vec<_>>(),
763 [2]
764 );
765 let full: Vec<_> = (0..MAX_LEVELS)
766 .map(|i| RestingOrder::new(i as u64, Buy, 100.0, 1.0))
767 .collect();
768 book.snapshot(10, DateTime::default(), full).unwrap();
769 assert_eq!(
770 book.apply(
771 11,
772 DateTime::default(),
773 OrderEvent::Add(RestingOrder::new(10_001, Buy, 99.0, 1.0))
774 )
775 .unwrap_err()
776 .to_string(),
777 "invalid data: added order 10001 exceeds the limit of 10000 bid orders"
778 );
779 assert_eq!(book.orders().count(), 0);
780 }
781
782 #[test]
783 fn decimal_executions_consume_the_order_without_losing_sequence() {
784 let mut book = OrderQueue::new(Some(0.25));
785 book.snapshot(
786 1,
787 DateTime::default(),
788 vec![RestingOrder::new(1, Sell, 100.0, 0.3)],
789 )
790 .unwrap();
791 for (sequence, size) in [(2, 0.1), (3, 0.2)] {
792 book.apply(
793 sequence,
794 DateTime::default(),
795 OrderEvent::Execute {
796 id: 1,
797 size,
798 trade: Some(
799 Tick::new(DateTime::default(), 100.0, size, Some(Buy)).with_id(sequence),
800 ),
801 },
802 )
803 .unwrap();
804 }
805 assert_eq!(book.sequence(), Some(3));
806 assert!(book.aggregate("ES", 0.0).asks.is_empty());
807 assert_eq!(
808 book.take_executions()
809 .iter()
810 .map(|fill| fill.size)
811 .collect::<Vec<_>>(),
812 [0.1, 0.2]
813 );
814 }
815}