Skip to main content

betfair_stream_api/cache/primitives/
runner_book_cache.rs

1//! Runner book cache (used for market book Stream API caching)
2
3use betfair_adapter::betfair_types::numeric::F64Ord;
4use betfair_adapter::betfair_types::price::Price;
5use betfair_adapter::betfair_types::size::Size;
6use betfair_adapter::betfair_types::types::sports_aping::SelectionId;
7use betfair_stream_types::response::market_change_message::{RunnerChange, RunnerDefinition};
8use betfair_stream_types::response::{UpdateSet2, UpdateSet3};
9use eyre::bail;
10
11use super::available_cache::Available;
12
13/// Runner book cache (used for market book Stream API caching)
14#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize)]
15pub struct RunnerBookCache {
16    selection_id: SelectionId,
17    last_price_traded: Option<Price>,
18    total_matched: Option<Size>,
19    traded: Available<UpdateSet2>,
20    available_to_back: Available<UpdateSet2>,
21    best_available_to_back: Available<UpdateSet3>,
22    best_display_available_to_back: Available<UpdateSet3>,
23    available_to_lay: Available<UpdateSet2>,
24    best_available_to_lay: Available<UpdateSet3>,
25    best_display_available_to_lay: Available<UpdateSet3>,
26    starting_price_back: Available<UpdateSet2>,
27    starting_price_lay: Available<UpdateSet2>,
28    starting_price_near: Option<Price>,
29    starting_price_far: Option<Price>,
30    handicap: Option<F64Ord>,
31    definition: Option<RunnerDefinition>,
32}
33
34impl RunnerBookCache {
35    pub fn new_from_runner_change(runner_change: RunnerChange) -> eyre::Result<Self> {
36        let Some(id) = runner_change.id else {
37            bail!("Invalid selection id");
38        };
39        let selection_id = id;
40        let handicap = runner_change.handicap;
41        let definition = None;
42
43        Ok(Self {
44            selection_id,
45            last_price_traded: runner_change.last_traded_price,
46            total_matched: runner_change.total_value,
47            traded: runner_change
48                .traded
49                .map_or_else(|| Available::new(&[]), Available::new),
50            available_to_back: runner_change
51                .available_to_back
52                .map_or_else(|| Available::new(&[]), Available::new),
53            best_available_to_back: runner_change
54                .best_available_to_back
55                .map_or_else(|| Available::new(&[]), Available::new),
56            best_display_available_to_back: runner_change
57                .best_display_available_to_back
58                .map_or_else(|| Available::new(&[]), Available::new),
59            available_to_lay: runner_change
60                .available_to_lay
61                .map_or_else(|| Available::new(&[]), Available::new),
62            best_available_to_lay: runner_change
63                .best_available_to_lay
64                .map_or_else(|| Available::new(&[]), Available::new),
65            best_display_available_to_lay: runner_change
66                .best_display_available_to_lay
67                .map_or_else(|| Available::new(&[]), Available::new),
68            starting_price_back: runner_change
69                .starting_price_back
70                .map_or_else(|| Available::new(&[]), Available::new),
71            starting_price_lay: runner_change
72                .starting_price_lay
73                .map_or_else(|| Available::new(&[]), Available::new),
74            starting_price_near: runner_change.starting_price_near,
75            starting_price_far: runner_change.starting_price_far,
76            handicap,
77            definition,
78        })
79    }
80
81    pub fn new_from_runner_definition(runner_definition: RunnerDefinition) -> eyre::Result<Self> {
82        let Some(selection_id) = runner_definition.id else {
83            bail!("Invalid selection id");
84        };
85        let definition = Some(runner_definition);
86
87        Ok(Self {
88            selection_id,
89            last_price_traded: None,
90            total_matched: None,
91            traded: Available::new(&[]),
92            available_to_back: Available::new(&[]),
93            best_available_to_back: Available::new(&[]),
94            best_display_available_to_back: Available::new(&[]),
95            available_to_lay: Available::new(&[]),
96            best_available_to_lay: Available::new(&[]),
97            best_display_available_to_lay: Available::new(&[]),
98            starting_price_back: Available::new(&[]),
99            starting_price_lay: Available::new(&[]),
100            starting_price_near: None,
101            starting_price_far: None,
102            handicap: None,
103            definition,
104        })
105    }
106
107    pub fn update_traded(&mut self, traded: &[UpdateSet2]) {
108        if traded.is_empty() {
109            self.traded.clear();
110            self.total_matched = Some(Size::zero());
111            return;
112        }
113        self.total_matched = Some(
114            traded
115                .iter()
116                .map(|x| x.1)
117                .fold(Size::zero(), |acc, x| acc.saturating_add(&x)),
118        );
119        self.traded.update(traded);
120    }
121
122    pub fn set_definition(&mut self, definition: RunnerDefinition) {
123        self.definition = Some(definition);
124    }
125
126    #[must_use]
127    pub const fn total_matched(&self) -> Option<Size> {
128        self.total_matched
129    }
130
131    #[must_use]
132    pub const fn selection_id(&self) -> &SelectionId {
133        &self.selection_id
134    }
135
136    pub fn set_last_price_traded(&mut self, last_price_traded: Price) {
137        self.last_price_traded = Some(last_price_traded);
138    }
139
140    pub fn set_total_matched(&mut self, total_matched: Size) {
141        self.total_matched = Some(total_matched);
142    }
143
144    pub(crate) fn set_starting_price_near(&mut self, spn: Price) {
145        self.starting_price_near = Some(spn);
146    }
147
148    pub(crate) fn set_starting_price_far(&mut self, spf: Price) {
149        self.starting_price_far = Some(spf);
150    }
151
152    pub(crate) fn update_available_to_back(&mut self, atb: impl AsRef<[UpdateSet2]>) {
153        self.available_to_back.update(atb);
154    }
155
156    pub(crate) fn update_available_to_lay(&mut self, atl: impl AsRef<[UpdateSet2]>) {
157        self.available_to_lay.update(atl);
158    }
159
160    pub(crate) fn update_best_available_to_back(&mut self, batb: impl AsRef<[UpdateSet3]>) {
161        self.best_available_to_back.update(batb);
162    }
163
164    pub(crate) fn update_best_available_to_lay(&mut self, batl: impl AsRef<[UpdateSet3]>) {
165        self.best_available_to_lay.update(batl);
166    }
167
168    pub(crate) fn update_best_display_available_to_back(
169        &mut self,
170        bdatb: impl AsRef<[UpdateSet3]>,
171    ) {
172        self.best_display_available_to_back.update(bdatb);
173    }
174
175    pub(crate) fn update_best_display_available_to_lay(&mut self, bdatl: impl AsRef<[UpdateSet3]>) {
176        self.best_display_available_to_lay.update(bdatl);
177    }
178
179    pub(crate) fn update_starting_price_back(&mut self, spb: impl AsRef<[UpdateSet2]>) {
180        self.starting_price_back.update(spb);
181    }
182
183    pub(crate) fn update_starting_price_lay(&mut self, spl: impl AsRef<[UpdateSet2]>) {
184        self.starting_price_lay.update(spl);
185    }
186
187    #[must_use]
188    pub const fn last_price_traded(&self) -> Option<&Price> {
189        self.last_price_traded.as_ref()
190    }
191
192    #[must_use]
193    pub const fn traded(&self) -> &Available<UpdateSet2> {
194        &self.traded
195    }
196
197    #[must_use]
198    pub const fn available_to_back(&self) -> &Available<UpdateSet2> {
199        &self.available_to_back
200    }
201
202    #[must_use]
203    pub const fn best_available_to_back(&self) -> &Available<UpdateSet3> {
204        &self.best_available_to_back
205    }
206
207    #[must_use]
208    pub const fn best_display_available_to_back(&self) -> &Available<UpdateSet3> {
209        &self.best_display_available_to_back
210    }
211
212    #[must_use]
213    pub const fn available_to_lay(&self) -> &Available<UpdateSet2> {
214        &self.available_to_lay
215    }
216
217    #[must_use]
218    pub const fn best_available_to_lay(&self) -> &Available<UpdateSet3> {
219        &self.best_available_to_lay
220    }
221
222    #[must_use]
223    pub const fn best_display_available_to_lay(&self) -> &Available<UpdateSet3> {
224        &self.best_display_available_to_lay
225    }
226
227    #[must_use]
228    pub const fn starting_price_back(&self) -> &Available<UpdateSet2> {
229        &self.starting_price_back
230    }
231
232    #[must_use]
233    pub const fn starting_price_lay(&self) -> &Available<UpdateSet2> {
234        &self.starting_price_lay
235    }
236
237    #[must_use]
238    pub const fn starting_price_near(&self) -> Option<&Price> {
239        self.starting_price_near.as_ref()
240    }
241
242    #[must_use]
243    pub const fn starting_price_far(&self) -> Option<&Price> {
244        self.starting_price_far.as_ref()
245    }
246
247    #[must_use]
248    pub const fn handicap(&self) -> Option<F64Ord> {
249        self.handicap
250    }
251
252    #[must_use]
253    pub const fn definition(&self) -> Option<&RunnerDefinition> {
254        self.definition.as_ref()
255    }
256}
257
258#[cfg(test)]
259mod tests {
260
261    #[test]
262    const fn test_update_traded() {}
263}