#![allow( clippy::assertions_on_constants, clippy::assertions_on_result_states, clippy::clone_on_copy, clippy::decimal_literal_representation, clippy::doc_markdown, clippy::empty_line_after_doc_comments, clippy::field_reassign_with_default, clippy::get_unwrap, clippy::identity_op, clippy::inconsistent_digit_grouping, clippy::indexing_slicing, clippy::integer_division, clippy::len_zero, clippy::let_underscore_must_use, clippy::manual_div_ceil, clippy::manual_let_else, clippy::manual_range_contains, clippy::modulo_arithmetic, clippy::needless_range_loop, clippy::non_ascii_literal, clippy::redundant_clone, clippy::shadow_reuse, clippy::shadow_same, clippy::shadow_unrelated, clippy::single_match_else, clippy::str_to_string, clippy::string_slice, clippy::tests_outside_test_module, clippy::too_many_lines, clippy::unnecessary_wraps, clippy::unseparated_literal_suffix, clippy::use_debug, clippy::useless_vec, clippy::wildcard_enum_match_arm, clippy::else_if_without_else, clippy::expect_used, clippy::missing_const_for_fn, clippy::similar_names, clippy::type_complexity, clippy::collapsible_else_if, clippy::doc_lazy_continuation, clippy::items_after_test_module, clippy::map_clone, clippy::multiple_unsafe_ops_per_block, clippy::unwrap_or_default, clippy::assign_op_pattern, clippy::needless_borrow, clippy::println_empty_string, clippy::unnecessary_cast, clippy::used_underscore_binding, clippy::create_dir, clippy::implicit_saturating_sub, clippy::exit, clippy::expect_fun_call, clippy::too_many_arguments, clippy::unnecessary_map_or, clippy::unwrap_used, dead_code, unused_imports, unused_variables, clippy::cloned_ref_to_slice_refs, clippy::neg_multiply, clippy::while_let_loop, clippy::bool_assert_comparison, clippy::excessive_precision, clippy::trivially_copy_pass_by_ref, clippy::op_ref, clippy::redundant_closure, clippy::unnecessary_lazy_evaluations, clippy::if_then_some_else_none, clippy::unnecessary_to_owned, clippy::single_component_path_imports, )] //! Performance Benchmarks for Alternative Bar Sampling (Wave B) //! //! Agent B14 - Alternative bar sampling performance validation: //! - Tick Bars (Agent B3) //! - Volume Bars (Wave B) //! - Dollar Bars (Wave B) //! - Imbalance Bars (Wave B) //! - Triple Barrier Labeling (Wave B) //! - Barrier Optimization (Wave B) //! //! ## Performance Targets //! - Tick bars: <50μs per bar formation //! - Volume bars: <50μs per bar formation //! - Dollar bars: <50μs per bar formation //! - Imbalance bars: <50μs per bar formation //! - Triple barrier labeling: <100μs per label //! - Barrier optimization: <10s for 100 parameter combinations //! //! ## Memory Targets //! - Each sampler: <1MB memory usage //! //! ## Run Benchmarks //! ```bash //! cargo bench -p ml --bench alternative_bars_bench //! ``` use chrono::{DateTime, Duration, Utc}; use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; use ml::features::alternative_bars::{ DollarBarSampler, TickBarSampler, VolumeBarSampler, }; use ml::features::barrier_optimization::{BarrierOptimizer, BarrierParams}; use ml::labeling::triple_barrier::{BarrierTracker, PricePoint, TripleBarrierEngine}; use ml::labeling::types::BarrierConfig; use std::time::Duration as StdDuration; // ============================================================================ // Test Data Generator // ============================================================================ /// Generate realistic tick-level trade data for benchmarking /// /// Returns: (price, volume, timestamp) tuples fn generate_tick_data(num_ticks: usize, seed: u64) -> Vec<(f64, f64, DateTime)> { let mut rng = fastrand::Rng::with_seed(seed); let mut data = Vec::with_capacity(num_ticks); let mut price = 100.0; let mut timestamp = Utc::now(); for _i in 0..num_ticks { // Random walk with mean reversion let drift = (100.0 - price) * 0.001; let noise = (rng.f64() - 0.5) * 0.05; price += drift + noise; price = price.max(90.0).min(110.0); // Volume varies between 1 and 100 contracts let volume = 1.0 + rng.f64() * 99.0; // Timestamps advance 10-1000ms per tick let ms_delta = 10 + (rng.f64() * 990.0) as i64; timestamp = timestamp + Duration::milliseconds(ms_delta); data.push((price, volume, timestamp)); } data } /// Generate price series for barrier optimization fn generate_price_series(num_prices: usize, seed: u64) -> Vec { let mut rng = fastrand::Rng::with_seed(seed); let mut prices = Vec::with_capacity(num_prices); let mut price = 100.0; for _ in 0..num_prices { price += (rng.f64() - 0.5) * 0.5; price = price.max(80.0).min(120.0); prices.push(price); } prices } // ============================================================================ // Tick Bar Benchmarks (Agent B3) // ============================================================================ /// Benchmark tick bar formation with various thresholds fn bench_tick_bars(c: &mut Criterion) { let mut group = c.benchmark_group("tick_bars"); group.measurement_time(StdDuration::from_secs(5)); let data = generate_tick_data(10000, 42); for threshold in [50, 100, 500, 1000].iter() { group.throughput(Throughput::Elements(*threshold as u64)); group.bench_with_input( BenchmarkId::new("formation", threshold), threshold, |b, &thresh| { b.iter(|| { let mut sampler = TickBarSampler::new(thresh); let mut bar_count = 0; for &(price, volume, timestamp) in data.iter().take(thresh * 2) { if let Some(_bar) = sampler.update( black_box(price), black_box(volume), black_box(timestamp), ) { bar_count += 1; } } black_box(bar_count); }); }, ); } group.finish(); } /// Benchmark tick bar incremental update (single tick) fn bench_tick_bars_incremental(c: &mut Criterion) { let mut group = c.benchmark_group("tick_bars_incremental"); group.measurement_time(StdDuration::from_secs(5)); let data = generate_tick_data(1000, 43); group.bench_function("single_tick_update", |b| { let mut idx = 50; b.iter(|| { let mut sam = TickBarSampler::new(100); // Warm up for &(p, v, t) in data.iter().take(50) { sam.update(p, v, t); } let (price, volume, timestamp) = data[idx % data.len()]; let result = sam.update(black_box(price), black_box(volume), black_box(timestamp)); black_box(result); idx += 1; }); }); group.finish(); } // ============================================================================ // Volume Bar Benchmarks // ============================================================================ /// Benchmark volume bar formation fn bench_volume_bars(c: &mut Criterion) { let mut group = c.benchmark_group("volume_bars"); group.measurement_time(StdDuration::from_secs(5)); let data = generate_tick_data(10000, 44); for threshold in [1000, 5000, 10000].iter() { group.throughput(Throughput::Elements(*threshold as u64)); group.bench_with_input( BenchmarkId::new("formation", threshold), threshold, |b, &thresh| { b.iter(|| { let mut sampler = VolumeBarSampler::new(thresh); let mut bar_count = 0; for &(price, volume, timestamp) in data.iter() { if let Some(_bar) = sampler.update( black_box(price), black_box(volume), black_box(timestamp), ) { bar_count += 1; if bar_count >= 10 { break; } } } black_box(bar_count); }); }, ); } group.finish(); } /// Benchmark volume bar incremental update fn bench_volume_bars_incremental(c: &mut Criterion) { let mut group = c.benchmark_group("volume_bars_incremental"); group.measurement_time(StdDuration::from_secs(5)); let data = generate_tick_data(1000, 45); group.bench_function("single_update", |b| { let mut idx = 50; b.iter(|| { let mut sam = VolumeBarSampler::new(5000); // Warm up for &(p, v, t) in data.iter().take(50) { sam.update(p, v, t); } let (price, volume, timestamp) = data[idx % data.len()]; let result = sam.update(black_box(price), black_box(volume), black_box(timestamp)); black_box(result); idx += 1; }); }); group.finish(); } // ============================================================================ // Dollar Bar Benchmarks // ============================================================================ /// Benchmark dollar bar formation (fixed threshold) fn bench_dollar_bars_fixed(c: &mut Criterion) { let mut group = c.benchmark_group("dollar_bars_fixed"); group.measurement_time(StdDuration::from_secs(5)); let data = generate_tick_data(10000, 46); for threshold in [50000.0, 100000.0, 500000.0].iter() { group.bench_with_input( BenchmarkId::new("formation", threshold), threshold, |b, &thresh| { b.iter(|| { let mut sampler = DollarBarSampler::new(thresh); let mut bar_count = 0; for &(price, volume, timestamp) in &data { if let Some(_bar) = sampler.update( black_box(price), black_box(volume), black_box(timestamp), ) { bar_count += 1; if bar_count >= 10 { break; } } } black_box(bar_count); }); }, ); } group.finish(); } /// Benchmark dollar bar formation (adaptive EWMA) fn bench_dollar_bars_adaptive(c: &mut Criterion) { let mut group = c.benchmark_group("dollar_bars_adaptive"); group.measurement_time(StdDuration::from_secs(5)); let data = generate_tick_data(10000, 47); for alpha in [0.1, 0.3, 0.5].iter() { group.bench_with_input(BenchmarkId::new("adaptive_ewma", alpha), alpha, |b, &a| { b.iter(|| { let mut sampler = DollarBarSampler::new_adaptive(100000.0, a); let mut bar_count = 0; for &(price, volume, timestamp) in &data { if let Some(_bar) = sampler.update(black_box(price), black_box(volume), black_box(timestamp)) { bar_count += 1; if bar_count >= 10 { break; } } } black_box(bar_count); }); }); } group.finish(); } /// Benchmark dollar bar incremental update fn bench_dollar_bars_incremental(c: &mut Criterion) { let mut group = c.benchmark_group("dollar_bars_incremental"); group.measurement_time(StdDuration::from_secs(5)); let data = generate_tick_data(1000, 48); group.bench_function("single_update", |b| { let mut idx = 50; b.iter(|| { let mut sam = DollarBarSampler::new(100000.0); // Warm up for &(p, v, t) in data.iter().take(50) { sam.update(p, v, t); } let (price, volume, timestamp) = data[idx % data.len()]; let result = sam.update(black_box(price), black_box(volume), black_box(timestamp)); black_box(result); idx += 1; }); }); group.finish(); } // ============================================================================ // Triple Barrier Labeling Benchmarks // ============================================================================ /// Benchmark triple barrier single tracker update fn bench_triple_barrier_single(c: &mut Criterion) { let mut group = c.benchmark_group("triple_barrier_single"); group.measurement_time(StdDuration::from_secs(5)); let config = BarrierConfig::conservative(); group.bench_function("tracker_update", |b| { b.iter(|| { let mut tracker = BarrierTracker::new(10000, 1692000000_000_000_000, config.clone()); let price_point = PricePoint::new(black_box(10050), black_box(1692000000_500_000_000)); let result = tracker.update(black_box(price_point)); black_box(result); }); }); group.finish(); } /// Benchmark triple barrier engine with multiple trackers fn bench_triple_barrier_engine(c: &mut Criterion) { let mut group = c.benchmark_group("triple_barrier_engine"); group.measurement_time(StdDuration::from_secs(5)); let config = BarrierConfig::conservative(); for num_trackers in [10, 50, 100, 500].iter() { group.throughput(Throughput::Elements(*num_trackers as u64)); group.bench_with_input( BenchmarkId::new("update_all", num_trackers), num_trackers, |b, &n| { b.iter(|| { let mut engine = TripleBarrierEngine::new(1000); // Start N trackers for i in 0..n { let _ = engine.start_tracking( config.clone(), 10000 + i * 10, 1692000000_000_000_000, ); } // Update all with new price let price_point = PricePoint::new(black_box(10050), black_box(1692000000_500_000_000)); let labels = engine.update_all(black_box(price_point)); black_box(labels); }); }, ); } group.finish(); } /// Benchmark triple barrier label generation throughput fn bench_triple_barrier_throughput(c: &mut Criterion) { let mut group = c.benchmark_group("triple_barrier_throughput"); group.measurement_time(StdDuration::from_secs(10)); let config = BarrierConfig::conservative(); let num_prices = 1000; let num_trackers = 100; group.throughput(Throughput::Elements((num_prices * num_trackers) as u64)); group.bench_function("generate_labels", |b| { b.iter(|| { let mut engine = TripleBarrierEngine::new(num_trackers * 2); // Start trackers for i in 0..num_trackers { let _ = engine.start_tracking( config.clone(), 10000 + (i as u64 * 10), 1692000000_000_000_000, ); } // Stream prices and generate labels let mut total_labels = 0; for i in 0..num_prices { let price = 10000 + ((i % 200) as u64); let timestamp = 1692000000_000_000_000 + (i as u64 * 100_000_000); let price_point = PricePoint::new(price, timestamp); let labels = engine.update_all(black_box(price_point)); total_labels += labels.len(); } black_box(total_labels); }); }); group.finish(); } // ============================================================================ // Barrier Optimization Benchmarks // ============================================================================ /// Benchmark barrier parameter optimization (grid search) fn bench_barrier_optimization_grid(c: &mut Criterion) { let mut group = c.benchmark_group("barrier_optimization"); group.measurement_time(StdDuration::from_secs(15)); let prices = generate_price_series(200, 49); // Default optimizer: 5 profit × 4 stop × 4 horizon = 80 combinations group.bench_function("grid_search_80_params", |b| { b.iter(|| { let optimizer = BarrierOptimizer::new(); let result = optimizer.optimize(black_box(&prices)); black_box(result); }); }); group.finish(); } /// Benchmark barrier optimization with custom search space fn bench_barrier_optimization_custom(c: &mut Criterion) { let mut group = c.benchmark_group("barrier_optimization_custom"); group.measurement_time(StdDuration::from_secs(20)); let prices = generate_price_series(200, 50); // Custom ranges: 10 profit × 6 stop × 5 horizon = 300 combinations let profit_range = vec![0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0]; let stop_range = vec![0.25, 0.5, 1.0, 1.5, 2.0, 2.5]; let horizon_range = vec![5, 10, 15, 20, 30]; group.bench_function("grid_search_300_params", |b| { b.iter(|| { let optimizer = BarrierOptimizer::with_ranges( profit_range.clone(), stop_range.clone(), horizon_range.clone(), ); let result = optimizer.optimize(black_box(&prices)); black_box(result); }); }); group.finish(); } /// Benchmark single barrier parameter evaluation fn bench_barrier_single_eval(c: &mut Criterion) { let mut group = c.benchmark_group("barrier_single_eval"); group.measurement_time(StdDuration::from_secs(5)); let prices = generate_price_series(200, 51); let optimizer = BarrierOptimizer::new(); let params = BarrierParams::new(2.0, 1.0, 10); group.bench_function("backtest_params", |b| { b.iter(|| { let sharpe = optimizer.backtest_params(black_box(¶ms), black_box(&prices)); black_box(sharpe); }); }); group.finish(); } /// Benchmark Sharpe ratio calculation fn bench_sharpe_calculation(c: &mut Criterion) { let mut group = c.benchmark_group("sharpe_calculation"); group.measurement_time(StdDuration::from_secs(3)); let returns: Vec = (0..100).map(|i| (i as f64 - 50.0) * 0.001).collect(); let optimizer = BarrierOptimizer::new(); group.bench_function("calculate_sharpe_100_returns", |b| { b.iter(|| { let sharpe = optimizer.calculate_sharpe(black_box(&returns)); black_box(sharpe); }); }); group.finish(); } // ============================================================================ // Comparison: Alternative Bars vs Time Bars // ============================================================================ /// Benchmark comparison of all bar sampling methods fn bench_bar_sampling_comparison(c: &mut Criterion) { let mut group = c.benchmark_group("bar_sampling_comparison"); group.measurement_time(StdDuration::from_secs(10)); let data = generate_tick_data(5000, 52); // Tick bars (100 ticks/bar) group.bench_function("tick_bars_100", |b| { b.iter(|| { let mut sampler = TickBarSampler::new(100); let mut bars = Vec::new(); for &(price, volume, timestamp) in &data { if let Some(bar) = sampler.update(black_box(price), black_box(volume), black_box(timestamp)) { bars.push(bar); } } black_box(bars); }); }); // Volume bars (5000 volume/bar) group.bench_function("volume_bars_5k", |b| { b.iter(|| { let mut sampler = VolumeBarSampler::new(5000); let mut bars = Vec::new(); for &(price, volume, timestamp) in &data { if let Some(bar) = sampler.update(black_box(price), black_box(volume), black_box(timestamp)) { bars.push(bar); } } black_box(bars); }); }); // Dollar bars (100k $/bar) group.bench_function("dollar_bars_100k", |b| { b.iter(|| { let mut sampler = DollarBarSampler::new(100000.0); let mut bars = Vec::new(); for &(price, volume, timestamp) in &data { if let Some(bar) = sampler.update(black_box(price), black_box(volume), black_box(timestamp)) { bars.push(bar); } } black_box(bars); }); }); group.finish(); } // ============================================================================ // Memory Usage Benchmarks // ============================================================================ /// Benchmark memory footprint of samplers (via repeated allocation) fn bench_sampler_memory_footprint(c: &mut Criterion) { let mut group = c.benchmark_group("sampler_memory"); group.measurement_time(StdDuration::from_secs(5)); group.bench_function("tick_sampler_allocation", |b| { b.iter(|| { let sampler = TickBarSampler::new(black_box(100)); black_box(sampler); }); }); group.bench_function("volume_sampler_allocation", |b| { b.iter(|| { let sampler = VolumeBarSampler::new(black_box(5000)); black_box(sampler); }); }); group.bench_function("dollar_sampler_allocation", |b| { b.iter(|| { let sampler = DollarBarSampler::new(black_box(100000.0)); black_box(sampler); }); }); group.finish(); } // ============================================================================ // Criterion Configuration // ============================================================================ criterion_group!( tick_bar_benches, bench_tick_bars, bench_tick_bars_incremental ); criterion_group!( volume_bar_benches, bench_volume_bars, bench_volume_bars_incremental ); criterion_group!( dollar_bar_benches, bench_dollar_bars_fixed, bench_dollar_bars_adaptive, bench_dollar_bars_incremental ); criterion_group!( triple_barrier_benches, bench_triple_barrier_single, bench_triple_barrier_engine, bench_triple_barrier_throughput ); criterion_group!( barrier_optimization_benches, bench_barrier_optimization_grid, bench_barrier_optimization_custom, bench_barrier_single_eval, bench_sharpe_calculation ); criterion_group!( comparison_benches, bench_bar_sampling_comparison, bench_sampler_memory_footprint ); criterion_main!( tick_bar_benches, volume_bar_benches, dollar_bar_benches, triple_barrier_benches, barrier_optimization_benches, comparison_benches );