ParallelLLC/algorithmic_trading
2732
1import pytest2import time3from unittest.mock import patch, MagicMock4from agentic_ai_system.execution_agent import ExecutionAgent5 6class TestExecutionAgent:7 """Test cases for ExecutionAgent"""8 9 @pytest.fixture10 def config(self):11 """Sample configuration for testing"""12 return {13 'execution': {14 'broker_api': 'paper',15 'order_size': 10,16 'delay_ms': 50,17 'success_rate': 0.9518 },19 'trading': {20 'symbol': 'AAPL',21 'timeframe': '1min',22 'capital': 10000023 },24 'risk': {25 'max_position': 100,26 'max_drawdown': 0.0527 }28 }29 30 @pytest.fixture31 def execution_agent(self, config):32 """Create an ExecutionAgent instance"""33 return ExecutionAgent(config)34 35 @pytest.fixture36 def valid_signal(self):37 """Create a valid trading signal"""38 return {39 'action': 'buy',40 'symbol': 'AAPL',41 'quantity': 10,42 'price': 150.0,43 'confidence': 0.844 }45 46 def test_initialization(self, execution_agent, config):47 """Test agent initialization"""48 assert execution_agent.broker_api == config['execution']['broker_api']49 assert execution_agent.order_size == config['execution']['order_size']50 assert execution_agent.execution_delay == config['execution']['delay_ms']51 assert execution_agent.success_rate == config['execution']['success_rate']52 53 def test_act_with_valid_signal(self, execution_agent, valid_signal):54 """Test order execution with valid signal"""55 result = execution_agent.act(valid_signal)56 57 # Check result structure58 assert isinstance(result, dict)59 assert 'order_id' in result60 assert 'status' in result61 assert 'action' in result62 assert 'symbol' in result63 assert 'quantity' in result64 assert 'price' in result65 assert 'execution_time' in result66 assert 'commission' in result67 assert 'total_value' in result68 assert 'success' in result69 assert 'error' in result70 71 # Check values72 assert result['action'] == valid_signal['action']73 assert result['symbol'] == valid_signal['symbol']74 assert result['quantity'] == valid_signal['quantity']75 assert result['price'] > 076 assert result['execution_time'] > 077 assert result['commission'] >= 078 assert result['total_value'] >= 079 80 def test_act_with_hold_signal(self, execution_agent):81 """Test order execution with hold signal"""82 hold_signal = {83 'action': 'hold',84 'symbol': 'AAPL',85 'quantity': 0,86 'price': 0,87 'confidence': 0.088 }89 90 result = execution_agent.act(hold_signal)91 92 assert result['action'] == 'hold'93 assert result['quantity'] == 094 assert result['success'] == True # Hold should always succeed95 96 def test_validate_signal_valid(self, execution_agent, valid_signal):97 """Test signal validation with valid signal"""98 assert execution_agent._validate_signal(valid_signal) == True99 100 def test_validate_signal_missing_fields(self, execution_agent):101 """Test signal validation with missing fields"""102 invalid_signal = {'action': 'buy'} # Missing symbol and quantity103 104 assert execution_agent._validate_signal(invalid_signal) == False105 106 def test_validate_signal_invalid_action(self, execution_agent):107 """Test signal validation with invalid action"""108 invalid_signal = {109 'action': 'invalid_action',110 'symbol': 'AAPL',111 'quantity': 10112 }113 114 assert execution_agent._validate_signal(invalid_signal) == False115 116 def test_validate_signal_invalid_quantity(self, execution_agent):117 """Test signal validation with invalid quantity"""118 invalid_signal = {119 'action': 'buy',120 'symbol': 'AAPL',121 'quantity': -5 # Negative quantity122 }123 124 assert execution_agent._validate_signal(invalid_signal) == False125 126 def test_validate_signal_invalid_symbol(self, execution_agent):127 """Test signal validation with invalid symbol"""128 invalid_signal = {129 'action': 'buy',130 'symbol': '', # Empty symbol131 'quantity': 10132 }133 134 assert execution_agent._validate_signal(invalid_signal) == False135 136 def test_calculate_commission(self, execution_agent):137 """Test commission calculation"""138 # Test buy order139 buy_signal = {'action': 'buy', 'quantity': 10}140 commission_buy = execution_agent._calculate_commission(buy_signal)141 142 # Base commission ($1) + per share commission ($0.01 * 10) = $1.10143 expected_commission = 1.0 + (10 * 0.01)144 assert commission_buy == expected_commission145 146 # Test sell order147 sell_signal = {'action': 'sell', 'quantity': 5}148 commission_sell = execution_agent._calculate_commission(sell_signal)149 150 expected_commission = 1.0 + (5 * 0.01)151 assert commission_sell == expected_commission152 153 # Test hold order (no commission)154 hold_signal = {'action': 'hold', 'quantity': 0}155 commission_hold = execution_agent._calculate_commission(hold_signal)156 157 assert commission_hold == 0.0158 159 def test_generate_order_id(self, execution_agent):160 """Test order ID generation"""161 order_id = execution_agent._generate_order_id()162 163 assert isinstance(order_id, str)164 assert order_id.startswith('ORD_')165 assert len(order_id) == 12 # 'ORD_' + 8 hex characters166 167 def test_simulate_successful_execution(self, execution_agent, valid_signal):168 """Test successful execution simulation"""169 result = execution_agent._simulate_successful_execution(valid_signal)170 171 assert result['status'] == 'filled'172 assert result['success'] == True173 assert result['error'] is None174 assert result['order_id'] is not None175 assert result['price'] > 0176 assert result['total_value'] > 0177 178 def test_simulate_failed_execution(self, execution_agent, valid_signal):179 """Test failed execution simulation"""180 result = execution_agent._simulate_failed_execution(valid_signal)181 182 assert result['status'] == 'rejected'183 assert result['success'] == False184 assert result['error'] is not None185 assert result['order_id'] is None186 assert result['price'] == 0187 assert result['total_value'] == 0188 189 def test_generate_execution_result(self, execution_agent, valid_signal):190 """Test execution result generation"""191 # Test successful result192 success_result = execution_agent._generate_execution_result(valid_signal, True)193 194 assert success_result['status'] == 'filled'195 assert success_result['success'] == True196 assert success_result['order_id'] is not None197 198 # Test failed result199 failed_result = execution_agent._generate_execution_result(valid_signal, False, "Test error")200 201 assert failed_result['status'] == 'rejected'202 assert failed_result['success'] == False203 assert failed_result['error'] == "Test error"204 assert failed_result['order_id'] is None205 206 def test_execution_delay(self, execution_agent, valid_signal):207 """Test that execution delay is applied"""208 start_time = time.time()209 210 with patch('time.sleep') as mock_sleep:211 execution_agent._execute_order(valid_signal)212 mock_sleep.assert_called_once()213 214 # Check that sleep was called with the correct delay215 call_args = mock_sleep.call_args[0][0]216 expected_delay = execution_agent.execution_delay / 1000.0217 assert abs(call_args - expected_delay) < 0.001218 219 def test_success_rate_simulation(self, execution_agent, valid_signal):220 """Test success rate simulation"""221 # Set success rate to 0.0 (should always fail)222 execution_agent.success_rate = 0.0223 224 with patch('random.random', return_value=0.5): # Always above 0.0225 result = execution_agent._execute_order(valid_signal)226 assert result['success'] == False227 228 # Set success rate to 1.0 (should always succeed)229 execution_agent.success_rate = 1.0230 231 with patch('random.random', return_value=0.5): # Always below 1.0232 result = execution_agent._execute_order(valid_signal)233 assert result['success'] == True234 235 def test_error_handling_in_execution(self, execution_agent, valid_signal):236 """Test error handling during execution"""237 # Mock _simulate_successful_execution to raise an exception238 with patch.object(execution_agent, '_simulate_successful_execution', side_effect=Exception("Test error")):239 result = execution_agent._execute_order(valid_signal)240 241 assert result['success'] == False242 assert "Test error" in result['error']243 244 def test_get_execution_statistics(self, execution_agent):245 """Test execution statistics retrieval"""246 stats = execution_agent.get_execution_statistics()247 248 expected_keys = [249 'total_orders', 'successful_orders', 'failed_orders',250 'success_rate', 'average_execution_time', 'total_commission'251 ]252 253 for key in expected_keys:254 assert key in stats255 256 # Check default values257 assert stats['total_orders'] == 0258 assert stats['successful_orders'] == 0259 assert stats['failed_orders'] == 0260 assert stats['success_rate'] == 0.0261 assert stats['average_execution_time'] == 0.0262 assert stats['total_commission'] == 0.0263 264 def test_price_slippage_simulation(self, execution_agent, valid_signal):265 """Test price slippage simulation"""266 # Mock random.uniform to return a known slippage value267 with patch('random.uniform', return_value=0.001): # 0.1% slippage268 result = execution_agent._simulate_successful_execution(valid_signal)269 270 # Price should be slightly different from original271 original_price = valid_signal['price']272 executed_price = result['price']273 274 # Should be within 0.2% of original price275 price_diff = abs(executed_price - original_price) / original_price276 assert price_diff <= 0.002277 278 def test_commission_calculation_edge_cases(self, execution_agent):279 """Test commission calculation edge cases"""280 # Test with zero quantity281 zero_signal = {'action': 'buy', 'quantity': 0}282 commission_zero = execution_agent._calculate_commission(zero_signal)283 assert commission_zero == 1.0 # Only base commission284 285 # Test with very large quantity286 large_signal = {'action': 'sell', 'quantity': 10000}287 commission_large = execution_agent._calculate_commission(large_signal)288 expected_large = 1.0 + (10000 * 0.01)289 assert commission_large == expected_large290 291 def test_order_id_uniqueness(self, execution_agent):292 """Test that order IDs are unique"""293 order_ids = set()294 295 for _ in range(100):296 order_id = execution_agent._generate_order_id()297 order_ids.add(order_id)298 299 # All order IDs should be unique300 assert len(order_ids) == 100 