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#[derive(Clone, Serialize, Deserialize)]
226#[cfg_attr(
227 feature = "python",
228 pyo3::pyclass(module = "posei_trader.core.nautilus_pyo3.model")
229)]
230pub struct DataType {
231 type_name: String,
232 metadata: Option<IndexMap<String, String>>,
233 topic: String,
234 hash: u64,
235}
236
237impl DataType {
238 pub fn new(type_name: &str, metadata: Option<IndexMap<String, String>>) -> Self {
240 let topic = if let Some(ref meta) = metadata {
242 let meta_str = meta
243 .iter()
244 .map(|(k, v)| format!("{}={}", k, v))
245 .collect::<Vec<_>>()
246 .join(".");
247 format!("{}.{}", type_name, meta_str)
248 } else {
249 type_name.to_string()
250 };
251
252 let mut hasher = std::collections::hash_map::DefaultHasher::new();
254 topic.hash(&mut hasher);
255
256 Self {
257 type_name: type_name.to_owned(),
258 metadata,
259 topic,
260 hash: hasher.finish(),
261 }
262 }
263
264 pub fn type_name(&self) -> &str {
266 self.type_name.as_str()
267 }
268
269 pub fn metadata(&self) -> Option<&IndexMap<String, String>> {
271 self.metadata.as_ref()
272 }
273
274 pub fn metadata_str(&self) -> String {
276 self.metadata
277 .as_ref()
278 .map(|metadata| to_string(metadata).unwrap_or_default())
279 .unwrap_or_else(|| "null".to_string())
280 }
281
282 pub fn topic(&self) -> &str {
284 self.topic.as_str()
285 }
286
287 pub fn instrument_id(&self) -> Option<InstrumentId> {
295 let metadata = self.metadata.as_ref().expect("metadata was `None`");
296 let instrument_id = metadata.get("instrument_id")?;
297 Some(
298 InstrumentId::from_str(instrument_id)
299 .expect("Invalid `InstrumentId` for 'instrument_id'"),
300 )
301 }
302
303 pub fn venue(&self) -> Option<Venue> {
311 let metadata = self.metadata.as_ref().expect("metadata was `None`");
312 let venue_str = metadata.get("venue")?;
313 Some(Venue::from(venue_str.as_str()))
314 }
315
316 pub fn start(&self) -> Option<UnixNanos> {
324 let metadata = self.metadata.as_ref()?;
325 let start_str = metadata.get("start")?;
326 Some(UnixNanos::from_str(start_str).expect("Invalid `UnixNanos` for 'start'"))
327 }
328
329 pub fn end(&self) -> Option<UnixNanos> {
337 let metadata = self.metadata.as_ref()?;
338 let end_str = metadata.get("end")?;
339 Some(UnixNanos::from_str(end_str).expect("Invalid `UnixNanos` for 'end'"))
340 }
341
342 pub fn limit(&self) -> Option<usize> {
350 let metadata = self.metadata.as_ref()?;
351 let depth_str = metadata.get("limit")?;
352 Some(
353 depth_str
354 .parse::<usize>()
355 .expect("Invalid `usize` for 'limit'"),
356 )
357 }
358}
359
360impl PartialEq for DataType {
361 fn eq(&self, other: &Self) -> bool {
362 self.topic == other.topic
363 }
364}
365
366impl Eq for DataType {}
367
368impl PartialOrd for DataType {
369 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
370 Some(self.cmp(other))
371 }
372}
373
374impl Ord for DataType {
375 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
376 self.topic.cmp(&other.topic)
377 }
378}
379
380impl Hash for DataType {
381 fn hash<H: Hasher>(&self, state: &mut H) {
382 self.hash.hash(state);
383 }
384}
385
386impl Display for DataType {
387 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
388 write!(f, "{}", self.topic)
389 }
390}
391
392impl Debug for DataType {
393 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394 write!(
395 f,
396 "DataType(type_name={}, metadata={:?})",
397 self.type_name, self.metadata
398 )
399 }
400}
401
402#[cfg(test)]
406mod tests {
407 use std::hash::DefaultHasher;
408
409 use rstest::*;
410
411 use super::*;
412
413 #[rstest]
414 fn test_data_type_creation_with_metadata() {
415 let metadata = Some(
416 [
417 ("key1".to_string(), "value1".to_string()),
418 ("key2".to_string(), "value2".to_string()),
419 ]
420 .iter()
421 .cloned()
422 .collect(),
423 );
424 let data_type = DataType::new("ExampleType", metadata.clone());
425
426 assert_eq!(data_type.type_name(), "ExampleType");
427 assert_eq!(data_type.topic(), "ExampleType.key1=value1.key2=value2");
428 assert_eq!(data_type.metadata(), metadata.as_ref());
429 }
430
431 #[rstest]
432 fn test_data_type_creation_without_metadata() {
433 let data_type = DataType::new("ExampleType", None);
434
435 assert_eq!(data_type.type_name(), "ExampleType");
436 assert_eq!(data_type.topic(), "ExampleType");
437 assert_eq!(data_type.metadata(), None);
438 }
439
440 #[rstest]
441 fn test_data_type_equality() {
442 let metadata1 = Some(
443 [("key1".to_string(), "value1".to_string())]
444 .iter()
445 .cloned()
446 .collect(),
447 );
448 let metadata2 = Some(
449 [("key1".to_string(), "value1".to_string())]
450 .iter()
451 .cloned()
452 .collect(),
453 );
454
455 let data_type1 = DataType::new("ExampleType", metadata1);
456 let data_type2 = DataType::new("ExampleType", metadata2);
457
458 assert_eq!(data_type1, data_type2);
459 }
460
461 #[rstest]
462 fn test_data_type_inequality() {
463 let metadata1 = Some(
464 [("key1".to_string(), "value1".to_string())]
465 .iter()
466 .cloned()
467 .collect(),
468 );
469 let metadata2 = Some(
470 [("key2".to_string(), "value2".to_string())]
471 .iter()
472 .cloned()
473 .collect(),
474 );
475
476 let data_type1 = DataType::new("ExampleType", metadata1);
477 let data_type2 = DataType::new("ExampleType", metadata2);
478
479 assert_ne!(data_type1, data_type2);
480 }
481
482 #[rstest]
483 fn test_data_type_ordering() {
484 let metadata1 = Some(
485 [("key1".to_string(), "value1".to_string())]
486 .iter()
487 .cloned()
488 .collect(),
489 );
490 let metadata2 = Some(
491 [("key2".to_string(), "value2".to_string())]
492 .iter()
493 .cloned()
494 .collect(),
495 );
496
497 let data_type1 = DataType::new("ExampleTypeA", metadata1);
498 let data_type2 = DataType::new("ExampleTypeB", metadata2);
499
500 assert!(data_type1 < data_type2);
501 }
502
503 #[rstest]
504 fn test_data_type_hash() {
505 let metadata = Some(
506 [("key1".to_string(), "value1".to_string())]
507 .iter()
508 .cloned()
509 .collect(),
510 );
511
512 let data_type1 = DataType::new("ExampleType", metadata.clone());
513 let data_type2 = DataType::new("ExampleType", metadata.clone());
514
515 let mut hasher1 = DefaultHasher::new();
516 data_type1.hash(&mut hasher1);
517 let hash1 = hasher1.finish();
518
519 let mut hasher2 = DefaultHasher::new();
520 data_type2.hash(&mut hasher2);
521 let hash2 = hasher2.finish();
522
523 assert_eq!(hash1, hash2);
524 }
525
526 #[rstest]
527 fn test_data_type_display() {
528 let metadata = Some(
529 [("key1".to_string(), "value1".to_string())]
530 .iter()
531 .cloned()
532 .collect(),
533 );
534 let data_type = DataType::new("ExampleType", metadata);
535
536 assert_eq!(format!("{}", data_type), "ExampleType.key1=value1");
537 }
538
539 #[rstest]
540 fn test_data_type_debug() {
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.clone());
548
549 assert_eq!(
550 format!("{data_type:?}"),
551 format!("DataType(type_name=ExampleType, metadata={metadata:?})")
552 );
553 }
554
555 #[rstest]
556 fn test_parse_instrument_id_from_metadata() {
557 let instrument_id_str = "MSFT.XNAS";
558 let metadata = Some(
559 [("instrument_id".to_string(), instrument_id_str.to_string())]
560 .iter()
561 .cloned()
562 .collect(),
563 );
564 let data_type = DataType::new("InstrumentAny", metadata);
565
566 assert_eq!(
567 data_type.instrument_id().unwrap(),
568 InstrumentId::from_str(instrument_id_str).unwrap()
569 );
570 }
571
572 #[rstest]
573 fn test_parse_venue_from_metadata() {
574 let venue_str = "BINANCE";
575 let metadata = Some(
576 [("venue".to_string(), venue_str.to_string())]
577 .iter()
578 .cloned()
579 .collect(),
580 );
581 let data_type = DataType::new(stringify!(InstrumentAny), metadata);
582
583 assert_eq!(data_type.venue().unwrap(), Venue::new(venue_str));
584 }
585
586 #[rstest]
587 fn test_parse_start_from_metadata() {
588 let start_ns = 1600054595844758000;
589 let metadata = Some(
590 [("start".to_string(), start_ns.to_string())]
591 .iter()
592 .cloned()
593 .collect(),
594 );
595 let data_type = DataType::new(stringify!(TradeTick), metadata);
596
597 assert_eq!(data_type.start().unwrap(), UnixNanos::from(start_ns),);
598 }
599
600 #[rstest]
601 fn test_parse_end_from_metadata() {
602 let end_ns = 1720954595844758000;
603 let metadata = Some(
604 [("end".to_string(), end_ns.to_string())]
605 .iter()
606 .cloned()
607 .collect(),
608 );
609 let data_type = DataType::new(stringify!(TradeTick), metadata);
610
611 assert_eq!(data_type.end().unwrap(), UnixNanos::from(end_ns),);
612 }
613
614 #[rstest]
615 fn test_parse_limit_from_metadata() {
616 let limit = 1000;
617 let metadata = Some(
618 [("limit".to_string(), limit.to_string())]
619 .iter()
620 .cloned()
621 .collect(),
622 );
623 let data_type = DataType::new(stringify!(TradeTick), metadata);
624
625 assert_eq!(data_type.limit().unwrap(), limit);
626 }
627}