Files
foxhunt/crates/ml/benches/alternative_bars_bench.rs
jgrusewski db6462ba7a fix(clippy): resolve all clippy warnings across entire workspace (--all-targets)
Systematic fix of 360+ clippy errors across 37+ crates covering lib,
test, bench, and example targets. Key changes:

- Add targeted #[allow(...)] on #[cfg(test)] modules for test-only lints
  (assertions_on_result_states, float_cmp, str_to_string, indexing, etc.)
- Feature-gate broken integration tests behind __<crate>_integration flags
  where public APIs changed (trading-service, backtesting-service, etc.)
- Remove dead [[test]] entries from Cargo.toml files pointing to deleted files
- Fix production code: field_reassign_with_default, manual_range_contains,
  assert!(false) → panic!(), format!("{}") simplification, len() > 0 → !is_empty()
- Delete truly unused code (Order struct, unused methods/fields/variants)
- Convert sqlx::query!() to sqlx::query() for SQLX_OFFLINE compatibility

Result: cargo clippy --workspace --all-targets -- -D warnings = 0 errors, 0 warnings

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 10:18:35 +01:00

743 lines
23 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#![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<Utc>)> {
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<f64> {
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(&params), 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<f64> = (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
);