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