1pub mod bar;
19pub mod bet;
20pub mod close;
21pub mod delta;
22pub mod deltas;
23pub mod depth;
24pub mod funding;
25pub mod greeks;
26pub mod order;
27pub mod prices;
28pub mod quote;
29pub mod status;
30pub mod trade;
31
32#[cfg(any(test, feature = "stubs"))]
33pub mod stubs;
34
35use std::{
36 fmt::{Debug, Display},
37 hash::{Hash, Hasher},
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 close::InstrumentClose;
50pub use delta::OrderBookDelta;
51pub use deltas::{OrderBookDeltas, OrderBookDeltas_API};
52pub use depth::{DEPTH10_LEN, OrderBookDepth10};
53pub use funding::FundingRateUpdate;
54pub use greeks::{
55 BlackScholesGreeksResult, GreeksData, PortfolioGreeks, YieldCurveData, black_scholes_greeks,
56 imply_vol_and_greeks,
57};
58pub use order::{BookOrder, NULL_ORDER};
59pub use prices::{IndexPriceUpdate, MarkPriceUpdate};
60pub use quote::QuoteTick;
61pub use status::InstrumentStatus;
62pub use trade::TradeTick;
63
64use crate::identifiers::{InstrumentId, Venue};
65
66#[repr(C)]
71#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
72pub enum Data {
73 Delta(OrderBookDelta),
74 Deltas(OrderBookDeltas_API),
75 Depth10(Box<OrderBookDepth10>), Quote(QuoteTick),
77 Trade(TradeTick),
78 Bar(Bar),
79 MarkPriceUpdate(MarkPriceUpdate), IndexPriceUpdate(IndexPriceUpdate), InstrumentClose(InstrumentClose),
82}
83
84macro_rules! impl_try_from_data {
85 ($variant:ident, $type:ty) => {
86 impl TryFrom<Data> for $type {
87 type Error = ();
88
89 fn try_from(value: Data) -> Result<Self, Self::Error> {
90 match value {
91 Data::$variant(x) => Ok(x),
92 _ => Err(()),
93 }
94 }
95 }
96 };
97}
98
99impl TryFrom<Data> for OrderBookDepth10 {
100 type Error = ();
101
102 fn try_from(value: Data) -> Result<Self, Self::Error> {
103 match value {
104 Data::Depth10(x) => Ok(*x),
105 _ => Err(()),
106 }
107 }
108}
109
110impl_try_from_data!(Quote, QuoteTick);
111impl_try_from_data!(Delta, OrderBookDelta);
112impl_try_from_data!(Deltas, OrderBookDeltas_API);
113impl_try_from_data!(Trade, TradeTick);
114impl_try_from_data!(Bar, Bar);
115impl_try_from_data!(MarkPriceUpdate, MarkPriceUpdate);
116impl_try_from_data!(IndexPriceUpdate, IndexPriceUpdate);
117impl_try_from_data!(InstrumentClose, InstrumentClose);
118
119pub fn to_variant<T: TryFrom<Data>>(data: Vec<Data>) -> Vec<T> {
124 data.into_iter()
125 .filter_map(|d| T::try_from(d).ok())
126 .collect()
127}
128
129impl Data {
130 pub fn instrument_id(&self) -> InstrumentId {
132 match self {
133 Self::Delta(delta) => delta.instrument_id,
134 Self::Deltas(deltas) => deltas.instrument_id,
135 Self::Depth10(depth) => depth.instrument_id,
136 Self::Quote(quote) => quote.instrument_id,
137 Self::Trade(trade) => trade.instrument_id,
138 Self::Bar(bar) => bar.bar_type.instrument_id(),
139 Self::MarkPriceUpdate(mark_price) => mark_price.instrument_id,
140 Self::IndexPriceUpdate(index_price) => index_price.instrument_id,
141 Self::InstrumentClose(close) => close.instrument_id,
142 }
143 }
144
145 pub fn is_order_book_data(&self) -> bool {
147 matches!(self, Self::Delta(_) | Self::Deltas(_) | Self::Depth10(_))
148 }
149}
150
151pub trait HasTsInit {
157 fn ts_init(&self) -> UnixNanos;
159}
160
161impl HasTsInit for Data {
162 fn ts_init(&self) -> UnixNanos {
163 match self {
164 Self::Delta(d) => d.ts_init,
165 Self::Deltas(d) => d.ts_init,
166 Self::Depth10(d) => d.ts_init,
167 Self::Quote(q) => q.ts_init,
168 Self::Trade(t) => t.ts_init,
169 Self::Bar(b) => b.ts_init,
170 Self::MarkPriceUpdate(p) => p.ts_init,
171 Self::IndexPriceUpdate(p) => p.ts_init,
172 Self::InstrumentClose(c) => c.ts_init,
173 }
174 }
175}
176
177pub fn is_monotonically_increasing_by_init<T: HasTsInit>(data: &[T]) -> bool {
181 data.windows(2)
182 .all(|window| window[0].ts_init() <= window[1].ts_init())
183}
184
185impl From<OrderBookDelta> for Data {
186 fn from(value: OrderBookDelta) -> Self {
187 Self::Delta(value)
188 }
189}
190
191impl From<OrderBookDeltas_API> for Data {
192 fn from(value: OrderBookDeltas_API) -> Self {
193 Self::Deltas(value)
194 }
195}
196
197impl From<OrderBookDepth10> for Data {
198 fn from(value: OrderBookDepth10) -> Self {
199 Self::Depth10(Box::new(value))
200 }
201}
202
203impl From<QuoteTick> for Data {
204 fn from(value: QuoteTick) -> Self {
205 Self::Quote(value)
206 }
207}
208
209impl From<TradeTick> for Data {
210 fn from(value: TradeTick) -> Self {
211 Self::Trade(value)
212 }
213}
214
215impl From<Bar> for Data {
216 fn from(value: Bar) -> Self {
217 Self::Bar(value)
218 }
219}
220
221impl From<MarkPriceUpdate> for Data {
222 fn from(value: MarkPriceUpdate) -> Self {
223 Self::MarkPriceUpdate(value)
224 }
225}
226
227impl From<IndexPriceUpdate> for Data {
228 fn from(value: IndexPriceUpdate) -> Self {
229 Self::IndexPriceUpdate(value)
230 }
231}
232
233impl From<InstrumentClose> for Data {
234 fn from(value: InstrumentClose) -> Self {
235 Self::InstrumentClose(value)
236 }
237}
238
239#[derive(Clone, Serialize, Deserialize)]
241#[cfg_attr(
242 feature = "python",
243 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
244)]
245pub struct DataType {
246 type_name: String,
247 metadata: Option<IndexMap<String, String>>,
248 topic: String,
249 hash: u64,
250}
251
252impl DataType {
253 pub fn new(type_name: &str, metadata: Option<IndexMap<String, String>>) -> Self {
255 let topic = if let Some(ref meta) = metadata {
257 let meta_str = meta
258 .iter()
259 .map(|(k, v)| format!("{k}={v}"))
260 .collect::<Vec<_>>()
261 .join(".");
262 format!("{type_name}.{meta_str}")
263 } else {
264 type_name.to_string()
265 };
266
267 let mut hasher = std::collections::hash_map::DefaultHasher::new();
269 topic.hash(&mut hasher);
270
271 Self {
272 type_name: type_name.to_owned(),
273 metadata,
274 topic,
275 hash: hasher.finish(),
276 }
277 }
278
279 pub fn type_name(&self) -> &str {
281 self.type_name.as_str()
282 }
283
284 pub fn metadata(&self) -> Option<&IndexMap<String, String>> {
286 self.metadata.as_ref()
287 }
288
289 pub fn metadata_str(&self) -> String {
291 self.metadata
292 .as_ref()
293 .map(|metadata| to_string(metadata).unwrap_or_default())
294 .unwrap_or_else(|| "null".to_string())
295 }
296
297 pub fn topic(&self) -> &str {
299 self.topic.as_str()
300 }
301
302 pub fn instrument_id(&self) -> Option<InstrumentId> {
310 let metadata = self.metadata.as_ref().expect("metadata was `None`");
311 let instrument_id = metadata.get("instrument_id")?;
312 Some(
313 InstrumentId::from_str(instrument_id)
314 .expect("Invalid `InstrumentId` for 'instrument_id'"),
315 )
316 }
317
318 pub fn venue(&self) -> Option<Venue> {
326 let metadata = self.metadata.as_ref().expect("metadata was `None`");
327 let venue_str = metadata.get("venue")?;
328 Some(Venue::from(venue_str.as_str()))
329 }
330
331 pub fn start(&self) -> Option<UnixNanos> {
339 let metadata = self.metadata.as_ref()?;
340 let start_str = metadata.get("start")?;
341 Some(UnixNanos::from_str(start_str).expect("Invalid `UnixNanos` for 'start'"))
342 }
343
344 pub fn end(&self) -> Option<UnixNanos> {
352 let metadata = self.metadata.as_ref()?;
353 let end_str = metadata.get("end")?;
354 Some(UnixNanos::from_str(end_str).expect("Invalid `UnixNanos` for 'end'"))
355 }
356
357 pub fn limit(&self) -> Option<usize> {
365 let metadata = self.metadata.as_ref()?;
366 let depth_str = metadata.get("limit")?;
367 Some(
368 depth_str
369 .parse::<usize>()
370 .expect("Invalid `usize` for 'limit'"),
371 )
372 }
373}
374
375impl PartialEq for DataType {
376 fn eq(&self, other: &Self) -> bool {
377 self.topic == other.topic
378 }
379}
380
381impl Eq for DataType {}
382
383impl PartialOrd for DataType {
384 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
385 Some(self.cmp(other))
386 }
387}
388
389impl Ord for DataType {
390 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
391 self.topic.cmp(&other.topic)
392 }
393}
394
395impl Hash for DataType {
396 fn hash<H: Hasher>(&self, state: &mut H) {
397 self.hash.hash(state);
398 }
399}
400
401impl Display for DataType {
402 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
403 write!(f, "{}", self.topic)
404 }
405}
406
407impl Debug for DataType {
408 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
409 write!(
410 f,
411 "DataType(type_name={}, metadata={:?})",
412 self.type_name, self.metadata
413 )
414 }
415}
416
417#[cfg(test)]
421mod tests {
422 use std::hash::DefaultHasher;
423
424 use rstest::*;
425
426 use super::*;
427
428 #[rstest]
429 fn test_data_type_creation_with_metadata() {
430 let metadata = Some(
431 [
432 ("key1".to_string(), "value1".to_string()),
433 ("key2".to_string(), "value2".to_string()),
434 ]
435 .iter()
436 .cloned()
437 .collect(),
438 );
439 let data_type = DataType::new("ExampleType", metadata.clone());
440
441 assert_eq!(data_type.type_name(), "ExampleType");
442 assert_eq!(data_type.topic(), "ExampleType.key1=value1.key2=value2");
443 assert_eq!(data_type.metadata(), metadata.as_ref());
444 }
445
446 #[rstest]
447 fn test_data_type_creation_without_metadata() {
448 let data_type = DataType::new("ExampleType", None);
449
450 assert_eq!(data_type.type_name(), "ExampleType");
451 assert_eq!(data_type.topic(), "ExampleType");
452 assert_eq!(data_type.metadata(), None);
453 }
454
455 #[rstest]
456 fn test_data_type_equality() {
457 let metadata1 = Some(
458 [("key1".to_string(), "value1".to_string())]
459 .iter()
460 .cloned()
461 .collect(),
462 );
463 let metadata2 = Some(
464 [("key1".to_string(), "value1".to_string())]
465 .iter()
466 .cloned()
467 .collect(),
468 );
469
470 let data_type1 = DataType::new("ExampleType", metadata1);
471 let data_type2 = DataType::new("ExampleType", metadata2);
472
473 assert_eq!(data_type1, data_type2);
474 }
475
476 #[rstest]
477 fn test_data_type_inequality() {
478 let metadata1 = Some(
479 [("key1".to_string(), "value1".to_string())]
480 .iter()
481 .cloned()
482 .collect(),
483 );
484 let metadata2 = Some(
485 [("key2".to_string(), "value2".to_string())]
486 .iter()
487 .cloned()
488 .collect(),
489 );
490
491 let data_type1 = DataType::new("ExampleType", metadata1);
492 let data_type2 = DataType::new("ExampleType", metadata2);
493
494 assert_ne!(data_type1, data_type2);
495 }
496
497 #[rstest]
498 fn test_data_type_ordering() {
499 let metadata1 = Some(
500 [("key1".to_string(), "value1".to_string())]
501 .iter()
502 .cloned()
503 .collect(),
504 );
505 let metadata2 = Some(
506 [("key2".to_string(), "value2".to_string())]
507 .iter()
508 .cloned()
509 .collect(),
510 );
511
512 let data_type1 = DataType::new("ExampleTypeA", metadata1);
513 let data_type2 = DataType::new("ExampleTypeB", metadata2);
514
515 assert!(data_type1 < data_type2);
516 }
517
518 #[rstest]
519 fn test_data_type_hash() {
520 let metadata = Some(
521 [("key1".to_string(), "value1".to_string())]
522 .iter()
523 .cloned()
524 .collect(),
525 );
526
527 let data_type1 = DataType::new("ExampleType", metadata.clone());
528 let data_type2 = DataType::new("ExampleType", metadata.clone());
529
530 let mut hasher1 = DefaultHasher::new();
531 data_type1.hash(&mut hasher1);
532 let hash1 = hasher1.finish();
533
534 let mut hasher2 = DefaultHasher::new();
535 data_type2.hash(&mut hasher2);
536 let hash2 = hasher2.finish();
537
538 assert_eq!(hash1, hash2);
539 }
540
541 #[rstest]
542 fn test_data_type_display() {
543 let metadata = Some(
544 [("key1".to_string(), "value1".to_string())]
545 .iter()
546 .cloned()
547 .collect(),
548 );
549 let data_type = DataType::new("ExampleType", metadata);
550
551 assert_eq!(format!("{data_type}"), "ExampleType.key1=value1");
552 }
553
554 #[rstest]
555 fn test_data_type_debug() {
556 let metadata = Some(
557 [("key1".to_string(), "value1".to_string())]
558 .iter()
559 .cloned()
560 .collect(),
561 );
562 let data_type = DataType::new("ExampleType", metadata.clone());
563
564 assert_eq!(
565 format!("{data_type:?}"),
566 format!("DataType(type_name=ExampleType, metadata={metadata:?})")
567 );
568 }
569
570 #[rstest]
571 fn test_parse_instrument_id_from_metadata() {
572 let instrument_id_str = "MSFT.XNAS";
573 let metadata = Some(
574 [("instrument_id".to_string(), instrument_id_str.to_string())]
575 .iter()
576 .cloned()
577 .collect(),
578 );
579 let data_type = DataType::new("InstrumentAny", metadata);
580
581 assert_eq!(
582 data_type.instrument_id().unwrap(),
583 InstrumentId::from_str(instrument_id_str).unwrap()
584 );
585 }
586
587 #[rstest]
588 fn test_parse_venue_from_metadata() {
589 let venue_str = "BINANCE";
590 let metadata = Some(
591 [("venue".to_string(), venue_str.to_string())]
592 .iter()
593 .cloned()
594 .collect(),
595 );
596 let data_type = DataType::new(stringify!(InstrumentAny), metadata);
597
598 assert_eq!(data_type.venue().unwrap(), Venue::new(venue_str));
599 }
600
601 #[rstest]
602 fn test_parse_start_from_metadata() {
603 let start_ns = 1600054595844758000;
604 let metadata = Some(
605 [("start".to_string(), start_ns.to_string())]
606 .iter()
607 .cloned()
608 .collect(),
609 );
610 let data_type = DataType::new(stringify!(TradeTick), metadata);
611
612 assert_eq!(data_type.start().unwrap(), UnixNanos::from(start_ns),);
613 }
614
615 #[rstest]
616 fn test_parse_end_from_metadata() {
617 let end_ns = 1720954595844758000;
618 let metadata = Some(
619 [("end".to_string(), end_ns.to_string())]
620 .iter()
621 .cloned()
622 .collect(),
623 );
624 let data_type = DataType::new(stringify!(TradeTick), metadata);
625
626 assert_eq!(data_type.end().unwrap(), UnixNanos::from(end_ns),);
627 }
628
629 #[rstest]
630 fn test_parse_limit_from_metadata() {
631 let limit = 1000;
632 let metadata = Some(
633 [("limit".to_string(), limit.to_string())]
634 .iter()
635 .cloned()
636 .collect(),
637 );
638 let data_type = DataType::new(stringify!(TradeTick), metadata);
639
640 assert_eq!(data_type.limit().unwrap(), limit);
641 }
642}