use std::collections::HashMap;
use std::sync::{Arc, Mutex, atomic::{AtomicBool, Ordering}};
use chrono::{DateTime, FixedOffset, Utc};
use tokio::test;
use crate::trading_mode::{ApiConfig, RiskConfig, SlippageConfig};
use crate::unified_data::{
Position, OrderRequest, OrderResult, MarketData,
OrderSide, OrderType, TimeInForce, OrderStatus,
TradingStrategy, Signal
};
use crate::risk_manager::{RiskManager, RiskError, RiskOrder};
fn create_test_market_data(symbol: &str, price: f64) -> MarketData {
let now = Utc::now().with_timezone(&FixedOffset::east(0));
MarketData::new(
symbol,
price,
price * 0.999, price * 1.001, 100.0, now,
)
}
#[test]
fn test_risk_manager_position_size_limits() {
let risk_config = RiskConfig {
max_position_size_pct: 0.01, max_daily_loss_pct: 0.05,
stop_loss_pct: 0.05,
take_profit_pct: 0.1,
max_leverage: 2.0,
max_concentration_pct: 0.1,
max_position_correlation: 0.5,
max_portfolio_volatility_pct: 0.1,
volatility_sizing_factor: 0.3,
max_drawdown_pct: 0.1,
};
let mut risk_manager = RiskManager::new(risk_config, 10000.0);
let mut positions = HashMap::new();
let small_order = OrderRequest::market("BTC", OrderSide::Buy, 0.01);
let result = risk_manager.validate_order(&small_order, &positions);
assert!(result.is_ok());
let large_order = OrderRequest::market("BTC", OrderSide::Buy, 0.5);
let result = risk_manager.validate_order(&large_order, &positions);
assert!(result.is_err());
match result {
Err(RiskError::PositionSizeLimitExceeded { symbol, size, limit }) => {
assert_eq!(symbol, "BTC");
assert_eq!(size, 0.5);
assert!(limit < 0.5);
},
_ => panic!("Expected PositionSizeLimitExceeded error"),
}
}
#[test]
fn test_risk_manager_leverage_limits() {
let risk_config = RiskConfig {
max_position_size_pct: 0.5, max_daily_loss_pct: 0.05,
stop_loss_pct: 0.05,
take_profit_pct: 0.1,
max_leverage: 1.5, max_concentration_pct: 0.5,
max_position_correlation: 0.5,
max_portfolio_volatility_pct: 0.1,
volatility_sizing_factor: 0.3,
max_drawdown_pct: 0.1,
};
let mut risk_manager = RiskManager::new(risk_config, 10000.0);
let mut positions = HashMap::new();
let now = Utc::now().with_timezone(&FixedOffset::east(0));
positions.insert("ETH".to_string(), Position::new(
"ETH",
1.0,
3000.0,
3000.0,
now,
));
let btc_order = OrderRequest::market("BTC", OrderSide::Buy, 0.2);
let result = risk_manager.validate_order(&btc_order, &positions);
assert!(result.is_ok());
let large_btc_order = OrderRequest::market("BTC", OrderSide::Buy, 0.3);
let result = risk_manager.validate_order(&large_btc_order, &positions);
assert!(result.is_err());
match result {
Err(RiskError::LeverageLimitExceeded { current, limit, would_be }) => {
assert_eq!(limit, 1.5);
assert!(would_be > 1.5);
},
_ => panic!("Expected LeverageLimitExceeded error"),
}
}
#[test]
fn test_risk_manager_concentration_limits() {
let risk_config = RiskConfig {
max_position_size_pct: 0.5, max_daily_loss_pct: 0.05,
stop_loss_pct: 0.05,
take_profit_pct: 0.1,
max_leverage: 3.0,
max_concentration_pct: 0.2, max_position_correlation: 0.5,
max_portfolio_volatility_pct: 0.1,
volatility_sizing_factor: 0.3,
max_drawdown_pct: 0.1,
};
let mut risk_manager = RiskManager::new(risk_config, 10000.0);
let mut positions = HashMap::new();
let now = Utc::now().with_timezone(&FixedOffset::east(0));
let btc_order = OrderRequest::market("BTC", OrderSide::Buy, 0.05);
let result = risk_manager.validate_order(&btc_order, &positions);
assert!(result.is_err());
match result {
Err(RiskError::ConcentrationLimitExceeded { symbol, concentration, limit }) => {
assert_eq!(symbol, "BTC");
assert!(concentration > 0.2);
assert_eq!(limit, 0.2);
},
_ => panic!("Expected ConcentrationLimitExceeded error"),
}
let small_btc_order = OrderRequest::market("BTC", OrderSide::Buy, 0.01);
let result = risk_manager.validate_order(&small_btc_order, &positions);
assert!(result.is_ok());
}
#[test]
fn test_risk_manager_daily_loss_limits() {
let risk_config = RiskConfig {
max_position_size_pct: 0.5,
max_daily_loss_pct: 0.02, stop_loss_pct: 0.05,
take_profit_pct: 0.1,
max_leverage: 3.0,
max_concentration_pct: 0.5,
max_position_correlation: 0.5,
max_portfolio_volatility_pct: 0.1,
volatility_sizing_factor: 0.3,
max_drawdown_pct: 0.1,
};
let mut risk_manager = RiskManager::new(risk_config, 10000.0);
risk_manager.update_daily_pnl(-150.0);
let small_order = OrderRequest::market("BTC", OrderSide::Buy, 0.01);
let result = risk_manager.validate_order(&small_order, &HashMap::new());
assert!(result.is_ok());
risk_manager.update_daily_pnl(-100.0);
let another_order = OrderRequest::market("BTC", OrderSide::Buy, 0.01);
let result = risk_manager.validate_order(&another_order, &HashMap::new());
assert!(result.is_err());
match result {
Err(RiskError::DailyLossLimitExceeded { loss, limit }) => {
assert!(loss > 0.02);
assert_eq!(limit, 0.02);
},
_ => panic!("Expected DailyLossLimitExceeded error"),
}
}
#[test]
fn test_risk_manager_stop_loss_generation() {
let risk_config = RiskConfig {
max_position_size_pct: 0.5,
max_daily_loss_pct: 0.05,
stop_loss_pct: 0.05, take_profit_pct: 0.1, max_leverage: 3.0,
max_concentration_pct: 0.5,
max_position_correlation: 0.5,
max_portfolio_volatility_pct: 0.1,
volatility_sizing_factor: 0.3,
max_drawdown_pct: 0.1,
};
let mut risk_manager = RiskManager::new(risk_config, 10000.0);
let now = Utc::now().with_timezone(&FixedOffset::east(0));
let position = Position::new(
"BTC",
0.1,
50000.0,
50000.0,
now,
);
let stop_loss = risk_manager.generate_stop_loss(&position, "order123");
assert!(stop_loss.is_some());
let stop_loss = stop_loss.unwrap();
assert_eq!(stop_loss.symbol, "BTC");
assert_eq!(stop_loss.position_size, 0.1);
assert_eq!(stop_loss.side, OrderSide::Sell); assert_eq!(stop_loss.trigger_price, 50000.0 * 0.95);
let take_profit = risk_manager.generate_take_profit(&position, "order123");
assert!(take_profit.is_some());
let take_profit = take_profit.unwrap();
assert_eq!(take_profit.symbol, "BTC");
assert_eq!(take_profit.position_size, 0.1);
assert_eq!(take_profit.side, OrderSide::Sell); assert_eq!(take_profit.trigger_price, 50000.0 * 1.1);
let short_position = Position::new(
"ETH",
-1.0,
3000.0,
3000.0,
now,
);
let stop_loss = risk_manager.generate_stop_loss(&short_position, "order456");
assert!(stop_loss.is_some());
let stop_loss = stop_loss.unwrap();
assert_eq!(stop_loss.symbol, "ETH");
assert_eq!(stop_loss.position_size, 1.0);
assert_eq!(stop_loss.side, OrderSide::Buy); assert_eq!(stop_loss.trigger_price, 3000.0 * 1.05); }
#[test]
fn test_risk_manager_drawdown_protection() {
let risk_config = RiskConfig {
max_position_size_pct: 0.5,
max_daily_loss_pct: 0.05,
stop_loss_pct: 0.05,
take_profit_pct: 0.1,
max_leverage: 3.0,
max_concentration_pct: 0.5,
max_position_correlation: 0.5,
max_portfolio_volatility_pct: 0.1,
volatility_sizing_factor: 0.3,
max_drawdown_pct: 0.1, };
let mut risk_manager = RiskManager::new(risk_config, 10000.0);
risk_manager.update_account_value(11000.0);
assert!(!risk_manager.should_stop_trading());
risk_manager.update_account_value(10000.0); assert!(!risk_manager.should_stop_trading());
risk_manager.update_account_value(9800.0); assert!(risk_manager.should_stop_trading());
}