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