1use crate::const_fn_candidates::simd_f64x4_chunks;
59use rust_decimal::Decimal;
60use rust_decimal::prelude::ToPrimitive;
61use simd_aligned::VecSimd;
62use smallvec::SmallVec;
63use wide::f64x4;
64
65#[derive(Debug)]
74pub struct SimdPriceCalculator<const N: usize = 32> {
75 price_buffer: VecSimd<f64x4>,
77
78 quantity_buffer: VecSimd<f64x4>,
80
81 work_buffer: VecSimd<f64x4>,
83
84 result_buffer: Vec<Decimal>,
86}
87
88impl<const N: usize> SimdPriceCalculator<N> {
89 #[must_use]
94 pub fn new(max_elements: usize) -> Self {
95 let effective_capacity = max_elements.min(N);
97 let simd_chunks = simd_f64x4_chunks(effective_capacity);
98
99 Self {
100 price_buffer: VecSimd::with(0.0, effective_capacity),
101 quantity_buffer: VecSimd::with(0.0, effective_capacity),
102 work_buffer: VecSimd::with(0.0, effective_capacity),
103 result_buffer: Vec::with_capacity(effective_capacity),
104 }
105 }
106
107 #[inline]
115 pub fn calculate_vwap(
116 &mut self,
117 prices: &[Decimal],
118 quantities: &[Decimal],
119 ) -> Result<Decimal, &'static str> {
120 if prices.len() != quantities.len() {
121 return Err("Price and quantity arrays must have equal length");
122 }
123
124 if prices.is_empty() {
125 return Err("Cannot calculate VWAP for empty arrays");
126 }
127
128 let len = prices.len();
129 if len > N {
130 return Err("Input data exceeds const generic N capacity limit");
131 }
132
133 let simd_chunks = simd_f64x4_chunks(len);
134
135 if simd_chunks > self.price_buffer.len() {
137 self.resize_buffers(simd_chunks);
138 }
139
140 self.load_data(prices, quantities)?;
142
143 let mut total_value = f64x4::ZERO;
145 let mut total_quantity = f64x4::ZERO;
146
147 for i in 0..simd_chunks {
148 let prices = self.price_buffer[i];
149 let quantities = self.quantity_buffer[i];
150
151 if prices.is_nan().any() || quantities.is_nan().any() {
153 return Err("Invalid price or quantity data (NaN detected)");
154 }
155
156 let values = prices * quantities;
158
159 total_value += values;
160 total_quantity += quantities;
161 }
162
163 let total_value_scalar = total_value.to_array().iter().sum::<f64>();
165 let total_quantity_scalar = total_quantity.to_array().iter().sum::<f64>();
166
167 if total_quantity_scalar == 0.0 {
168 return Err("Total quantity is zero - cannot calculate VWAP");
169 }
170
171 let vwap = total_value_scalar / total_quantity_scalar;
172
173 Decimal::try_from(vwap).map_err(|_| "VWAP result cannot be represented as Decimal")
175 }
176
177 #[inline]
184 pub fn calculate_spreads(
185 &mut self,
186 bid_prices: &[Decimal],
187 ask_prices: &[Decimal],
188 ) -> Result<SmallVec<[Decimal; N]>, &'static str> {
189 if bid_prices.len() != ask_prices.len() {
190 return Err("Bid and ask price arrays must have equal length");
191 }
192
193 let len = bid_prices.len();
194 if len == 0 {
195 return Ok(SmallVec::new());
196 }
197
198 if len > N {
199 return Err("Input data exceeds const generic N capacity limit");
200 }
201
202 let simd_chunks = simd_f64x4_chunks(len);
203
204 if simd_chunks > self.price_buffer.len() {
206 self.resize_buffers(simd_chunks);
207 }
208
209 for (i, chunk) in bid_prices.chunks(4).enumerate() {
211 let mut values = [0.0; 4];
212 for (j, &price) in chunk.iter().enumerate() {
213 values[j] = price
214 .to_f64()
215 .ok_or("Bid price value cannot be converted to f64 for SIMD processing")?;
216 }
217 self.price_buffer[i] = f64x4::from(values);
218 }
219
220 for (i, chunk) in ask_prices.chunks(4).enumerate() {
222 let mut values = [0.0; 4];
223 for (j, &price) in chunk.iter().enumerate() {
224 values[j] = price
225 .to_f64()
226 .ok_or("Ask price value cannot be converted to f64 for SIMD processing")?;
227 }
228 self.quantity_buffer[i] = f64x4::from(values);
229 }
230
231 self.result_buffer.clear();
233 self.result_buffer.reserve(len);
234
235 for i in 0..simd_chunks {
236 let bids = self.price_buffer[i];
237 let asks = self.quantity_buffer[i];
238
239 if bids.is_nan().any() || asks.is_nan().any() {
241 return Err("Invalid price data (NaN detected)");
242 }
243
244 let spreads = asks - bids;
246 let spread_array = spreads.to_array();
247
248 let chunk_len = (len - i * 4).min(4);
250 for j in 0..chunk_len {
251 let spread_decimal = Decimal::try_from(spread_array[j])
252 .map_err(|_| "Spread result cannot be represented as Decimal")?;
253 self.result_buffer.push(spread_decimal);
254 }
255 }
256
257 Ok(self.result_buffer.iter().copied().collect())
258 }
259
260 #[inline]
267 pub fn calculate_price_impact(
268 &mut self,
269 prices: &[Decimal],
270 quantities: &[Decimal],
271 trade_quantity: Decimal,
272 ) -> Result<Decimal, &'static str> {
273 if prices.len() != quantities.len() {
274 return Err("Price and quantity arrays must have equal length");
275 }
276
277 if prices.is_empty() {
278 return Err("Cannot calculate price impact for empty levels");
279 }
280
281 let len = prices.len();
282 if len > N {
283 return Err("Input data exceeds const generic N capacity limit");
284 }
285
286 let trade_qty_f64 = trade_quantity
287 .to_f64()
288 .ok_or("Trade quantity cannot be converted to f64")?;
289
290 if trade_qty_f64 <= 0.0 {
291 return Err("Trade quantity must be positive");
292 }
293
294 let simd_chunks = simd_f64x4_chunks(len);
295
296 if simd_chunks > self.price_buffer.len() {
298 self.resize_buffers(simd_chunks);
299 }
300
301 self.load_data(prices, quantities)?;
303
304 let mut remaining_quantity = trade_qty_f64;
306 let mut total_cost = 0.0;
307 let mut total_filled = 0.0;
308
309 for i in 0..simd_chunks {
310 let level_prices = self.price_buffer[i];
311 let level_quantities = self.quantity_buffer[i];
312
313 if level_prices.is_nan().any() || level_quantities.is_nan().any() {
315 return Err("Invalid market data (NaN detected)");
316 }
317
318 let price_array = level_prices.to_array();
319 let qty_array = level_quantities.to_array();
320
321 let chunk_len = (len - i * 4).min(4);
323 for j in 0..chunk_len {
324 if remaining_quantity <= 0.0 {
325 break;
326 }
327
328 let level_qty = qty_array[j];
329 let level_price = price_array[j];
330
331 if level_qty <= 0.0 || level_price <= 0.0 {
332 continue; }
334
335 let fill_qty = remaining_quantity.min(level_qty);
336 total_cost += fill_qty * level_price;
337 total_filled += fill_qty;
338 remaining_quantity -= fill_qty;
339 }
340
341 if remaining_quantity <= 0.0 {
342 break;
343 }
344 }
345
346 if total_filled == 0.0 {
347 return Err("No liquidity available for price impact calculation");
348 }
349
350 let avg_price = total_cost / total_filled;
351
352 let best_price = prices[0]
354 .to_f64()
355 .ok_or("Best price cannot be converted to f64")?;
356
357 if best_price <= 0.0 {
358 return Err("Best price must be positive");
359 }
360
361 let impact = (avg_price - best_price) / best_price;
362
363 Decimal::try_from(impact).map_err(|_| "Price impact cannot be represented as Decimal")
364 }
365
366 #[inline]
368 fn load_data(
369 &mut self,
370 prices: &[Decimal],
371 quantities: &[Decimal],
372 ) -> Result<(), &'static str> {
373 let len = prices.len();
374 if len > N {
375 return Err("Input data exceeds const generic N capacity limit");
376 }
377
378 let simd_chunks = simd_f64x4_chunks(len);
379
380 if simd_chunks > self.price_buffer.len() {
382 self.resize_buffers(simd_chunks);
383 }
384
385 for (i, chunk) in prices.chunks(4).enumerate() {
387 let mut values = [0.0; 4];
388 for (j, &price) in chunk.iter().enumerate() {
389 values[j] = price
390 .to_f64()
391 .ok_or("Price value cannot be converted to f64 for SIMD processing")?;
392 }
393 self.price_buffer[i] = f64x4::from(values);
394 }
395
396 for (i, chunk) in quantities.chunks(4).enumerate() {
398 let mut values = [0.0; 4];
399 for (j, &qty) in chunk.iter().enumerate() {
400 values[j] = qty
401 .to_f64()
402 .ok_or("Quantity value cannot be converted to f64 for SIMD processing")?;
403 }
404 self.quantity_buffer[i] = f64x4::from(values);
405 }
406
407 Ok(())
408 }
409
410 fn resize_buffers(&mut self, new_capacity: usize) {
415 let max_chunks = simd_f64x4_chunks(N);
417 let safe_capacity = new_capacity.min(max_chunks).max(self.price_buffer.len());
418
419 let element_capacity = safe_capacity * 4;
421 self.price_buffer = VecSimd::<f64x4>::with(0.0, element_capacity);
422 self.quantity_buffer = VecSimd::<f64x4>::with(0.0, element_capacity);
423 self.work_buffer = VecSimd::<f64x4>::with(0.0, element_capacity);
424 self.result_buffer.reserve(element_capacity);
425 }
426}
427
428impl<const N: usize> Default for SimdPriceCalculator<N> {
429 fn default() -> Self {
430 Self::new(N) }
432}
433
434#[inline]
437pub fn simd_vwap(prices: &[Decimal], quantities: &[Decimal]) -> Result<Decimal, &'static str> {
438 thread_local! {
439 static CALCULATOR: std::cell::RefCell<SimdPriceCalculator<32>> =
440 std::cell::RefCell::new(SimdPriceCalculator::new(32));
441 }
442
443 CALCULATOR.with(|calc| calc.borrow_mut().calculate_vwap(prices, quantities))
444}
445
446#[inline]
448pub fn simd_spreads(
449 bid_prices: &[Decimal],
450 ask_prices: &[Decimal],
451) -> Result<SmallVec<[Decimal; 32]>, &'static str> {
452 thread_local! {
453 static CALCULATOR: std::cell::RefCell<SimdPriceCalculator<32>> =
454 std::cell::RefCell::new(SimdPriceCalculator::new(32));
455 }
456
457 CALCULATOR.with(|calc| calc.borrow_mut().calculate_spreads(bid_prices, ask_prices))
458}
459
460#[inline]
462pub fn simd_price_impact(
463 prices: &[Decimal],
464 quantities: &[Decimal],
465 trade_quantity: Decimal,
466) -> Result<Decimal, &'static str> {
467 thread_local! {
468 static CALCULATOR: std::cell::RefCell<SimdPriceCalculator<32>> =
469 std::cell::RefCell::new(SimdPriceCalculator::new(32));
470 }
471
472 CALCULATOR.with(|calc| {
473 calc.borrow_mut()
474 .calculate_price_impact(prices, quantities, trade_quantity)
475 })
476}
477
478pub type DefaultSimdPriceCalculator = SimdPriceCalculator<32>;
481pub type SimdPriceCalculator16 = SimdPriceCalculator<16>;
483pub type SimdPriceCalculator64 = SimdPriceCalculator<64>;
485pub type SimdPriceCalculator128 = SimdPriceCalculator<128>;
487
488#[inline]
491pub fn simd_spreads_16(
492 bid_prices: &[Decimal],
493 ask_prices: &[Decimal],
494) -> Result<SmallVec<[Decimal; 16]>, &'static str> {
495 thread_local! {
496 static CALCULATOR: std::cell::RefCell<SimdPriceCalculator<16>> =
497 std::cell::RefCell::new(SimdPriceCalculator::new(16));
498 }
499
500 CALCULATOR.with(|calc| calc.borrow_mut().calculate_spreads(bid_prices, ask_prices))
501}
502
503#[inline]
505pub fn simd_spreads_64(
506 bid_prices: &[Decimal],
507 ask_prices: &[Decimal],
508) -> Result<SmallVec<[Decimal; 64]>, &'static str> {
509 thread_local! {
510 static CALCULATOR: std::cell::RefCell<SimdPriceCalculator<64>> =
511 std::cell::RefCell::new(SimdPriceCalculator::new(64));
512 }
513
514 CALCULATOR.with(|calc| calc.borrow_mut().calculate_spreads(bid_prices, ask_prices))
515}
516
517#[cfg(test)]
518mod tests {
519 use super::*;
520 use rust_decimal_macros::dec;
521
522 #[test]
523 fn test_simd_vwap_calculation() {
524 let prices = vec![dec!(100.0), dec!(100.5), dec!(101.0), dec!(101.5)];
525 let quantities = vec![dec!(10.0), dec!(20.0), dec!(15.0), dec!(5.0)];
526
527 let vwap = simd_vwap(&prices, &quantities).unwrap();
528
529 let expected = dec!(100.65);
532 assert!((vwap - expected).abs() < dec!(0.01));
533 }
534
535 #[test]
536 fn test_simd_spreads_calculation() {
537 let bids = vec![dec!(99.5), dec!(99.0), dec!(98.5)];
538 let asks = vec![dec!(100.5), dec!(101.0), dec!(101.5)];
539
540 let spreads = simd_spreads(&bids, &asks).unwrap();
541
542 assert_eq!(spreads.len(), 3);
543 assert_eq!(spreads[0], dec!(1.0)); assert_eq!(spreads[1], dec!(2.0)); assert_eq!(spreads[2], dec!(3.0)); }
547
548 #[test]
549 fn test_simd_price_impact() {
550 let prices = vec![dec!(100.0), dec!(100.1), dec!(100.2), dec!(100.3)];
551 let quantities = vec![dec!(10.0), dec!(20.0), dec!(30.0), dec!(40.0)];
552 let trade_qty = dec!(25.0);
553
554 let impact = simd_price_impact(&prices, &quantities, trade_qty).unwrap();
555
556 assert!(impact > dec!(0.0));
559 assert!(impact < dec!(0.01));
560 }
561
562 #[test]
563 fn test_calculator_reuse() {
564 let mut calc = SimdPriceCalculator::<32>::new(16);
565
566 let prices1 = vec![dec!(100.0), dec!(101.0)];
567 let quantities1 = vec![dec!(10.0), dec!(20.0)];
568
569 let vwap1 = calc.calculate_vwap(&prices1, &quantities1).unwrap();
570
571 let prices2 = vec![dec!(200.0), dec!(201.0), dec!(202.0)];
572 let quantities2 = vec![dec!(5.0), dec!(10.0), dec!(15.0)];
573
574 let vwap2 = calc.calculate_vwap(&prices2, &quantities2).unwrap();
575
576 assert_ne!(vwap1, vwap2);
577 assert!(vwap1 > dec!(100.0));
578 assert!(vwap2 > dec!(200.0));
579 }
580
581 #[test]
582 fn test_error_handling() {
583 let prices = vec![dec!(100.0)];
584 let quantities = vec![]; let result = simd_vwap(&prices, &quantities);
587 assert!(result.is_err());
588
589 let zero_quantities = vec![dec!(0.0)];
591 let result = simd_vwap(&prices, &zero_quantities);
592 assert!(result.is_err());
593 }
594
595 #[test]
596 fn test_default_buffer_size() {
597 let size = 32;
599 let prices: Vec<Decimal> = (0..size)
600 .map(|i| dec!(100.0) + Decimal::from(i) * dec!(0.01))
601 .collect();
602 let quantities: Vec<Decimal> = (0..size)
603 .map(|i| dec!(10.0) + Decimal::from(i % 100))
604 .collect();
605
606 let vwap = simd_vwap(&prices, &quantities).unwrap();
607 assert!(vwap > dec!(100.0));
608 assert!(vwap < dec!(200.0));
609 }
610
611 #[test]
612 fn test_large_dataset() {
613 let size = 1000;
615 let prices: Vec<Decimal> = (0..size)
616 .map(|i| dec!(100.0) + Decimal::from(i) * dec!(0.01))
617 .collect();
618 let quantities: Vec<Decimal> = (0..size)
619 .map(|i| dec!(10.0) + Decimal::from(i % 100))
620 .collect();
621
622 let result = simd_vwap(&prices, &quantities);
623 assert!(result.is_err());
624
625 let size = 32;
627 let prices: Vec<Decimal> = (0..size)
628 .map(|i| dec!(100.0) + Decimal::from(i) * dec!(0.01))
629 .collect();
630 let quantities: Vec<Decimal> = (0..size)
631 .map(|i| dec!(10.0) + Decimal::from(i % 100))
632 .collect();
633
634 let result = simd_vwap(&prices, &quantities);
635 assert!(result.is_ok());
636 let vwap = result.unwrap();
637 assert!(vwap > dec!(100.0));
638 assert!(vwap < dec!(200.0));
639 }
640
641 #[test]
642 fn test_thread_local_large_spreads() {
643 let size = 2000;
645 let bid_prices: Vec<Decimal> = (0..size)
646 .map(|i| dec!(100.0) + Decimal::from(i) * dec!(0.01))
647 .collect();
648 let ask_prices: Vec<Decimal> = (0..size)
649 .map(|i| dec!(100.5) + Decimal::from(i) * dec!(0.01))
650 .collect();
651
652 let result = simd_spreads_16(&bid_prices, &ask_prices);
654 assert!(result.is_err());
655
656 let result = simd_spreads_64(&bid_prices, &ask_prices);
658 assert!(result.is_err());
659
660 let size_16 = 16;
662 let bid_prices_16: Vec<Decimal> = (0..size_16)
663 .map(|i| dec!(100.0) + Decimal::from(i) * dec!(0.01))
664 .collect();
665 let ask_prices_16: Vec<Decimal> = (0..size_16)
666 .map(|i| dec!(100.5) + Decimal::from(i) * dec!(0.01))
667 .collect();
668
669 let result = simd_spreads_16(&bid_prices_16, &ask_prices_16);
670 assert!(result.is_ok());
671 let spreads = result.unwrap();
672 assert_eq!(spreads.len(), size_16);
673 assert!(spreads[0] > dec!(0.0));
674
675 let size_64 = 64;
676 let bid_prices_64: Vec<Decimal> = (0..size_64)
677 .map(|i| dec!(100.0) + Decimal::from(i) * dec!(0.01))
678 .collect();
679 let ask_prices_64: Vec<Decimal> = (0..size_64)
680 .map(|i| dec!(100.5) + Decimal::from(i) * dec!(0.01))
681 .collect();
682
683 let result = simd_spreads_64(&bid_prices_64, &ask_prices_64);
684 assert!(result.is_ok());
685 let spreads = result.unwrap();
686 assert_eq!(spreads.len(), size_64);
687 assert!(spreads[0] > dec!(0.0));
688 }
689
690 #[test]
691 fn test_const_generic_enforcement() {
692 let mut calc_16 = SimdPriceCalculator::<16>::new(100);
694 let mut calc_32 = SimdPriceCalculator::<32>::new(100);
695
696 let prices_16: Vec<Decimal> = (0..16)
698 .map(|i| dec!(100.0) + Decimal::from(i) * dec!(0.01))
699 .collect();
700 let quantities_16: Vec<Decimal> = (0..16).map(|i| dec!(10.0) + Decimal::from(i)).collect();
701
702 let result = calc_16.calculate_vwap(&prices_16, &quantities_16);
704 assert!(result.is_ok());
705
706 let result = calc_32.calculate_vwap(&prices_16, &quantities_16);
708 assert!(result.is_ok());
709
710 let prices_32: Vec<Decimal> = (0..32)
712 .map(|i| dec!(100.0) + Decimal::from(i) * dec!(0.01))
713 .collect();
714 let quantities_32: Vec<Decimal> = (0..32).map(|i| dec!(10.0) + Decimal::from(i)).collect();
715
716 let result = calc_16.calculate_vwap(&prices_32, &quantities_32);
718 assert!(result.is_err());
719
720 let result = calc_32.calculate_vwap(&prices_32, &quantities_32);
722 assert!(result.is_ok());
723 }
724}