1use std::time::{SystemTime, UNIX_EPOCH};
2
3use rand::Rng;
4
5use crate::types::{OrderSide, TickSize};
6
7pub fn current_timestamp() -> u64 {
9 SystemTime::now()
10 .duration_since(UNIX_EPOCH)
11 .unwrap_or_default()
12 .as_secs()
13}
14
15pub fn calculate_order_amounts(
17 price: f64,
18 size: f64,
19 side: OrderSide,
20 tick_size: TickSize,
21) -> (String, String) {
22 const SIZE_DECIMALS: u32 = 2; let tick_decimals = tick_size.decimals();
25
26 let price_rounded = round_to_decimals(price, tick_decimals);
28
29 let size_rounded = round_to_decimals(size, SIZE_DECIMALS);
31
32 let cost = price_rounded * size_rounded;
34 let cost_rounded = round_to_decimals(cost, tick_decimals);
35
36 let share_amount = to_raw_amount(size_rounded, SIZE_DECIMALS);
38 let cost_amount = to_raw_amount(cost_rounded, SIZE_DECIMALS);
39
40 match side {
41 OrderSide::Buy => {
42 (cost_amount, share_amount)
44 }
45 OrderSide::Sell => {
46 (share_amount, cost_amount)
48 }
49 }
50}
51
52fn round_to_decimals(value: f64, decimals: u32) -> f64 {
54 let multiplier = 10_f64.powi(decimals as i32);
55 (value * multiplier).round() / multiplier
56}
57
58fn to_raw_amount(value: f64, decimals: u32) -> String {
60 let multiplier = 10_f64.powi(decimals as i32);
61 let raw = (value * multiplier).floor() as u128;
62 raw.to_string()
63}
64
65pub fn generate_salt() -> String {
67 rand::rng().random::<u128>().to_string()
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 #[test]
75 fn test_calculate_order_amounts_buy() {
76 let (maker, taker) =
77 calculate_order_amounts(0.52, 100.0, OrderSide::Buy, TickSize::Hundredth);
78
79 assert_eq!(maker, "5200");
81 assert_eq!(taker, "10000");
82 }
83
84 #[test]
85 fn test_calculate_order_amounts_sell() {
86 let (maker, taker) =
87 calculate_order_amounts(0.52, 100.0, OrderSide::Sell, TickSize::Hundredth);
88
89 assert_eq!(maker, "10000");
91 assert_eq!(taker, "5200");
92 }
93
94 #[test]
95 fn test_calculate_order_amounts_tenth_tick_size() {
96 let (maker, taker) = calculate_order_amounts(0.5, 50.0, OrderSide::Buy, TickSize::Tenth);
97
98 assert_eq!(maker, "2500");
101 assert_eq!(taker, "5000");
102 }
103
104 #[test]
105 fn test_calculate_order_amounts_thousandth_tick_size() {
106 let (maker, taker) =
107 calculate_order_amounts(0.523, 100.0, OrderSide::Buy, TickSize::Thousandth);
108
109 assert_eq!(maker, "5230");
112 assert_eq!(taker, "10000");
113 }
114
115 #[test]
116 fn test_calculate_order_amounts_ten_thousandth_tick_size() {
117 let (maker, taker) =
118 calculate_order_amounts(0.5234, 100.0, OrderSide::Buy, TickSize::TenThousandth);
119
120 assert_eq!(maker, "5234");
123 assert_eq!(taker, "10000");
124 }
125
126 #[test]
127 fn test_calculate_order_amounts_price_rounding() {
128 let (maker, taker) =
130 calculate_order_amounts(0.526, 100.0, OrderSide::Buy, TickSize::Hundredth);
131
132 assert_eq!(maker, "5300");
134 assert_eq!(taker, "10000");
135 }
136
137 #[test]
138 fn test_calculate_order_amounts_size_rounding() {
139 let (maker, taker) =
141 calculate_order_amounts(0.50, 100.567, OrderSide::Buy, TickSize::Hundredth);
142
143 assert_eq!(maker, "5029");
145 assert_eq!(taker, "10057");
146 }
147
148 #[test]
149 fn test_calculate_order_amounts_minimum_price() {
150 let (maker, taker) =
151 calculate_order_amounts(0.01, 100.0, OrderSide::Buy, TickSize::Hundredth);
152
153 assert_eq!(maker, "100");
155 assert_eq!(taker, "10000");
156 }
157
158 #[test]
159 fn test_calculate_order_amounts_maximum_price() {
160 let (maker, taker) =
161 calculate_order_amounts(0.99, 100.0, OrderSide::Buy, TickSize::Hundredth);
162
163 assert_eq!(maker, "9900");
165 assert_eq!(taker, "10000");
166 }
167
168 #[test]
169 fn test_calculate_order_amounts_small_size() {
170 let (maker, taker) =
171 calculate_order_amounts(0.50, 0.01, OrderSide::Buy, TickSize::Hundredth);
172
173 assert_eq!(maker, "1");
175 assert_eq!(taker, "1");
176 }
177
178 #[test]
179 fn test_calculate_order_amounts_large_size() {
180 let (maker, taker) =
181 calculate_order_amounts(0.50, 10000.0, OrderSide::Buy, TickSize::Hundredth);
182
183 assert_eq!(maker, "500000");
185 assert_eq!(taker, "1000000");
186 }
187
188 #[test]
189 fn test_current_timestamp_is_reasonable() {
190 let timestamp = current_timestamp();
191
192 assert!(
194 timestamp > 1704067200,
195 "Timestamp should be after 2024-01-01"
196 );
197
198 assert!(
200 timestamp < 4102444800,
201 "Timestamp should be before 2100-01-01"
202 );
203 }
204
205 #[test]
206 fn test_current_timestamp_increases() {
207 let t1 = current_timestamp();
208 let t2 = current_timestamp();
209
210 assert!(t2 >= t1);
212 }
213
214 #[test]
215 fn test_generate_salt_is_numeric() {
216 let salt = generate_salt();
217
218 assert!(
220 salt.parse::<u128>().is_ok(),
221 "Salt should be a valid u128 string"
222 );
223 }
224
225 #[test]
226 fn test_generate_salt_uniqueness() {
227 let salt1 = generate_salt();
228 let salt2 = generate_salt();
229 let salt3 = generate_salt();
230
231 assert_ne!(salt1, salt2, "Salts should be unique");
233 assert_ne!(salt2, salt3, "Salts should be unique");
234 assert_ne!(salt1, salt3, "Salts should be unique");
235 }
236
237 #[test]
238 fn test_generate_salt_not_empty() {
239 let salt = generate_salt();
240 assert!(!salt.is_empty(), "Salt should not be empty");
241 }
242
243 #[test]
244 fn test_round_to_decimals() {
245 let (maker, _) = calculate_order_amounts(0.555, 100.0, OrderSide::Buy, TickSize::Hundredth);
248 assert_eq!(maker, "5600"); let (maker, _) = calculate_order_amounts(0.554, 100.0, OrderSide::Buy, TickSize::Hundredth);
252 assert_eq!(maker, "5500"); }
254
255 #[test]
256 fn test_symmetry_buy_sell() {
257 let (buy_maker, buy_taker) =
259 calculate_order_amounts(0.60, 50.0, OrderSide::Buy, TickSize::Hundredth);
260 let (sell_maker, sell_taker) =
261 calculate_order_amounts(0.60, 50.0, OrderSide::Sell, TickSize::Hundredth);
262
263 assert_eq!(buy_maker, sell_taker, "Buy maker should equal sell taker");
264 assert_eq!(buy_taker, sell_maker, "Buy taker should equal sell maker");
265 }
266}