1#[inline]
10#[must_use]
11pub const fn const_max_u64(a: u64, b: u64) -> u64 {
12 if a > b { a } else { b }
13}
14
15#[inline]
17#[must_use]
18pub const fn const_min_u64(a: u64, b: u64) -> u64 {
19 if a < b { a } else { b }
20}
21
22#[inline]
24#[must_use]
25pub const fn const_max_i64(a: i64, b: i64) -> i64 {
26 if a > b { a } else { b }
27}
28
29#[inline]
31#[must_use]
32pub const fn const_min_i64(a: i64, b: i64) -> i64 {
33 if a < b { a } else { b }
34}
35
36#[inline]
38#[must_use]
39pub const fn is_power_of_two(n: u64) -> bool {
40 n != 0 && (n & (n - 1)) == 0
41}
42
43#[inline]
45#[must_use]
46pub const fn next_power_of_two(n: u64) -> u64 {
47 if n == 0 {
48 return 1;
49 }
50
51 let mut power = 1;
52 while power < n {
53 power <<= 1;
54 }
55 power
56}
57
58#[inline]
61#[must_use]
62pub const fn bytes_to_kb(bytes: u64) -> u64 {
63 bytes / 1024
64}
65
66#[inline]
68#[must_use]
69pub const fn kb_to_bytes(kb: u64) -> u64 {
70 kb * 1024
71}
72
73#[inline]
75#[must_use]
76pub const fn bytes_to_mb(bytes: u64) -> u64 {
77 bytes / (1024 * 1024)
78}
79
80#[inline]
82#[must_use]
83pub const fn mb_to_bytes(mb: u64) -> u64 {
84 mb * 1024 * 1024
85}
86
87#[inline]
90#[must_use]
91pub const fn array_len<T, const N: usize>(_arr: &[T; N]) -> usize {
92 N
93}
94
95#[inline]
97#[must_use]
98pub const fn is_valid_index(index: usize, len: usize) -> bool {
99 index < len
100}
101
102#[inline]
105#[must_use]
106pub const fn percent_to_bps(percent: u64) -> u64 {
107 percent * 100
108}
109
110#[inline]
112#[must_use]
113pub const fn bps_to_percent(bps: u64) -> u64 {
114 bps / 100
115}
116
117#[inline]
120#[must_use]
121pub const fn tick_size_to_decimals(tick_size_multiplier: u64) -> u8 {
122 if tick_size_multiplier == 1 {
123 0
124 } else if tick_size_multiplier == 10 {
125 1
126 } else if tick_size_multiplier == 100 {
127 2
128 } else if tick_size_multiplier == 1000 {
129 3
130 } else if tick_size_multiplier == 10000 {
131 4
132 } else if tick_size_multiplier == 100000 {
133 5
134 } else if tick_size_multiplier == 1000000 {
135 6
136 } else if tick_size_multiplier == 10000000 {
137 7
138 } else if tick_size_multiplier == 100000000 {
139 8
140 } else {
141 9
142 } }
144
145#[inline]
148#[must_use]
149pub const fn micros_to_nanos(micros: u64) -> u64 {
150 micros * 1000
151}
152
153#[inline]
155#[must_use]
156pub const fn nanos_to_micros(nanos: u64) -> u64 {
157 nanos / 1000
158}
159
160#[inline]
162#[must_use]
163pub const fn seconds_to_nanos(seconds: u64) -> u64 {
164 seconds * 1_000_000_000
165}
166
167#[inline]
169#[must_use]
170pub const fn nanos_to_seconds(nanos: u64) -> u64 {
171 nanos / 1_000_000_000
172}
173
174#[inline]
176#[must_use]
177pub const fn millis_to_nanos(millis: u64) -> u64 {
178 millis * 1_000_000
179}
180
181#[inline]
183#[must_use]
184pub const fn nanos_to_millis(nanos: u64) -> u64 {
185 nanos / 1_000_000
186}
187
188#[inline]
191#[must_use]
192pub const fn buffer_size_power_of_two(min_size: usize) -> usize {
193 if min_size == 0 {
194 return 1;
195 }
196
197 let mut size = 1;
198 while size < min_size {
199 size <<= 1;
200 }
201 size
202}
203
204#[inline]
206#[must_use]
207pub const fn simd_aligned_size(size: usize) -> usize {
208 (size + 3) & !3 }
210
211#[inline]
213#[must_use]
214pub const fn cache_aligned_size(size: usize) -> usize {
215 (size + 63) & !63 }
217
218#[inline]
221#[must_use]
222pub const fn orderbook_depth_for_memory(memory_bytes: usize, level_size: usize) -> usize {
223 if level_size == 0 {
224 return 0;
225 }
226 memory_bytes / level_size
227}
228
229#[inline]
231#[must_use]
232pub const fn websocket_max_message_size(buffer_size: usize) -> usize {
233 buffer_size * 9 / 10
235}
236
237#[inline]
239#[must_use]
240pub const fn pool_capacity_for_throughput(messages_per_second: u64, latency_ms: u64) -> usize {
241 let messages_in_window = (messages_per_second * latency_ms) / 1000;
243 (messages_in_window * 2) as usize
244}
245
246#[inline]
249#[must_use]
250pub const fn simd_f64x4_chunks(elements: usize) -> usize {
251 elements.div_ceil(4) }
253
254#[inline]
256#[must_use]
257pub const fn simd_f64x4_buffer_size(elements: usize) -> usize {
258 simd_f64x4_chunks(elements) * 4
259}
260
261#[inline]
263#[must_use]
264pub const fn optimal_simd_chunks(elements: usize, max_chunks: usize) -> usize {
265 let needed_chunks = simd_f64x4_chunks(elements);
266 if needed_chunks > max_chunks {
267 max_chunks
268 } else {
269 needed_chunks
270 }
271}
272
273#[inline]
276#[must_use]
277pub const fn price_diff_bps(price1: u64, price2: u64) -> u64 {
278 if price1 == 0 {
279 return 0;
280 }
281 let diff = price1.abs_diff(price2);
282 (diff * 10000) / price1 }
284
285#[inline]
287#[must_use]
288pub const fn tick_count(price1: u64, price2: u64, tick_size: u64) -> u64 {
289 if tick_size == 0 {
290 return 0;
291 }
292 let diff = price1.abs_diff(price2);
293 diff / tick_size
294}
295
296#[inline]
298#[must_use]
299pub const fn spread_bps(bid: u64, ask: u64) -> u64 {
300 if bid == 0 {
301 return 0;
302 }
303 if ask <= bid {
304 return 0; }
306 let spread = ask - bid;
307 let mid = (bid + ask) / 2;
308 if mid == 0 {
309 return 0;
310 }
311 (spread * 10000) / mid
313}
314
315#[inline]
317#[must_use]
318pub const fn mid_price(bid: u64, ask: u64) -> u64 {
319 (bid + ask) / 2
320}
321
322#[inline]
324#[must_use]
325pub const fn is_valid_tick_price(price: u64, tick_size: u64) -> bool {
326 if tick_size == 0 {
327 return false;
328 }
329 price.is_multiple_of(tick_size)
330}
331
332#[inline]
334#[must_use]
335pub const fn round_to_tick(price: u64, tick_size: u64) -> u64 {
336 if tick_size == 0 {
337 return price;
338 }
339 let remainder = price % tick_size;
340 if remainder * 2 >= tick_size {
341 price + (tick_size - remainder) } else {
343 price - remainder }
345}
346
347#[inline]
350#[must_use]
351pub const fn exchange_supports_websocket_compression(exchange: u8) -> bool {
352 match exchange {
354 1 | 3 => true, _ => false, }
357}
358
359#[inline]
361#[must_use]
362pub const fn exchange_default_websocket_port(exchange: u8) -> u16 {
363 match exchange {
365 0..=4 => 443, _ => 443, }
368}
369
370#[inline]
372#[must_use]
373pub const fn exchange_websocket_ping_interval_ms(exchange: u8) -> u64 {
374 match exchange {
375 0 => 180_000, 1 => 30_000, 2 => 20_000, 3 => 60_000, 4 => 30_000, _ => 30_000, }
382}
383
384#[inline]
386#[must_use]
387pub const fn exchange_requires_custom_ping(exchange: u8) -> bool {
388 match exchange {
389 2..=4 => true, _ => false, }
392}
393
394#[inline]
396#[must_use]
397pub const fn exchange_max_websocket_message_size(exchange: u8) -> usize {
398 match exchange {
399 0 => 1024 * 1024, 1 => 4 * 1024 * 1024, 2 => 1024 * 1024, 3 => 1024 * 1024, 4 => 512 * 1024, _ => 1024 * 1024, }
406}
407
408#[inline]
411#[must_use]
412pub const fn exchange_max_signature_length(exchange: u8) -> usize {
413 match exchange {
414 0 => 64, 1 => 256, 2 => 64, 3 => 512, 4 => 128, _ => 256, }
421}
422
423#[inline]
425#[must_use]
426pub const fn exchange_requires_passphrase(exchange: u8) -> bool {
427 match exchange {
428 1 => true, _ => false, }
431}
432
433#[inline]
435#[must_use]
436pub const fn exchange_max_api_key_length(exchange: u8) -> usize {
437 match exchange {
438 0 => 64, 1 => 128, 2 => 64, 3 => 128, 4 => 64, _ => 128, }
445}
446
447#[inline]
450#[must_use]
451pub const fn exchange_optimal_batch_size(exchange: u8, message_rate_per_second: u64) -> usize {
452 let base_batch_size = match exchange {
453 0 => 100, 1 => 50, 2 => 75, 3 => 25, 4 => 25, _ => 50, };
460
461 if message_rate_per_second > 1000 {
463 base_batch_size * 2
464 } else if message_rate_per_second > 100 {
465 base_batch_size
466 } else {
467 base_batch_size / 2
468 }
469}
470
471#[inline]
473#[must_use]
474pub const fn exchange_reconnection_delay_ms(exchange: u8, attempt: u32) -> u64 {
475 let base_delay = match exchange {
476 0 => 1000, 1 => 2000, 2 => 1000, 3 => 3000, 4 => 2000, _ => 1000, };
483
484 let capped_attempt = if attempt > 6 { 6 } else { attempt }; let delay = base_delay * (1u64 << capped_attempt);
487 const_min_u64(delay, 60_000) }
489
490#[cfg(test)]
491mod tests {
492 use super::*;
493
494 #[test]
495 fn test_const_max_min() {
496 assert_eq!(const_max_u64(5, 10), 10);
497 assert_eq!(const_max_u64(10, 5), 10);
498 assert_eq!(const_min_u64(5, 10), 5);
499 assert_eq!(const_min_u64(10, 5), 5);
500
501 assert_eq!(const_max_i64(-5, 10), 10);
502 assert_eq!(const_max_i64(10, -5), 10);
503 assert_eq!(const_min_i64(-5, 10), -5);
504 assert_eq!(const_min_i64(10, -5), -5);
505 }
506
507 #[test]
508 fn test_power_of_two() {
509 assert!(is_power_of_two(1));
510 assert!(is_power_of_two(2));
511 assert!(is_power_of_two(4));
512 assert!(is_power_of_two(64));
513 assert!(!is_power_of_two(0));
514 assert!(!is_power_of_two(3));
515 assert!(!is_power_of_two(100));
516
517 assert_eq!(next_power_of_two(0), 1);
518 assert_eq!(next_power_of_two(1), 1);
519 assert_eq!(next_power_of_two(2), 2);
520 assert_eq!(next_power_of_two(3), 4);
521 assert_eq!(next_power_of_two(5), 8);
522 assert_eq!(next_power_of_two(100), 128);
523 }
524
525 #[test]
526 fn test_byte_conversions() {
527 assert_eq!(bytes_to_kb(1024), 1);
528 assert_eq!(bytes_to_kb(2048), 2);
529 assert_eq!(kb_to_bytes(1), 1024);
530 assert_eq!(kb_to_bytes(2), 2048);
531
532 assert_eq!(bytes_to_mb(1024 * 1024), 1);
533 assert_eq!(bytes_to_mb(2 * 1024 * 1024), 2);
534 assert_eq!(mb_to_bytes(1), 1024 * 1024);
535 assert_eq!(mb_to_bytes(2), 2 * 1024 * 1024);
536 }
537
538 #[test]
539 fn test_trading_utilities() {
540 assert_eq!(percent_to_bps(1), 100);
541 assert_eq!(percent_to_bps(5), 500);
542 assert_eq!(bps_to_percent(100), 1);
543 assert_eq!(bps_to_percent(500), 5);
544
545 assert_eq!(tick_size_to_decimals(1), 0);
546 assert_eq!(tick_size_to_decimals(10), 1);
547 assert_eq!(tick_size_to_decimals(100), 2);
548 assert_eq!(tick_size_to_decimals(1000), 3);
549 }
550
551 #[test]
552 fn test_time_conversions() {
553 assert_eq!(micros_to_nanos(1), 1000);
554 assert_eq!(micros_to_nanos(1000), 1_000_000);
555 assert_eq!(nanos_to_micros(1000), 1);
556 assert_eq!(nanos_to_micros(1_000_000), 1000);
557
558 assert_eq!(seconds_to_nanos(1), 1_000_000_000);
559 assert_eq!(seconds_to_nanos(60), 60_000_000_000);
560 assert_eq!(nanos_to_seconds(1_000_000_000), 1);
561 assert_eq!(nanos_to_seconds(60_000_000_000), 60);
562 }
563
564 #[test]
565 fn test_const_contexts() {
566 const MAX_U64: u64 = const_max_u64(100, 200);
568 const MIN_U64: u64 = const_min_u64(100, 200);
569 const MAX_I64: i64 = const_max_i64(-100, 200);
570 const MIN_I64: i64 = const_min_i64(-100, 200);
571
572 assert_eq!(MAX_U64, 200);
573 assert_eq!(MIN_U64, 100);
574 assert_eq!(MAX_I64, 200);
575 assert_eq!(MIN_I64, -100);
576
577 const IS_POWER: bool = is_power_of_two(64);
578 const NEXT_POWER: u64 = next_power_of_two(100);
579
580 assert!(IS_POWER);
581 assert_eq!(NEXT_POWER, 128);
582
583 const KB_SIZE: u64 = bytes_to_kb(4096);
584 const MB_SIZE: u64 = bytes_to_mb(4_194_304);
585
586 assert_eq!(KB_SIZE, 4);
587 assert_eq!(MB_SIZE, 4);
588
589 const BPS: u64 = percent_to_bps(2);
590 const DECIMALS: u8 = tick_size_to_decimals(100);
591
592 assert_eq!(BPS, 200);
593 assert_eq!(DECIMALS, 2);
594
595 const NANOS: u64 = micros_to_nanos(100);
596 const SECONDS: u64 = nanos_to_seconds(5_000_000_000);
597
598 assert_eq!(NANOS, 100_000);
599 assert_eq!(SECONDS, 5);
600 }
601
602 #[test]
603 fn test_millis_conversions() {
604 assert_eq!(millis_to_nanos(1), 1_000_000);
605 assert_eq!(millis_to_nanos(1000), 1_000_000_000);
606 assert_eq!(nanos_to_millis(1_000_000), 1);
607 assert_eq!(nanos_to_millis(1_000_000_000), 1000);
608 }
609
610 #[test]
611 fn test_buffer_sizing() {
612 assert_eq!(buffer_size_power_of_two(0), 1);
613 assert_eq!(buffer_size_power_of_two(1), 1);
614 assert_eq!(buffer_size_power_of_two(2), 2);
615 assert_eq!(buffer_size_power_of_two(3), 4);
616 assert_eq!(buffer_size_power_of_two(5), 8);
617 assert_eq!(buffer_size_power_of_two(100), 128);
618
619 assert_eq!(simd_aligned_size(1), 4);
620 assert_eq!(simd_aligned_size(4), 4);
621 assert_eq!(simd_aligned_size(5), 8);
622 assert_eq!(simd_aligned_size(8), 8);
623
624 assert_eq!(cache_aligned_size(1), 64);
625 assert_eq!(cache_aligned_size(64), 64);
626 assert_eq!(cache_aligned_size(65), 128);
627 assert_eq!(cache_aligned_size(128), 128);
628 }
629
630 #[test]
631 fn test_hft_utilities() {
632 assert_eq!(orderbook_depth_for_memory(1024, 32), 32);
633 assert_eq!(orderbook_depth_for_memory(2048, 64), 32);
634 assert_eq!(orderbook_depth_for_memory(100, 0), 0);
635
636 assert_eq!(websocket_max_message_size(1000), 900);
637 assert_eq!(websocket_max_message_size(2048), 1843);
638
639 assert_eq!(pool_capacity_for_throughput(1000, 10), 20);
640 assert_eq!(pool_capacity_for_throughput(5000, 5), 50);
641 assert_eq!(pool_capacity_for_throughput(10000, 1), 20);
642 }
643
644 #[test]
645 fn test_new_const_contexts() {
646 const MILLIS_NANOS: u64 = millis_to_nanos(500);
648 const NANOS_MILLIS: u64 = nanos_to_millis(500_000_000);
649
650 assert_eq!(MILLIS_NANOS, 500_000_000);
651 assert_eq!(NANOS_MILLIS, 500);
652
653 const BUFFER_SIZE: usize = buffer_size_power_of_two(100);
654 const SIMD_SIZE: usize = simd_aligned_size(10);
655 const CACHE_SIZE: usize = cache_aligned_size(100);
656
657 assert_eq!(BUFFER_SIZE, 128);
658 assert_eq!(SIMD_SIZE, 12);
659 assert_eq!(CACHE_SIZE, 128);
660
661 const DEPTH: usize = orderbook_depth_for_memory(2048, 64);
662 const MAX_MSG_SIZE: usize = websocket_max_message_size(1024);
663 const POOL_CAP: usize = pool_capacity_for_throughput(1000, 10);
664
665 assert_eq!(DEPTH, 32);
666 assert_eq!(MAX_MSG_SIZE, 921);
667 assert_eq!(POOL_CAP, 20);
668 }
669
670 #[test]
671 fn test_simd_utilities() {
672 assert_eq!(simd_f64x4_chunks(1), 1);
674 assert_eq!(simd_f64x4_chunks(4), 1);
675 assert_eq!(simd_f64x4_chunks(5), 2);
676 assert_eq!(simd_f64x4_chunks(8), 2);
677 assert_eq!(simd_f64x4_chunks(9), 3);
678
679 assert_eq!(simd_f64x4_buffer_size(1), 4);
681 assert_eq!(simd_f64x4_buffer_size(4), 4);
682 assert_eq!(simd_f64x4_buffer_size(5), 8);
683 assert_eq!(simd_f64x4_buffer_size(9), 12);
684
685 assert_eq!(optimal_simd_chunks(10, 5), 3); assert_eq!(optimal_simd_chunks(20, 3), 3); assert_eq!(optimal_simd_chunks(4, 10), 1); }
690
691 #[test]
692 fn test_hft_financial_calculations() {
693 assert_eq!(price_diff_bps(10000, 10100), 100); assert_eq!(price_diff_bps(10000, 9900), 100); assert_eq!(price_diff_bps(0, 100), 0); assert_eq!(tick_count(10000, 10010, 5), 2); assert_eq!(tick_count(10010, 10000, 5), 2); assert_eq!(tick_count(10000, 10010, 0), 0); assert_eq!(spread_bps(9990, 10010), 20); assert_eq!(spread_bps(10000, 10000), 0); assert_eq!(spread_bps(10010, 9990), 0); assert_eq!(spread_bps(0, 100), 0); assert_eq!(mid_price(9990, 10010), 10000);
711 assert_eq!(mid_price(10000, 10000), 10000);
712 assert_eq!(mid_price(0, 200), 100);
713
714 assert!(is_valid_tick_price(10000, 5)); assert!(!is_valid_tick_price(10001, 5)); assert!(!is_valid_tick_price(10000, 0)); assert_eq!(round_to_tick(10001, 5), 10000); assert_eq!(round_to_tick(10003, 5), 10005); assert_eq!(round_to_tick(10000, 5), 10000); assert_eq!(round_to_tick(10001, 0), 10001); }
725
726 #[test]
727 fn test_simd_const_contexts() {
728 const CHUNKS_10: usize = simd_f64x4_chunks(10);
730 const BUFFER_SIZE_10: usize = simd_f64x4_buffer_size(10);
731 const OPTIMAL_CHUNKS: usize = optimal_simd_chunks(20, 5);
732
733 assert_eq!(CHUNKS_10, 3);
734 assert_eq!(BUFFER_SIZE_10, 12);
735 assert_eq!(OPTIMAL_CHUNKS, 5);
736 }
737
738 #[test]
739 fn test_hft_const_contexts() {
740 const PRICE_DIFF: u64 = price_diff_bps(10000, 10100);
742 const TICK_COUNT: u64 = tick_count(10000, 10010, 5);
743 const SPREAD: u64 = spread_bps(9990, 10010);
744 const MID: u64 = mid_price(9990, 10010);
745 const IS_VALID: bool = is_valid_tick_price(10000, 5);
746 const ROUNDED: u64 = round_to_tick(10003, 5);
747
748 assert_eq!(PRICE_DIFF, 100);
749 assert_eq!(TICK_COUNT, 2);
750 assert_eq!(SPREAD, 20);
751 assert_eq!(MID, 10000);
752 assert!(IS_VALID);
753 assert_eq!(ROUNDED, 10005);
754 }
755}