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