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(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> {
118 data.into_iter()
119 .filter_map(|d| T::try_from(d).ok())
120 .collect()
121}
122
123impl Data {
124 pub fn instrument_id(&self) -> InstrumentId {
126 match self {
127 Self::Delta(delta) => delta.instrument_id,
128 Self::Deltas(deltas) => deltas.instrument_id,
129 Self::Depth10(depth) => depth.instrument_id,
130 Self::Quote(quote) => quote.instrument_id,
131 Self::Trade(trade) => trade.instrument_id,
132 Self::Bar(bar) => bar.bar_type.instrument_id(),
133 Self::MarkPriceUpdate(mark_price) => mark_price.instrument_id,
134 Self::IndexPriceUpdate(index_price) => index_price.instrument_id,
135 Self::InstrumentClose(close) => close.instrument_id,
136 }
137 }
138
139 pub fn is_order_book_data(&self) -> bool {
141 matches!(self, Self::Delta(_) | Self::Deltas(_) | Self::Depth10(_))
142 }
143}
144
145pub trait GetTsInit {
146 fn ts_init(&self) -> UnixNanos;
147}
148
149impl GetTsInit for Data {
150 fn ts_init(&self) -> UnixNanos {
151 match self {
152 Self::Delta(d) => d.ts_init,
153 Self::Deltas(d) => d.ts_init,
154 Self::Depth10(d) => d.ts_init,
155 Self::Quote(q) => q.ts_init,
156 Self::Trade(t) => t.ts_init,
157 Self::Bar(b) => b.ts_init,
158 Self::MarkPriceUpdate(p) => p.ts_init,
159 Self::IndexPriceUpdate(p) => p.ts_init,
160 Self::InstrumentClose(c) => c.ts_init,
161 }
162 }
163}
164
165pub fn is_monotonically_increasing_by_init<T: GetTsInit>(data: &[T]) -> bool {
166 data.windows(2)
167 .all(|window| window[0].ts_init() <= window[1].ts_init())
168}
169
170impl From<OrderBookDelta> for Data {
171 fn from(value: OrderBookDelta) -> Self {
172 Self::Delta(value)
173 }
174}
175
176impl From<OrderBookDeltas_API> for Data {
177 fn from(value: OrderBookDeltas_API) -> Self {
178 Self::Deltas(value)
179 }
180}
181
182impl From<OrderBookDepth10> for Data {
183 fn from(value: OrderBookDepth10) -> Self {
184 Self::Depth10(Box::new(value))
185 }
186}
187
188impl From<QuoteTick> for Data {
189 fn from(value: QuoteTick) -> Self {
190 Self::Quote(value)
191 }
192}
193
194impl From<TradeTick> for Data {
195 fn from(value: TradeTick) -> Self {
196 Self::Trade(value)
197 }
198}
199
200impl From<Bar> for Data {
201 fn from(value: Bar) -> Self {
202 Self::Bar(value)
203 }
204}
205
206impl From<MarkPriceUpdate> for Data {
207 fn from(value: MarkPriceUpdate) -> Self {
208 Self::MarkPriceUpdate(value)
209 }
210}
211
212impl From<IndexPriceUpdate> for Data {
213 fn from(value: IndexPriceUpdate) -> Self {
214 Self::IndexPriceUpdate(value)
215 }
216}
217
218impl From<InstrumentClose> for Data {
219 fn from(value: InstrumentClose) -> Self {
220 Self::InstrumentClose(value)
221 }
222}
223
224#[unsafe(no_mangle)]
228#[cfg_attr(feature = "high-precision", allow(improper_ctypes_definitions))]
229pub extern "C" fn data_clone(data: &Data) -> Data {
230 data.clone()
232}
233
234#[derive(Clone, Serialize, Deserialize)]
236#[cfg_attr(
237 feature = "python",
238 pyo3::pyclass(module = "nautilus_trader.core.nautilus_pyo3.model")
239)]
240pub struct DataType {
241 type_name: String,
242 metadata: Option<IndexMap<String, String>>,
243 topic: String,
244 hash: u64,
245}
246
247impl DataType {
248 pub fn new(type_name: &str, metadata: Option<IndexMap<String, String>>) -> Self {
250 let topic = if let Some(ref meta) = metadata {
252 let meta_str = meta
253 .iter()
254 .map(|(k, v)| format!("{}={}", k, v))
255 .collect::<Vec<_>>()
256 .join(".");
257 format!("{}.{}", type_name, meta_str)
258 } else {
259 type_name.to_string()
260 };
261
262 let mut hasher = std::collections::hash_map::DefaultHasher::new();
264 topic.hash(&mut hasher);
265
266 Self {
267 type_name: type_name.to_owned(),
268 metadata,
269 topic,
270 hash: hasher.finish(),
271 }
272 }
273
274 pub fn type_name(&self) -> &str {
276 self.type_name.as_str()
277 }
278
279 pub fn metadata(&self) -> Option<&IndexMap<String, String>> {
281 self.metadata.as_ref()
282 }
283
284 pub fn metadata_str(&self) -> String {
286 self.metadata
287 .as_ref()
288 .map(|metadata| to_string(metadata).unwrap_or_default())
289 .unwrap_or_else(|| "null".to_string())
290 }
291
292 pub fn topic(&self) -> &str {
294 self.topic.as_str()
295 }
296
297 pub fn instrument_id(&self) -> Option<InstrumentId> {
305 let metadata = self.metadata.as_ref().expect("metadata was `None`");
306 let instrument_id = metadata.get("instrument_id")?;
307 Some(
308 InstrumentId::from_str(instrument_id)
309 .expect("Invalid `InstrumentId` for 'instrument_id'"),
310 )
311 }
312
313 pub fn venue(&self) -> Option<Venue> {
321 let metadata = self.metadata.as_ref().expect("metadata was `None`");
322 let venue_str = metadata.get("venue")?;
323 Some(Venue::from(venue_str.as_str()))
324 }
325
326 pub fn start(&self) -> Option<UnixNanos> {
334 let metadata = self.metadata.as_ref()?;
335 let start_str = metadata.get("start")?;
336 Some(UnixNanos::from_str(start_str).expect("Invalid `UnixNanos` for 'start'"))
337 }
338
339 pub fn end(&self) -> Option<UnixNanos> {
347 let metadata = self.metadata.as_ref()?;
348 let end_str = metadata.get("end")?;
349 Some(UnixNanos::from_str(end_str).expect("Invalid `UnixNanos` for 'end'"))
350 }
351
352 pub fn limit(&self) -> Option<usize> {
360 let metadata = self.metadata.as_ref()?;
361 let depth_str = metadata.get("limit")?;
362 Some(
363 depth_str
364 .parse::<usize>()
365 .expect("Invalid `usize` for 'limit'"),
366 )
367 }
368}
369
370impl PartialEq for DataType {
371 fn eq(&self, other: &Self) -> bool {
372 self.topic == other.topic
373 }
374}
375
376impl Eq for DataType {}
377
378impl PartialOrd for DataType {
379 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
380 Some(self.cmp(other))
381 }
382}
383
384impl Ord for DataType {
385 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
386 self.topic.cmp(&other.topic)
387 }
388}
389
390impl Hash for DataType {
391 fn hash<H: Hasher>(&self, state: &mut H) {
392 self.hash.hash(state);
393 }
394}
395
396impl Display for DataType {
397 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
398 write!(f, "{}", self.topic)
399 }
400}
401
402impl Debug for DataType {
403 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
404 write!(
405 f,
406 "DataType(type_name={}, metadata={:?})",
407 self.type_name, self.metadata
408 )
409 }
410}
411
412#[cfg(test)]
416mod tests {
417 use std::hash::DefaultHasher;
418
419 use rstest::*;
420
421 use super::*;
422
423 #[rstest]
424 fn test_data_type_creation_with_metadata() {
425 let metadata = Some(
426 [
427 ("key1".to_string(), "value1".to_string()),
428 ("key2".to_string(), "value2".to_string()),
429 ]
430 .iter()
431 .cloned()
432 .collect(),
433 );
434 let data_type = DataType::new("ExampleType", metadata.clone());
435
436 assert_eq!(data_type.type_name(), "ExampleType");
437 assert_eq!(data_type.topic(), "ExampleType.key1=value1.key2=value2");
438 assert_eq!(data_type.metadata(), metadata.as_ref());
439 }
440
441 #[rstest]
442 fn test_data_type_creation_without_metadata() {
443 let data_type = DataType::new("ExampleType", None);
444
445 assert_eq!(data_type.type_name(), "ExampleType");
446 assert_eq!(data_type.topic(), "ExampleType");
447 assert_eq!(data_type.metadata(), None);
448 }
449
450 #[rstest]
451 fn test_data_type_equality() {
452 let metadata1 = Some(
453 [("key1".to_string(), "value1".to_string())]
454 .iter()
455 .cloned()
456 .collect(),
457 );
458 let metadata2 = Some(
459 [("key1".to_string(), "value1".to_string())]
460 .iter()
461 .cloned()
462 .collect(),
463 );
464
465 let data_type1 = DataType::new("ExampleType", metadata1);
466 let data_type2 = DataType::new("ExampleType", metadata2);
467
468 assert_eq!(data_type1, data_type2);
469 }
470
471 #[rstest]
472 fn test_data_type_inequality() {
473 let metadata1 = Some(
474 [("key1".to_string(), "value1".to_string())]
475 .iter()
476 .cloned()
477 .collect(),
478 );
479 let metadata2 = Some(
480 [("key2".to_string(), "value2".to_string())]
481 .iter()
482 .cloned()
483 .collect(),
484 );
485
486 let data_type1 = DataType::new("ExampleType", metadata1);
487 let data_type2 = DataType::new("ExampleType", metadata2);
488
489 assert_ne!(data_type1, data_type2);
490 }
491
492 #[rstest]
493 fn test_data_type_ordering() {
494 let metadata1 = Some(
495 [("key1".to_string(), "value1".to_string())]
496 .iter()
497 .cloned()
498 .collect(),
499 );
500 let metadata2 = Some(
501 [("key2".to_string(), "value2".to_string())]
502 .iter()
503 .cloned()
504 .collect(),
505 );
506
507 let data_type1 = DataType::new("ExampleTypeA", metadata1);
508 let data_type2 = DataType::new("ExampleTypeB", metadata2);
509
510 assert!(data_type1 < data_type2);
511 }
512
513 #[rstest]
514 fn test_data_type_hash() {
515 let metadata = Some(
516 [("key1".to_string(), "value1".to_string())]
517 .iter()
518 .cloned()
519 .collect(),
520 );
521
522 let data_type1 = DataType::new("ExampleType", metadata.clone());
523 let data_type2 = DataType::new("ExampleType", metadata.clone());
524
525 let mut hasher1 = DefaultHasher::new();
526 data_type1.hash(&mut hasher1);
527 let hash1 = hasher1.finish();
528
529 let mut hasher2 = DefaultHasher::new();
530 data_type2.hash(&mut hasher2);
531 let hash2 = hasher2.finish();
532
533 assert_eq!(hash1, hash2);
534 }
535
536 #[rstest]
537 fn test_data_type_display() {
538 let metadata = Some(
539 [("key1".to_string(), "value1".to_string())]
540 .iter()
541 .cloned()
542 .collect(),
543 );
544 let data_type = DataType::new("ExampleType", metadata);
545
546 assert_eq!(format!("{}", data_type), "ExampleType.key1=value1");
547 }
548
549 #[rstest]
550 fn test_data_type_debug() {
551 let metadata = Some(
552 [("key1".to_string(), "value1".to_string())]
553 .iter()
554 .cloned()
555 .collect(),
556 );
557 let data_type = DataType::new("ExampleType", metadata.clone());
558
559 assert_eq!(
560 format!("{data_type:?}"),
561 format!("DataType(type_name=ExampleType, metadata={metadata:?})")
562 );
563 }
564
565 #[rstest]
566 fn test_parse_instrument_id_from_metadata() {
567 let instrument_id_str = "MSFT.XNAS";
568 let metadata = Some(
569 [("instrument_id".to_string(), instrument_id_str.to_string())]
570 .iter()
571 .cloned()
572 .collect(),
573 );
574 let data_type = DataType::new("InstrumentAny", metadata);
575
576 assert_eq!(
577 data_type.instrument_id().unwrap(),
578 InstrumentId::from_str(instrument_id_str).unwrap()
579 );
580 }
581
582 #[rstest]
583 fn test_parse_venue_from_metadata() {
584 let venue_str = "BINANCE";
585 let metadata = Some(
586 [("venue".to_string(), venue_str.to_string())]
587 .iter()
588 .cloned()
589 .collect(),
590 );
591 let data_type = DataType::new(stringify!(InstrumentAny), metadata);
592
593 assert_eq!(data_type.venue().unwrap(), Venue::new(venue_str));
594 }
595
596 #[rstest]
597 fn test_parse_start_from_metadata() {
598 let start_ns = 1600054595844758000;
599 let metadata = Some(
600 [("start".to_string(), start_ns.to_string())]
601 .iter()
602 .cloned()
603 .collect(),
604 );
605 let data_type = DataType::new(stringify!(TradeTick), metadata);
606
607 assert_eq!(data_type.start().unwrap(), UnixNanos::from(start_ns),);
608 }
609
610 #[rstest]
611 fn test_parse_end_from_metadata() {
612 let end_ns = 1720954595844758000;
613 let metadata = Some(
614 [("end".to_string(), end_ns.to_string())]
615 .iter()
616 .cloned()
617 .collect(),
618 );
619 let data_type = DataType::new(stringify!(TradeTick), metadata);
620
621 assert_eq!(data_type.end().unwrap(), UnixNanos::from(end_ns),);
622 }
623
624 #[rstest]
625 fn test_parse_limit_from_metadata() {
626 let limit = 1000;
627 let metadata = Some(
628 [("limit".to_string(), limit.to_string())]
629 .iter()
630 .cloned()
631 .collect(),
632 );
633 let data_type = DataType::new(stringify!(TradeTick), metadata);
634
635 assert_eq!(data_type.limit().unwrap(), limit);
636 }
637}