/// Hurst Exponent Division by Zero Validation Test /// /// This test validates the fixes for Hurst exponent division by zero errors: /// 1. ml/src/regime/trending.rs:394 - Window size validation (n > 1.5) /// 2. ml/src/features/price_features.rs - Same validation in feature extraction /// /// Expected behavior: /// - 1-bar window: Returns 0.5 (random walk assumption) /// - 2-bar window: Returns 0.5 (n=1.0, below threshold) /// - Small windows (<10): Returns 0.5 (fallback) /// - Valid windows (>=10): Calculates Hurst exponent normally use std::collections::VecDeque; #[path = "ml/src/features/price_features.rs"] mod price_features; use price_features::{OHLCVBar, PriceFeatureExtractor}; fn create_test_bar(close: f64) -> OHLCVBar { OHLCVBar { timestamp: chrono::Utc::now(), open: close, high: close * 1.01, low: close * 0.99, close, volume: 1000.0, } } fn main() { println!("=== Hurst Exponent Division by Zero Validation ===\n"); // Test 1: 1-bar window (edge case) let mut bars_1 = VecDeque::new(); bars_1.push_back(create_test_bar(100.0)); let hurst_1 = PriceFeatureExtractor::compute_hurst_exponent(&bars_1, 1); println!("✓ Test 1: 1-bar window"); println!(" Hurst exponent: {:.4}", hurst_1); println!(" Expected: 0.5000 (random walk)"); assert_eq!(hurst_1, 0.5, "1-bar window should return 0.5"); println!(" PASS: No division by zero\n"); // Test 2: 2-bar window (n=1.0 after returns, below 1.5 threshold) let mut bars_2 = VecDeque::new(); bars_2.push_back(create_test_bar(100.0)); bars_2.push_back(create_test_bar(101.0)); let hurst_2 = PriceFeatureExtractor::compute_hurst_exponent(&bars_2, 2); println!("✓ Test 2: 2-bar window"); println!(" Hurst exponent: {:.4}", hurst_2); println!(" Expected: 0.5000 (n=1.0, below threshold)"); assert_eq!(hurst_2, 0.5, "2-bar window should return 0.5"); println!(" PASS: No division by zero\n"); // Test 3: Small window (<10 bars) let mut bars_small = VecDeque::new(); for i in 0..5 { bars_small.push_back(create_test_bar(100.0 + i as f64)); } let hurst_small = PriceFeatureExtractor::compute_hurst_exponent(&bars_small, 5); println!("✓ Test 3: 5-bar window (insufficient data)"); println!(" Hurst exponent: {:.4}", hurst_small); println!(" Expected: 0.5000 (fallback for <10 bars)"); assert_eq!(hurst_small, 0.5, "Small window should return 0.5"); println!(" PASS: Fallback to random walk\n"); // Test 4: Valid window (30 bars, trending) let mut bars_valid = VecDeque::new(); for i in 0..30 { bars_valid.push_back(create_test_bar(100.0 + (i as f64 * 0.5))); } let hurst_valid = PriceFeatureExtractor::compute_hurst_exponent(&bars_valid, 20); println!("✓ Test 4: 30-bar trending series"); println!(" Hurst exponent: {:.4}", hurst_valid); println!(" Expected: 0.5-1.0 (valid calculation)"); assert!(hurst_valid >= 0.0 && hurst_valid <= 1.0, "Valid Hurst should be in [0, 1]"); assert!(hurst_valid.is_finite(), "Valid Hurst should be finite"); println!(" PASS: Valid Hurst calculation (no NaN/Inf)\n"); // Test 5: Constant prices (zero volatility) let mut bars_const = VecDeque::new(); for _ in 0..30 { bars_const.push_back(create_test_bar(100.0)); } let hurst_const = PriceFeatureExtractor::compute_hurst_exponent(&bars_const, 20); println!("✓ Test 5: 30-bar constant series (zero volatility)"); println!(" Hurst exponent: {:.4}", hurst_const); println!(" Expected: 0.5000 (zero volatility fallback)"); assert_eq!(hurst_const, 0.5, "Zero volatility should return 0.5"); println!(" PASS: Zero volatility handled\n"); // Test 6: Feature extraction with edge cases let mut bars_feature = VecDeque::new(); bars_feature.push_back(create_test_bar(100.0)); let features = PriceFeatureExtractor::extract_all(&bars_feature); println!("✓ Test 6: Feature extraction with 1-bar window"); println!(" Feature 13 (Hurst): {:.4}", features[13]); println!(" Feature 14 (Fractal): {:.4}", features[14]); assert!(features[13].is_finite(), "Hurst feature should be finite"); assert!(features[14].is_finite(), "Fractal dimension should be finite"); println!(" PASS: No NaN/Inf in feature extraction\n"); println!("=== All Tests Passed ==="); println!("✓ Window size validation working (n > 1.5)"); println!("✓ 1-bar window returns 0.5 (no division by zero)"); println!("✓ Zero volatility returns 0.5 (no NaN)"); println!("✓ Valid windows calculate Hurst correctly"); println!("✓ No NaN or Inf values in feature extraction"); }