1pub mod bar;
19pub mod bet;
20pub mod delta;
21pub mod deltas;
22pub mod depth;
23pub mod greeks;
24pub mod order;
25pub mod quote;
26pub mod status;
27pub mod trade;
28
29#[cfg(feature = "stubs")]
30pub mod stubs;
31
32use std::{
33 fmt::{Debug, Display},
34 hash::{Hash, Hasher},
35 num::NonZeroU64,
36 str::FromStr,
37};
38
39use indexmap::IndexMap;
40use nautilus_core::UnixNanos;
41use serde::{Deserialize, Serialize};
42use serde_json::to_string;
43
44#[rustfmt::skip] pub use bar::{Bar, BarSpecification, BarType};
47pub use delta::OrderBookDelta;
48pub use deltas::{OrderBookDeltas, OrderBookDeltas_API};
49pub use depth::{OrderBookDepth10, DEPTH10_LEN};
50pub use greeks::{black_scholes_greeks, BlackScholesGreeksResult};
51pub use order::{BookOrder, NULL_ORDER};
52pub use quote::QuoteTick;
53pub use status::InstrumentStatus;
54pub use trade::TradeTick;
55
56use crate::{
57 enums::BookType,
58 identifiers::{InstrumentId, Venue},
59};
60
61#[repr(C)]
66#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
67#[allow(clippy::large_enum_variant)]
68pub enum Data {
69 Delta(OrderBookDelta),
70 Deltas(OrderBookDeltas_API),
71 Depth10(OrderBookDepth10), Quote(QuoteTick),
73 Trade(TradeTick),
74 Bar(Bar),
75}
76
77macro_rules! impl_try_from_data {
78 ($variant:ident, $type:ty) => {
79 impl TryFrom<Data> for $type {
80 type Error = ();
81
82 fn try_from(value: Data) -> Result<Self, Self::Error> {
83 match value {
84 Data::$variant(x) => Ok(x),
85 _ => Err(()),
86 }
87 }
88 }
89 };
90}
91
92impl_try_from_data!(Delta, OrderBookDelta);
93impl_try_from_data!(Deltas, OrderBookDeltas_API);
94impl_try_from_data!(Depth10, OrderBookDepth10);
95impl_try_from_data!(Quote, QuoteTick);
96impl_try_from_data!(Trade, TradeTick);
97impl_try_from_data!(Bar, Bar);
98
99pub fn to_variant<T: TryFrom<Data>>(data: Vec<Data>) -> Vec<T> {
100 data.into_iter()
101 .filter_map(|d| T::try_from(d).ok())
102 .collect()
103}
104
105impl Data {
106 pub fn instrument_id(&self) -> InstrumentId {
108 match self {
109 Self::Delta(delta) => delta.instrument_id,
110 Self::Deltas(deltas) => deltas.instrument_id,
111 Self::Depth10(depth) => depth.instrument_id,
112 Self::Quote(quote) => quote.instrument_id,
113 Self::Trade(trade) => trade.instrument_id,
114 Self::Bar(bar) => bar.bar_type.instrument_id(),
115 }
116 }
117
118 pub fn is_order_book_data(&self) -> bool {
120 matches!(self, Self::Delta(_) | Self::Deltas(_) | Self::Depth10(_))
121 }
122}
123
124pub trait GetTsInit {
125 fn ts_init(&self) -> UnixNanos;
126}
127
128impl GetTsInit for Data {
129 fn ts_init(&self) -> UnixNanos {
130 match self {
131 Self::Delta(d) => d.ts_init,
132 Self::Deltas(d) => d.ts_init,
133 Self::Depth10(d) => d.ts_init,
134 Self::Quote(q) => q.ts_init,
135 Self::Trade(t) => t.ts_init,
136 Self::Bar(b) => b.ts_init,
137 }
138 }
139}
140
141pub fn is_monotonically_increasing_by_init<T: GetTsInit>(data: &[T]) -> bool {
142 data.windows(2)
143 .all(|window| window[0].ts_init() <= window[1].ts_init())
144}
145
146impl From<OrderBookDelta> for Data {
147 fn from(value: OrderBookDelta) -> Self {
148 Self::Delta(value)
149 }
150}
151
152impl From<OrderBookDeltas_API> for Data {
153 fn from(value: OrderBookDeltas_API) -> Self {
154 Self::Deltas(value)
155 }
156}
157
158impl From<OrderBookDepth10> for Data {
159 fn from(value: OrderBookDepth10) -> Self {
160 Self::Depth10(value)
161 }
162}
163
164impl From<QuoteTick> for Data {
165 fn from(value: QuoteTick) -> Self {
166 Self::Quote(value)
167 }
168}
169
170impl From<TradeTick> for Data {
171 fn from(value: TradeTick) -> Self {
172 Self::Trade(value)
173 }
174}
175
176impl From<Bar> for Data {
177 fn from(value: Bar) -> Self {
178 Self::Bar(value)
179 }
180}
181
182#[no_mangle]
186#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
187pub extern "C" fn data_clone(data: &Data) -> Data {
188 data.clone()
190}
191
192#[derive(Clone, Serialize, Deserialize)]
194#[cfg_attr(
195 feature = "python",
196 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
197)]
198pub struct DataType {
199 type_name: String,
200 metadata: Option<IndexMap<String, String>>,
201 topic: String,
202 hash: u64,
203}
204
205impl DataType {
206 pub fn new(type_name: &str, metadata: Option<IndexMap<String, String>>) -> Self {
208 let topic = if let Some(ref meta) = metadata {
210 let meta_str = meta
211 .iter()
212 .map(|(k, v)| format!("{}={}", k, v))
213 .collect::<Vec<_>>()
214 .join(".");
215 format!("{}.{}", type_name, meta_str)
216 } else {
217 type_name.to_string()
218 };
219
220 let mut hasher = std::collections::hash_map::DefaultHasher::new();
222 topic.hash(&mut hasher);
223
224 Self {
225 type_name: type_name.to_owned(),
226 metadata,
227 topic,
228 hash: hasher.finish(),
229 }
230 }
231
232 pub fn type_name(&self) -> &str {
234 self.type_name.as_str()
235 }
236
237 pub fn metadata(&self) -> Option<&IndexMap<String, String>> {
239 self.metadata.as_ref()
240 }
241
242 pub fn metadata_str(&self) -> String {
244 self.metadata
245 .as_ref()
246 .map(|metadata| to_string(metadata).unwrap_or_default())
247 .unwrap_or_else(|| "null".to_string())
248 }
249
250 pub fn topic(&self) -> &str {
252 self.topic.as_str()
253 }
254
255 pub fn instrument_id(&self) -> Option<InstrumentId> {
263 let metadata = self.metadata.as_ref().expect("metadata was `None`");
264 let instrument_id = metadata.get("instrument_id")?;
265 Some(
266 InstrumentId::from_str(instrument_id)
267 .expect("Invalid `InstrumentId` for 'instrument_id'"),
268 )
269 }
270
271 pub fn venue(&self) -> Option<Venue> {
279 let metadata = self.metadata.as_ref().expect("metadata was `None`");
280 let venue_str = metadata.get("venue")?;
281 Some(Venue::from(venue_str.as_str()))
282 }
283
284 pub fn bar_type(&self) -> BarType {
293 let metadata = self.metadata.as_ref().expect("metadata was `None`");
294 let bar_type_str = metadata
295 .get("bar_type")
296 .expect("No 'bar_type' found in metadata");
297 BarType::from_str(bar_type_str).expect("Invalid `BarType` for 'bar_type'")
298 }
299
300 pub fn start(&self) -> Option<UnixNanos> {
308 let metadata = self.metadata.as_ref()?;
309 let start_str = metadata.get("start")?;
310 Some(UnixNanos::from_str(start_str).expect("Invalid `UnixNanos` for 'start'"))
311 }
312
313 pub fn end(&self) -> Option<UnixNanos> {
321 let metadata = self.metadata.as_ref()?;
322 let end_str = metadata.get("end")?;
323 Some(UnixNanos::from_str(end_str).expect("Invalid `UnixNanos` for 'end'"))
324 }
325
326 pub fn limit(&self) -> Option<usize> {
334 let metadata = self.metadata.as_ref()?;
335 let depth_str = metadata.get("limit")?;
336 Some(
337 depth_str
338 .parse::<usize>()
339 .expect("Invalid `usize` for 'limit'"),
340 )
341 }
342
343 pub fn book_type(&self) -> BookType {
351 let metadata = self.metadata.as_ref().expect("metadata was `None`");
352 let book_type_str = metadata
353 .get("book_type")
354 .expect("'book_type' not found in metadata");
355 BookType::from_str(book_type_str).expect("Invalid `BookType` for 'book_type'")
356 }
357
358 pub fn depth(&self) -> Option<usize> {
366 let metadata = self.metadata.as_ref()?;
367 let depth_str = metadata.get("depth")?;
368 Some(
369 depth_str
370 .parse::<usize>()
371 .expect("Invalid `usize` for 'depth'"),
372 )
373 }
374
375 pub fn interval_ms(&self) -> NonZeroU64 {
383 let metadata = self.metadata.as_ref().expect("metadata was `None`");
384 let interval_ms_str = metadata
385 .get("interval_ms")
386 .expect("No 'interval_ms' in metadata");
387
388 interval_ms_str
389 .parse::<NonZeroU64>()
390 .expect("Invalid `NonZeroU64` for 'interval_ms'")
391 }
392
393 pub fn managed(&self) -> bool {
401 let metadata = self.metadata.as_ref().expect("metadata was `None`");
402 let managed_str = metadata.get("managed").expect("No 'managed' in metadata");
403 managed_str
404 .parse::<bool>()
405 .expect("Invalid `bool` for 'managed'")
406 }
407}
408
409impl PartialEq for DataType {
410 fn eq(&self, other: &Self) -> bool {
411 self.topic == other.topic
412 }
413}
414
415impl Eq for DataType {}
416
417impl PartialOrd for DataType {
418 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
419 Some(self.cmp(other))
420 }
421}
422
423impl Ord for DataType {
424 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
425 self.topic.cmp(&other.topic)
426 }
427}
428
429impl Hash for DataType {
430 fn hash<H: Hasher>(&self, state: &mut H) {
431 self.hash.hash(state);
432 }
433}
434
435impl Display for DataType {
436 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
437 write!(f, "{}", self.topic)
438 }
439}
440
441impl Debug for DataType {
442 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
443 write!(
444 f,
445 "DataType(type_name={}, metadata={:?})",
446 self.type_name, self.metadata
447 )
448 }
449}
450
451#[cfg(test)]
455mod tests {
456 use std::hash::DefaultHasher;
457
458 use rstest::*;
459
460 use super::*;
461
462 #[rstest]
463 fn test_data_type_creation_with_metadata() {
464 let metadata = Some(
465 [
466 ("key1".to_string(), "value1".to_string()),
467 ("key2".to_string(), "value2".to_string()),
468 ]
469 .iter()
470 .cloned()
471 .collect(),
472 );
473 let data_type = DataType::new("ExampleType", metadata.clone());
474
475 assert_eq!(data_type.type_name(), "ExampleType");
476 assert_eq!(data_type.topic(), "ExampleType.key1=value1.key2=value2");
477 assert_eq!(data_type.metadata(), metadata.as_ref());
478 }
479
480 #[rstest]
481 fn test_data_type_creation_without_metadata() {
482 let data_type = DataType::new("ExampleType", None);
483
484 assert_eq!(data_type.type_name(), "ExampleType");
485 assert_eq!(data_type.topic(), "ExampleType");
486 assert_eq!(data_type.metadata(), None);
487 }
488
489 #[rstest]
490 fn test_data_type_equality() {
491 let metadata1 = Some(
492 [("key1".to_string(), "value1".to_string())]
493 .iter()
494 .cloned()
495 .collect(),
496 );
497 let metadata2 = Some(
498 [("key1".to_string(), "value1".to_string())]
499 .iter()
500 .cloned()
501 .collect(),
502 );
503
504 let data_type1 = DataType::new("ExampleType", metadata1);
505 let data_type2 = DataType::new("ExampleType", metadata2);
506
507 assert_eq!(data_type1, data_type2);
508 }
509
510 #[rstest]
511 fn test_data_type_inequality() {
512 let metadata1 = Some(
513 [("key1".to_string(), "value1".to_string())]
514 .iter()
515 .cloned()
516 .collect(),
517 );
518 let metadata2 = Some(
519 [("key2".to_string(), "value2".to_string())]
520 .iter()
521 .cloned()
522 .collect(),
523 );
524
525 let data_type1 = DataType::new("ExampleType", metadata1);
526 let data_type2 = DataType::new("ExampleType", metadata2);
527
528 assert_ne!(data_type1, data_type2);
529 }
530
531 #[rstest]
532 fn test_data_type_ordering() {
533 let metadata1 = Some(
534 [("key1".to_string(), "value1".to_string())]
535 .iter()
536 .cloned()
537 .collect(),
538 );
539 let metadata2 = Some(
540 [("key2".to_string(), "value2".to_string())]
541 .iter()
542 .cloned()
543 .collect(),
544 );
545
546 let data_type1 = DataType::new("ExampleTypeA", metadata1);
547 let data_type2 = DataType::new("ExampleTypeB", metadata2);
548
549 assert!(data_type1 < data_type2);
550 }
551
552 #[rstest]
553 fn test_data_type_hash() {
554 let metadata = Some(
555 [("key1".to_string(), "value1".to_string())]
556 .iter()
557 .cloned()
558 .collect(),
559 );
560
561 let data_type1 = DataType::new("ExampleType", metadata.clone());
562 let data_type2 = DataType::new("ExampleType", metadata.clone());
563
564 let mut hasher1 = DefaultHasher::new();
565 data_type1.hash(&mut hasher1);
566 let hash1 = hasher1.finish();
567
568 let mut hasher2 = DefaultHasher::new();
569 data_type2.hash(&mut hasher2);
570 let hash2 = hasher2.finish();
571
572 assert_eq!(hash1, hash2);
573 }
574
575 #[test]
576 fn test_data_type_display() {
577 let metadata = Some(
578 [("key1".to_string(), "value1".to_string())]
579 .iter()
580 .cloned()
581 .collect(),
582 );
583 let data_type = DataType::new("ExampleType", metadata);
584
585 assert_eq!(format!("{}", data_type), "ExampleType.key1=value1");
586 }
587
588 #[test]
589 fn test_data_type_debug() {
590 let metadata = Some(
591 [("key1".to_string(), "value1".to_string())]
592 .iter()
593 .cloned()
594 .collect(),
595 );
596 let data_type = DataType::new("ExampleType", metadata.clone());
597
598 assert_eq!(
599 format!("{:?}", data_type),
600 format!("DataType(type_name=ExampleType, metadata={:?})", metadata)
601 );
602 }
603
604 #[rstest]
605 fn test_parse_instrument_id_from_metadata() {
606 let instrument_id_str = "MSFT.XNAS";
607 let metadata = Some(
608 [("instrument_id".to_string(), instrument_id_str.to_string())]
609 .iter()
610 .cloned()
611 .collect(),
612 );
613 let data_type = DataType::new("InstrumentAny", metadata);
614
615 assert_eq!(
616 data_type.instrument_id().unwrap(),
617 InstrumentId::from_str(instrument_id_str).unwrap()
618 );
619 }
620
621 #[rstest]
622 fn test_parse_venue_from_metadata() {
623 let venue_str = "BINANCE";
624 let metadata = Some(
625 [("venue".to_string(), venue_str.to_string())]
626 .iter()
627 .cloned()
628 .collect(),
629 );
630 let data_type = DataType::new(stringify!(InstrumentAny), metadata);
631
632 assert_eq!(data_type.venue().unwrap(), Venue::new(venue_str));
633 }
634
635 #[rstest]
636 fn test_parse_bar_type_from_metadata() {
637 let bar_type_str = "MSFT.XNAS-1000-TICK-LAST-INTERNAL";
638 let metadata = Some(
639 [("bar_type".to_string(), bar_type_str.to_string())]
640 .iter()
641 .cloned()
642 .collect(),
643 );
644 let data_type = DataType::new(stringify!(BarType), metadata);
645
646 assert_eq!(
647 data_type.bar_type(),
648 BarType::from_str(bar_type_str).unwrap()
649 );
650 }
651
652 #[rstest]
653 fn test_parse_start_from_metadata() {
654 let start_ns = 1600054595844758000;
655 let metadata = Some(
656 [("start".to_string(), start_ns.to_string())]
657 .iter()
658 .cloned()
659 .collect(),
660 );
661 let data_type = DataType::new(stringify!(TradeTick), metadata);
662
663 assert_eq!(data_type.start().unwrap(), UnixNanos::from(start_ns),);
664 }
665
666 #[rstest]
667 fn test_parse_end_from_metadata() {
668 let end_ns = 1720954595844758000;
669 let metadata = Some(
670 [("end".to_string(), end_ns.to_string())]
671 .iter()
672 .cloned()
673 .collect(),
674 );
675 let data_type = DataType::new(stringify!(TradeTick), metadata);
676
677 assert_eq!(data_type.end().unwrap(), UnixNanos::from(end_ns),);
678 }
679
680 #[rstest]
681 fn test_parse_limit_from_metadata() {
682 let limit = 1000;
683 let metadata = Some(
684 [("limit".to_string(), limit.to_string())]
685 .iter()
686 .cloned()
687 .collect(),
688 );
689 let data_type = DataType::new(stringify!(TradeTick), metadata);
690
691 assert_eq!(data_type.limit().unwrap(), limit);
692 }
693
694 #[rstest]
695 fn test_parse_book_type_from_metadata() {
696 let book_type_str = "L3_MBO";
697 let metadata = Some(
698 [("book_type".to_string(), book_type_str.to_string())]
699 .iter()
700 .cloned()
701 .collect(),
702 );
703 let data_type = DataType::new(stringify!(OrderBookDelta), metadata);
704
705 assert_eq!(
706 data_type.book_type(),
707 BookType::from_str(book_type_str).unwrap()
708 );
709 }
710
711 #[rstest]
712 fn test_parse_depth_from_metadata() {
713 let depth = 25;
714 let metadata = Some(
715 [("depth".to_string(), depth.to_string())]
716 .iter()
717 .cloned()
718 .collect(),
719 );
720 let data_type = DataType::new(stringify!(OrderBookDeltas), metadata);
721
722 assert_eq!(data_type.depth().unwrap(), depth);
723 }
724}