Skip to main content

digdigdig3_station/data/
ticker.rs

1use digdigdig3::core::types::{StreamEvent, Ticker};
2use serde::{Deserialize, Serialize};
3
4use crate::series::DataPoint;
5
6/// 64 B ticker record (LE):
7///   u64 ts_ms
8///   f64 last, bid, ask
9///   f64 high_24h, low_24h, vol_24h, quote_vol_24h, price_change_pct_24h
10///
11/// Absent Optional fields are stored as f64::NAN.
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct TickerPoint {
14    pub ts_ms: i64,
15    pub last: f64,
16    pub bid: f64,
17    pub ask: f64,
18    pub high_24h: f64,
19    pub low_24h: f64,
20    pub vol_24h: f64,
21    pub quote_vol_24h: f64,
22    pub change_pct_24h: f64,
23}
24
25const SIZE: usize = 72;
26
27impl TickerPoint {
28    pub fn from_ticker(t: &Ticker) -> Self {
29        Self {
30            ts_ms: t.timestamp,
31            last: t.last_price,
32            bid: t.bid_price.unwrap_or(f64::NAN),
33            ask: t.ask_price.unwrap_or(f64::NAN),
34            high_24h: t.high_24h.unwrap_or(f64::NAN),
35            low_24h: t.low_24h.unwrap_or(f64::NAN),
36            vol_24h: t.volume_24h.unwrap_or(f64::NAN),
37            quote_vol_24h: t.quote_volume_24h.unwrap_or(f64::NAN),
38            change_pct_24h: t.price_change_percent_24h.unwrap_or(f64::NAN),
39        }
40    }
41}
42
43impl DataPoint for TickerPoint {
44    const RECORD_SIZE: usize = SIZE;
45
46    fn encode(&self, out: &mut [u8]) {
47        out[0..8].copy_from_slice(&(self.ts_ms as u64).to_le_bytes());
48        out[8..16].copy_from_slice(&self.last.to_le_bytes());
49        out[16..24].copy_from_slice(&self.bid.to_le_bytes());
50        out[24..32].copy_from_slice(&self.ask.to_le_bytes());
51        out[32..40].copy_from_slice(&self.high_24h.to_le_bytes());
52        out[40..48].copy_from_slice(&self.low_24h.to_le_bytes());
53        out[48..56].copy_from_slice(&self.vol_24h.to_le_bytes());
54        out[56..64].copy_from_slice(&self.quote_vol_24h.to_le_bytes());
55        out[64..72].copy_from_slice(&self.change_pct_24h.to_le_bytes());
56    }
57
58    fn decode(bytes: &[u8]) -> Option<Self> {
59        if bytes.len() != SIZE { return None; }
60        Some(Self {
61            ts_ms: u64::from_le_bytes(bytes[0..8].try_into().ok()?) as i64,
62            last: f64::from_le_bytes(bytes[8..16].try_into().ok()?),
63            bid: f64::from_le_bytes(bytes[16..24].try_into().ok()?),
64            ask: f64::from_le_bytes(bytes[24..32].try_into().ok()?),
65            high_24h: f64::from_le_bytes(bytes[32..40].try_into().ok()?),
66            low_24h: f64::from_le_bytes(bytes[40..48].try_into().ok()?),
67            vol_24h: f64::from_le_bytes(bytes[48..56].try_into().ok()?),
68            quote_vol_24h: f64::from_le_bytes(bytes[56..64].try_into().ok()?),
69            change_pct_24h: f64::from_le_bytes(bytes[64..72].try_into().ok()?),
70        })
71    }
72
73    fn timestamp_ms(&self) -> i64 { self.ts_ms }
74
75    fn from_stream_event(ev: &StreamEvent) -> Option<Self> {
76        if let StreamEvent::Ticker { ticker, .. } = ev {
77            Some(Self::from_ticker(ticker))
78        } else {
79            None
80        }
81    }
82}