//! Order Processing Benchmarks - Fixed Working Version //! //! This benchmark validates order processing performance for the Foxhunt HFT system. //! Tests core operations like order book updates, order matching, and execution reporting. use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion}; use foxhunt_core::types::prelude::*; use std::collections::{HashMap, VecDeque}; use std::sync::atomic::{AtomicU64, Ordering}; use std::time::{Duration, Instant}; /// Simple order book implementation for benchmarking #[derive(Debug)] struct SimpleOrderBook { bids: Vec<(Price, Quantity)>, asks: Vec<(Price, Quantity)>, last_sequence: AtomicU64, } impl SimpleOrderBook { fn new() -> Self { Self { bids: Vec::with_capacity(100), asks: Vec::with_capacity(100), last_sequence: AtomicU64::new(0), } } fn update_bid(&mut self, price: Price, quantity: Quantity) { self.bids.push((price, quantity)); if self.bids.len() > 50 { self.bids.remove(0); } self.last_sequence.fetch_add(1, Ordering::Relaxed); } fn update_ask(&mut self, price: Price, quantity: Quantity) { self.asks.push((price, quantity)); if self.asks.len() > 50 { self.asks.remove(0); } self.last_sequence.fetch_add(1, Ordering::Relaxed); } fn get_best_bid(&self) -> Option<(Price, Quantity)> { self.bids.last().copied() } fn get_best_ask(&self) -> Option<(Price, Quantity)> { self.asks.first().copied() } fn sequence(&self) -> u64 { self.last_sequence.load(Ordering::Relaxed) } } /// Simple order manager for benchmarking #[derive(Debug)] struct SimpleOrderManager { orders: HashMap, next_order_id: AtomicU64, } impl SimpleOrderManager { fn new() -> Self { Self { orders: HashMap::new(), next_order_id: AtomicU64::new(1), } } fn submit_order( &mut self, symbol: Symbol, side: Side, quantity: Quantity, price: Option, ) -> Result { let order_id = self .next_order_id .fetch_add(1, Ordering::Relaxed) .to_string(); let order = Order { id: order_id.clone().into(), order_id: order_id.clone().into(), client_order_id: format!("client_{}", order_id), broker_order_id: None, account_id: "test_account".to_string(), symbol, side, order_type: if price.is_some() { OrderType::Limit } else { OrderType::Market }, quantity, price, stop_price: None, filled_quantity: Quantity::ZERO, remaining_quantity: quantity, average_price: None, time_in_force: TimeInForce::Day, status: OrderStatus::New, timestamp: chrono::Utc::now(), created_at: chrono::Utc::now(), }; self.orders.insert(order_id.clone(), order.clone()); Ok(order) } fn cancel_order(&mut self, order_id: &str) -> Result<(), FoxhuntError> { if let Some(order) = self.orders.get_mut(order_id) { order.status = OrderStatus::Cancelled; Ok(()) } else { Err(FoxhuntError::NotFound { resource_type: "order".to_string(), resource_id: order_id.to_string(), context: Some("cancel_order".to_string()), }) } } fn fill_order( &mut self, order_id: &str, fill_quantity: Quantity, fill_price: Price, ) -> Result<(), FoxhuntError> { if let Some(order) = self.orders.get_mut(order_id) { order.filled_quantity = order.filled_quantity + fill_quantity; order.remaining_quantity = order.remaining_quantity - fill_quantity; order.average_price = Some(fill_price); if order.remaining_quantity.is_zero() { order.status = OrderStatus::Filled; } else { order.status = OrderStatus::PartiallyFilled; } Ok(()) } else { Err(FoxhuntError::NotFound { resource_type: "order".to_string(), resource_id: order_id.to_string(), context: Some("fill_order".to_string()), }) } } fn get_order(&self, order_id: &str) -> Option<&Order> { self.orders.get(order_id) } fn active_orders_count(&self) -> usize { self.orders.values().filter(|o| o.is_active()).count() } } /// Benchmark order book updates fn benchmark_order_book_updates(c: &mut Criterion) { let mut group = c.benchmark_group("order_book_updates"); group.measurement_time(Duration::from_secs(5)); // Test different batch sizes for batch_size in [1, 10, 100].iter() { group.bench_with_input( BenchmarkId::new("price_level_update", batch_size), batch_size, |b, &batch_size| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let mut book = SimpleOrderBook::new(); let _symbol = Symbol::from_str("EURUSD"); for i in 0..iters { let start = Instant::now(); // Process batch updates for j in 0..batch_size { let price_offset = (i * batch_size + j) as f64 * 0.0001; let base_price = 1.1000 + price_offset; let bid_price = Price::from_f64(base_price - 0.0001).unwrap_or(Price::ZERO); let ask_price = Price::from_f64(base_price + 0.0001).unwrap_or(Price::ZERO); let quantity = Quantity::from_f64(1000.0 + j as f64).unwrap_or(Quantity::ZERO); book.update_bid(bid_price, quantity); book.update_ask(ask_price, quantity); } let end = Instant::now(); total_duration += end.duration_since(start); // Verify book state let _best_bid = book.get_best_bid(); let _best_ask = book.get_best_ask(); let _sequence = book.sequence(); black_box(&book); } total_duration }); }, ); } group.finish(); } /// Benchmark order management operations fn benchmark_order_management(c: &mut Criterion) { let mut group = c.benchmark_group("order_management"); group.measurement_time(Duration::from_secs(5)); group.bench_function("order_submission", |b| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let mut order_manager = SimpleOrderManager::new(); let symbol = Symbol::from_str("EURUSD"); for i in 0..iters { let start = Instant::now(); let side = if i % 2 == 0 { Side::Buy } else { Side::Sell }; let quantity = Quantity::from_f64(1000.0).unwrap_or(Quantity::ZERO); let price = Some(Price::from_f64(1.1000 + (i as f64 * 0.0001)).unwrap_or(Price::ZERO)); let result = order_manager.submit_order(symbol.clone(), side, quantity, price); let end = Instant::now(); total_duration += end.duration_since(start); // Verify order was created if let Ok(order) = result { assert_eq!(order.symbol, symbol); assert_eq!(order.side, side); assert_eq!(order.quantity, quantity); black_box(order); } else { panic!("Order submission failed"); } } total_duration }); }); group.bench_function("order_cancellation", |b| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let mut order_manager = SimpleOrderManager::new(); let symbol = Symbol::from_str("EURUSD"); // Pre-create orders to cancel let mut order_ids = Vec::new(); for i in 0..iters { let quantity = Quantity::from_f64(1000.0).unwrap_or(Quantity::ZERO); let price = Some(Price::from_f64(1.1000).unwrap_or(Price::ZERO)); if let Ok(order) = order_manager.submit_order(symbol.clone(), Side::Buy, quantity, price) { order_ids.push(order.id); } } for order_id in order_ids { let start = Instant::now(); let result = order_manager.cancel_order(&order_id.to_string()); let end = Instant::now(); total_duration += end.duration_since(start); // Verify cancellation succeeded if result.is_err() { panic!("Order cancellation failed for order {}", order_id); } black_box(&result); } total_duration }); }); group.bench_function("order_fill_processing", |b| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let mut order_manager = SimpleOrderManager::new(); let symbol = Symbol::from_str("EURUSD"); // Pre-create orders to fill let mut order_ids = Vec::new(); for _i in 0..iters { let quantity = Quantity::from_f64(1000.0).unwrap_or(Quantity::ZERO); let price = Some(Price::from_f64(1.1000).unwrap_or(Price::ZERO)); if let Ok(order) = order_manager.submit_order(symbol.clone(), Side::Buy, quantity, price) { order_ids.push(order.id); } } for order_id in order_ids { let start = Instant::now(); let fill_quantity = Quantity::from_f64(1000.0).unwrap_or(Quantity::ZERO); let fill_price = Price::from_f64(1.1001).unwrap_or(Price::ZERO); let result = order_manager.fill_order(&order_id.to_string(), fill_quantity, fill_price); let end = Instant::now(); total_duration += end.duration_since(start); // Verify fill processing succeeded if result.is_err() { panic!("Order fill processing failed for order {}", order_id); } black_box(&result); } total_duration }); }); group.finish(); } /// Benchmark atomic operations for lock-free structures fn benchmark_atomic_operations(c: &mut Criterion) { let mut group = c.benchmark_group("atomic_operations"); group.measurement_time(Duration::from_secs(3)); group.bench_function("atomic_increment", |b| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let counter = AtomicU64::new(0); for _i in 0..iters { let start = Instant::now(); let _value = counter.fetch_add(1, Ordering::Relaxed); let end = Instant::now(); total_duration += end.duration_since(start); } black_box(counter.load(Ordering::Relaxed)); total_duration }); }); group.bench_function("atomic_compare_and_swap", |b| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let counter = AtomicU64::new(0); for i in 0..iters { let start = Instant::now(); let current = counter.load(Ordering::Relaxed); let _result = counter.compare_exchange_weak( current, current + 1, Ordering::Relaxed, Ordering::Relaxed, ); let end = Instant::now(); total_duration += end.duration_since(start); black_box(i); } total_duration }); }); group.finish(); } /// Benchmark queue operations fn benchmark_queue_operations(c: &mut Criterion) { let mut group = c.benchmark_group("queue_operations"); group.measurement_time(Duration::from_secs(3)); group.bench_function("queue_enqueue_dequeue", |b| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let mut queue = VecDeque::with_capacity(1000); for i in 0..iters { let start = Instant::now(); // Enqueue operation queue.push_back(i); // Dequeue operation (if queue not empty) if !queue.is_empty() { let _value = queue.pop_front(); } let end = Instant::now(); total_duration += end.duration_since(start); } black_box(queue.len()); total_duration }); }); group.finish(); } /// Comprehensive order processing pipeline benchmark fn benchmark_order_processing_pipeline(c: &mut Criterion) { let mut group = c.benchmark_group("order_processing_pipeline"); group.measurement_time(Duration::from_secs(10)); group.bench_function("complete_order_lifecycle", |b| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let mut order_manager = SimpleOrderManager::new(); let mut order_book = SimpleOrderBook::new(); let symbol = Symbol::from_str("EURUSD"); for i in 0..iters { let start = Instant::now(); // 1. Submit order let side = if i % 2 == 0 { Side::Buy } else { Side::Sell }; let quantity = Quantity::from_f64(1000.0).unwrap_or(Quantity::ZERO); let price = Price::from_f64(1.1000 + (i as f64 * 0.0001)).unwrap_or(Price::ZERO); let order = order_manager .submit_order(symbol.clone(), side, quantity, Some(price)) .expect("Order submission failed"); // 2. Update order book match side { Side::Buy => order_book.update_bid(price, quantity), Side::Sell => order_book.update_ask(price, quantity), } // 3. Simulate order matching and fill let fill_price = price; order_manager .fill_order(&order.id.to_string(), quantity, fill_price) .expect("Order fill failed"); // 4. Verify order state let final_order = order_manager .get_order(&order.id.to_string()) .expect("Order not found"); assert_eq!(final_order.status, OrderStatus::Filled); let end = Instant::now(); total_duration += end.duration_since(start); black_box(&final_order); } total_duration }); }); group.finish(); } /// Performance metrics collection fn benchmark_performance_metrics_collection(c: &mut Criterion) { let mut group = c.benchmark_group("performance_metrics"); group.measurement_time(Duration::from_secs(3)); group.bench_function("latency_measurement", |b| { b.iter_custom(|iters| { let mut total_duration = Duration::from_nanos(0); let mut latencies = Vec::with_capacity(iters as usize); for _i in 0..iters { let measurement_start = Instant::now(); // Simulate some work (order book update) let start = Instant::now(); let _price = Price::from_f64(1.1000).unwrap_or(Price::ZERO); let _quantity = Quantity::from_f64(1000.0).unwrap_or(Quantity::ZERO); let work_end = Instant::now(); // Record latency let latency = work_end.duration_since(start); latencies.push(latency.as_nanos() as u64); let measurement_end = Instant::now(); total_duration += measurement_end.duration_since(measurement_start); } // Calculate some basic statistics if !latencies.is_empty() { let avg_latency = latencies.iter().sum::() / latencies.len() as u64; let min_latency = *latencies.iter().min().unwrap_or(&0); let max_latency = *latencies.iter().max().unwrap_or(&0); black_box((avg_latency, min_latency, max_latency)); } total_duration }); }); group.finish(); } criterion_group!( order_processing_benches, benchmark_order_book_updates, benchmark_order_management, benchmark_atomic_operations, benchmark_queue_operations, benchmark_order_processing_pipeline, benchmark_performance_metrics_collection ); criterion_main!(order_processing_benches);