//! DBN Statistical Analysis Example //! //! This example demonstrates advanced statistical analysis and data transformation //! features of the DBN repository. //! //! ## Usage //! //! ```bash //! cargo run --example dbn_statistical_analysis //! ``` use backtesting_service::dbn_repository::DbnMarketDataRepository; use std::collections::HashMap; #[tokio::main] async fn main() -> anyhow::Result<()> { println!("=== DBN Statistical Analysis Example ===\n"); // 1. Setup repository let mut file_mapping = HashMap::new(); file_mapping.insert( "ES.FUT".to_string(), "test_data/real/databento/ES.FUT_ohlcv-1m_2024-01-02.dbn".to_string(), ); let repo = DbnMarketDataRepository::new(file_mapping).await?; // 2. Load all data println!("Loading data..."); let symbols = vec!["ES.FUT".to_string()]; let start_time = 1704153600_000_000_000i64; // 2024-01-02 00:00:00 let end_time = 1704240000_000_000_000i64; // 2024-01-03 00:00:00 let bars = repo.load_historical_data(&symbols, start_time, end_time).await?; println!("āœ… Loaded {} bars\n", bars.len()); // 3. Summary Statistics println!("=== Summary Statistics ==="); let stats = repo.generate_summary_stats(&bars); println!("Count: {}", stats.get("count").unwrap()); println!("Mean Close: ${:.2}", stats.get("mean_close").unwrap()); println!("Std Close: ${:.2}", stats.get("std_close").unwrap()); println!("Min Close: ${:.2}", stats.get("min_close").unwrap()); println!("Max Close: ${:.2}", stats.get("max_close").unwrap()); println!("Mean Volume: {:.0}", stats.get("mean_volume").unwrap()); println!("Total Volume: {:.0}", stats.get("total_volume").unwrap()); // 4. Rolling Window Statistics println!("\n=== Rolling 20-Bar Window Statistics ==="); let window_size = 20; let rolling_stats = repo.calculate_rolling_stats(&bars, window_size); println!("Window size: {} bars", window_size); println!("Windows calculated: {}", rolling_stats.len()); // Display last 5 windows println!("\nLast 5 windows:"); for (i, (mean, std, min, max)) in rolling_stats.iter().rev().take(5).enumerate() { println!( " Window {}: mean=${:.2}, std=${:.2}, range=[${:.2}, ${:.2}]", rolling_stats.len() - i, mean, std, min, max ); } // 5. Bar Resampling println!("\n=== Bar Resampling ==="); // Resample to different timeframes for target_minutes in [5, 15, 60] { let resampled = repo.resample_bars(&bars, target_minutes)?; let reduction = (1.0 - (resampled.len() as f64 / bars.len() as f64)) * 100.0; println!( "{:>2}-minute bars: {:>3} bars ({:.1}% reduction)", target_minutes, resampled.len(), reduction ); // Show first resampled bar if let Some(first) = resampled.first() { println!( " First: {} @ {} | O={} H={} L={} C={} V={}", first.symbol, first.timestamp.format("%H:%M"), first.open, first.high, first.low, first.close, first.volume ); } } // 6. Regime Detection (Simple Heuristic) println!("\n=== Regime Detection ==="); let regime_types = ["trending", "ranging", "volatile", "stable"]; for regime_type in regime_types.iter() { match repo.load_regime_samples(regime_type, 5, &symbols).await { Ok(samples) => { println!("{:<10} regime: {} sample bars found", regime_type, samples.len()); if !samples.is_empty() { let sample = &samples[0]; let range = sample.high - sample.low; let avg_price = (sample.high + sample.low) / rust_decimal::Decimal::from(2); let range_pct = (range / avg_price * rust_decimal::Decimal::from(10000)) .to_string() .parse::() .unwrap() / 100.0; println!(" Example: {} @ {} (range: {:.2}%)", sample.symbol, sample.timestamp, range_pct); } } Err(e) => { println!("{:<10} regime: Error - {}", regime_type, e); } } } // 7. Price Return Analysis println!("\n=== Price Return Analysis ==="); let mut returns: Vec = Vec::new(); for i in 1..bars.len() { let prev_close = bars[i - 1].close.to_string().parse::().unwrap(); let curr_close = bars[i].close.to_string().parse::().unwrap(); let ret = (curr_close - prev_close) / prev_close; returns.push(ret); } let mean_return = returns.iter().sum::() / returns.len() as f64; let variance = returns .iter() .map(|r| (r - mean_return).powi(2)) .sum::() / returns.len() as f64; let std_dev = variance.sqrt(); println!("Mean return: {:.6} ({:.4}%)", mean_return, mean_return * 100.0); println!("Std dev: {:.6} ({:.4}%)", std_dev, std_dev * 100.0); println!( "Min return: {:.6} ({:.4}%)", returns.iter().cloned().fold(f64::INFINITY, f64::min), returns.iter().cloned().fold(f64::INFINITY, f64::min) * 100.0 ); println!( "Max return: {:.6} ({:.4}%)", returns.iter().cloned().fold(f64::NEG_INFINITY, f64::max), returns.iter().cloned().fold(f64::NEG_INFINITY, f64::max) * 100.0 ); // Sharpe ratio (annualized, assuming 252 trading days) let sharpe = (mean_return / std_dev) * (252.0 * 390.0_f64).sqrt(); // 390 bars per day println!("Sharpe ratio: {:.2}", sharpe); // 8. Volume Analysis println!("\n=== Volume Analysis ==="); let volumes: Vec = bars .iter() .map(|b| b.volume.to_string().parse().unwrap()) .collect(); let mean_volume = volumes.iter().sum::() / volumes.len() as f64; let max_volume = volumes.iter().cloned().fold(f64::NEG_INFINITY, f64::max); let min_volume = volumes.iter().cloned().fold(f64::INFINITY, f64::min); println!("Mean volume: {:.0}", mean_volume); println!("Max volume: {:.0}", max_volume); println!("Min volume: {:.0}", min_volume); // Find high-volume bars let high_volume_threshold = mean_volume * 2.0; let high_volume_bars: Vec<_> = bars .iter() .filter(|b| b.volume.to_string().parse::().unwrap() > high_volume_threshold) .collect(); println!("\nHigh-volume bars (>2x mean): {}", high_volume_bars.len()); for bar in high_volume_bars.iter().take(3) { println!( " {} @ {} | Volume: {}", bar.symbol, bar.timestamp, bar.volume ); } println!("\nāœ… Example completed successfully!"); Ok(()) }