apple_quant_algorithmic/
liquidity.rs1use crate::{
2 aggregation::TradeTradeTimestamp,
3 instrument::InstrumentSpec,
4 price::{AskPriceVolumeLevel, BidPriceVolumeLevel},
5};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
8struct Levels<IS: InstrumentSpec> {
9 lowest_bid_level: BidPriceVolumeLevel<IS>,
10 highest_ask_level: AskPriceVolumeLevel<IS>,
11}
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub struct LiquidityEstimation<IS: InstrumentSpec>(Option<Levels<IS>>);
15
16impl<IS: InstrumentSpec> LiquidityEstimation<IS> {
17 pub fn reset(&mut self) {
18 self.0 = None;
19 }
20
21 pub fn collect_furthest_bid_ask(
22 &mut self
23 ) -> Option<(
24 BidPriceVolumeLevel<IS>,
25 AskPriceVolumeLevel<IS>,
26 )> {
27 let Levels {
28 highest_ask_level,
29 lowest_bid_level,
30 } = self.0.take()?;
31
32 Some((
33 lowest_bid_level,
34 highest_ask_level,
35 ))
36 }
37
38 pub fn furthest_bid_ask(
39 &self
40 ) -> Option<(
41 &BidPriceVolumeLevel<IS>,
42 &AskPriceVolumeLevel<IS>,
43 )> {
44 let Some(Levels {
45 lowest_bid_level,
46 highest_ask_level,
47 }) = &self.0
48 else {
49 return None;
50 };
51
52 Some((
53 lowest_bid_level,
54 highest_ask_level,
55 ))
56 }
57
58 pub fn walk_trades<'a>(
59 &mut self,
60 mut trades: impl Iterator<Item = &'a TradeTradeTimestamp<IS>>,
61 ) where
62 IS: 'a,
63 {
64 let Some(first_trade_trade_timestamp) = trades.next() else {
65 return;
66 };
67
68 if self.0.is_none() {
69 let highest_ask_level = AskPriceVolumeLevel {
70 price: first_trade_trade_timestamp
71 .trade()
72 .price,
73 directionless_volume: *first_trade_trade_timestamp
74 .trade()
75 .aggressive_volume
76 .as_directionless_volume(),
77 };
78
79 let lowest_bid_level = BidPriceVolumeLevel {
80 price: highest_ask_level.price,
81 directionless_volume: highest_ask_level.directionless_volume,
82 };
83
84 self.0 = Some(Levels {
85 highest_ask_level,
86 lowest_bid_level,
87 });
88 }
89
90 let levels = self.0.as_mut().unwrap();
91
92 for trade_trade_timestamp in trades {
93 let trade_price = &trade_trade_timestamp
94 .trade()
95 .price;
96
97 let trade_aggressive_volume = &trade_trade_timestamp
98 .trade()
99 .aggressive_volume;
100
101 if trade_price == &levels.highest_ask_level.price {
102 levels
103 .highest_ask_level
104 .directionless_volume += *trade_aggressive_volume.as_directionless_volume();
105 } else if trade_price > &levels.highest_ask_level.price {
106 levels.highest_ask_level.price = *trade_price;
107 levels
108 .highest_ask_level
109 .directionless_volume = *trade_aggressive_volume.as_directionless_volume();
110 }
111
112 if trade_price == &levels.lowest_bid_level.price {
113 levels
114 .lowest_bid_level
115 .directionless_volume += *trade_aggressive_volume.as_directionless_volume();
116 } else if trade_price < &levels.lowest_bid_level.price {
117 levels.lowest_bid_level.price = *trade_price;
118 levels
119 .lowest_bid_level
120 .directionless_volume = *trade_aggressive_volume.as_directionless_volume();
121 }
122 }
123 }
124}
125
126impl<IS: InstrumentSpec> Default for LiquidityEstimation<IS> {
127 fn default() -> Self {
128 Self(None)
129 }
130}