//! Integration tests for regime persistence //! //! Tests the RegimePersistenceManager with real database operations. use anyhow::Result; use chrono::Utc; use common::database::DatabasePool; use common::regime_persistence::{RegimePersistenceManager, RegimeType}; /// Helper to create test database pool async fn create_test_pool() -> Result { let database_url = std::env::var("DATABASE_URL") .unwrap_or_else(|_| "postgresql://foxhunt:foxhunt_dev_password@localhost:5432/foxhunt".to_string()); DatabasePool::new(&database_url).await } #[tokio::test] #[ignore] // Requires database connection async fn test_regime_classification() { // Test volatile regime let regime = RegimeType::from_features(0.5, 3.0, 30.0); assert_eq!(regime, RegimeType::Volatile); // Test trending regime let regime = RegimeType::from_features(2.0, 1.0, 35.0); assert_eq!(regime, RegimeType::Trending); // Test ranging regime let regime = RegimeType::from_features(0.2, 0.5, 15.0); assert_eq!(regime, RegimeType::Ranging); // Test normal regime let regime = RegimeType::from_features(0.5, 1.0, 22.0); assert_eq!(regime, RegimeType::Normal); } #[tokio::test] #[ignore] // Requires database connection async fn test_regime_state_persistence() -> Result<()> { let db_pool = create_test_pool().await?; let mut manager = RegimePersistenceManager::new(db_pool.clone()); let symbol = "TEST.FUT"; let timestamp = Utc::now(); // Create mock regime features (24 features) let regime_features = [ // CUSUM features (201-210) 1.5, 2.5, 0.5, -0.3, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // ADX features (211-215) 35.0, 0.0, 0.0, 0.0, 0.0, // Transition probabilities (216-220) 0.7, 0.2, 0.1, 0.0, 0.0, // Adaptive metrics (221-224) 1.2, 2.5, 0.0, 0.0, ]; // Process features manager .process_regime_features(symbol, ®ime_features, timestamp) .await?; // Verify regime was persisted let latest_regime = db_pool.get_latest_regime(symbol).await?; assert_eq!(latest_regime.symbol, symbol); assert_eq!(latest_regime.regime, "Volatile"); // Expected based on cusum_std > 2.0 Ok(()) } #[tokio::test] #[ignore] // Requires database connection async fn test_regime_transition_tracking() -> Result<()> { let db_pool = create_test_pool().await?; let mut manager = RegimePersistenceManager::new(db_pool.clone()); let symbol = "TRANSITION.TEST"; let timestamp1 = Utc::now(); let timestamp2 = timestamp1 + chrono::Duration::seconds(60); // First regime: Volatile (cusum_std > 2.0) let regime_features_1 = [ 1.5, 3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // CUSUM 35.0, 0.0, 0.0, 0.0, 0.0, // ADX 0.0, 0.0, 0.0, 0.0, 0.0, // Transitions 1.0, 2.0, 0.0, 0.0, // Adaptive ]; manager .process_regime_features(symbol, ®ime_features_1, timestamp1) .await?; // Second regime: Trending (cusum_mean > 1.5 && adx > 25) let regime_features_2 = [ 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // CUSUM 30.0, 0.0, 0.0, 0.0, 0.0, // ADX 0.0, 0.0, 0.0, 0.0, 0.0, // Transitions 1.2, 2.5, 0.0, 0.0, // Adaptive ]; manager .process_regime_features(symbol, ®ime_features_2, timestamp2) .await?; // Verify transition was recorded let transitions = db_pool.get_regime_transitions(symbol, 10).await?; assert!(!transitions.is_empty(), "Expected at least one transition"); let transition = &transitions[0]; assert_eq!(transition.from_regime, "Volatile"); assert_eq!(transition.to_regime, "Trending"); Ok(()) } #[tokio::test] #[ignore] // Requires database connection async fn test_adaptive_metrics_update() -> Result<()> { let db_pool = create_test_pool().await?; let mut manager = RegimePersistenceManager::new(db_pool.clone()); let symbol = "METRICS.TEST"; let regime = "Trending"; let timestamp = Utc::now(); // Create features with specific adaptive metrics let regime_features = [ 2.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, // CUSUM 30.0, 0.0, 0.0, 0.0, 0.0, // ADX 0.0, 0.0, 0.0, 0.0, 0.0, // Transitions 1.5, 3.0, 0.0, 0.0, // Adaptive (pos_mult=1.5, stop_mult=3.0) ]; manager .process_regime_features(symbol, ®ime_features, timestamp) .await?; // Verify adaptive metrics were persisted let performance = db_pool.get_regime_performance(Some(symbol), 24).await?; // Find metrics for Trending regime let trending_metrics = performance .iter() .find(|p| p.regime == "Trending"); assert!(trending_metrics.is_some(), "Expected Trending regime metrics"); let metrics = trending_metrics.unwrap(); assert!((metrics.avg_position_multiplier - 1.5).abs() < 0.01); assert!((metrics.avg_stop_loss_multiplier - 3.0).abs() < 0.01); Ok(()) } #[tokio::test] #[ignore] // Requires database connection async fn test_trade_metrics_accumulation() -> Result<()> { let db_pool = create_test_pool().await?; let mut manager = RegimePersistenceManager::new(db_pool.clone()); let symbol = "TRADE.TEST"; let regime = "Trending"; let timestamp = Utc::now(); // Simulate winning trade manager .update_trade_metrics(symbol, regime, timestamp, 1000, true) .await?; // Simulate losing trade manager .update_trade_metrics(symbol, regime, timestamp, -500, false) .await?; // Simulate another winning trade manager .update_trade_metrics(symbol, regime, timestamp, 750, true) .await?; // Verify metrics accumulated correctly let performance = db_pool.get_regime_performance(Some(symbol), 24).await?; let trending_metrics = performance .iter() .find(|p| p.regime == "Trending"); assert!(trending_metrics.is_some()); let metrics = trending_metrics.unwrap(); assert_eq!(metrics.total_trades, 3); assert_eq!(metrics.total_pnl, rust_decimal::Decimal::new(1250, 0)); // 1000 - 500 + 750 assert!((metrics.win_rate - 0.666).abs() < 0.01); // 2/3 = 66.6% Ok(()) } #[tokio::test] #[ignore] // Requires database connection async fn test_multiple_symbols() -> Result<()> { let db_pool = create_test_pool().await?; let mut manager = RegimePersistenceManager::new(db_pool.clone()); let symbols = vec!["ES.FUT", "NQ.FUT", "6E.FUT"]; let timestamp = Utc::now(); for symbol in &symbols { let regime_features = [ 1.0, 1.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 25.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.3, 0.2, 0.0, 0.0, 1.0, 2.0, 0.0, 0.0, ]; manager .process_regime_features(symbol, ®ime_features, timestamp) .await?; } // Verify all symbols have regime states for symbol in &symbols { let latest = db_pool.get_latest_regime(symbol).await?; assert_eq!(latest.symbol, *symbol); } Ok(()) }