1use nautilus_model::types::{
23 Price, Quantity, fixed::FIXED_PRECISION, price::PriceRaw, quantity::QuantityRaw,
24};
25
26const POWERS_OF_10: [i128; 19] = [
28 1,
29 10,
30 100,
31 1_000,
32 10_000,
33 100_000,
34 1_000_000,
35 10_000_000,
36 100_000_000,
37 1_000_000_000,
38 10_000_000_000,
39 100_000_000_000,
40 1_000_000_000_000,
41 10_000_000_000_000,
42 100_000_000_000_000,
43 1_000_000_000_000_000,
44 10_000_000_000_000_000,
45 100_000_000_000_000_000,
46 1_000_000_000_000_000_000,
47];
48
49#[inline]
51fn pow10(exp: u8) -> i128 {
52 POWERS_OF_10[exp as usize]
53}
54
55#[inline]
59fn scale_mantissa(mantissa: i64, exponent: i8) -> PriceRaw {
60 let scale_exp = FIXED_PRECISION as i8 + exponent;
61 let mantissa_wide = mantissa as i128;
62
63 let scaled = if scale_exp >= 0 {
64 mantissa_wide * pow10(scale_exp as u8)
65 } else {
66 mantissa_wide / pow10((-scale_exp) as u8)
67 };
68
69 scaled as PriceRaw
70}
71
72#[must_use]
76pub fn mantissa_to_price(mantissa: i64, exponent: i8, precision: u8) -> Price {
77 let raw = scale_mantissa(mantissa, exponent);
78 Price::from_raw(raw, precision)
79}
80
81#[must_use]
89pub fn mantissa_to_quantity(mantissa: i64, exponent: i8, precision: u8) -> Quantity {
90 assert!(mantissa >= 0, "Quantity cannot be negative: {mantissa}");
91 let raw = scale_mantissa(mantissa, exponent);
92 Quantity::from_raw(raw as QuantityRaw, precision)
93}
94
95#[must_use]
100#[inline]
101pub fn mantissa_to_f64(mantissa: i64, exponent: i8) -> f64 {
102 mantissa as f64 * 10_f64.powi(exponent as i32)
103}
104
105#[cfg(test)]
106mod tests {
107 use nautilus_core::approx_eq;
108 use rstest::rstest;
109
110 use super::*;
111
112 #[rstest]
113 #[case(12345678, -8, 8, 0.12345678)]
114 #[case(9876543210, -8, 8, 98.7654321)]
115 #[case(100000000, -8, 8, 1.0)]
116 #[case(50000, -2, 2, 500.0)]
117 #[case(123, 0, 0, 123.0)]
118 fn test_mantissa_to_price(
119 #[case] mantissa: i64,
120 #[case] exponent: i8,
121 #[case] precision: u8,
122 #[case] expected: f64,
123 ) {
124 let price = mantissa_to_price(mantissa, exponent, precision);
125 assert!(
126 approx_eq!(f64, price.as_f64(), expected, epsilon = 1e-10),
127 "Expected {expected}, got {}",
128 price.as_f64()
129 );
130 assert_eq!(price.precision, precision);
131 }
132
133 #[rstest]
134 #[case(12345678, -8, 8, 0.12345678)]
135 #[case(100000000, -8, 8, 1.0)]
136 #[case(50000, -2, 2, 500.0)]
137 fn test_mantissa_to_quantity(
138 #[case] mantissa: i64,
139 #[case] exponent: i8,
140 #[case] precision: u8,
141 #[case] expected: f64,
142 ) {
143 let qty = mantissa_to_quantity(mantissa, exponent, precision);
144 assert!(
145 approx_eq!(f64, qty.as_f64(), expected, epsilon = 1e-10),
146 "Expected {expected}, got {}",
147 qty.as_f64()
148 );
149 assert_eq!(qty.precision, precision);
150 }
151
152 #[rstest]
153 fn test_mantissa_to_f64() {
154 assert!(approx_eq!(
155 f64,
156 mantissa_to_f64(12345678, -8),
157 0.12345678,
158 epsilon = 1e-15
159 ));
160 assert!(approx_eq!(
161 f64,
162 mantissa_to_f64(100, 2),
163 10000.0,
164 epsilon = 1e-10
165 ));
166 }
167
168 #[rstest]
169 #[should_panic(expected = "Quantity cannot be negative")]
170 fn test_negative_quantity_panics() {
171 let _ = mantissa_to_quantity(-100, 0, 0);
172 }
173}