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)]
245#[cfg_attr(
246 feature = "python",
247 pyo3_stub_gen::derive::gen_stub_pyclass(module = "nautilus_trader.model")
248)]
249pub struct DataType {
250 type_name: String,
251 metadata: Option<IndexMap<String, String>>,
252 topic: String,
253 hash: u64,
254}
255
256impl DataType {
257 pub fn new(type_name: &str, metadata: Option<IndexMap<String, String>>) -> Self {
259 let topic = if let Some(ref meta) = metadata {
261 let meta_str = meta
262 .iter()
263 .map(|(k, v)| format!("{k}={v}"))
264 .collect::<Vec<_>>()
265 .join(".");
266 format!("{type_name}.{meta_str}")
267 } else {
268 type_name.to_string()
269 };
270
271 let mut hasher = std::collections::hash_map::DefaultHasher::new();
273 topic.hash(&mut hasher);
274
275 Self {
276 type_name: type_name.to_owned(),
277 metadata,
278 topic,
279 hash: hasher.finish(),
280 }
281 }
282
283 pub fn type_name(&self) -> &str {
285 self.type_name.as_str()
286 }
287
288 pub fn metadata(&self) -> Option<&IndexMap<String, String>> {
290 self.metadata.as_ref()
291 }
292
293 pub fn metadata_str(&self) -> String {
295 self.metadata
296 .as_ref()
297 .map(|metadata| to_string(metadata).unwrap_or_default())
298 .unwrap_or_else(|| "null".to_string())
299 }
300
301 pub fn topic(&self) -> &str {
303 self.topic.as_str()
304 }
305
306 pub fn instrument_id(&self) -> Option<InstrumentId> {
314 let metadata = self.metadata.as_ref().expect("metadata was `None`");
315 let instrument_id = metadata.get("instrument_id")?;
316 Some(
317 InstrumentId::from_str(instrument_id)
318 .expect("Invalid `InstrumentId` for 'instrument_id'"),
319 )
320 }
321
322 pub fn venue(&self) -> Option<Venue> {
330 let metadata = self.metadata.as_ref().expect("metadata was `None`");
331 let venue_str = metadata.get("venue")?;
332 Some(Venue::from(venue_str.as_str()))
333 }
334
335 pub fn start(&self) -> Option<UnixNanos> {
343 let metadata = self.metadata.as_ref()?;
344 let start_str = metadata.get("start")?;
345 Some(UnixNanos::from_str(start_str).expect("Invalid `UnixNanos` for 'start'"))
346 }
347
348 pub fn end(&self) -> Option<UnixNanos> {
356 let metadata = self.metadata.as_ref()?;
357 let end_str = metadata.get("end")?;
358 Some(UnixNanos::from_str(end_str).expect("Invalid `UnixNanos` for 'end'"))
359 }
360
361 pub fn limit(&self) -> Option<usize> {
369 let metadata = self.metadata.as_ref()?;
370 let depth_str = metadata.get("limit")?;
371 Some(
372 depth_str
373 .parse::<usize>()
374 .expect("Invalid `usize` for 'limit'"),
375 )
376 }
377}
378
379impl PartialEq for DataType {
380 fn eq(&self, other: &Self) -> bool {
381 self.topic == other.topic
382 }
383}
384
385impl Eq for DataType {}
386
387impl PartialOrd for DataType {
388 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
389 Some(self.cmp(other))
390 }
391}
392
393impl Ord for DataType {
394 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
395 self.topic.cmp(&other.topic)
396 }
397}
398
399impl Hash for DataType {
400 fn hash<H: Hasher>(&self, state: &mut H) {
401 self.hash.hash(state);
402 }
403}
404
405impl Display for DataType {
406 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
407 write!(f, "{}", self.topic)
408 }
409}
410
411impl Debug for DataType {
412 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
413 write!(
414 f,
415 "DataType(type_name={}, metadata={:?})",
416 self.type_name, self.metadata
417 )
418 }
419}
420
421#[cfg(test)]
425mod tests {
426 use std::hash::DefaultHasher;
427
428 use rstest::*;
429
430 use super::*;
431
432 #[rstest]
433 fn test_data_type_creation_with_metadata() {
434 let metadata = Some(
435 [
436 ("key1".to_string(), "value1".to_string()),
437 ("key2".to_string(), "value2".to_string()),
438 ]
439 .iter()
440 .cloned()
441 .collect(),
442 );
443 let data_type = DataType::new("ExampleType", metadata.clone());
444
445 assert_eq!(data_type.type_name(), "ExampleType");
446 assert_eq!(data_type.topic(), "ExampleType.key1=value1.key2=value2");
447 assert_eq!(data_type.metadata(), metadata.as_ref());
448 }
449
450 #[rstest]
451 fn test_data_type_creation_without_metadata() {
452 let data_type = DataType::new("ExampleType", None);
453
454 assert_eq!(data_type.type_name(), "ExampleType");
455 assert_eq!(data_type.topic(), "ExampleType");
456 assert_eq!(data_type.metadata(), None);
457 }
458
459 #[rstest]
460 fn test_data_type_equality() {
461 let metadata1 = Some(
462 [("key1".to_string(), "value1".to_string())]
463 .iter()
464 .cloned()
465 .collect(),
466 );
467 let metadata2 = Some(
468 [("key1".to_string(), "value1".to_string())]
469 .iter()
470 .cloned()
471 .collect(),
472 );
473
474 let data_type1 = DataType::new("ExampleType", metadata1);
475 let data_type2 = DataType::new("ExampleType", metadata2);
476
477 assert_eq!(data_type1, data_type2);
478 }
479
480 #[rstest]
481 fn test_data_type_inequality() {
482 let metadata1 = Some(
483 [("key1".to_string(), "value1".to_string())]
484 .iter()
485 .cloned()
486 .collect(),
487 );
488 let metadata2 = Some(
489 [("key2".to_string(), "value2".to_string())]
490 .iter()
491 .cloned()
492 .collect(),
493 );
494
495 let data_type1 = DataType::new("ExampleType", metadata1);
496 let data_type2 = DataType::new("ExampleType", metadata2);
497
498 assert_ne!(data_type1, data_type2);
499 }
500
501 #[rstest]
502 fn test_data_type_ordering() {
503 let metadata1 = Some(
504 [("key1".to_string(), "value1".to_string())]
505 .iter()
506 .cloned()
507 .collect(),
508 );
509 let metadata2 = Some(
510 [("key2".to_string(), "value2".to_string())]
511 .iter()
512 .cloned()
513 .collect(),
514 );
515
516 let data_type1 = DataType::new("ExampleTypeA", metadata1);
517 let data_type2 = DataType::new("ExampleTypeB", metadata2);
518
519 assert!(data_type1 < data_type2);
520 }
521
522 #[rstest]
523 fn test_data_type_hash() {
524 let metadata = Some(
525 [("key1".to_string(), "value1".to_string())]
526 .iter()
527 .cloned()
528 .collect(),
529 );
530
531 let data_type1 = DataType::new("ExampleType", metadata.clone());
532 let data_type2 = DataType::new("ExampleType", metadata.clone());
533
534 let mut hasher1 = DefaultHasher::new();
535 data_type1.hash(&mut hasher1);
536 let hash1 = hasher1.finish();
537
538 let mut hasher2 = DefaultHasher::new();
539 data_type2.hash(&mut hasher2);
540 let hash2 = hasher2.finish();
541
542 assert_eq!(hash1, hash2);
543 }
544
545 #[rstest]
546 fn test_data_type_display() {
547 let metadata = Some(
548 [("key1".to_string(), "value1".to_string())]
549 .iter()
550 .cloned()
551 .collect(),
552 );
553 let data_type = DataType::new("ExampleType", metadata);
554
555 assert_eq!(format!("{data_type}"), "ExampleType.key1=value1");
556 }
557
558 #[rstest]
559 fn test_data_type_debug() {
560 let metadata = Some(
561 [("key1".to_string(), "value1".to_string())]
562 .iter()
563 .cloned()
564 .collect(),
565 );
566 let data_type = DataType::new("ExampleType", metadata.clone());
567
568 assert_eq!(
569 format!("{data_type:?}"),
570 format!("DataType(type_name=ExampleType, metadata={metadata:?})")
571 );
572 }
573
574 #[rstest]
575 fn test_parse_instrument_id_from_metadata() {
576 let instrument_id_str = "MSFT.XNAS";
577 let metadata = Some(
578 [("instrument_id".to_string(), instrument_id_str.to_string())]
579 .iter()
580 .cloned()
581 .collect(),
582 );
583 let data_type = DataType::new("InstrumentAny", metadata);
584
585 assert_eq!(
586 data_type.instrument_id().unwrap(),
587 InstrumentId::from_str(instrument_id_str).unwrap()
588 );
589 }
590
591 #[rstest]
592 fn test_parse_venue_from_metadata() {
593 let venue_str = "BINANCE";
594 let metadata = Some(
595 [("venue".to_string(), venue_str.to_string())]
596 .iter()
597 .cloned()
598 .collect(),
599 );
600 let data_type = DataType::new(stringify!(InstrumentAny), metadata);
601
602 assert_eq!(data_type.venue().unwrap(), Venue::new(venue_str));
603 }
604
605 #[rstest]
606 fn test_parse_start_from_metadata() {
607 let start_ns = 1600054595844758000;
608 let metadata = Some(
609 [("start".to_string(), start_ns.to_string())]
610 .iter()
611 .cloned()
612 .collect(),
613 );
614 let data_type = DataType::new(stringify!(TradeTick), metadata);
615
616 assert_eq!(data_type.start().unwrap(), UnixNanos::from(start_ns),);
617 }
618
619 #[rstest]
620 fn test_parse_end_from_metadata() {
621 let end_ns = 1720954595844758000;
622 let metadata = Some(
623 [("end".to_string(), end_ns.to_string())]
624 .iter()
625 .cloned()
626 .collect(),
627 );
628 let data_type = DataType::new(stringify!(TradeTick), metadata);
629
630 assert_eq!(data_type.end().unwrap(), UnixNanos::from(end_ns),);
631 }
632
633 #[rstest]
634 fn test_parse_limit_from_metadata() {
635 let limit = 1000;
636 let metadata = Some(
637 [("limit".to_string(), limit.to_string())]
638 .iter()
639 .cloned()
640 .collect(),
641 );
642 let data_type = DataType::new(stringify!(TradeTick), metadata);
643
644 assert_eq!(data_type.limit().unwrap(), limit);
645 }
646}