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