Skip to main content

apple_quant_algorithmic/
liquidity.rs

1use 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}