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