wickra_core/indicators/
microprice.rs1use crate::microstructure::OrderBook;
4use crate::traits::Indicator;
5
6#[derive(Debug, Clone, Default)]
38pub struct Microprice {
39 has_emitted: bool,
40}
41
42impl Microprice {
43 pub const fn new() -> Self {
45 Self { has_emitted: false }
46 }
47}
48
49impl Indicator for Microprice {
50 type Input = OrderBook;
51 type Output = f64;
52
53 fn update(&mut self, book: OrderBook) -> Option<f64> {
54 self.has_emitted = true;
55 let (Some(bid), Some(ask)) = (book.best_bid(), book.best_ask()) else {
56 return Some(0.0);
57 };
58 let total = bid.size + ask.size;
59 if total <= 0.0 {
60 return Some(f64::midpoint(bid.price, ask.price));
61 }
62 Some((bid.price * ask.size + ask.price * bid.size) / total)
63 }
64
65 fn reset(&mut self) {
66 self.has_emitted = false;
67 }
68
69 fn warmup_period(&self) -> usize {
70 1
71 }
72
73 fn is_ready(&self) -> bool {
74 self.has_emitted
75 }
76
77 fn name(&self) -> &'static str {
78 "Microprice"
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use crate::microstructure::Level;
86 use crate::traits::BatchExt;
87
88 fn book(bids: &[(f64, f64)], asks: &[(f64, f64)]) -> OrderBook {
89 let to_levels = |xs: &[(f64, f64)]| {
90 xs.iter()
91 .map(|&(p, s)| Level::new(p, s).unwrap())
92 .collect::<Vec<_>>()
93 };
94 OrderBook::new(to_levels(bids), to_levels(asks)).unwrap()
95 }
96
97 #[test]
98 fn accessors_and_metadata() {
99 let mp = Microprice::new();
100 assert_eq!(mp.name(), "Microprice");
101 assert_eq!(mp.warmup_period(), 1);
102 assert!(!mp.is_ready());
103 }
104
105 #[test]
106 fn weights_toward_thin_side() {
107 let mut mp = Microprice::new();
108 assert_eq!(
110 mp.update(book(&[(100.0, 1.0)], &[(101.0, 3.0)])),
111 Some(100.25)
112 );
113 assert!(mp.is_ready());
114 }
115
116 #[test]
117 fn balanced_top_equals_mid() {
118 let mut mp = Microprice::new();
119 assert_eq!(
120 mp.update(book(&[(100.0, 2.0)], &[(101.0, 2.0)])),
121 Some(100.5)
122 );
123 }
124
125 #[test]
126 fn zero_size_falls_back_to_mid() {
127 let mut mp = Microprice::new();
128 assert_eq!(
129 mp.update(book(&[(100.0, 0.0)], &[(102.0, 0.0)])),
130 Some(101.0)
131 );
132 }
133
134 #[test]
135 fn empty_book_is_zero() {
136 let mut mp = Microprice::new();
137 assert_eq!(
138 mp.update(OrderBook::new_unchecked(vec![], vec![])),
139 Some(0.0)
140 );
141 }
142
143 #[test]
144 fn batch_equals_streaming() {
145 let books: Vec<OrderBook> = (0..20)
146 .map(|i| {
147 let ask = 1.0 + f64::from(i % 4);
148 book(&[(100.0, 2.0)], &[(101.0, ask)])
149 })
150 .collect();
151 let mut a = Microprice::new();
152 let mut b = Microprice::new();
153 assert_eq!(
154 a.batch(&books),
155 books
156 .iter()
157 .map(|x| b.update(x.clone()))
158 .collect::<Vec<_>>()
159 );
160 }
161
162 #[test]
163 fn reset_clears_state() {
164 let mut mp = Microprice::new();
165 mp.update(book(&[(100.0, 1.0)], &[(101.0, 1.0)]));
166 assert!(mp.is_ready());
167 mp.reset();
168 assert!(!mp.is_ready());
169 }
170}