//! Full Trading Cycle Performance Profiling //! //! This benchmark profiles the complete end-to-end trading flow: //! 1. Order submission → TradingOperations::submit_order() //! 2. Order validation → TradingOperations::validate_order() //! 3. Execution routing → TradingOperations::process_execution() //! 4. Audit trail persistence → AuditTrailService::log_event() //! 5. Metrics collection → Prometheus recording //! //! HFT Performance Targets: //! - Order submission: <50μs P99 //! - Order validation: <5μs P99 //! - Execution routing: <20μs P99 //! - Audit persistence (async): <100μs P99 //! - **Total critical path**: <100μs P99 (excluding async audit) //! //! This profiling completes the 30% → 100% performance validation requirement. use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use std::sync::Arc; use std::time::{Duration, Instant}; use tokio::runtime::Runtime; // Trading engine components use chrono::Utc; use common::{OrderId, OrderSide, OrderStatus}; use rust_decimal::Decimal; use std::collections::HashMap; use trading_engine::trading_operations::{ ExecutionResult, LiquidityFlag, OrderType, TimeInForce, TradingOperations, TradingOrder, }; /// Helper to create a TradingOrder with all required fields fn create_order( order_type: OrderType, side: OrderSide, quantity: Decimal, price: Decimal, ) -> TradingOrder { TradingOrder { id: OrderId::new(), symbol: "BTCUSD".to_string(), order_type, side, quantity, price, time_in_force: TimeInForce::GoodTillCancel, account_id: Some("benchmark_account".to_string()), metadata: HashMap::new(), created_at: Utc::now(), submitted_at: Some(Utc::now()), executed_at: None, status: OrderStatus::New, fill_quantity: Decimal::ZERO, average_fill_price: None, } } /// Helper to create an ExecutionResult with all required fields fn create_execution( order_id: OrderId, quantity: Decimal, price: Decimal, side: OrderSide, liquidity_flag: LiquidityFlag, ) -> ExecutionResult { ExecutionResult { order_id, symbol: "BTCUSD".to_string(), side, executed_quantity: quantity, execution_price: price, execution_time: Utc::now(), commission: Decimal::new(1, 2), // 0.01 liquidity_flag, } } /// Benchmark 1: Order submission latency fn bench_order_submission(c: &mut Criterion) { let mut group = c.benchmark_group("order_submission"); group.throughput(Throughput::Elements(1)); let rt = Runtime::new().expect("Failed to create runtime"); group.bench_function("submit_limit_order", |b| { let trading_ops = Arc::new(TradingOperations::new()); b.to_async(&rt).iter(|| async { let order = create_order( OrderType::Limit, OrderSide::Buy, Decimal::new(1, 0), Decimal::new(50000, 0), ); let result = trading_ops.submit_order(order).await; black_box(result) }); }); group.bench_function("submit_market_order", |b| { let trading_ops = Arc::new(TradingOperations::new()); b.to_async(&rt).iter(|| async { let order = create_order( OrderType::Market, OrderSide::Sell, Decimal::new(1, 0), Decimal::ZERO, ); let result = trading_ops.submit_order(order).await; black_box(result) }); }); group.finish(); } /// Benchmark 2: Execution processing latency fn bench_execution_processing(c: &mut Criterion) { let mut group = c.benchmark_group("execution_processing"); group.throughput(Throughput::Elements(1)); let rt = Runtime::new().expect("Failed to create runtime"); group.bench_function("process_full_fill", |b| { let trading_ops = Arc::new(TradingOperations::new()); b.to_async(&rt).iter(|| async { // First submit an order let order = create_order( OrderType::Limit, OrderSide::Buy, Decimal::new(1, 0), Decimal::new(50000, 0), ); let order_id = order.id; let _ = trading_ops .submit_order(order) .await .expect("Failed to submit order"); // Process execution let execution = create_execution( order_id, Decimal::new(1, 0), Decimal::new(50000, 0), OrderSide::Buy, LiquidityFlag::Maker, ); let result = trading_ops.process_execution(execution).await; black_box(result) }); }); group.bench_function("process_partial_fill", |b| { let trading_ops = Arc::new(TradingOperations::new()); b.to_async(&rt).iter(|| async { let order = create_order( OrderType::Limit, OrderSide::Buy, Decimal::new(10, 0), Decimal::new(50000, 0), ); let order_id = order.id; let _ = trading_ops .submit_order(order) .await .expect("Failed to submit order"); // Partial fill let execution = create_execution( order_id, Decimal::new(3, 0), Decimal::new(50000, 0), OrderSide::Buy, LiquidityFlag::Taker, ); let result = trading_ops.process_execution(execution).await; black_box(result) }); }); group.finish(); } /// Benchmark 3: Full trading cycle (critical path) fn bench_full_trading_cycle(c: &mut Criterion) { let mut group = c.benchmark_group("full_trading_cycle"); group.measurement_time(Duration::from_secs(20)); group.sample_size(1000); let rt = Runtime::new().expect("Failed to create runtime"); group.bench_function("complete_cycle_limit_order", |b| { let trading_ops = Arc::new(TradingOperations::new()); b.to_async(&rt).iter(|| async { let cycle_start = Instant::now(); // Stage 1: Order creation and submission let submission_start = Instant::now(); let order = create_order( OrderType::Limit, OrderSide::Buy, Decimal::new(1, 0), Decimal::new(50000, 0), ); let order_id = order.id; let _ = trading_ops .submit_order(order) .await .expect("Failed to submit order"); let submission_latency = submission_start.elapsed(); // Stage 2: Execution routing and processing let execution_start = Instant::now(); let execution = create_execution( order_id, Decimal::new(1, 0), Decimal::new(50000, 0), OrderSide::Buy, LiquidityFlag::Maker, ); trading_ops .process_execution(execution) .await .expect("Failed to process execution"); let execution_latency = execution_start.elapsed(); let total_latency = cycle_start.elapsed(); black_box((submission_latency, execution_latency, total_latency)) }); }); group.bench_function("complete_cycle_market_order", |b| { let trading_ops = Arc::new(TradingOperations::new()); b.to_async(&rt).iter(|| async { let cycle_start = Instant::now(); let order = create_order( OrderType::Market, OrderSide::Sell, Decimal::new(1, 0), Decimal::ZERO, ); let order_id = order.id; trading_ops .submit_order(order) .await .expect("Failed to submit order"); let execution = create_execution( order_id, Decimal::new(1, 0), Decimal::new(50000, 0), OrderSide::Sell, LiquidityFlag::Taker, ); trading_ops .process_execution(execution) .await .expect("Failed to process execution"); let total_latency = cycle_start.elapsed(); black_box(total_latency) }); }); group.finish(); } /// Benchmark 4: Throughput under load fn bench_trading_throughput(c: &mut Criterion) { let mut group = c.benchmark_group("trading_throughput"); let rt = Runtime::new().expect("Failed to create runtime"); for orders_per_batch in &[10, 100, 1000] { group.bench_with_input( BenchmarkId::new("orders_per_batch", orders_per_batch), orders_per_batch, |b, &count| { let trading_ops = Arc::new(TradingOperations::new()); b.to_async(&rt).iter(|| async { let start = Instant::now(); for i in 0..count { let order = create_order( if i % 2 == 0 { OrderType::Limit } else { OrderType::Market }, if i % 2 == 0 { OrderSide::Buy } else { OrderSide::Sell }, Decimal::new(1, 0), Decimal::new(50000 + i as i64, 0), ); let _ = trading_ops.submit_order(order).await; } black_box(start.elapsed()) }); }, ); } group.finish(); } criterion_group! { name = full_trading_cycle_benchmarks; config = Criterion::default() .measurement_time(Duration::from_secs(30)) .sample_size(1000) .warm_up_time(Duration::from_secs(5)) .with_plots(); targets = bench_order_submission, bench_execution_processing, bench_full_trading_cycle, bench_trading_throughput } criterion_main!(full_trading_cycle_benchmarks); /// Validation tests with percentile calculations #[cfg(test)] mod performance_validation { #[allow(unused_imports)] use super::*; #[tokio::test] async fn validate_full_cycle_latency_targets() { println!("\n=== Full Trading Cycle Performance Validation ===\n"); let trading_ops = Arc::new(TradingOperations::new()); let iterations = 10000; let mut submission_latencies = Vec::new(); let mut execution_latencies = Vec::new(); let mut total_latencies = Vec::new(); for i in 0..iterations { let cycle_start = Instant::now(); // Submit order let submission_start = Instant::now(); let order = create_order( OrderType::Limit, OrderSide::Buy, Decimal::new(1, 0), Decimal::new(50000 + i as i64, 0), ); let order_id = order.id; trading_ops .submit_order(order) .await .expect("Failed to submit order"); submission_latencies.push(submission_start.elapsed()); // Process execution let execution_start = Instant::now(); let execution = create_execution( order_id, Decimal::new(1, 0), Decimal::new(50000, 0), OrderSide::Buy, LiquidityFlag::Maker, ); trading_ops .process_execution(execution) .await .expect("Failed to process execution"); execution_latencies.push(execution_start.elapsed()); total_latencies.push(cycle_start.elapsed()); } // Calculate percentiles inline submission_latencies.sort(); execution_latencies.sort(); total_latencies.sort(); let sub_len = submission_latencies.len(); let sub_p50 = submission_latencies[sub_len / 2]; let sub_p99 = submission_latencies[(sub_len * 99) / 100]; let sub_p999 = submission_latencies[(sub_len * 999) / 1000]; let exec_len = execution_latencies.len(); let exec_p50 = execution_latencies[exec_len / 2]; let exec_p99 = execution_latencies[(exec_len * 99) / 100]; let exec_p999 = execution_latencies[(exec_len * 999) / 1000]; let total_len = total_latencies.len(); let total_p50 = total_latencies[total_len / 2]; let total_p99 = total_latencies[(total_len * 99) / 100]; let total_p999 = total_latencies[(total_len * 999) / 1000]; println!("Order Submission Latency:"); println!(" P50: {:.1}us", sub_p50.as_micros()); println!(" P99: {:.1}us (target: <50us)", sub_p99.as_micros()); println!(" P999: {:.1}us", sub_p999.as_micros()); println!("\nExecution Processing Latency:"); println!(" P50: {:.1}us", exec_p50.as_micros()); println!(" P99: {:.1}us (target: <20us)", exec_p99.as_micros()); println!(" P999: {:.1}us", exec_p999.as_micros()); println!("\nTotal Critical Path Latency:"); println!(" P50: {:.1}us", total_p50.as_micros()); println!(" P99: {:.1}us (target: <100us)", total_p99.as_micros()); println!(" P999: {:.1}us", total_p999.as_micros()); // Check performance targets let mut violations = Vec::new(); let sub_p99_us = sub_p99.as_micros() as f64; let exec_p99_us = exec_p99.as_micros() as f64; let total_p99_us = total_p99.as_micros() as f64; if sub_p99_us > 50.0 { violations.push(format!( "Order submission P99 {:.1}us exceeds 50us target", sub_p99_us )); } if exec_p99_us > 20.0 { violations.push(format!( "Execution routing P99 {:.1}us exceeds 20us target", exec_p99_us )); } if total_p99_us > 100.0 { violations.push(format!( "Total critical path P99 {:.1}us exceeds 100us target", total_p99_us )); } if !violations.is_empty() { println!("\nPerformance Target Violations:"); for violation in &violations { println!(" - {}", violation); } } else { println!("\nAll HFT performance targets met!"); } println!("\n=== Performance Validation Complete ===\n"); // Assertions assert!( sub_p99.as_micros() < 50, "Order submission P99 exceeds 50us: {}us", sub_p99.as_micros() ); assert!( exec_p99.as_micros() < 20, "Execution processing P99 exceeds 20us: {}us", exec_p99.as_micros() ); assert!( total_p99.as_micros() < 100, "Total critical path P99 exceeds 100us: {}us", total_p99.as_micros() ); } #[tokio::test] async fn validate_throughput_capacity() { println!("\n=== Throughput Capacity Validation ===\n"); let trading_ops = Arc::new(TradingOperations::new()); let total_orders = 100000; let start = Instant::now(); for i in 0..total_orders { let order = create_order( OrderType::Limit, if i % 2 == 0 { OrderSide::Buy } else { OrderSide::Sell }, Decimal::new(1, 0), Decimal::new(50000 + (i % 100) as i64, 0), ); let _ = trading_ops.submit_order(order).await; } let elapsed = start.elapsed(); let orders_per_sec = (total_orders as f64 / elapsed.as_secs_f64()) as u64; println!("Total orders processed: {}", total_orders); println!("Total time: {:?}", elapsed); println!("Throughput: {} orders/sec", orders_per_sec); println!("\n=== Throughput Validation Complete ===\n"); // HFT systems should handle >10K orders/sec assert!( orders_per_sec >= 10000, "Throughput too low: {} orders/sec (target: >10K)", orders_per_sec ); } }