digdigdig3_station/data/
order_update.rs1use digdigdig3::core::types::StreamEvent;
2use serde::{Deserialize, Serialize};
3
4use crate::series::DataPoint;
5
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23pub struct OrderUpdatePoint {
24 pub ts_ms: i64,
25 pub order_id_hash: u64,
26 pub client_id_hash: u64,
27 pub status: u8,
30 pub side: u8,
32 pub order_type: u8,
34 pub price: f64,
35 pub qty: f64,
36 pub filled_qty: f64,
37 pub avg_price: f64,
38 pub last_fill_price: f64,
39 pub last_fill_qty: f64,
40 pub last_fill_commission: f64,
41}
42
43const SIZE: usize = 96;
44
45impl OrderUpdatePoint {
46 pub fn status_label(&self) -> &'static str {
47 match self.status {
48 0 => "New",
49 1 => "PartiallyFilled",
50 2 => "Filled",
51 3 => "Canceled",
52 4 => "Rejected/Expired",
53 _ => "Unknown",
54 }
55 }
56
57 pub fn side_label(&self) -> &'static str {
58 match self.side {
59 0 => "Buy",
60 1 => "Sell",
61 _ => "?",
62 }
63 }
64
65 pub fn order_type_label(&self) -> &'static str {
66 match self.order_type {
67 0 => "Market",
68 1 => "Limit",
69 _ => "Other",
70 }
71 }
72}
73
74impl DataPoint for OrderUpdatePoint {
75 const RECORD_SIZE: usize = SIZE;
76
77 fn encode(&self, out: &mut [u8]) {
78 out[0..8].copy_from_slice(&(self.ts_ms as u64).to_le_bytes());
79 out[8..16].copy_from_slice(&self.order_id_hash.to_le_bytes());
80 out[16..24].copy_from_slice(&self.client_id_hash.to_le_bytes());
81 out[24] = self.status;
82 out[25] = self.side;
83 out[26] = self.order_type;
84 out[28..36].copy_from_slice(&self.price.to_le_bytes());
86 out[36..44].copy_from_slice(&self.qty.to_le_bytes());
87 out[44..52].copy_from_slice(&self.filled_qty.to_le_bytes());
88 out[52..60].copy_from_slice(&self.avg_price.to_le_bytes());
89 out[60..68].copy_from_slice(&self.last_fill_price.to_le_bytes());
90 out[68..76].copy_from_slice(&self.last_fill_qty.to_le_bytes());
91 out[76..84].copy_from_slice(&self.last_fill_commission.to_le_bytes());
92 }
94
95 fn decode(bytes: &[u8]) -> Option<Self> {
96 if bytes.len() != SIZE {
97 return None;
98 }
99 let ts_ms = u64::from_le_bytes(bytes[0..8].try_into().ok()?) as i64;
100 let order_id_hash = u64::from_le_bytes(bytes[8..16].try_into().ok()?);
101 let client_id_hash = u64::from_le_bytes(bytes[16..24].try_into().ok()?);
102 let status = bytes[24];
103 let side = bytes[25];
104 let order_type = bytes[26];
105 let price = f64::from_le_bytes(bytes[28..36].try_into().ok()?);
106 let qty = f64::from_le_bytes(bytes[36..44].try_into().ok()?);
107 let filled_qty = f64::from_le_bytes(bytes[44..52].try_into().ok()?);
108 let avg_price = f64::from_le_bytes(bytes[52..60].try_into().ok()?);
109 let last_fill_price = f64::from_le_bytes(bytes[60..68].try_into().ok()?);
110 let last_fill_qty = f64::from_le_bytes(bytes[68..76].try_into().ok()?);
111 let last_fill_commission = f64::from_le_bytes(bytes[76..84].try_into().ok()?);
112 Some(Self {
113 ts_ms,
114 order_id_hash,
115 client_id_hash,
116 status,
117 side,
118 order_type,
119 price,
120 qty,
121 filled_qty,
122 avg_price,
123 last_fill_price,
124 last_fill_qty,
125 last_fill_commission,
126 })
127 }
128
129 fn timestamp_ms(&self) -> i64 {
130 self.ts_ms
131 }
132
133 fn from_stream_event(ev: &StreamEvent) -> Option<Self> {
134 if let StreamEvent::OrderUpdate { event, .. } = ev {
135 use digdigdig3::core::types::{OrderSide, OrderStatus, OrderType};
136 let status = match event.status {
137 OrderStatus::New => 0,
138 OrderStatus::Open => 0,
139 OrderStatus::PartiallyFilled => 1,
140 OrderStatus::Filled => 2,
141 OrderStatus::Canceled => 3,
142 OrderStatus::Rejected => 4,
143 OrderStatus::Expired => 4,
144 };
145 let side = match event.side {
146 OrderSide::Buy => 0,
147 OrderSide::Sell => 1,
148 };
149 let order_type = match event.order_type {
150 OrderType::Market => 0,
151 OrderType::Limit { .. } => 1,
152 _ => 2,
153 };
154 Some(Self {
155 ts_ms: event.timestamp,
156 order_id_hash: fnv1a_64(event.order_id.as_bytes()),
157 client_id_hash: event
158 .client_order_id
159 .as_deref()
160 .map(|s| fnv1a_64(s.as_bytes()))
161 .unwrap_or(0),
162 status,
163 side,
164 order_type,
165 price: event.price.unwrap_or(0.0),
166 qty: event.quantity,
167 filled_qty: event.filled_quantity,
168 avg_price: event.average_price.unwrap_or(0.0),
169 last_fill_price: event.last_fill_price.unwrap_or(0.0),
170 last_fill_qty: event.last_fill_quantity.unwrap_or(0.0),
171 last_fill_commission: event.last_fill_commission.unwrap_or(0.0),
172 })
173 } else {
174 None
175 }
176 }
177}
178
179fn fnv1a_64(bytes: &[u8]) -> u64 {
180 let mut h: u64 = 0xcbf29ce484222325;
181 for b in bytes {
182 h ^= *b as u64;
183 h = h.wrapping_mul(0x100000001b3);
184 }
185 h
186}