#[cfg(test)] mod hyperopt_kelly_integration { use ml::hyperopt::adapters::dqn::DQNParams; use ml::hyperopt::ParameterSpace; #[test] fn test_22d_parameter_round_trip() { // Create 22D parameter vector let x = vec![ // Core DQN (11D) (5e-5_f64).ln(), // 0: learning_rate (log scale, within bounds [2e-5, 8e-5]) 112.0, // 1: batch_size 0.97, // 2: gamma (75000.0_f64).ln(), // 3: buffer_size (log scale) 1.5, // 4: hold_penalty_weight 6.0, // 5: max_position_absolute (25.0_f64).ln(), // 6: huber_delta (log scale) 0.05, // 7: entropy_coefficient 1.25, // 8: transaction_cost_multiplier 0.6, // 9: per_alpha 0.4, // 10: per_beta_start // Rainbow DQN (6D) -2.0, // 11: v_min 2.0, // 12: v_max (0.5_f64).ln(), // 13: noisy_sigma_init (log scale) 320.0, // 14: dueling_hidden_dim 3.0, // 15: n_steps 126.0, // 16: num_atoms // Bug Fix (1D) 1.55, // 17: minimum_profit_factor // WAVE 19: Kelly params (4D) 0.5, // 18: kelly_fractional 0.25, // 19: kelly_max_fraction 30.0, // 20: kelly_min_trades 20.0, // 21: volatility_window ]; // Convert to DQNParams let params = DQNParams::from_continuous(&x).expect("Should convert 22D vector"); // Verify Kelly parameters assert!((params.kelly_fractional - 0.5).abs() < 0.01, "Kelly fractional"); assert!((params.kelly_max_fraction - 0.25).abs() < 0.01, "Kelly max fraction"); assert_eq!(params.kelly_min_trades, 30, "Kelly min trades"); assert_eq!(params.volatility_window, 20, "Volatility window"); // Verify other parameters still work assert!((params.gamma - 0.97).abs() < 0.01, "Gamma unchanged"); assert_eq!(params.batch_size, 112, "Batch size unchanged"); } #[test] fn test_kelly_params_within_bounds() { let bounds = DQNParams::continuous_bounds(); // Test all corners of Kelly parameter space let test_cases = vec![ vec![0.25, 0.1, 10.0, 10.0], // Min values vec![1.0, 0.5, 50.0, 30.0], // Max values vec![0.5, 0.25, 20.0, 20.0], // Conservative (old default) vec![0.75, 0.4, 35.0, 25.0], // Aggressive ]; for kelly_values in test_cases { let mut x = vec![ (5e-5_f64).ln(), 112.0, 0.97, (75000.0_f64).ln(), 1.5, 6.0, (25.0_f64).ln(), 0.05, 1.25, 0.6, 0.4, -2.0, 2.0, (0.5_f64).ln(), 320.0, 3.0, 126.0, 1.55, ]; x.extend(kelly_values); let params = DQNParams::from_continuous(&x); assert!(params.is_ok(), "Should accept Kelly params within bounds"); } } #[test] fn test_kelly_params_clamping() { // Test out-of-bounds values are clamped let x = vec![ (5e-5_f64).ln(), 112.0, 0.97, (75000.0_f64).ln(), 1.5, 6.0, (25.0_f64).ln(), 0.05, 1.25, 0.6, 0.4, -2.0, 2.0, (0.5_f64).ln(), 320.0, 3.0, 126.0, 1.55, // Out-of-bounds Kelly values 1.5, // kelly_fractional > 1.0 (should clamp to 1.0) 0.8, // kelly_max_fraction > 0.5 (should clamp to 0.5) 100.0, // kelly_min_trades > 50 (should clamp to 50) 5.0, // volatility_window < 10 (should clamp to 10) ]; let params = DQNParams::from_continuous(&x).unwrap(); assert_eq!(params.kelly_fractional, 1.0, "Should clamp to max"); assert_eq!(params.kelly_max_fraction, 0.5, "Should clamp to max"); assert_eq!(params.kelly_min_trades, 50, "Should clamp to max"); assert_eq!(params.volatility_window, 10, "Should clamp to min"); } #[test] fn test_hyperparams_construction_uses_kelly_params() { let x = vec![ (5e-5_f64).ln(), 112.0, 0.97, (75000.0_f64).ln(), 1.5, 6.0, (25.0_f64).ln(), 0.05, 1.25, 0.6, 0.4, -2.0, 2.0, 0.5_f64.ln().exp(), 320.0, 3.0, 126.0, 1.55, // Custom Kelly values 0.75, 0.4, 35.0, 25.0, ]; let params = DQNParams::from_continuous(&x).unwrap(); // Convert to hyperparams (this is done in the objective function) // We can't easily test this without running the full objective, // but we verify the params struct has the right values assert_eq!(params.kelly_fractional, 0.75); assert_eq!(params.kelly_max_fraction, 0.4); assert_eq!(params.kelly_min_trades, 35); assert_eq!(params.volatility_window, 25); } }