1use std::{collections::HashMap, fmt::Display};
17
18use indexmap::IndexMap;
19use nautilus_core::{UnixNanos, serialization::Serializable};
20use serde::{Deserialize, Serialize};
21
22use super::GetTsInit;
23use crate::{
24 identifiers::InstrumentId,
25 types::{Price, fixed::FIXED_SIZE_BINARY},
26};
27
28#[repr(C)]
30#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
31#[serde(tag = "type")]
32#[cfg_attr(
33 feature = "python",
34 pyo3::pyclass(module = "posei_trader.core.nautilus_pyo3.model")
35)]
36pub struct MarkPriceUpdate {
37 pub instrument_id: InstrumentId,
39 pub value: Price,
41 pub ts_event: UnixNanos,
43 pub ts_init: UnixNanos,
45}
46
47impl MarkPriceUpdate {
48 #[must_use]
50 pub fn new(
51 instrument_id: InstrumentId,
52 value: Price,
53 ts_event: UnixNanos,
54 ts_init: UnixNanos,
55 ) -> Self {
56 Self {
57 instrument_id,
58 value,
59 ts_event,
60 ts_init,
61 }
62 }
63
64 #[must_use]
66 pub fn get_metadata(
67 instrument_id: &InstrumentId,
68 price_precision: u8,
69 ) -> HashMap<String, String> {
70 let mut metadata = HashMap::new();
71 metadata.insert("instrument_id".to_string(), instrument_id.to_string());
72 metadata.insert("price_precision".to_string(), price_precision.to_string());
73 metadata
74 }
75
76 #[must_use]
78 pub fn get_fields() -> IndexMap<String, String> {
79 let mut metadata = IndexMap::new();
80 metadata.insert("value".to_string(), FIXED_SIZE_BINARY.to_string());
81 metadata.insert("ts_event".to_string(), "UInt64".to_string());
82 metadata.insert("ts_init".to_string(), "UInt64".to_string());
83 metadata
84 }
85}
86
87impl Display for MarkPriceUpdate {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(
90 f,
91 "{},{},{},{}",
92 self.instrument_id, self.value, self.ts_event, self.ts_init
93 )
94 }
95}
96
97impl Serializable for MarkPriceUpdate {}
98
99impl GetTsInit for MarkPriceUpdate {
100 fn ts_init(&self) -> UnixNanos {
101 self.ts_init
102 }
103}
104
105#[repr(C)]
107#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
108#[serde(tag = "type")]
109#[cfg_attr(
110 feature = "python",
111 pyo3::pyclass(module = "posei_trader.core.nautilus_pyo3.model")
112)]
113pub struct IndexPriceUpdate {
114 pub instrument_id: InstrumentId,
116 pub value: Price,
118 pub ts_event: UnixNanos,
120 pub ts_init: UnixNanos,
122}
123
124impl IndexPriceUpdate {
125 #[must_use]
127 pub fn new(
128 instrument_id: InstrumentId,
129 value: Price,
130 ts_event: UnixNanos,
131 ts_init: UnixNanos,
132 ) -> Self {
133 Self {
134 instrument_id,
135 value,
136 ts_event,
137 ts_init,
138 }
139 }
140
141 #[must_use]
143 pub fn get_metadata(
144 instrument_id: &InstrumentId,
145 price_precision: u8,
146 ) -> HashMap<String, String> {
147 let mut metadata = HashMap::new();
148 metadata.insert("instrument_id".to_string(), instrument_id.to_string());
149 metadata.insert("price_precision".to_string(), price_precision.to_string());
150 metadata
151 }
152
153 #[must_use]
155 pub fn get_fields() -> IndexMap<String, String> {
156 let mut metadata = IndexMap::new();
157 metadata.insert("value".to_string(), FIXED_SIZE_BINARY.to_string());
158 metadata.insert("ts_event".to_string(), "UInt64".to_string());
159 metadata.insert("ts_init".to_string(), "UInt64".to_string());
160 metadata
161 }
162}
163
164impl Display for IndexPriceUpdate {
165 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
166 write!(
167 f,
168 "{},{},{},{}",
169 self.instrument_id, self.value, self.ts_event, self.ts_init
170 )
171 }
172}
173
174impl Serializable for IndexPriceUpdate {}
175
176impl GetTsInit for IndexPriceUpdate {
177 fn ts_init(&self) -> UnixNanos {
178 self.ts_init
179 }
180}
181
182#[cfg(test)]
186mod tests {
187 use std::{
188 collections::hash_map::DefaultHasher,
189 hash::{Hash, Hasher},
190 };
191
192 use nautilus_core::serialization::Serializable;
193 use rstest::{fixture, rstest};
194 use serde_json;
195
196 use super::*;
197
198 #[fixture]
199 fn instrument_id() -> InstrumentId {
200 InstrumentId::from("BTC-USDT.OKX")
201 }
202
203 #[fixture]
204 fn price() -> Price {
205 Price::from("150_500.10")
206 }
207
208 #[rstest]
209 fn test_mark_price_update_new(instrument_id: InstrumentId, price: Price) {
210 let ts_event = UnixNanos::from(1);
211 let ts_init = UnixNanos::from(2);
212
213 let mark_price = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
214
215 assert_eq!(mark_price.instrument_id, instrument_id);
216 assert_eq!(mark_price.value, price);
217 assert_eq!(mark_price.ts_event, ts_event);
218 assert_eq!(mark_price.ts_init, ts_init);
219 }
220
221 #[rstest]
222 fn test_mark_price_update_display(instrument_id: InstrumentId, price: Price) {
223 let ts_event = UnixNanos::from(1);
224 let ts_init = UnixNanos::from(2);
225
226 let mark_price = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
227
228 assert_eq!(format!("{mark_price}"), "BTC-USDT.OKX,150500.10,1,2");
229 }
230
231 #[rstest]
232 fn test_mark_price_update_get_ts_init(instrument_id: InstrumentId, price: Price) {
233 let ts_event = UnixNanos::from(1);
234 let ts_init = UnixNanos::from(2);
235
236 let mark_price = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
237
238 assert_eq!(mark_price.ts_init(), ts_init);
239 }
240
241 #[rstest]
242 fn test_mark_price_update_eq_hash(instrument_id: InstrumentId, price: Price) {
243 let ts_event = UnixNanos::from(1);
244 let ts_init = UnixNanos::from(2);
245
246 let mark_price1 = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
247 let mark_price2 = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
248 let mark_price3 =
249 MarkPriceUpdate::new(instrument_id, Price::from("143_500.50"), ts_event, ts_init);
250
251 assert_eq!(mark_price1, mark_price2);
252 assert_ne!(mark_price1, mark_price3);
253
254 use std::{
256 collections::hash_map::DefaultHasher,
257 hash::{Hash, Hasher},
258 };
259
260 let mut hasher1 = DefaultHasher::new();
261 let mut hasher2 = DefaultHasher::new();
262 mark_price1.hash(&mut hasher1);
263 mark_price2.hash(&mut hasher2);
264 assert_eq!(hasher1.finish(), hasher2.finish());
265 }
266
267 #[rstest]
268 fn test_mark_price_update_json_serialization(instrument_id: InstrumentId, price: Price) {
269 let ts_event = UnixNanos::from(1);
270 let ts_init = UnixNanos::from(2);
271
272 let mark_price = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
273
274 let serialized = mark_price.to_json_bytes().unwrap();
275 let deserialized = MarkPriceUpdate::from_json_bytes(&serialized).unwrap();
276
277 assert_eq!(mark_price, deserialized);
278 }
279
280 #[rstest]
281 fn test_mark_price_update_msgpack_serialization(instrument_id: InstrumentId, price: Price) {
282 let ts_event = UnixNanos::from(1);
283 let ts_init = UnixNanos::from(2);
284
285 let mark_price = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
286
287 let serialized = mark_price.to_msgpack_bytes().unwrap();
288 let deserialized = MarkPriceUpdate::from_msgpack_bytes(&serialized).unwrap();
289
290 assert_eq!(mark_price, deserialized);
291 }
292
293 #[rstest]
294 fn test_mark_price_update_clone(instrument_id: InstrumentId, price: Price) {
295 let ts_event = UnixNanos::from(1);
296 let ts_init = UnixNanos::from(2);
297
298 let mark_price = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
299 let cloned = mark_price.clone();
300
301 assert_eq!(mark_price, cloned);
302 }
303
304 #[rstest]
305 fn test_mark_price_update_serde_json(instrument_id: InstrumentId, price: Price) {
306 let ts_event = UnixNanos::from(1);
307 let ts_init = UnixNanos::from(2);
308
309 let mark_price = MarkPriceUpdate::new(instrument_id, price, ts_event, ts_init);
310
311 let json_str = serde_json::to_string(&mark_price).unwrap();
312 let deserialized: MarkPriceUpdate = serde_json::from_str(&json_str).unwrap();
313
314 assert_eq!(mark_price, deserialized);
315 }
316
317 #[rstest]
318 fn test_index_price_update_new(instrument_id: InstrumentId, price: Price) {
319 let ts_event = UnixNanos::from(1);
320 let ts_init = UnixNanos::from(2);
321
322 let index_price = IndexPriceUpdate::new(instrument_id, price, ts_event, ts_init);
323
324 assert_eq!(index_price.instrument_id, instrument_id);
325 assert_eq!(index_price.value, price);
326 assert_eq!(index_price.ts_event, ts_event);
327 assert_eq!(index_price.ts_init, ts_init);
328 }
329
330 #[rstest]
331 fn test_index_price_update_display(instrument_id: InstrumentId, price: Price) {
332 let ts_event = UnixNanos::from(1);
333 let ts_init = UnixNanos::from(2);
334
335 let index_price = IndexPriceUpdate::new(instrument_id, price, ts_event, ts_init);
336
337 assert_eq!(format!("{index_price}"), "BTC-USDT.OKX,150500.10,1,2");
338 }
339
340 #[rstest]
341 fn test_index_price_update_get_ts_init(instrument_id: InstrumentId, price: Price) {
342 let ts_event = UnixNanos::from(1);
343 let ts_init = UnixNanos::from(2);
344
345 let index_price = IndexPriceUpdate::new(instrument_id, price, ts_event, ts_init);
346
347 assert_eq!(index_price.ts_init(), ts_init);
348 }
349
350 #[rstest]
351 fn test_index_price_update_eq_hash(instrument_id: InstrumentId, price: Price) {
352 let ts_event = UnixNanos::from(1);
353 let ts_init = UnixNanos::from(2);
354
355 let index_price1 = IndexPriceUpdate::new(instrument_id, price, ts_event, ts_init);
356 let index_price2 = IndexPriceUpdate::new(instrument_id, price, ts_event, ts_init);
357 let index_price3 = IndexPriceUpdate::new(instrument_id, price, UnixNanos::from(3), ts_init);
358
359 assert_eq!(index_price1, index_price2);
360 assert_ne!(index_price1, index_price3);
361
362 let mut hasher1 = DefaultHasher::new();
363 let mut hasher2 = DefaultHasher::new();
364 index_price1.hash(&mut hasher1);
365 index_price2.hash(&mut hasher2);
366 assert_eq!(hasher1.finish(), hasher2.finish());
367 }
368
369 #[rstest]
370 fn test_index_price_update_json_serialization(instrument_id: InstrumentId, price: Price) {
371 let ts_event = UnixNanos::from(1);
372 let ts_init = UnixNanos::from(2);
373
374 let index_price = IndexPriceUpdate::new(instrument_id, price, ts_event, ts_init);
375
376 let serialized = index_price.to_json_bytes().unwrap();
377 let deserialized = IndexPriceUpdate::from_json_bytes(&serialized).unwrap();
378
379 assert_eq!(index_price, deserialized);
380 }
381
382 #[rstest]
383 fn test_index_price_update_msgpack_serialization(instrument_id: InstrumentId, price: Price) {
384 let ts_event = UnixNanos::from(1);
385 let ts_init = UnixNanos::from(2);
386
387 let index_price = IndexPriceUpdate::new(instrument_id, price, ts_event, ts_init);
388
389 let serialized = index_price.to_msgpack_bytes().unwrap();
390 let deserialized = IndexPriceUpdate::from_msgpack_bytes(&serialized).unwrap();
391
392 assert_eq!(index_price, deserialized);
393 }
394
395 #[rstest]
396 fn test_index_price_update_serde_json(instrument_id: InstrumentId, price: Price) {
397 let ts_event = UnixNanos::from(1);
398 let ts_init = UnixNanos::from(2);
399
400 let index_price = IndexPriceUpdate::new(instrument_id, price, ts_event, ts_init);
401
402 let json_str = serde_json::to_string(&index_price).unwrap();
403 let deserialized: IndexPriceUpdate = serde_json::from_str(&json_str).unwrap();
404
405 assert_eq!(index_price, deserialized);
406 }
407}