1use rand::Rng;
2use rust_decimal::prelude::ToPrimitive;
3
4use crate::{
5 api::markets::OrderLevel,
6 types::{OrderSide, TickSize},
7};
8
9pub fn calculate_order_amounts(
22 price: f64,
23 size: f64,
24 side: OrderSide,
25 tick_size: TickSize,
26) -> (String, String) {
27 const SIZE_DECIMALS: u32 = 6;
28 let tick_decimals = tick_size.decimals();
29
30 let price_rounded = round_bankers(price, tick_decimals);
31 let size_rounded = round_bankers(size, SIZE_DECIMALS);
32
33 let cost = price_rounded * size_rounded;
34 let cost_rounded = round_bankers(cost, SIZE_DECIMALS);
35
36 let share_amount = to_raw_amount(size_rounded, SIZE_DECIMALS);
37 let cost_amount = to_raw_amount(cost_rounded, SIZE_DECIMALS);
38
39 match side {
40 OrderSide::Buy => (cost_amount, share_amount),
41 OrderSide::Sell => (share_amount, cost_amount),
42 }
43}
44
45pub fn calculate_market_order_amounts(
47 amount: f64,
48 price: f64,
49 side: OrderSide,
50 tick_size: TickSize,
51) -> (String, String) {
52 const SIZE_DECIMALS: u32 = 6;
53 let tick_decimals = tick_size.decimals();
54
55 let price_rounded = round_bankers(price, tick_decimals);
56 let amount_rounded = round_bankers(amount, SIZE_DECIMALS); if price_rounded == 0.0 {
59 return ("0".to_string(), "0".to_string());
60 }
61
62 match side {
63 OrderSide::Buy => {
64 let maker_amount = amount_rounded;
67 let taker_amount_raw = maker_amount / price_rounded;
68 let taker_amount = round_to_zero(taker_amount_raw, SIZE_DECIMALS); (
72 to_raw_amount(maker_amount, SIZE_DECIMALS),
73 to_raw_amount(taker_amount, SIZE_DECIMALS),
74 )
75 }
76 OrderSide::Sell => {
77 let maker_amount = round_to_zero(amount, SIZE_DECIMALS); let taker_amount_raw = maker_amount * price_rounded;
81 let taker_amount = round_bankers(taker_amount_raw, SIZE_DECIMALS);
82
83 (
84 to_raw_amount(maker_amount, SIZE_DECIMALS),
85 to_raw_amount(taker_amount, SIZE_DECIMALS),
86 )
87 }
88 }
89}
90
91pub fn calculate_market_price(levels: &[OrderLevel], amount: f64, side: OrderSide) -> Option<f64> {
93 if levels.is_empty() {
94 return None;
95 }
96
97 let mut sum = 0.0;
98
99 for level in levels {
100 let p = level.price.to_f64()?;
101 let s = level.size.to_f64()?;
102
103 match side {
104 OrderSide::Buy => {
105 sum += p * s;
106 }
107 OrderSide::Sell => {
108 sum += s;
109 }
110 }
111
112 if sum >= amount {
113 return Some(p);
114 }
115 }
116
117 None
119}
120
121fn to_raw_amount(val: f64, decimals: u32) -> String {
123 let factor = 10f64.powi(decimals as i32);
124 let raw = (val * factor).round();
126 format!("{:.0}", raw)
129}
130
131pub fn generate_salt() -> String {
133 rand::rng().random::<u128>().to_string()
134}
135
136fn round_bankers(val: f64, decimals: u32) -> f64 {
140 let factor = 10f64.powi(decimals as i32);
141 let v = val * factor;
142 let r = v.round();
143 let diff = (v - r).abs();
144
145 if (diff - 0.5).abs() < 1e-10 {
146 if r % 2.0 != 0.0 {
148 if v > 0.0 {
157 return (r - 1.0) / factor;
158 } else {
159 return (r + 1.0) / factor;
160 }
161 }
162 }
163 r / factor
164}
165
166fn round_to_zero(val: f64, decimals: u32) -> f64 {
168 let factor = 10f64.powi(decimals as i32);
169 (val * factor).trunc() / factor
170}
171
172#[cfg(test)]
173mod tests {
174 use super::*;
175
176 #[test]
177 fn test_calculate_order_amounts_buy() {
178 let (maker, taker) =
179 calculate_order_amounts(0.52, 100.0, OrderSide::Buy, TickSize::Hundredth);
180 assert_eq!(maker, "52000000");
181 assert_eq!(taker, "100000000");
182 }
183
184 #[test]
185 fn test_calculate_order_amounts_sell() {
186 let (maker, taker) =
187 calculate_order_amounts(0.52, 100.0, OrderSide::Sell, TickSize::Hundredth);
188 assert_eq!(maker, "100000000");
189 assert_eq!(taker, "52000000");
190 }
191
192 #[test]
193 fn test_round_bankers() {
194 assert_eq!(round_bankers(0.5, 0), 0.0);
195 assert_eq!(round_bankers(1.5, 0), 2.0);
196 assert_eq!(round_bankers(2.5, 0), 2.0);
197 assert_eq!(round_bankers(3.5, 0), 4.0);
198 }
199
200 #[test]
201 fn test_calculate_market_order_amounts_buy() {
202 let (maker, taker) =
205 calculate_market_order_amounts(100.0, 0.50, OrderSide::Buy, TickSize::Hundredth);
206 assert_eq!(maker, "100000000");
207 assert_eq!(taker, "200000000");
208 }
209
210 #[test]
211 fn test_calculate_market_price_buy_simple() {
212 use rust_decimal_macros::dec;
213 let levels = vec![OrderLevel {
215 price: dec!(0.50),
216 size: dec!(1000),
217 }];
218 let price = calculate_market_price(&levels, 100.0, OrderSide::Buy);
219 assert_eq!(price, Some(0.50));
220 }
221
222 #[test]
223 fn test_calculate_market_price_insufficient_liquidity() {
224 use rust_decimal_macros::dec;
225 let levels = vec![OrderLevel {
227 price: dec!(0.50),
228 size: dec!(10),
229 }];
230 let price = calculate_market_price(&levels, 1000.0, OrderSide::Buy);
232 assert_eq!(
233 price, None,
234 "Should return None when liquidity is insufficient"
235 );
236 }
237
238 #[test]
239 fn test_calculate_market_price_empty_levels() {
240 let price = calculate_market_price(&[], 100.0, OrderSide::Buy);
241 assert_eq!(price, None);
242 }
243
244 #[test]
245 fn test_calculate_market_price_sell_insufficient() {
246 use rust_decimal_macros::dec;
247 let levels = vec![OrderLevel {
248 price: dec!(0.50),
249 size: dec!(10),
250 }];
251 let price = calculate_market_price(&levels, 100.0, OrderSide::Sell);
253 assert_eq!(
254 price, None,
255 "Should return None when sell liquidity is insufficient"
256 );
257 }
258
259 #[test]
260 fn test_generate_salt_large_range() {
261 let salt = generate_salt();
263 let _parsed: u128 = salt.parse().expect("Salt should parse as u128");
264 let salt2 = generate_salt();
266 assert_ne!(salt, salt2, "Two random salts should differ");
267 }
268
269 #[test]
272 fn test_calculate_market_order_amounts_sell() {
273 let (maker, taker) =
275 calculate_market_order_amounts(100.0, 0.50, OrderSide::Sell, TickSize::Hundredth);
276 assert_eq!(maker, "100000000"); assert_eq!(taker, "50000000"); }
279
280 #[test]
281 fn test_calculate_market_order_amounts_zero_price() {
282 let (maker, taker) =
283 calculate_market_order_amounts(100.0, 0.0, OrderSide::Buy, TickSize::Hundredth);
284 assert_eq!(maker, "0");
285 assert_eq!(taker, "0");
286 }
287
288 #[test]
289 fn test_calculate_market_order_amounts_sell_zero_price() {
290 let (maker, taker) =
291 calculate_market_order_amounts(100.0, 0.0, OrderSide::Sell, TickSize::Hundredth);
292 assert_eq!(maker, "0");
293 assert_eq!(taker, "0");
294 }
295
296 #[test]
297 fn test_calculate_market_order_amounts_tenth_tick() {
298 let (maker, taker) =
300 calculate_market_order_amounts(100.0, 0.5, OrderSide::Buy, TickSize::Tenth);
301 assert_eq!(maker, "100000000");
302 assert_eq!(taker, "200000000");
303 }
304
305 #[test]
306 fn test_calculate_market_order_amounts_thousandth_tick() {
307 let (maker, taker) =
308 calculate_market_order_amounts(100.0, 0.555, OrderSide::Buy, TickSize::Thousandth);
309 assert_eq!(maker, "100000000");
310 let taker_val: u64 = taker.parse().unwrap();
312 assert!(taker_val > 180_000_000); }
314
315 #[test]
316 fn test_calculate_order_amounts_tenth_tick() {
317 let (maker, taker) = calculate_order_amounts(0.5, 100.0, OrderSide::Buy, TickSize::Tenth);
318 assert_eq!(maker, "50000000");
319 assert_eq!(taker, "100000000");
320 }
321
322 #[test]
323 fn test_calculate_order_amounts_thousandth_tick() {
324 let (maker, taker) =
325 calculate_order_amounts(0.555, 100.0, OrderSide::Buy, TickSize::Thousandth);
326 assert_eq!(maker, "55500000");
327 assert_eq!(taker, "100000000");
328 }
329
330 #[test]
333 fn test_calculate_market_price_sell_simple() {
334 use rust_decimal_macros::dec;
335 let levels = vec![OrderLevel {
336 price: dec!(0.50),
337 size: dec!(200),
338 }];
339 let price = calculate_market_price(&levels, 100.0, OrderSide::Sell);
341 assert_eq!(price, Some(0.50));
342 }
343
344 #[test]
345 fn test_calculate_market_price_buy_multiple_levels() {
346 use rust_decimal_macros::dec;
347 let levels = vec![
348 OrderLevel {
349 price: dec!(0.50),
350 size: dec!(100),
351 }, OrderLevel {
353 price: dec!(0.55),
354 size: dec!(100),
355 }, OrderLevel {
357 price: dec!(0.60),
358 size: dec!(100),
359 }, ];
361 let price = calculate_market_price(&levels, 100.0, OrderSide::Buy);
365 assert_eq!(price, Some(0.55));
366 }
367
368 #[test]
371 fn test_round_to_zero() {
372 assert_eq!(round_to_zero(1.999999, 6), 1.999999);
373 assert_eq!(round_to_zero(1.9999999, 6), 1.999999);
374 assert_eq!(round_to_zero(-1.9999999, 6), -1.999999);
375 assert_eq!(round_to_zero(0.0, 6), 0.0);
376 }
377
378 #[test]
379 fn test_round_bankers_decimals() {
380 assert_eq!(round_bankers(1.235, 2), 1.24); assert_eq!(round_bankers(1.245, 2), 1.24); assert_eq!(round_bankers(1.265, 2), 1.26); }
386
387 #[test]
388 fn test_round_bankers_negative() {
389 assert_eq!(round_bankers(-0.5, 0), 0.0);
390 assert_eq!(round_bankers(-1.5, 0), -2.0);
391 assert_eq!(round_bankers(-2.5, 0), -2.0);
392 }
393
394 #[test]
395 fn test_to_raw_amount() {
396 assert_eq!(to_raw_amount(1.0, 6), "1000000");
397 assert_eq!(to_raw_amount(0.5, 6), "500000");
398 assert_eq!(to_raw_amount(0.0, 6), "0");
399 assert_eq!(to_raw_amount(123.456789, 6), "123456789");
400 }
401
402 #[test]
403 fn test_calculate_market_order_amounts_negative_price_treated_as_zero() {
404 let (maker, taker) =
407 calculate_market_order_amounts(100.0, -0.5, OrderSide::Buy, TickSize::Hundredth);
408 let taker_val: i64 = taker.parse().unwrap();
410 assert!(
411 taker_val < 0,
412 "Negative price produces negative taker amount"
413 );
414 assert_eq!(maker, "100000000");
416 }
417
418 #[test]
419 fn test_calculate_order_amounts_small_fractional_size() {
420 let (maker, taker) =
422 calculate_order_amounts(0.50, 0.000001, OrderSide::Buy, TickSize::Hundredth);
423 assert_eq!(taker, "1"); assert_eq!(maker, "0"); }
427
428 #[test]
429 fn test_calculate_market_price_exact_boundary() {
430 use rust_decimal_macros::dec;
431 let levels = vec![
433 OrderLevel {
434 price: dec!(0.50),
435 size: dec!(100),
436 },
437 OrderLevel {
438 price: dec!(0.60),
439 size: dec!(100),
440 },
441 ];
442 let price = calculate_market_price(&levels, 50.0, OrderSide::Buy);
444 assert_eq!(price, Some(0.50));
445 }
446}