dbn/record/methods.rs
1use std::fmt::Debug;
2
3use num_enum::TryFromPrimitive;
4
5use crate::{
6 enums::{ErrorCode, StatusAction, StatusReason, SystemCode},
7 pretty::px_to_f64,
8 SType, StatType, TradingEvent, TriState,
9};
10
11use super::*;
12
13impl RecordHeader {
14 /// The multiplier for converting the `length` field to the number of bytes.
15 pub const LENGTH_MULTIPLIER: usize = 4;
16
17 /// Creates a new `RecordHeader`. `R` and `rtype` should be compatible.
18 pub const fn new<R: HasRType>(
19 rtype: u8,
20 publisher_id: u16,
21 instrument_id: u32,
22 ts_event: u64,
23 ) -> Self {
24 Self {
25 length: (mem::size_of::<R>() / Self::LENGTH_MULTIPLIER) as u8,
26 rtype,
27 publisher_id,
28 instrument_id,
29 ts_event,
30 }
31 }
32
33 /// Returns the size of the **entire** record in bytes. The size of a `RecordHeader`
34 /// is constant.
35 pub const fn record_size(&self) -> usize {
36 self.length as usize * Self::LENGTH_MULTIPLIER
37 }
38
39 /// Tries to convert the raw record type into an enum.
40 ///
41 /// # Errors
42 /// This function returns an error if the `rtype` field does not
43 /// contain a valid, known [`RType`].
44 pub fn rtype(&self) -> crate::Result<RType> {
45 RType::try_from(self.rtype)
46 .map_err(|_| Error::conversion::<RType>(format!("{:#04X}", self.rtype)))
47 }
48
49 /// Tries to convert the raw `publisher_id` into an enum which is useful for
50 /// exhaustive pattern matching.
51 ///
52 /// # Errors
53 /// This function returns an error if the `publisher_id` does not correspond with
54 /// any known [`Publisher`].
55 pub fn publisher(&self) -> crate::Result<Publisher> {
56 Publisher::try_from(self.publisher_id)
57 .map_err(|_| Error::conversion::<Publisher>(self.publisher_id))
58 }
59
60 /// Parses the raw matching-engine-received timestamp into a datetime. Returns
61 /// `None` if `ts_event` contains the sentinel for a null timestamp.
62 pub fn ts_event(&self) -> Option<time::OffsetDateTime> {
63 if self.ts_event == crate::UNDEF_TIMESTAMP {
64 None
65 } else {
66 // u64::MAX is within maximum allowable range
67 Some(time::OffsetDateTime::from_unix_timestamp_nanos(self.ts_event as i128).unwrap())
68 }
69 }
70}
71
72impl Debug for RecordHeader {
73 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
74 let mut debug_struct = f.debug_struct("RecordHeader");
75 debug_struct.field("length", &self.length);
76 match self.rtype() {
77 Ok(rtype) => debug_struct.field("rtype", &format_args!("{rtype:?}")),
78 Err(_) => debug_struct.field("rtype", &format_args!("{:#04X}", &self.rtype)),
79 };
80 match self.publisher() {
81 Ok(p) => debug_struct.field("publisher_id", &format_args!("{p:?}")),
82 Err(_) => debug_struct.field("publisher_id", &self.publisher_id),
83 };
84 debug_struct
85 .field("instrument_id", &self.instrument_id)
86 .field("ts_event", &self.ts_event)
87 .finish()
88 }
89}
90
91impl MboMsg {
92 /// Converts the order price to a floating point.
93 ///
94 /// `UNDEF_PRICE` will be converted to NaN.
95 ///
96 /// <div class="warning">
97 /// This may introduce floating-point error.
98 /// </div>
99 pub fn price_f64(&self) -> f64 {
100 px_to_f64(self.price)
101 }
102
103 /// Parses the action into an enum.
104 ///
105 /// # Errors
106 /// This function returns an error if the `action` field does not
107 /// contain a valid [`Action`].
108 pub fn action(&self) -> crate::Result<Action> {
109 Action::try_from(self.action as u8)
110 .map_err(|_| Error::conversion::<Action>(format!("{:#04X}", self.action as u8)))
111 }
112
113 /// Parses the side that initiates the event into an enum.
114 ///
115 /// # Errors
116 /// This function returns an error if the `side` field does not
117 /// contain a valid [`Side`].
118 pub fn side(&self) -> crate::Result<Side> {
119 Side::try_from(self.side as u8)
120 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.side as u8)))
121 }
122
123 /// Parses the capture-server-received timestamp into a datetime.
124 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
125 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
126 ts_to_dt(self.ts_recv)
127 }
128
129 /// Parses the difference between `ts_recv` and the matching-engine-sending timestamp into a duration.
130 pub fn ts_in_delta(&self) -> time::Duration {
131 time::Duration::new(0, self.ts_in_delta)
132 }
133}
134
135impl BidAskPair {
136 /// Converts the bid price to a floating point.
137 ///
138 /// `UNDEF_PRICE` will be converted to NaN.
139 ///
140 /// <div class="warning">
141 /// This may introduce floating-point error.
142 /// </div>
143 pub fn bid_px_f64(&self) -> f64 {
144 px_to_f64(self.bid_px)
145 }
146
147 /// Converts the ask price to a floating point.
148 ///
149 /// `UNDEF_PRICE` will be converted to NaN.
150 ///
151 /// <div class="warning">
152 /// This may introduce floating-point error.
153 /// </div>
154 pub fn ask_px_f64(&self) -> f64 {
155 px_to_f64(self.ask_px)
156 }
157}
158
159impl ConsolidatedBidAskPair {
160 /// Converts the bid price to a floating point.
161 ///
162 /// `UNDEF_PRICE` will be converted to NaN.
163 ///
164 /// <div class="warning">
165 /// This may introduce floating-point error.
166 /// </div>
167 pub fn bid_px_f64(&self) -> f64 {
168 px_to_f64(self.bid_px)
169 }
170
171 /// Converts the ask price to a floating point.
172 ///
173 /// `UNDEF_PRICE` will be converted to NaN.
174 ///
175 /// <div class="warning">
176 /// This may introduce floating-point error.
177 /// </div>
178 pub fn ask_px_f64(&self) -> f64 {
179 px_to_f64(self.ask_px)
180 }
181
182 /// Parses the bid publisher into an enum.
183 ///
184 /// # Errors
185 /// This function returns an error if the `bid_pb` field does not
186 /// contain a valid [`Publisher`].
187 pub fn bid_pb(&self) -> crate::Result<Publisher> {
188 Publisher::try_from(self.bid_pb)
189 .map_err(|_| Error::conversion::<Publisher>(format!("{:#04X}", self.bid_pb)))
190 }
191
192 /// Parses the ask publisher into an enum.
193 ///
194 /// # Errors
195 /// This function returns an error if the `ask_pb` field does not
196 /// contain a valid [`Publisher`].
197 pub fn ask_pb(&self) -> crate::Result<Publisher> {
198 Publisher::try_from(self.ask_pb)
199 .map_err(|_| Error::conversion::<Publisher>(format!("{:#04X}", self.ask_pb)))
200 }
201}
202
203impl TradeMsg {
204 /// Converts the price to a floating point.
205 ///
206 /// `UNDEF_PRICE` will be converted to NaN.
207 ///
208 /// <div class="warning">
209 /// This may introduce floating-point error.
210 /// </div>
211 pub fn price_f64(&self) -> f64 {
212 px_to_f64(self.price)
213 }
214
215 /// Parses the action into an enum.
216 ///
217 /// # Errors
218 /// This function returns an error if the `action` field does not
219 /// contain a valid [`Action`].
220 pub fn action(&self) -> crate::Result<Action> {
221 Action::try_from(self.action as u8)
222 .map_err(|_| Error::conversion::<Action>(format!("{:#04X}", self.action as u8)))
223 }
224
225 /// Parses the side into an enum.
226 ///
227 /// # Errors
228 /// This function returns an error if the `side` field does not
229 /// contain a valid [`Side`].
230 pub fn side(&self) -> crate::Result<Side> {
231 Side::try_from(self.side as u8)
232 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.side as u8)))
233 }
234
235 /// Parses the capture-server-received timestamp into a datetime.
236 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
237 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
238 ts_to_dt(self.ts_recv)
239 }
240
241 /// Parses the difference between `ts_recv` and the matching-engine-sending timestamp into a duration.
242 pub fn ts_in_delta(&self) -> time::Duration {
243 time::Duration::new(0, self.ts_in_delta)
244 }
245}
246
247impl Mbp1Msg {
248 /// Converts the order price to a floating point.
249 ///
250 /// `UNDEF_PRICE` will be converted to NaN.
251 ///
252 /// <div class="warning">
253 /// This may introduce floating-point error.
254 /// </div>
255 pub fn price_f64(&self) -> f64 {
256 px_to_f64(self.price)
257 }
258
259 /// Parses the action into an enum.
260 ///
261 /// # Errors
262 /// This function returns an error if the `action` field does not
263 /// contain a valid [`Action`].
264 pub fn action(&self) -> crate::Result<Action> {
265 Action::try_from(self.action as u8)
266 .map_err(|_| Error::conversion::<Action>(format!("{:#04X}", self.action as u8)))
267 }
268
269 /// Parses the side that initiates the event into an enum.
270 ///
271 /// # Errors
272 /// This function returns an error if the `side` field does not
273 /// contain a valid [`Side`].
274 pub fn side(&self) -> crate::Result<Side> {
275 Side::try_from(self.side as u8)
276 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.side as u8)))
277 }
278
279 /// Parses the capture-server-received timestamp into a datetime.
280 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
281 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
282 ts_to_dt(self.ts_recv)
283 }
284
285 /// Parses the difference between `ts_recv` and the matching-engine-sending timestamp into a duration.
286 pub fn ts_in_delta(&self) -> time::Duration {
287 time::Duration::new(0, self.ts_in_delta)
288 }
289}
290
291impl Mbp10Msg {
292 /// Converts the order price to a floating point.
293 ///
294 /// `UNDEF_PRICE` will be converted to NaN.
295 ///
296 /// <div class="warning">
297 /// This may introduce floating-point error.
298 /// </div>
299 pub fn price_f64(&self) -> f64 {
300 px_to_f64(self.price)
301 }
302
303 /// Parses the action into an enum.
304 ///
305 /// # Errors
306 /// This function returns an error if the `action` field does not
307 /// contain a valid [`Action`].
308 pub fn action(&self) -> crate::Result<Action> {
309 Action::try_from(self.action as u8)
310 .map_err(|_| Error::conversion::<Action>(format!("{:#04X}", self.action as u8)))
311 }
312
313 /// Parses the side that initiates the event into an enum.
314 ///
315 /// # Errors
316 /// This function returns an error if the `side` field does not
317 /// contain a valid [`Side`].
318 pub fn side(&self) -> crate::Result<Side> {
319 Side::try_from(self.side as u8)
320 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.side as u8)))
321 }
322
323 /// Parses the capture-server-received timestamp into a datetime.
324 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
325 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
326 ts_to_dt(self.ts_recv)
327 }
328
329 /// Parses the difference between `ts_recv` and the matching-engine-sending timestamp into a duration.
330 pub fn ts_in_delta(&self) -> time::Duration {
331 time::Duration::new(0, self.ts_in_delta)
332 }
333}
334
335impl BboMsg {
336 /// Converts the last trade price to a floating point.
337 ///
338 /// `UNDEF_PRICE` will be converted to NaN.
339 ///
340 /// <div class="warning">
341 /// This may introduce floating-point error.
342 /// </div>
343 pub fn price_f64(&self) -> f64 {
344 px_to_f64(self.price)
345 }
346
347 /// Parses the side that initiated the last trade into an enum.
348 ///
349 /// # Errors
350 /// This function returns an error if the `side` field does not
351 /// contain a valid [`Side`].
352 pub fn side(&self) -> crate::Result<Side> {
353 Side::try_from(self.side as u8)
354 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.side as u8)))
355 }
356
357 /// Parses the end timestamp of the interval capture-server-received timestamp into a datetime.
358 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
359 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
360 ts_to_dt(self.ts_recv)
361 }
362}
363
364impl Cmbp1Msg {
365 /// Converts the order price to a floating point.
366 ///
367 /// `UNDEF_PRICE` will be converted to NaN.
368 ///
369 /// <div class="warning">
370 /// This may introduce floating-point error.
371 /// </div>
372 pub fn price_f64(&self) -> f64 {
373 px_to_f64(self.price)
374 }
375
376 /// Parses the action into an enum.
377 ///
378 /// # Errors
379 /// This function returns an error if the `action` field does not
380 /// contain a valid [`Action`].
381 pub fn action(&self) -> crate::Result<Action> {
382 Action::try_from(self.action as u8)
383 .map_err(|_| Error::conversion::<Action>(format!("{:#04X}", self.action as u8)))
384 }
385
386 /// Parses the side that initiates the event into an enum.
387 ///
388 /// # Errors
389 /// This function returns an error if the `side` field does not
390 /// contain a valid [`Side`].
391 pub fn side(&self) -> crate::Result<Side> {
392 Side::try_from(self.side as u8)
393 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.side as u8)))
394 }
395
396 /// Parses the capture-server-received timestamp into a datetime.
397 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
398 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
399 ts_to_dt(self.ts_recv)
400 }
401
402 /// Parses the difference between `ts_recv` and the matching-engine-sending timestamp into a duration.
403 pub fn ts_in_delta(&self) -> time::Duration {
404 time::Duration::new(0, self.ts_in_delta)
405 }
406}
407
408impl CbboMsg {
409 /// Converts the last trade price to a floating point.
410 ///
411 /// `UNDEF_PRICE` will be converted to NaN.
412 ///
413 /// <div class="warning">
414 /// This may introduce floating-point error.
415 /// </div>
416 pub fn price_f64(&self) -> f64 {
417 px_to_f64(self.price)
418 }
419
420 /// Parses the side that initiated the last trade into an enum.
421 ///
422 /// # Errors
423 /// This function returns an error if the `side` field does not
424 /// contain a valid [`Side`].
425 pub fn side(&self) -> crate::Result<Side> {
426 Side::try_from(self.side as u8)
427 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.side as u8)))
428 }
429
430 /// Parses the end timestamp of the interval capture-server-received timestamp into a datetime.
431 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
432 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
433 ts_to_dt(self.ts_recv)
434 }
435}
436
437impl OhlcvMsg {
438 /// Converts the open price to a floating point.
439 ///
440 /// `UNDEF_PRICE` will be converted to NaN.
441 ///
442 /// <div class="warning">
443 /// This may introduce floating-point error.
444 /// </div>
445 pub fn open_f64(&self) -> f64 {
446 px_to_f64(self.open)
447 }
448
449 /// Converts the high price to a floating point.
450 ///
451 /// `UNDEF_PRICE` will be converted to NaN.
452 ///
453 /// <div class="warning">
454 /// This may introduce floating-point error.
455 /// </div>
456 pub fn high_f64(&self) -> f64 {
457 px_to_f64(self.high)
458 }
459
460 /// Converts the low price to a floating point.
461 ///
462 /// `UNDEF_PRICE` will be converted to NaN.
463 ///
464 /// <div class="warning">
465 /// This may introduce floating-point error.
466 /// </div>
467 pub fn low_f64(&self) -> f64 {
468 px_to_f64(self.low)
469 }
470
471 /// Converts the close price to a floating point.
472 ///
473 /// `UNDEF_PRICE` will be converted to NaN.
474 ///
475 /// <div class="warning">
476 /// This may introduce floating-point error.
477 /// </div>
478 pub fn close_f64(&self) -> f64 {
479 px_to_f64(self.close)
480 }
481}
482
483impl StatusMsg {
484 /// Parses the capture-server-received timestamp into a datetime.
485 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
486 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
487 ts_to_dt(self.ts_recv)
488 }
489
490 /// Parses the action into an enum.
491 ///
492 /// # Errors
493 /// This function returns an error if the `action` field does not
494 /// contain a valid [`StatusAction`].
495 pub fn action(&self) -> crate::Result<StatusAction> {
496 StatusAction::try_from(self.action)
497 .map_err(|_| Error::conversion::<StatusAction>(format!("{:#04X}", self.action)))
498 }
499
500 /// Parses the reason into an enum.
501 ///
502 /// # Errors
503 /// This function returns an error if the `reason` field does not
504 /// contain a valid [`StatusReason`].
505 pub fn reason(&self) -> crate::Result<StatusReason> {
506 StatusReason::try_from(self.reason)
507 .map_err(|_| Error::conversion::<StatusReason>(format!("{:#04X}", self.reason)))
508 }
509
510 /// Parses the trading event into an enum.
511 ///
512 /// # Errors
513 /// This function returns an error if the `trading_event` field does not
514 /// contain a valid [`TradingEvent`].
515 pub fn trading_event(&self) -> crate::Result<TradingEvent> {
516 TradingEvent::try_from(self.trading_event)
517 .map_err(|_| Error::conversion::<TradingEvent>(format!("{:#04X}", self.trading_event)))
518 }
519
520 /// Parses the trading state into an `Option<bool>` where `None` indicates
521 /// a value is not applicable or available.
522 pub fn is_trading(&self) -> Option<bool> {
523 TriState::try_from_primitive(self.is_trading as c_char as u8)
524 .map(Option::<bool>::from)
525 .unwrap_or_default()
526 }
527
528 /// Parses the quoting state into an `Option<bool>` where `None` indicates
529 /// a value is not applicable or available.
530 pub fn is_quoting(&self) -> Option<bool> {
531 TriState::try_from_primitive(self.is_quoting as c_char as u8)
532 .map(Option::<bool>::from)
533 .unwrap_or_default()
534 }
535
536 /// Parses the short selling state into an `Option<bool>` where `None` indicates
537 /// a value is not applicable or available.
538 pub fn is_short_sell_restricted(&self) -> Option<bool> {
539 TriState::try_from_primitive(self.is_short_sell_restricted as c_char as u8)
540 .map(Option::<bool>::from)
541 .unwrap_or_default()
542 }
543}
544
545impl InstrumentDefMsg {
546 /// Parses the capture-server-received timestamp into a datetime.
547 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
548 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
549 ts_to_dt(self.ts_recv)
550 }
551
552 /// Converts the minimum constant tick to a floating point.
553 ///
554 /// `UNDEF_PRICE` will be converted to NaN.
555 ///
556 /// <div class="warning">
557 /// This may introduce floating-point error.
558 /// </div>
559 pub fn min_price_increment_f64(&self) -> f64 {
560 px_to_f64(self.min_price_increment)
561 }
562
563 /// Converts the display factor to a floating point.
564 ///
565 /// `UNDEF_PRICE` will be converted to NaN.
566 ///
567 /// <div class="warning">
568 /// This may introduce floating-point error.
569 /// </div>
570 pub fn display_factor_f64(&self) -> f64 {
571 px_to_f64(self.display_factor)
572 }
573
574 /// Parses the last eligible trade time into a datetime.
575 /// Returns `None` if `expiration` contains the sentinel for a null timestamp.
576 pub fn expiration(&self) -> Option<time::OffsetDateTime> {
577 ts_to_dt(self.expiration)
578 }
579
580 /// Parses the time of instrument activation into a datetime.
581 /// Returns `None` if `activation` contains the sentinel for a null timestamp.
582 pub fn activation(&self) -> Option<time::OffsetDateTime> {
583 ts_to_dt(self.activation)
584 }
585
586 /// Converts the high limit price to a floating point.
587 ///
588 /// `UNDEF_PRICE` will be converted to NaN.
589 ///
590 /// <div class="warning">
591 /// This may introduce floating-point error.
592 /// </div>
593 pub fn high_limit_price_f64(&self) -> f64 {
594 px_to_f64(self.high_limit_price)
595 }
596
597 /// Converts the low limit price to a floating point.
598 ///
599 /// `UNDEF_PRICE` will be converted to NaN.
600 ///
601 /// <div class="warning">
602 /// This may introduce floating-point error.
603 /// </div>
604 pub fn low_limit_price_f64(&self) -> f64 {
605 px_to_f64(self.low_limit_price)
606 }
607
608 /// Converts the differential value for price banding to a floating point.
609 ///
610 /// `UNDEF_PRICE` will be converted to NaN.
611 ///
612 /// <div class="warning">
613 /// This may introduce floating-point error.
614 /// </div>
615 pub fn max_price_variation_f64(&self) -> f64 {
616 px_to_f64(self.max_price_variation)
617 }
618
619 /// Converts the contract size for each instrument to a floating point.
620 ///
621 /// `UNDEF_PRICE` will be converted to NaN.
622 ///
623 /// <div class="warning">
624 /// This may introduce floating-point error.
625 /// </div>
626 pub fn unit_of_measure_qty_f64(&self) -> f64 {
627 px_to_f64(self.unit_of_measure_qty)
628 }
629
630 /// Converts the min price increment amount to a floating point.
631 ///
632 /// `UNDEF_PRICE` will be converted to NaN.
633 ///
634 /// <div class="warning">
635 /// This may introduce floating-point error.
636 /// </div>
637 pub fn min_price_increment_amount_f64(&self) -> f64 {
638 px_to_f64(self.min_price_increment_amount)
639 }
640
641 /// Converts the price ratio to a floating point.
642 ///
643 /// `UNDEF_PRICE` will be converted to NaN.
644 ///
645 /// <div class="warning">
646 /// This may introduce floating-point error.
647 /// </div>
648 pub fn price_ratio_f64(&self) -> f64 {
649 px_to_f64(self.price_ratio)
650 }
651
652 /// Converts the strike price to a floating point.
653 ///
654 /// `UNDEF_PRICE` will be converted to NaN.
655 ///
656 /// <div class="warning">
657 /// This may introduce floating-point error.
658 /// </div>
659 pub fn strike_price_f64(&self) -> f64 {
660 px_to_f64(self.strike_price)
661 }
662
663 /// Converts the leg price to a floating point.
664 ///
665 /// `UNDEF_PRICE` will be converted to NaN.
666 ///
667 /// <div class="warning">
668 /// This may introduce floating-point error.
669 /// </div>
670 pub fn leg_price_f64(&self) -> f64 {
671 px_to_f64(self.leg_price)
672 }
673
674 /// Converts the leg delta to a floating point.
675 ///
676 /// `UNDEF_PRICE` will be converted to NaN.
677 ///
678 /// <div class="warning">
679 /// This may introduce floating-point error.
680 /// </div>
681 pub fn leg_delta_f64(&self) -> f64 {
682 px_to_f64(self.leg_delta)
683 }
684
685 /// Parses the currency into a `&str`.
686 ///
687 /// # Errors
688 /// This function returns an error if `currency` contains invalid UTF-8.
689 pub fn currency(&self) -> crate::Result<&str> {
690 c_chars_to_str(&self.currency)
691 }
692
693 /// Parses the currency used for settlement into a `&str`.
694 ///
695 /// # Errors
696 /// This function returns an error if `settl_currency` contains invalid UTF-8.
697 pub fn settl_currency(&self) -> crate::Result<&str> {
698 c_chars_to_str(&self.settl_currency)
699 }
700
701 /// Parses the strategy type of the spread into a `&str`.
702 ///
703 /// # Errors
704 /// This function returns an error if `secsubtype` contains invalid UTF-8.
705 pub fn secsubtype(&self) -> crate::Result<&str> {
706 c_chars_to_str(&self.secsubtype)
707 }
708
709 /// Parses the raw symbol into a `&str`.
710 ///
711 /// # Errors
712 /// This function returns an error if `raw_symbol` contains invalid UTF-8.
713 pub fn raw_symbol(&self) -> crate::Result<&str> {
714 c_chars_to_str(&self.raw_symbol)
715 }
716
717 /// Parses the security group code into a `&str`.
718 ///
719 /// # Errors
720 /// This function returns an error if `group` contains invalid UTF-8.
721 pub fn group(&self) -> crate::Result<&str> {
722 c_chars_to_str(&self.group)
723 }
724
725 /// Parses the exchange into a `&str`.
726 ///
727 /// # Errors
728 /// This function returns an error if `exchange` contains invalid UTF-8.
729 pub fn exchange(&self) -> crate::Result<&str> {
730 c_chars_to_str(&self.exchange)
731 }
732
733 /// Parses the asset into a `&str`.
734 ///
735 /// # Errors
736 /// This function returns an error if `asset` contains invalid UTF-8.
737 pub fn asset(&self) -> crate::Result<&str> {
738 c_chars_to_str(&self.asset)
739 }
740
741 /// Parses the CFI code into a `&str`.
742 ///
743 /// # Errors
744 /// This function returns an error if `cfi` contains invalid UTF-8.
745 pub fn cfi(&self) -> crate::Result<&str> {
746 c_chars_to_str(&self.cfi)
747 }
748
749 /// Parses the security type into a `&str`.
750 ///
751 /// # Errors
752 /// This function returns an error if `security_type` contains invalid UTF-8.
753 pub fn security_type(&self) -> crate::Result<&str> {
754 c_chars_to_str(&self.security_type)
755 }
756
757 /// Parses the unit of measure into a `&str`.
758 ///
759 /// # Errors
760 /// This function returns an error if `unit_of_measure` contains invalid UTF-8.
761 pub fn unit_of_measure(&self) -> crate::Result<&str> {
762 c_chars_to_str(&self.unit_of_measure)
763 }
764
765 /// Parses the underlying into a `&str`.
766 ///
767 /// # Errors
768 /// This function returns an error if `underlying` contains invalid UTF-8.
769 pub fn underlying(&self) -> crate::Result<&str> {
770 c_chars_to_str(&self.underlying)
771 }
772
773 /// Parses the strike price currency into a `&str`.
774 ///
775 /// # Errors
776 /// This function returns an error if `strike_price_currency` contains invalid UTF-8.
777 pub fn strike_price_currency(&self) -> crate::Result<&str> {
778 c_chars_to_str(&self.strike_price_currency)
779 }
780
781 /// Parses the leg raw symbol into a `&str`.
782 ///
783 /// # Errors
784 /// This function returns an error if `leg_raw_symbol` contains invalid UTF-8.
785 pub fn leg_raw_symbol(&self) -> crate::Result<&str> {
786 c_chars_to_str(&self.leg_raw_symbol)
787 }
788
789 /// Parses the instrument class into an enum.
790 ///
791 /// # Errors
792 /// This function returns an error if the `instrument_class` field does not
793 /// contain a valid [`InstrumentClass`].
794 pub fn instrument_class(&self) -> crate::Result<InstrumentClass> {
795 InstrumentClass::try_from(self.instrument_class as u8).map_err(|_| {
796 Error::conversion::<InstrumentClass>(format!("{:#04X}", self.instrument_class as u8))
797 })
798 }
799
800 /// Parses the match algorithm into an enum.
801 ///
802 /// # Errors
803 /// This function returns an error if the `match_algorithm` field does not
804 /// contain a valid [`MatchAlgorithm`].
805 pub fn match_algorithm(&self) -> crate::Result<MatchAlgorithm> {
806 MatchAlgorithm::try_from(self.match_algorithm as u8).map_err(|_| {
807 Error::conversion::<MatchAlgorithm>(format!("{:#04X}", self.match_algorithm as u8))
808 })
809 }
810
811 /// Parses the security update action into an enum.
812 ///
813 /// # Errors
814 /// This function returns an error if the `security_update_action` field does not
815 /// contain a valid [`SecurityUpdateAction`].
816 pub fn security_update_action(&self) -> crate::Result<SecurityUpdateAction> {
817 SecurityUpdateAction::try_from(self.security_update_action as u8).map_err(|_| {
818 Error::conversion::<SecurityUpdateAction>(format!(
819 "{:#04X}",
820 self.security_update_action as u8
821 ))
822 })
823 }
824
825 /// Parses the user-defined instrument flag into an enum.
826 ///
827 /// # Errors
828 /// This function returns an error if the `user_defined_instrument` field does not
829 /// contain a valid [`UserDefinedInstrument`].
830 pub fn user_defined_instrument(&self) -> crate::Result<UserDefinedInstrument> {
831 UserDefinedInstrument::try_from(self.user_defined_instrument as u8).map_err(|_| {
832 Error::conversion::<UserDefinedInstrument>(format!(
833 "{:#04X}",
834 self.user_defined_instrument as u8
835 ))
836 })
837 }
838
839 /// Parses the leg instrument class into an enum.
840 ///
841 /// # Errors
842 /// This function returns an error if the `leg_instrument_class` field does not
843 /// contain a valid [`InstrumentClass`].
844 pub fn leg_instrument_class(&self) -> crate::Result<InstrumentClass> {
845 InstrumentClass::try_from(self.leg_instrument_class as u8).map_err(|_| {
846 Error::conversion::<InstrumentClass>(format!(
847 "{:#04X}",
848 self.leg_instrument_class as u8
849 ))
850 })
851 }
852
853 /// Parses the leg side into an enum.
854 ///
855 /// # Errors
856 /// This function returns an error if the `leg_side` field does not
857 /// contain a valid [`Side`].
858 pub fn leg_side(&self) -> crate::Result<Side> {
859 Side::try_from(self.leg_side as u8)
860 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.leg_side as u8)))
861 }
862}
863
864impl ImbalanceMsg {
865 /// Parses the capture-server-received timestamp into a datetime.
866 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
867 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
868 ts_to_dt(self.ts_recv)
869 }
870
871 /// Converts the ref price to a floating point.
872 ///
873 /// `UNDEF_PRICE` will be converted to NaN.
874 ///
875 /// <div class="warning">
876 /// This may introduce floating-point error.
877 /// </div>
878 pub fn ref_price_f64(&self) -> f64 {
879 px_to_f64(self.ref_price)
880 }
881
882 /// Parses the auction time into a datetime.
883 /// Returns `None` if `auction_time` contains the sentinel for a null timestamp.
884 pub fn auction_time(&self) -> Option<time::OffsetDateTime> {
885 ts_to_dt(self.auction_time)
886 }
887
888 /// Converts the cont book clr price to a floating point.
889 ///
890 /// `UNDEF_PRICE` will be converted to NaN.
891 ///
892 /// <div class="warning">
893 /// This may introduce floating-point error.
894 /// </div>
895 pub fn cont_book_clr_price_f64(&self) -> f64 {
896 px_to_f64(self.cont_book_clr_price)
897 }
898
899 /// Converts the auct interest clr price to a floating point.
900 ///
901 /// `UNDEF_PRICE` will be converted to NaN.
902 ///
903 /// <div class="warning">
904 /// This may introduce floating-point error.
905 /// </div>
906 pub fn auct_interest_clr_price_f64(&self) -> f64 {
907 px_to_f64(self.auct_interest_clr_price)
908 }
909
910 /// Converts the ssr filling price to a floating point.
911 ///
912 /// `UNDEF_PRICE` will be converted to NaN.
913 ///
914 /// <div class="warning">
915 /// This may introduce floating-point error.
916 /// </div>
917 pub fn ssr_filling_price_f64(&self) -> f64 {
918 px_to_f64(self.ssr_filling_price)
919 }
920
921 /// Converts the ind match price to a floating point.
922 ///
923 /// `UNDEF_PRICE` will be converted to NaN.
924 ///
925 /// <div class="warning">
926 /// This may introduce floating-point error.
927 /// </div>
928 pub fn ind_match_price_f64(&self) -> f64 {
929 px_to_f64(self.ind_match_price)
930 }
931
932 /// Converts the upper collar to a floating point.
933 ///
934 /// `UNDEF_PRICE` will be converted to NaN.
935 ///
936 /// <div class="warning">
937 /// This may introduce floating-point error.
938 /// </div>
939 pub fn upper_collar_f64(&self) -> f64 {
940 px_to_f64(self.upper_collar)
941 }
942
943 /// Converts the lower collar to a floating point.
944 ///
945 /// `UNDEF_PRICE` will be converted to NaN.
946 ///
947 /// <div class="warning">
948 /// This may introduce floating-point error.
949 /// </div>
950 pub fn lower_collar_f64(&self) -> f64 {
951 px_to_f64(self.lower_collar)
952 }
953
954 /// Parses the side into an enum.
955 ///
956 /// # Errors
957 /// This function returns an error if the `side` field does not
958 /// contain a valid [`Side`].
959 pub fn side(&self) -> crate::Result<Side> {
960 Side::try_from(self.side as u8)
961 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.side as u8)))
962 }
963
964 /// Parses the unpaired side into an enum.
965 ///
966 /// # Errors
967 /// This function returns an error if the `unpaired_side` field does not
968 /// contain a valid [`Side`].
969 pub fn unpaired_side(&self) -> crate::Result<Side> {
970 Side::try_from(self.unpaired_side as u8)
971 .map_err(|_| Error::conversion::<Side>(format!("{:#04X}", self.unpaired_side as u8)))
972 }
973}
974
975impl StatMsg {
976 /// Parses the capture-server-received timestamp into a datetime.
977 /// Returns `None` if `ts_recv` contains the sentinel for a null timestamp.
978 pub fn ts_recv(&self) -> Option<time::OffsetDateTime> {
979 ts_to_dt(self.ts_recv)
980 }
981
982 /// Parses the reference timestamp of the statistic value into a datetime.
983 /// Returns `None` if `ts_ref` contains the sentinel for a null timestamp.
984 pub fn ts_ref(&self) -> Option<time::OffsetDateTime> {
985 ts_to_dt(self.ts_ref)
986 }
987
988 /// Converts the value for price statistics to a floating point.
989 ///
990 /// `UNDEF_PRICE` will be converted to NaN.
991 ///
992 /// <div class="warning">
993 /// This may introduce floating-point error.
994 /// </div>
995 pub fn price_f64(&self) -> f64 {
996 px_to_f64(self.price)
997 }
998
999 /// Parses the difference between `ts_recv` and the matching-engine-sending timestamp into a duration.
1000 pub fn ts_in_delta(&self) -> time::Duration {
1001 time::Duration::new(0, self.ts_in_delta)
1002 }
1003
1004 /// Parses the type of statistic value into an enum.
1005 ///
1006 /// # Errors
1007 /// This function returns an error if the `stat_type` field does not
1008 /// contain a valid [`StatType`].
1009 pub fn stat_type(&self) -> crate::Result<StatType> {
1010 StatType::try_from(self.stat_type)
1011 .map_err(|_| Error::conversion::<StatType>(format!("{:#04X}", self.stat_type)))
1012 }
1013
1014 /// Parses the update action into an enum.
1015 ///
1016 /// # Errors
1017 /// This function returns an error if the `update_action` field does not
1018 /// contain a valid [`StatUpdateAction`].
1019 pub fn update_action(&self) -> crate::Result<StatUpdateAction> {
1020 StatUpdateAction::try_from(self.update_action).map_err(|_| {
1021 Error::conversion::<StatUpdateAction>(format!("{:#04X}", self.update_action))
1022 })
1023 }
1024}
1025
1026impl ErrorMsg {
1027 /// Creates a new `ErrorMsg`. `msg` will be truncated if it's too long.
1028 pub fn new(ts_event: u64, code: Option<ErrorCode>, msg: &str, is_last: bool) -> Self {
1029 let mut error = Self {
1030 hd: RecordHeader::new::<Self>(rtype::ERROR, 0, 0, ts_event),
1031 is_last: is_last as u8,
1032 ..Default::default()
1033 };
1034 if let Some(code) = code {
1035 error.code = code as u8;
1036 }
1037 // leave at least one null byte
1038 for (i, byte) in msg.as_bytes().iter().take(error.err.len() - 1).enumerate() {
1039 error.err[i] = *byte as c_char;
1040 }
1041 error
1042 }
1043
1044 /// Parses the error message into a `&str`.
1045 ///
1046 /// # Errors
1047 /// This function returns an error if `err` contains invalid UTF-8.
1048 pub fn err(&self) -> crate::Result<&str> {
1049 c_chars_to_str(&self.err)
1050 }
1051
1052 /// Parses the error code into an enum.
1053 ///
1054 /// # Errors
1055 /// This function returns an error if the `code` field does not
1056 /// contain a valid [`ErrorCode`].
1057 pub fn code(&self) -> crate::Result<ErrorCode> {
1058 ErrorCode::try_from(self.code)
1059 .map_err(|_| Error::conversion::<ErrorCode>(format!("{:#04X}", self.code)))
1060 }
1061}
1062
1063impl SymbolMappingMsg {
1064 /// Creates a new `SymbolMappingMsg`.
1065 ///
1066 /// # Errors
1067 /// This function returns an error if `stype_in_symbol` or `stype_out_symbol`
1068 /// contain more than maximum number of 70 characters.
1069 #[allow(clippy::too_many_arguments)]
1070 pub fn new(
1071 instrument_id: u32,
1072 ts_event: u64,
1073 stype_in: SType,
1074 stype_in_symbol: &str,
1075 stype_out: SType,
1076 stype_out_symbol: &str,
1077 start_ts: u64,
1078 end_ts: u64,
1079 ) -> crate::Result<Self> {
1080 Ok(Self {
1081 // symbol mappings aren't publisher-specific
1082 hd: RecordHeader::new::<Self>(rtype::SYMBOL_MAPPING, 0, instrument_id, ts_event),
1083 stype_in: stype_in as u8,
1084 stype_in_symbol: str_to_c_chars(stype_in_symbol)?,
1085 stype_out: stype_out as u8,
1086 stype_out_symbol: str_to_c_chars(stype_out_symbol)?,
1087 start_ts,
1088 end_ts,
1089 })
1090 }
1091
1092 /// Parses the stype in into an enum.
1093 ///
1094 /// # Errors
1095 /// This function returns an error if the `stype_in` field does not
1096 /// contain a valid [`SType`].
1097 pub fn stype_in(&self) -> crate::Result<SType> {
1098 SType::try_from(self.stype_in)
1099 .map_err(|_| Error::conversion::<SType>(format!("{:#04X}", self.stype_in)))
1100 }
1101
1102 /// Parses the input symbol into a `&str`.
1103 ///
1104 /// # Errors
1105 /// This function returns an error if `stype_in_symbol` contains invalid UTF-8.
1106 pub fn stype_in_symbol(&self) -> crate::Result<&str> {
1107 c_chars_to_str(&self.stype_in_symbol)
1108 }
1109
1110 /// Parses the stype out into an enum.
1111 ///
1112 /// # Errors
1113 /// This function returns an error if the `stype_out` field does not
1114 /// contain a valid [`SType`].
1115 pub fn stype_out(&self) -> crate::Result<SType> {
1116 SType::try_from(self.stype_out)
1117 .map_err(|_| Error::conversion::<SType>(format!("{:#04X}", self.stype_out)))
1118 }
1119
1120 /// Parses the output symbol into a `&str`.
1121 ///
1122 /// # Errors
1123 /// This function returns an error if `stype_out_symbol` contains invalid UTF-8.
1124 pub fn stype_out_symbol(&self) -> crate::Result<&str> {
1125 c_chars_to_str(&self.stype_out_symbol)
1126 }
1127
1128 /// Parses the start of the mapping interval into a datetime.
1129 /// Returns `None` if `start_ts` contains the sentinel for a null timestamp.
1130 pub fn start_ts(&self) -> Option<time::OffsetDateTime> {
1131 ts_to_dt(self.start_ts)
1132 }
1133
1134 /// Parses the end of the mapping interval into a datetime.
1135 /// Returns `None` if `end_ts` contains the sentinel for a null timestamp.
1136 pub fn end_ts(&self) -> Option<time::OffsetDateTime> {
1137 ts_to_dt(self.end_ts)
1138 }
1139}
1140
1141impl SystemMsg {
1142 pub(crate) const HEARTBEAT: &'static str = "Heartbeat";
1143
1144 /// Creates a new `SystemMsg`.
1145 ///
1146 /// # Errors
1147 /// This function returns an error if `msg` is too long.
1148 pub fn new(ts_event: u64, code: Option<SystemCode>, msg: &str) -> Result<Self> {
1149 Ok(Self {
1150 hd: RecordHeader::new::<Self>(rtype::SYSTEM, 0, 0, ts_event),
1151 msg: str_to_c_chars(msg)?,
1152 code: code.map(u8::from).unwrap_or(u8::MAX),
1153 })
1154 }
1155
1156 /// Creates a new heartbeat `SystemMsg`.
1157 pub fn heartbeat(ts_event: u64) -> Self {
1158 Self {
1159 hd: RecordHeader::new::<Self>(rtype::SYSTEM, 0, 0, ts_event),
1160 msg: str_to_c_chars(Self::HEARTBEAT).unwrap(),
1161 code: SystemCode::Heartbeat as u8,
1162 }
1163 }
1164
1165 /// Checks whether the message is a heartbeat from the gateway.
1166 pub fn is_heartbeat(&self) -> bool {
1167 if let Ok(code) = self.code() {
1168 code == SystemCode::Heartbeat
1169 } else {
1170 self.msg()
1171 .map(|msg| msg == Self::HEARTBEAT)
1172 .unwrap_or_default()
1173 }
1174 }
1175
1176 /// Parses the message from the Databento gateway into a `&str`.
1177 ///
1178 /// # Errors
1179 /// This function returns an error if `msg` contains invalid UTF-8.
1180 pub fn msg(&self) -> crate::Result<&str> {
1181 c_chars_to_str(&self.msg)
1182 }
1183
1184 /// Parses the type of system message into an enum.
1185 ///
1186 /// # Errors
1187 /// This function returns an error if the `code` field does not
1188 /// contain a valid [`SystemCode`].
1189 pub fn code(&self) -> crate::Result<SystemCode> {
1190 SystemCode::try_from(self.code)
1191 .map_err(|_| Error::conversion::<SystemCode>(format!("{:#04X}", self.code)))
1192 }
1193}