nautilus_serialization/arrow/display/
mod.rs1pub mod account_state;
28pub mod bar;
29pub mod close;
30pub mod delta;
31pub mod depth;
32pub mod index_price;
33pub mod instrument;
34pub mod mark_price;
35pub mod order_filled;
36pub mod position;
37pub mod quote;
38pub mod report;
39pub mod trade;
40
41use arrow::datatypes::{DataType, Field};
42use nautilus_model::types::{Money, fixed::MAX_FLOAT_PRECISION};
43use rust_decimal::prelude::ToPrimitive;
44
45use super::display_conversion::DISPLAY_MAX_PRECISION;
46pub(super) use super::display_conversion::{
47 float64_field, price_to_f64, quantity_to_f64, timestamp_field, utf8_field,
48};
49
50pub(super) fn bool_field(name: &str, nullable: bool) -> Field {
52 Field::new(name, DataType::Boolean, nullable)
53}
54
55pub(super) fn uint8_field(name: &str, nullable: bool) -> Field {
57 Field::new(name, DataType::UInt8, nullable)
58}
59
60pub(super) fn uint32_field(name: &str, nullable: bool) -> Field {
62 Field::new(name, DataType::UInt32, nullable)
63}
64
65pub(super) fn uint64_field(name: &str, nullable: bool) -> Field {
67 Field::new(name, DataType::UInt64, nullable)
68}
69
70pub(super) fn unix_nanos_to_i64(value: u64) -> i64 {
75 i64::try_from(value).unwrap_or(i64::MAX)
76}
77
78pub(super) fn money_to_f64(money: &Money) -> f64 {
87 if money.currency.precision > DISPLAY_MAX_PRECISION {
88 return f64::NAN;
89 }
90
91 if money.currency.precision <= MAX_FLOAT_PRECISION {
92 money.as_f64()
93 } else {
94 money.as_decimal().to_f64().unwrap_or(f64::NAN)
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use nautilus_model::types::{
101 Currency, Money, Price, Quantity,
102 price::{ERROR_PRICE, PRICE_ERROR, PRICE_UNDEF},
103 quantity::QUANTITY_UNDEF,
104 };
105 use rstest::rstest;
106
107 use super::{money_to_f64, price_to_f64, quantity_to_f64};
108
109 #[rstest]
110 fn test_price_to_f64_normal_value() {
111 let price = Price::from("100.10");
112 assert!((price_to_f64(&price) - 100.10).abs() < 1e-9);
113 }
114
115 #[rstest]
116 fn test_price_to_f64_undef_sentinel_is_nan() {
117 let price = Price::from_raw(PRICE_UNDEF, 0);
118 assert!(price_to_f64(&price).is_nan());
119 }
120
121 #[rstest]
122 fn test_price_to_f64_error_sentinel_is_nan() {
123 let price = Price::from_raw(PRICE_ERROR, 0);
124 assert!(price_to_f64(&price).is_nan());
125 }
126
127 #[rstest]
128 fn test_price_to_f64_error_price_constant_is_nan() {
129 assert!(price_to_f64(&ERROR_PRICE).is_nan());
131 }
132
133 #[rstest]
134 fn test_price_to_f64_wei_precision_boundary_is_finite() {
135 let mut price = Price::from_raw(1_000_000_000_000_000_000, 0);
139 price.precision = 18;
140 let value = price_to_f64(&price);
141
142 assert!(value.is_finite(), "precision 18 should not return NaN");
143 assert!((value - 1.0).abs() < 1e-9);
144 }
145
146 #[rstest]
147 fn test_quantity_to_f64_normal_value() {
148 let quantity = Quantity::from(1_000);
149 assert!((quantity_to_f64(&quantity) - 1_000.0).abs() < 1e-9);
150 }
151
152 #[rstest]
153 fn test_quantity_to_f64_undef_sentinel_is_nan() {
154 let quantity = Quantity::from_raw(QUANTITY_UNDEF, 0);
155 assert!(quantity_to_f64(&quantity).is_nan());
156 }
157
158 #[rstest]
159 fn test_quantity_to_f64_pathological_precision_is_nan() {
160 let mut quantity = Quantity::zero(0);
164 quantity.precision = 200;
165 assert!(quantity_to_f64(&quantity).is_nan());
166 }
167
168 #[rstest]
169 fn test_money_to_f64_normal_value() {
170 let money = Money::new(123.45, Currency::USD());
171 assert!((money_to_f64(&money) - 123.45).abs() < 1e-9);
172 }
173
174 #[rstest]
175 fn test_money_to_f64_pathological_precision_is_nan() {
176 let mut money = Money::new(0.0, Currency::USD());
180 money.currency.precision = 200;
181 assert!(money_to_f64(&money).is_nan());
182 }
183}