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