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