#![allow(unused_crate_dependencies)] //! Comprehensive trading engine order validation tests //! Target: 95%+ coverage for order validation and business logic use rust_decimal::Decimal; use std::str::FromStr; #[cfg(test)] mod order_validation_tests { use super::*; // ======================================================================== // Order Price Validation Tests // ======================================================================== #[test] fn test_validate_order_price_positive() { let price = Decimal::from_str("100.50").unwrap(); assert!(validate_order_price(price)); } #[test] fn test_validate_order_price_zero() { let price = Decimal::from_str("0.00").unwrap(); assert!(!validate_order_price(price)); } #[test] fn test_validate_order_price_negative() { let price = Decimal::from_str("-10.00").unwrap(); assert!(!validate_order_price(price)); } #[test] fn test_validate_order_price_tick_size() { let price = Decimal::from_str("100.55").unwrap(); let tick_size = Decimal::from_str("0.05").unwrap(); assert!(validate_price_tick_size(price, tick_size)); let invalid_price = Decimal::from_str("100.53").unwrap(); assert!(!validate_price_tick_size(invalid_price, tick_size)); } #[test] fn test_validate_order_price_extreme_values() { let very_high = Decimal::from_str("999999.99").unwrap(); assert!(validate_order_price(very_high)); let very_low = Decimal::from_str("0.0001").unwrap(); assert!(validate_order_price(very_low)); } // ======================================================================== // Order Quantity Validation Tests // ======================================================================== #[test] fn test_validate_order_quantity_positive() { let quantity = Decimal::from_str("100").unwrap(); assert!(validate_order_quantity(quantity)); } #[test] fn test_validate_order_quantity_zero() { let quantity = Decimal::from_str("0").unwrap(); assert!(!validate_order_quantity(quantity)); } #[test] fn test_validate_order_quantity_negative() { let quantity = Decimal::from_str("-50").unwrap(); assert!(!validate_order_quantity(quantity)); } #[test] fn test_validate_order_quantity_fractional() { let quantity = Decimal::from_str("10.5").unwrap(); // Some instruments allow fractional shares assert!(validate_order_quantity(quantity)); } #[test] fn test_validate_order_quantity_limits() { let quantity = Decimal::from_str("100").unwrap(); let min_quantity = Decimal::from_str("10").unwrap(); let max_quantity = Decimal::from_str("1000").unwrap(); assert!(validate_quantity_limits( quantity, min_quantity, max_quantity )); let too_small = Decimal::from_str("5").unwrap(); assert!(!validate_quantity_limits( too_small, min_quantity, max_quantity )); let too_large = Decimal::from_str("5000").unwrap(); assert!(!validate_quantity_limits( too_large, min_quantity, max_quantity )); } // ======================================================================== // Order Notional Value Validation Tests // ======================================================================== #[test] fn test_calculate_notional_value() { let price = Decimal::from_str("100.00").unwrap(); let quantity = Decimal::from_str("50").unwrap(); let notional = calculate_notional_value(price, quantity); assert_eq!(notional, Decimal::from_str("5000.00").unwrap()); } #[test] fn test_validate_notional_limits() { let notional = Decimal::from_str("10000.00").unwrap(); let min_notional = Decimal::from_str("1000.00").unwrap(); let max_notional = Decimal::from_str("100000.00").unwrap(); assert!(validate_notional_limits( notional, min_notional, max_notional )); } #[test] fn test_validate_notional_below_minimum() { let notional = Decimal::from_str("500.00").unwrap(); let min_notional = Decimal::from_str("1000.00").unwrap(); let max_notional = Decimal::from_str("100000.00").unwrap(); assert!(!validate_notional_limits( notional, min_notional, max_notional )); } #[test] fn test_validate_notional_above_maximum() { let notional = Decimal::from_str("150000.00").unwrap(); let min_notional = Decimal::from_str("1000.00").unwrap(); let max_notional = Decimal::from_str("100000.00").unwrap(); assert!(!validate_notional_limits( notional, min_notional, max_notional )); } // ======================================================================== // Order Side Validation Tests // ======================================================================== #[test] fn test_validate_order_side_buy() { assert!(validate_order_side("BUY")); assert!(validate_order_side("buy")); assert!(validate_order_side("Buy")); } #[test] fn test_validate_order_side_sell() { assert!(validate_order_side("SELL")); assert!(validate_order_side("sell")); assert!(validate_order_side("Sell")); } #[test] fn test_validate_order_side_invalid() { assert!(!validate_order_side("INVALID")); assert!(!validate_order_side("")); assert!(!validate_order_side("BID")); } // ======================================================================== // Order Type Validation Tests // ======================================================================== #[test] fn test_validate_order_type_market() { assert!(validate_order_type("MARKET")); } #[test] fn test_validate_order_type_limit() { assert!(validate_order_type("LIMIT")); } #[test] fn test_validate_order_type_stop() { assert!(validate_order_type("STOP")); assert!(validate_order_type("STOP_LIMIT")); } #[test] fn test_validate_order_type_invalid() { assert!(!validate_order_type("INVALID")); assert!(!validate_order_type("")); } #[test] fn test_validate_limit_order_has_price() { let order_type = "LIMIT"; let price = Some(Decimal::from_str("100.00").unwrap()); assert!(validate_limit_order_requirements(order_type, price)); } #[test] fn test_validate_limit_order_missing_price() { let order_type = "LIMIT"; let price = None; assert!(!validate_limit_order_requirements(order_type, price)); } #[test] fn test_validate_market_order_no_price_required() { let order_type = "MARKET"; let price = None; assert!(validate_market_order_requirements(order_type, price)); } // ======================================================================== // Time in Force Validation Tests // ======================================================================== #[test] fn test_validate_time_in_force_day() { assert!(validate_time_in_force("DAY")); } #[test] fn test_validate_time_in_force_gtc() { assert!(validate_time_in_force("GTC")); } #[test] fn test_validate_time_in_force_ioc() { assert!(validate_time_in_force("IOC")); } #[test] fn test_validate_time_in_force_fok() { assert!(validate_time_in_force("FOK")); } #[test] fn test_validate_time_in_force_invalid() { assert!(!validate_time_in_force("INVALID")); assert!(!validate_time_in_force("")); } // ======================================================================== // Symbol Validation Tests // ======================================================================== #[test] fn test_validate_symbol_format_valid() { assert!(validate_symbol_format("AAPL")); assert!(validate_symbol_format("SPY")); assert!(validate_symbol_format("MSFT")); } #[test] fn test_validate_symbol_format_with_exchange() { assert!(validate_symbol_format("AAPL.NASDAQ")); assert!(validate_symbol_format("SPY.NYSE")); } #[test] fn test_validate_symbol_format_invalid() { assert!(!validate_symbol_format("")); assert!(!validate_symbol_format("@#$")); assert!(!validate_symbol_format("SYMBOL_WITH_SPACES")); } #[test] fn test_validate_symbol_exists() { let valid_symbols = vec!["AAPL", "MSFT", "SPY"]; assert!(validate_symbol_exists("AAPL", &valid_symbols)); assert!(!validate_symbol_exists("INVALID", &valid_symbols)); } // ======================================================================== // Market Hours Validation Tests // ======================================================================== #[test] fn test_validate_market_hours_during_trading() { // Assume 9:30 AM to 4:00 PM EST trading hours let order_time = "14:30:00"; // 2:30 PM assert!(validate_market_hours(order_time, "09:30:00", "16:00:00")); } #[test] fn test_validate_market_hours_before_open() { let order_time = "09:00:00"; // Before market open assert!(!validate_market_hours(order_time, "09:30:00", "16:00:00")); } #[test] fn test_validate_market_hours_after_close() { let order_time = "17:00:00"; // After market close assert!(!validate_market_hours(order_time, "09:30:00", "16:00:00")); } #[test] fn test_validate_market_hours_boundary_cases() { let open_time = "09:30:00"; let close_time = "16:00:00"; // At exact open time assert!(validate_market_hours("09:30:00", open_time, close_time)); // At exact close time assert!(validate_market_hours("16:00:00", open_time, close_time)); } // ======================================================================== // Order Status Transition Validation Tests // ======================================================================== #[test] fn test_validate_order_status_transition_new_to_filled() { assert!(validate_status_transition("NEW", "FILLED")); } #[test] fn test_validate_order_status_transition_new_to_cancelled() { assert!(validate_status_transition("NEW", "CANCELLED")); } #[test] fn test_validate_order_status_transition_filled_to_cancelled() { // Cannot cancel a filled order assert!(!validate_status_transition("FILLED", "CANCELLED")); } #[test] fn test_validate_order_status_transition_cancelled_to_filled() { // Cannot fill a cancelled order assert!(!validate_status_transition("CANCELLED", "FILLED")); } #[test] fn test_validate_order_status_transition_rejected_is_terminal() { assert!(!validate_status_transition("REJECTED", "FILLED")); assert!(!validate_status_transition("REJECTED", "CANCELLED")); } // ======================================================================== // Position Limit Validation Tests // ======================================================================== #[test] fn test_validate_position_limit_within_bounds() { let current_position = Decimal::from_str("500").unwrap(); let order_quantity = Decimal::from_str("300").unwrap(); let position_limit = Decimal::from_str("1000").unwrap(); assert!(validate_position_limit( current_position, order_quantity, position_limit )); } #[test] fn test_validate_position_limit_exceeds_limit() { let current_position = Decimal::from_str("800").unwrap(); let order_quantity = Decimal::from_str("300").unwrap(); let position_limit = Decimal::from_str("1000").unwrap(); assert!(!validate_position_limit( current_position, order_quantity, position_limit )); } #[test] fn test_validate_position_limit_exact_limit() { let current_position = Decimal::from_str("700").unwrap(); let order_quantity = Decimal::from_str("300").unwrap(); let position_limit = Decimal::from_str("1000").unwrap(); assert!(validate_position_limit( current_position, order_quantity, position_limit )); } #[test] fn test_validate_position_limit_short_position() { let current_position = Decimal::from_str("-500").unwrap(); let order_quantity = Decimal::from_str("-300").unwrap(); // Adding to short let position_limit = Decimal::from_str("1000").unwrap(); // Absolute value should be checked let new_position = current_position + order_quantity; assert!(new_position.abs() <= position_limit); } // ======================================================================== // Risk Check Validation Tests // ======================================================================== #[test] fn test_validate_order_risk_within_limits() { let order_value = Decimal::from_str("10000.00").unwrap(); let available_capital = Decimal::from_str("100000.00").unwrap(); let max_order_percentage = Decimal::from_str("0.20").unwrap(); assert!(validate_order_risk( order_value, available_capital, max_order_percentage )); } #[test] fn test_validate_order_risk_exceeds_limit() { let order_value = Decimal::from_str("30000.00").unwrap(); let available_capital = Decimal::from_str("100000.00").unwrap(); let max_order_percentage = Decimal::from_str("0.20").unwrap(); // 20% max assert!(!validate_order_risk( order_value, available_capital, max_order_percentage )); } #[test] fn test_validate_order_risk_insufficient_capital() { let order_value = Decimal::from_str("10000.00").unwrap(); let available_capital = Decimal::from_str("5000.00").unwrap(); let max_order_percentage = Decimal::from_str("0.50").unwrap(); assert!(!validate_order_risk( order_value, available_capital, max_order_percentage )); } // ======================================================================== // Duplicate Order Detection Tests // ======================================================================== #[test] fn test_validate_no_duplicate_order() { let order_id = "ORD123"; let existing_orders = vec!["ORD456", "ORD789"]; assert!(validate_no_duplicate(order_id, &existing_orders)); } #[test] fn test_validate_duplicate_order_detected() { let order_id = "ORD123"; let existing_orders = vec!["ORD456", "ORD123", "ORD789"]; assert!(!validate_no_duplicate(order_id, &existing_orders)); } // ======================================================================== // Order Rate Limiting Tests // ======================================================================== #[test] fn test_validate_order_rate_within_limit() { let orders_count = 50; let time_window_seconds = 60; let max_orders_per_minute = 100; assert!(validate_order_rate( orders_count, time_window_seconds, max_orders_per_minute )); } #[test] fn test_validate_order_rate_exceeds_limit() { let orders_count = 150; let time_window_seconds = 60; let max_orders_per_minute = 100; assert!(!validate_order_rate( orders_count, time_window_seconds, max_orders_per_minute )); } // ======================================================================== // Edge Case Tests // ======================================================================== #[test] fn test_validate_order_with_very_small_price() { let price = Decimal::from_str("0.0001").unwrap(); assert!(validate_order_price(price)); } #[test] fn test_validate_order_with_very_large_quantity() { let quantity = Decimal::from_str("1000000").unwrap(); assert!(validate_order_quantity(quantity)); } #[test] fn test_validate_order_decimal_precision() { let price = Decimal::from_str("100.123456").unwrap(); let max_precision = 2; assert!(!validate_price_precision(price, max_precision)); let valid_price = Decimal::from_str("100.12").unwrap(); assert!(validate_price_precision(valid_price, max_precision)); } #[test] fn test_validate_order_symbol_case_insensitive() { assert!(validate_symbol_format("aapl")); assert!(validate_symbol_format("AAPL")); assert!(validate_symbol_format("AaPl")); } } // Helper validation functions fn validate_order_price(price: Decimal) -> bool { price > Decimal::ZERO } fn validate_price_tick_size(price: Decimal, tick_size: Decimal) -> bool { (price % tick_size) == Decimal::ZERO } fn validate_order_quantity(quantity: Decimal) -> bool { quantity > Decimal::ZERO } fn validate_quantity_limits(quantity: Decimal, min: Decimal, max: Decimal) -> bool { quantity >= min && quantity <= max } fn calculate_notional_value(price: Decimal, quantity: Decimal) -> Decimal { price * quantity } fn validate_notional_limits(notional: Decimal, min: Decimal, max: Decimal) -> bool { notional >= min && notional <= max } fn validate_order_side(side: &str) -> bool { let upper = side.to_uppercase(); upper == "BUY" || upper == "SELL" } fn validate_order_type(order_type: &str) -> bool { let upper = order_type.to_uppercase(); matches!(upper.as_str(), "MARKET" | "LIMIT" | "STOP" | "STOP_LIMIT") } fn validate_limit_order_requirements(order_type: &str, price: Option) -> bool { if order_type.to_uppercase() == "LIMIT" { price.is_some() } else { true } } fn validate_market_order_requirements(order_type: &str, _price: Option) -> bool { order_type.to_uppercase() == "MARKET" } fn validate_time_in_force(tif: &str) -> bool { let upper = tif.to_uppercase(); matches!(upper.as_str(), "DAY" | "GTC" | "IOC" | "FOK") } fn validate_symbol_format(symbol: &str) -> bool { !symbol.is_empty() && symbol.chars().all(|c| c.is_alphanumeric() || c == '.') } fn validate_symbol_exists(symbol: &str, valid_symbols: &[&str]) -> bool { valid_symbols.contains(&symbol) } fn validate_market_hours(order_time: &str, open: &str, close: &str) -> bool { order_time >= open && order_time <= close } fn validate_status_transition(from: &str, to: &str) -> bool { let valid_transitions = vec![ ("NEW", "FILLED"), ("NEW", "PARTIALLY_FILLED"), ("NEW", "CANCELLED"), ("NEW", "REJECTED"), ("PARTIALLY_FILLED", "FILLED"), ("PARTIALLY_FILLED", "CANCELLED"), ]; valid_transitions.contains(&(from, to)) } fn validate_position_limit(current: Decimal, order_qty: Decimal, limit: Decimal) -> bool { (current + order_qty).abs() <= limit } fn validate_order_risk(order_value: Decimal, capital: Decimal, max_pct: Decimal) -> bool { order_value <= capital && order_value <= capital * max_pct } fn validate_no_duplicate(order_id: &str, existing: &[&str]) -> bool { !existing.contains(&order_id) } fn validate_order_rate(count: usize, window: usize, max_per_window: usize) -> bool { if window == 0 { return false; } count <= max_per_window } fn validate_price_precision(price: Decimal, max_decimal_places: u32) -> bool { let scale = price.scale(); scale <= max_decimal_places }