diff --git a/Cargo.lock b/Cargo.lock index 7194530e2..f382ddf30 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3827,6 +3827,7 @@ dependencies = [ "common", "once_cell", "rust_decimal", + "rust_decimal_macros", "serde", "serde_json", "sqlx", @@ -4124,6 +4125,7 @@ dependencies = [ "serde_json", "sqlx", "storage", + "tempfile", "thiserror 1.0.69", "tokio", "tokio-retry", diff --git a/backtesting/src/lib.rs b/backtesting/src/lib.rs index 5621a4c69..e4da61939 100644 --- a/backtesting/src/lib.rs +++ b/backtesting/src/lib.rs @@ -74,7 +74,7 @@ pub mod strategy_runner; pub use strategy_tester::{Strategy, StrategyConfig, StrategyContext, StrategyResult, TradingSignal, SignalType, StrategyTester}; pub use replay_engine::{MarketReplay, ReplayConfig}; pub use metrics::{MetricsCalculator, PerformanceAnalytics}; -pub use strategy_runner::{AdaptiveStrategyConfig, create_adaptive_strategy_with_config}; +pub use strategy_runner::{AdaptiveStrategyConfig, create_adaptive_strategy_with_config, RiskSettings, FeatureSettings}; // Import events from trading_engine types @@ -673,8 +673,10 @@ pub struct ComparisonMetrics { #[cfg(test)] mod tests { use super::*; - use std::io::Write; - use tempfile::NamedTempFile; + use std::collections::HashMap; + use common::{Order, OrderSide, OrderStatus, Position, Price, Quantity}; + use rust_decimal_macros::dec; + use num_traits::ToPrimitive; #[tokio::test] async fn test_backtest_engine_creation() { @@ -708,7 +710,7 @@ mod tests { entry_threshold: Decimal, exit_threshold: Decimal, current_position: Option, - position_side: Option, + position_side: Option, trades_executed: usize, total_pnl: Decimal, max_drawdown: Decimal, @@ -775,7 +777,8 @@ mod tests { if let Some(ref _position) = self.current_position { if let Some(ref side) = self.position_side { match side { - OrderSide::Buy => z_score > -self.exit_threshold, // Long position OrderSide::Sell => z_score < self.exit_threshold, // Short position + OrderSide::Buy => z_score > -self.exit_threshold, // Long position + OrderSide::Sell => z_score < self.exit_threshold, // Short position } } else { false @@ -872,7 +875,8 @@ mod tests { if let Some(ref position) = self.current_position { if let Some(ref side) = self.position_side { let exit_signal_type = match side { - OrderSide::Buy => SignalType::Sell, // Exit long position OrderSide::Sell => SignalType::Cover, // Exit short position + OrderSide::Buy => SignalType::Sell, // Exit long position + OrderSide::Sell => SignalType::Cover, // Exit short position }; let mut metadata = HashMap::new(); diff --git a/backtesting/tests/test_ml_integration.rs b/backtesting/tests/test_ml_integration.rs index fa997dd3e..562b9f532 100644 --- a/backtesting/tests/test_ml_integration.rs +++ b/backtesting/tests/test_ml_integration.rs @@ -1,9 +1,10 @@ //! Integration tests for ML models in backtesting framework use backtesting::{ - create_adaptive_strategy_with_config, AdaptiveStrategyConfig, AdaptiveStrategyRunner, - BacktestConfig, BacktestEngine, FeatureSettings, RiskSettings, Strategy, + create_adaptive_strategy_with_config, AdaptiveStrategyConfig, + BacktestConfig, BacktestEngine, RiskSettings, FeatureSettings, }; +use rust_decimal::Decimal; #[tokio::test] async fn test_dqn_strategy_integration() { diff --git a/market-data/Cargo.toml b/market-data/Cargo.toml index 68bbb93e7..bff5a05b1 100644 --- a/market-data/Cargo.toml +++ b/market-data/Cargo.toml @@ -52,4 +52,5 @@ common = { path = "../common" } [dev-dependencies] tokio-test = { workspace = true } tempfile = { workspace = true } -test-case = { workspace = true } \ No newline at end of file +test-case = { workspace = true } +rust_decimal_macros = { workspace = true } \ No newline at end of file diff --git a/market-data/tests/basic_test.rs b/market-data/tests/basic_test.rs index 0548ae163..d55c7d9bf 100644 --- a/market-data/tests/basic_test.rs +++ b/market-data/tests/basic_test.rs @@ -68,13 +68,13 @@ fn test_technical_indicator_model() { #[test] fn test_hash_traits() { - // Test OrderSide is hashable + // Test BookSide is hashable let mut side_map: HashMap = HashMap::new(); - side_map.insert(OrderSide::Bid, 1); - side_map.insert(OrderSide::Ask, 2); + side_map.insert(BookSide::Bid, 1); + side_map.insert(BookSide::Ask, 2); - assert_eq!(side_map.get(&OrderSide::Bid), Some(&1)); - assert_eq!(side_map.get(&OrderSide::Ask), Some(&2)); + assert_eq!(side_map.get(&BookSide::Bid), Some(&1)); + assert_eq!(side_map.get(&BookSide::Ask), Some(&2)); // Test IndicatorType is hashable let mut indicator_map: HashMap = HashMap::new(); diff --git a/risk/src/compliance.rs b/risk/src/compliance.rs index 3403930fa..1238bfe5f 100644 --- a/risk/src/compliance.rs +++ b/risk/src/compliance.rs @@ -1717,7 +1717,8 @@ impl ComplianceValidator { #[cfg(test)] mod tests { use super::*; - use common::operations; + use common::{Symbol, OrderSide, Quantity, OrderType}; + // operations module removed - use direct imports from common fn create_test_config() -> Result> { use std::collections::HashMap; @@ -1731,6 +1732,8 @@ mod tests { global_limit: Price::from_f64(10000000.0).unwrap_or(Price::ZERO), }, audit_retention_days: 2555, + market_abuse_threshold: Some(Price::from_f64(100000.0).unwrap_or(Price::ZERO)), + large_exposure_threshold: Price::from_f64(500000.0).unwrap_or(Price::ZERO), }) } diff --git a/risk/src/drawdown_monitor.rs b/risk/src/drawdown_monitor.rs index 51d08d33e..1e06142cd 100644 --- a/risk/src/drawdown_monitor.rs +++ b/risk/src/drawdown_monitor.rs @@ -265,7 +265,8 @@ impl DrawdownMonitor { #[cfg(test)] mod tests { use super::*; - use common::operations; + use common::Price; + // operations module removed - use direct imports from common fn create_test_pnl_metrics(portfolio_id: &str, pnl: i64) -> PnLMetrics { PnLMetrics { diff --git a/risk/src/operations.rs b/risk/src/operations.rs index a40333a7b..12b0bd4ab 100644 --- a/risk/src/operations.rs +++ b/risk/src/operations.rs @@ -82,7 +82,6 @@ pub fn f64_to_decimal_safe(value: f64, context: &str) -> RiskResult { #[cfg(test)] pub fn create_test_price(value: f64) -> Price { use std::num::NonZeroU64; - use common::basic::*; // For test scenarios, create Price with raw decimal value if value >= 0.0 { diff --git a/risk/src/safety/emergency_response.rs b/risk/src/safety/emergency_response.rs index ee3e9a1ab..fdcef21c5 100644 --- a/risk/src/safety/emergency_response.rs +++ b/risk/src/safety/emergency_response.rs @@ -254,7 +254,10 @@ impl EmergencyResponseSystem { mod tests { use super::*; use crate::safety::KillSwitchConfig; - use common::operations; + use crate::error::RiskResult; + use config::risk_config::{AssetClass, MarketCapTier}; + use common::{Symbol, Price, Quantity}; + // operations module removed - use direct imports from common // CANONICAL TYPE IMPORTS - ENFORCED BY TYPE SYSTEM AGENT async fn create_test_system() -> RiskResult<(EmergencyResponseSystem, Arc)> { @@ -295,7 +298,7 @@ mod tests { AssetClass::Future { .. } => 10.0, // Moderate for futures AssetClass::Unknown => 3.0, // Very conservative for unknown assets _ => { - log::error!("Unknown asset class in emergency response concentration calculation - using ultra-conservative limit"); + tracing::error!("Unknown asset class in emergency response concentration calculation - using ultra-conservative limit"); 1.0 // Ultra-conservative 1% limit for unknown asset classes } }; diff --git a/risk/src/safety/position_limiter.rs b/risk/src/safety/position_limiter.rs index 98e0ed0a4..2750e85cb 100644 --- a/risk/src/safety/position_limiter.rs +++ b/risk/src/safety/position_limiter.rs @@ -280,8 +280,8 @@ mod tests { OrderSide::Buy, Quantity::from_f64(test_quantity).unwrap_or(Quantity::ZERO), Some(Price::from_f64(150.0).unwrap_or(Price::ONE)), + OrderType::Limit, ).with_account_id("account_001".to_string()) - .with_order_type(OrderType::Limit) } #[tokio::test] diff --git a/risk/src/safety/safety_coordinator.rs b/risk/src/safety/safety_coordinator.rs index 5e255a069..b8692711f 100644 --- a/risk/src/safety/safety_coordinator.rs +++ b/risk/src/safety/safety_coordinator.rs @@ -261,7 +261,8 @@ impl SafetyCoordinator { mod tests { use super::*; use std::time::Duration; - use common::operations; + use crate::safety::{KillSwitchConfig, PositionLimiterConfig, EmergencyResponseConfig}; + // operations module removed - use direct imports from common fn create_test_config() -> SafetyConfig { SafetyConfig { diff --git a/risk/src/safety/trading_gate.rs b/risk/src/safety/trading_gate.rs index dcb45c183..d9cc819d2 100644 --- a/risk/src/safety/trading_gate.rs +++ b/risk/src/safety/trading_gate.rs @@ -380,6 +380,8 @@ mod tests { .activate( KillSwitchScope::Symbol("AAPL".to_string()), "Test".to_string(), + "test_user".to_string(), + false, ) .await?; diff --git a/risk/src/stress_tester.rs b/risk/src/stress_tester.rs index 673203028..b60afbfbb 100644 --- a/risk/src/stress_tester.rs +++ b/risk/src/stress_tester.rs @@ -431,40 +431,75 @@ fn convert_config_to_scenario( #[cfg(test)] mod tests { use super::*; - use common::operations; + // operations module removed - use direct imports from common // Types already imported via prelude at top of file fn create_test_positions() -> Result, Box> { + let now = chrono::Utc::now(); Ok(vec![ - { - let mut pos = Position { - symbol: Symbol::from("AAPL".to_string()), - quantity: FromPrimitive::from_f64(100.0).ok_or_else(|| { - RiskError::CalculationError("Failed to convert 100.0 to decimal".to_owned()) - })?, - avg_cost: Price::from_f64(150.0)?, - average_price: Price::from_f64(150.0)?, - market_value: Price::from_f64(15000.0)?, - unrealized_pnl: Decimal::ZERO, - realized_pnl: Decimal::ZERO, - last_updated: chrono::Utc::now(), - }; - pos + Position { + id: uuid::Uuid::new_v4(), + symbol: "AAPL".to_string(), + quantity: FromPrimitive::from_f64(100.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 100.0 to decimal".to_owned()) + })?, + avg_price: FromPrimitive::from_f64(150.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 150.0 to decimal".to_owned()) + })?, + avg_cost: FromPrimitive::from_f64(150.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 150.0 to decimal".to_owned()) + })?, + basis: FromPrimitive::from_f64(15000.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 15000.0 to decimal".to_owned()) + })?, + average_price: FromPrimitive::from_f64(150.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 150.0 to decimal".to_owned()) + })?, + market_value: FromPrimitive::from_f64(15000.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 15000.0 to decimal".to_owned()) + })?, + unrealized_pnl: Decimal::ZERO, + realized_pnl: Decimal::ZERO, + created_at: now, + updated_at: now, + last_updated: now, + current_price: None, + notional_value: FromPrimitive::from_f64(15000.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 15000.0 to decimal".to_owned()) + })?, + margin_requirement: Decimal::ZERO, }, - { - let mut pos = Position { - symbol: Symbol::from("GOOGL".to_string()), - quantity: FromPrimitive::from_f64(50.0).ok_or_else(|| { - RiskError::CalculationError("Failed to convert 50.0 to decimal".to_owned()) - })?, - avg_cost: Price::from_f64(2500.0)?, - average_price: Price::from_f64(2500.0)?, - market_value: Price::from_f64(125000.0)?, - unrealized_pnl: Decimal::ZERO, - realized_pnl: Decimal::ZERO, - last_updated: chrono::Utc::now(), - }; - pos + Position { + id: uuid::Uuid::new_v4(), + symbol: "GOOGL".to_string(), + quantity: FromPrimitive::from_f64(50.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 50.0 to decimal".to_owned()) + })?, + avg_price: FromPrimitive::from_f64(2500.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 2500.0 to decimal".to_owned()) + })?, + avg_cost: FromPrimitive::from_f64(2500.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 2500.0 to decimal".to_owned()) + })?, + basis: FromPrimitive::from_f64(125000.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 125000.0 to decimal".to_owned()) + })?, + average_price: FromPrimitive::from_f64(2500.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 2500.0 to decimal".to_owned()) + })?, + market_value: FromPrimitive::from_f64(125000.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 125000.0 to decimal".to_owned()) + })?, + unrealized_pnl: Decimal::ZERO, + realized_pnl: Decimal::ZERO, + created_at: now, + updated_at: now, + last_updated: now, + current_price: None, + notional_value: FromPrimitive::from_f64(125000.0).ok_or_else(|| { + RiskError::CalculationError("Failed to convert 125000.0 to decimal".to_owned()) + })?, + margin_requirement: Decimal::ZERO, }, ]) } diff --git a/risk/src/var_calculator/historical_simulation.rs b/risk/src/var_calculator/historical_simulation.rs index 31790b5c2..4ebad978a 100644 --- a/risk/src/var_calculator/historical_simulation.rs +++ b/risk/src/var_calculator/historical_simulation.rs @@ -950,7 +950,8 @@ impl HistoricalSimulationVaR { mod tests { use super::*; use chrono::Duration; - use common::operations; + use num_traits::FromPrimitive; + // operations module removed - use direct imports from common fn create_test_historical_prices( symbol: &Symbol, @@ -972,7 +973,7 @@ mod tests { high: Price::from_f64(current_price * 1.005)?, low: Price::from_f64(current_price * 0.995)?, price: Price::from_f64(current_price)?, - volume: Decimal::try_from(1000000.0).map_err(|_| { + volume: FromPrimitive::from_f64(1000000.0).ok_or_else(|| { RiskError::CalculationError("Failed to convert 1000000.0 to decimal".to_owned()) })?, }); diff --git a/risk/src/var_calculator/monte_carlo.rs b/risk/src/var_calculator/monte_carlo.rs index d749b5355..1adf219ae 100644 --- a/risk/src/var_calculator/monte_carlo.rs +++ b/risk/src/var_calculator/monte_carlo.rs @@ -1057,7 +1057,8 @@ impl MonteCarloVaR { mod tests { use super::*; use chrono::Duration; - use common::operations; + use num_traits::FromPrimitive; + // operations module removed - use direct imports from common fn create_test_historical_prices( symbol: &str, @@ -1094,13 +1095,12 @@ mod tests { fn create_test_position(symbol: &str, quantity: f64, market_price: f64) -> PositionInfo { PositionInfo { symbol: symbol.to_string().into(), - quantity: Decimal::try_from(quantity).unwrap_or(Decimal::ZERO), + quantity: FromPrimitive::from_f64(quantity).unwrap_or(Decimal::ZERO), market_value: Price::from_f64(quantity * market_price).unwrap_or(Price::ZERO), average_cost: Price::from_f64(market_price * 0.95).unwrap_or(Price::ZERO), - unrealized_pnl: Decimal::try_from(quantity * market_price * 0.05) - .unwrap_or(Decimal::ZERO) - .into(), - realized_pnl: Decimal::ZERO, + unrealized_pnl: FromPrimitive::from_f64(quantity * market_price * 0.05) + .unwrap_or(Decimal::ZERO), + realized_pnl: FromPrimitive::from_f64(0.0).unwrap_or(Decimal::ZERO), currency: "USD".to_string(), timestamp: Utc::now(), } diff --git a/risk/src/var_calculator/var_engine.rs b/risk/src/var_calculator/var_engine.rs index 541ee6b96..54a2e884c 100644 --- a/risk/src/var_calculator/var_engine.rs +++ b/risk/src/var_calculator/var_engine.rs @@ -1471,7 +1471,7 @@ impl VaRCalculationResult { #[cfg(test)] mod tests { use super::*; - use common::operations; + // operations module removed - use direct imports from common // CANONICAL TYPE IMPORTS - ENFORCED BY TYPE SYSTEM AGENT #[test] diff --git a/services/ml_training_service/Cargo.toml b/services/ml_training_service/Cargo.toml index 7fbac9d29..bf1516c1f 100644 --- a/services/ml_training_service/Cargo.toml +++ b/services/ml_training_service/Cargo.toml @@ -73,6 +73,9 @@ prost-build.workspace = true name = "ml_training_service" path = "src/main.rs" +[dev-dependencies] +tempfile.workspace = true + [features] default = ["minimal"] minimal = ["ml/financial"] diff --git a/services/ml_training_service/src/database.rs b/services/ml_training_service/src/database.rs index 63eedfd9f..5f61a782b 100644 --- a/services/ml_training_service/src/database.rs +++ b/services/ml_training_service/src/database.rs @@ -555,8 +555,13 @@ mod tests { let config = DatabaseConfig { url: "postgresql://test:test@localhost:5432/test_ml_training".to_string(), max_connections: 5, - connection_timeout_secs: 10, - auto_migrate: true, + min_connections: 1, + connect_timeout: std::time::Duration::from_secs(10), + query_timeout: std::time::Duration::from_secs(30), + enable_query_logging: true, + application_name: Some("ml_training_service_test".to_string()), + pool: config::database::PoolConfig::default(), + transaction: config::database::TransactionConfig::default(), }; DatabaseManager::new(&config).await diff --git a/services/ml_training_service/src/main.rs b/services/ml_training_service/src/main.rs index 4127bd280..ce9c7bad3 100644 --- a/services/ml_training_service/src/main.rs +++ b/services/ml_training_service/src/main.rs @@ -512,6 +512,6 @@ mod tests { fn test_config_validation() { let config = MLConfig::default(); // Basic validation test - config should have sensible defaults - assert!(!config.model_name.is_empty()); + assert!(!config.model_config.model_type.is_empty()); } }