Skip to main content

betfair_stream_api/cache/primitives/
available_cache.rs

1//! Inspired by  [this source](https://github.com/betcode-org/betfair/blob/1ece2bf0ffede3a41bf14ba4ea1c7004f25964dd/betfairlightweight/streaming/cache.py)
2
3use alloc::collections::BTreeMap;
4
5use betfair_adapter::betfair_types::price::Price;
6use betfair_adapter::betfair_types::size::Size;
7use betfair_stream_types::response::{Position, UpdateSet2, UpdateSet3};
8use serde::de::DeserializeOwned;
9use serde::{Deserialize, Serialize};
10
11/// Data structure to hold prices/traded amount
12#[derive(Debug, PartialEq, PartialOrd, Clone, Serialize, Deserialize, Eq, Hash, Ord)]
13pub struct Available<T: UpdateSet> {
14    pub book: BTreeMap<T::Key, T::Value>,
15}
16
17impl<T: UpdateSet> Available<T> {
18    pub fn new<A: AsRef<[T]>>(prices: A) -> Self {
19        let mut instance = Self {
20            book: BTreeMap::new(),
21        };
22
23        instance.update(prices);
24        instance
25    }
26
27    pub fn update<A: AsRef<[T]>>(&mut self, book_update: A) {
28        for prices in book_update.as_ref() {
29            let key = prices.key(); // this is either `price` or `position`
30            let value = prices.value(); // this is either `(price, size)` or `size`
31
32            // If the "key" is zero, then we need to delete the item
33            if prices.should_be_deleted() {
34                self.book.remove(&key);
35            } else {
36                self.book.insert(key, value);
37            }
38        }
39    }
40
41    pub fn clear(&mut self) {
42        self.book.clear();
43    }
44}
45
46/// Helper trait fro working with fields that have either 2 or 3 tuple elements (present in the
47/// Stream API ladder)
48pub trait UpdateSet {
49    type Key: core::hash::Hash + PartialEq + Eq + Ord + Serialize + DeserializeOwned;
50    type Value: PartialEq + Serialize + DeserializeOwned;
51    fn value(&self) -> Self::Value;
52    fn key(&self) -> Self::Key;
53    fn should_be_deleted(&self) -> bool;
54}
55
56impl UpdateSet for UpdateSet2 {
57    type Key = Price;
58    type Value = Size;
59
60    fn value(&self) -> Self::Value {
61        self.1
62    }
63
64    fn key(&self) -> Self::Key {
65        self.0
66    }
67
68    fn should_be_deleted(&self) -> bool {
69        self.1 == Size::zero()
70    }
71}
72
73impl UpdateSet for UpdateSet3 {
74    type Key = Position;
75    type Value = (Price, Size);
76
77    fn value(&self) -> Self::Value {
78        (self.1, self.2)
79    }
80
81    fn key(&self) -> Self::Key {
82        self.0
83    }
84
85    fn should_be_deleted(&self) -> bool {
86        self.2 == Size::zero()
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use betfair_adapter::betfair_types::{num, num_u8};
93    use pretty_assertions::assert_eq;
94
95    use super::*;
96
97    fn setup_set3() -> Available<UpdateSet3> {
98        let prices = &[
99            UpdateSet3(
100                Position(num_u8!(1)),
101                Price::new(num!(1.02)).unwrap(),
102                Size::new(num!(34.45)),
103            ),
104            UpdateSet3(
105                Position(num_u8!(0)),
106                Price::new(num!(1.01)).unwrap(),
107                Size::new(num!(12)),
108            ),
109        ];
110        Available::new(prices)
111    }
112
113    #[test]
114    fn test_init() {
115        let init = setup_set3();
116
117        let mut expected = BTreeMap::new();
118        expected.insert(
119            Position(num_u8!(0)),
120            (Price::new(num!(1.01)).unwrap(), Size::new(num!(12))),
121        );
122        expected.insert(
123            Position(num_u8!(1)),
124            (Price::new(num!(1.02)).unwrap(), Size::new(num!(34.45))),
125        );
126
127        assert_eq!(init.book, expected);
128    }
129
130    #[test]
131    fn test_init_2() {
132        let prices = &[
133            UpdateSet2(Price::new(num!(27)).unwrap(), Size::new(num!(0.95))),
134            UpdateSet2(Price::new(num!(13)).unwrap(), Size::new(num!(28.01))),
135            UpdateSet2(Price::new(num!(1.02)).unwrap(), Size::new(num!(1157.21))),
136        ];
137        let init = Available::new(prices);
138
139        let mut expected = BTreeMap::new();
140        expected.insert(Price::new(num!(1.02)).unwrap(), Size::new(num!(1157.21)));
141        expected.insert(Price::new(num!(13)).unwrap(), Size::new(num!(28.01)));
142        expected.insert(Price::new(num!(27)).unwrap(), Size::new(num!(0.95)));
143
144        assert_eq!(init.book, expected);
145    }
146
147    #[test]
148    fn test_clear() {
149        let mut init = setup_set3();
150        init.clear();
151
152        assert_eq!(init.book, BTreeMap::new());
153    }
154
155    #[test]
156    fn test_update_set_2() {
157        let init = Available::new([
158            UpdateSet2(Price::new(num!(27)).unwrap(), Size::new(num!(0.95))),
159            UpdateSet2(Price::new(num!(13)).unwrap(), Size::new(num!(28.01))),
160            UpdateSet2(Price::new(num!(1.02)).unwrap(), Size::new(num!(1157.21))),
161        ]);
162        let update = &[UpdateSet2(
163            Price::new(num!(27)).unwrap(),
164            Size::new(num!(2)),
165        )];
166        let mut expected = BTreeMap::new();
167        expected.insert(Price::new(num!(1.02)).unwrap(), Size::new(num!(1157.21)));
168        expected.insert(Price::new(num!(13)).unwrap(), Size::new(num!(28.01)));
169        expected.insert(Price::new(num!(27)).unwrap(), Size::new(num!(2)));
170
171        let mut actual = init;
172        actual.update(update);
173
174        assert_eq!(actual.book, expected);
175    }
176
177    #[test]
178    fn test_update_set_3() {
179        let init = Available::new([
180            UpdateSet3(
181                Position(num_u8!(1)),
182                Price::new(num!(1.02)).unwrap(),
183                Size::new(num!(34.45)),
184            ),
185            UpdateSet3(
186                Position(num_u8!(0)),
187                Price::new(num!(1.01)).unwrap(),
188                Size::new(num!(12)),
189            ),
190        ]);
191        let update = &[UpdateSet3(
192            Position(num_u8!(1)),
193            Price::new(num!(1.02)).unwrap(),
194            Size::new(num!(22)),
195        )];
196        let mut expected = BTreeMap::new();
197        expected.insert(
198            Position(num_u8!(1)),
199            (Price::new(num!(1.02)).unwrap(), Size::new(num!(22))),
200        );
201        expected.insert(
202            Position(num_u8!(0)),
203            (Price::new(num!(1.01)).unwrap(), Size::new(num!(12))),
204        );
205
206        let mut actual = init;
207        actual.update(update);
208
209        assert_eq!(actual.book, expected);
210    }
211
212    #[test]
213    fn test_update_set_2_delete() {
214        let init = Available::new([
215            UpdateSet2(Price::new(num!(27)).unwrap(), Size::new(num!(0.95))),
216            UpdateSet2(Price::new(num!(13)).unwrap(), Size::new(num!(28.01))),
217        ]);
218        let update = &[UpdateSet2(
219            Price::new(num!(27)).unwrap(),
220            Size::new(num!(0)),
221        )];
222        let mut expected = BTreeMap::new();
223        expected.insert(Price::new(num!(13)).unwrap(), Size::new(num!(28.01)));
224
225        let mut actual = init;
226        actual.update(update);
227
228        assert_eq!(actual.book, expected);
229    }
230
231    #[test]
232    fn test_update_set_3_delete() {
233        let init = Available::new([
234            UpdateSet3(
235                Position(num_u8!(1)),
236                Price::new(num!(1.02)).unwrap(),
237                Size::new(num!(34.45)),
238            ),
239            UpdateSet3(
240                Position(num_u8!(0)),
241                Price::new(num!(1.01)).unwrap(),
242                Size::new(num!(12)),
243            ),
244        ]);
245        let update = &[UpdateSet3(
246            Position(num_u8!(1)),
247            Price::new(num!(1.02)).unwrap(),
248            Size::new(num!(0)),
249        )];
250        let mut expected = BTreeMap::new();
251        expected.insert(
252            Position(num_u8!(0)),
253            (Price::new(num!(1.01)).unwrap(), Size::new(num!(12))),
254        );
255
256        let mut actual = init;
257        actual.update(update);
258
259        assert_eq!(actual.book, expected);
260    }
261}