Edwin Salguero
Initial commit: Enhanced Algorithmic Trading System with Synthetic Data Generation, Comprehensive Logging, and Extensive Testing
859af74
import pytest | |
import time | |
from unittest.mock import patch, MagicMock | |
from agentic_ai_system.execution_agent import ExecutionAgent | |
class TestExecutionAgent: | |
"""Test cases for ExecutionAgent""" | |
def config(self): | |
"""Sample configuration for testing""" | |
return { | |
'execution': { | |
'broker_api': 'paper', | |
'order_size': 10, | |
'delay_ms': 50, | |
'success_rate': 0.95 | |
}, | |
'trading': { | |
'symbol': 'AAPL', | |
'timeframe': '1min', | |
'capital': 100000 | |
}, | |
'risk': { | |
'max_position': 100, | |
'max_drawdown': 0.05 | |
} | |
} | |
def execution_agent(self, config): | |
"""Create an ExecutionAgent instance""" | |
return ExecutionAgent(config) | |
def valid_signal(self): | |
"""Create a valid trading signal""" | |
return { | |
'action': 'buy', | |
'symbol': 'AAPL', | |
'quantity': 10, | |
'price': 150.0, | |
'confidence': 0.8 | |
} | |
def test_initialization(self, execution_agent, config): | |
"""Test agent initialization""" | |
assert execution_agent.broker_api == config['execution']['broker_api'] | |
assert execution_agent.order_size == config['execution']['order_size'] | |
assert execution_agent.execution_delay == config['execution']['delay_ms'] | |
assert execution_agent.success_rate == config['execution']['success_rate'] | |
def test_act_with_valid_signal(self, execution_agent, valid_signal): | |
"""Test order execution with valid signal""" | |
result = execution_agent.act(valid_signal) | |
# Check result structure | |
assert isinstance(result, dict) | |
assert 'order_id' in result | |
assert 'status' in result | |
assert 'action' in result | |
assert 'symbol' in result | |
assert 'quantity' in result | |
assert 'price' in result | |
assert 'execution_time' in result | |
assert 'commission' in result | |
assert 'total_value' in result | |
assert 'success' in result | |
assert 'error' in result | |
# Check values | |
assert result['action'] == valid_signal['action'] | |
assert result['symbol'] == valid_signal['symbol'] | |
assert result['quantity'] == valid_signal['quantity'] | |
assert result['price'] > 0 | |
assert result['execution_time'] > 0 | |
assert result['commission'] >= 0 | |
assert result['total_value'] >= 0 | |
def test_act_with_hold_signal(self, execution_agent): | |
"""Test order execution with hold signal""" | |
hold_signal = { | |
'action': 'hold', | |
'symbol': 'AAPL', | |
'quantity': 0, | |
'price': 0, | |
'confidence': 0.0 | |
} | |
result = execution_agent.act(hold_signal) | |
assert result['action'] == 'hold' | |
assert result['quantity'] == 0 | |
assert result['success'] == True # Hold should always succeed | |
def test_validate_signal_valid(self, execution_agent, valid_signal): | |
"""Test signal validation with valid signal""" | |
assert execution_agent._validate_signal(valid_signal) == True | |
def test_validate_signal_missing_fields(self, execution_agent): | |
"""Test signal validation with missing fields""" | |
invalid_signal = {'action': 'buy'} # Missing symbol and quantity | |
assert execution_agent._validate_signal(invalid_signal) == False | |
def test_validate_signal_invalid_action(self, execution_agent): | |
"""Test signal validation with invalid action""" | |
invalid_signal = { | |
'action': 'invalid_action', | |
'symbol': 'AAPL', | |
'quantity': 10 | |
} | |
assert execution_agent._validate_signal(invalid_signal) == False | |
def test_validate_signal_invalid_quantity(self, execution_agent): | |
"""Test signal validation with invalid quantity""" | |
invalid_signal = { | |
'action': 'buy', | |
'symbol': 'AAPL', | |
'quantity': -5 # Negative quantity | |
} | |
assert execution_agent._validate_signal(invalid_signal) == False | |
def test_validate_signal_invalid_symbol(self, execution_agent): | |
"""Test signal validation with invalid symbol""" | |
invalid_signal = { | |
'action': 'buy', | |
'symbol': '', # Empty symbol | |
'quantity': 10 | |
} | |
assert execution_agent._validate_signal(invalid_signal) == False | |
def test_calculate_commission(self, execution_agent): | |
"""Test commission calculation""" | |
# Test buy order | |
buy_signal = {'action': 'buy', 'quantity': 10} | |
commission_buy = execution_agent._calculate_commission(buy_signal) | |
# Base commission ($1) + per share commission ($0.01 * 10) = $1.10 | |
expected_commission = 1.0 + (10 * 0.01) | |
assert commission_buy == expected_commission | |
# Test sell order | |
sell_signal = {'action': 'sell', 'quantity': 5} | |
commission_sell = execution_agent._calculate_commission(sell_signal) | |
expected_commission = 1.0 + (5 * 0.01) | |
assert commission_sell == expected_commission | |
# Test hold order (no commission) | |
hold_signal = {'action': 'hold', 'quantity': 0} | |
commission_hold = execution_agent._calculate_commission(hold_signal) | |
assert commission_hold == 0.0 | |
def test_generate_order_id(self, execution_agent): | |
"""Test order ID generation""" | |
order_id = execution_agent._generate_order_id() | |
assert isinstance(order_id, str) | |
assert order_id.startswith('ORD_') | |
assert len(order_id) == 12 # 'ORD_' + 8 hex characters | |
def test_simulate_successful_execution(self, execution_agent, valid_signal): | |
"""Test successful execution simulation""" | |
result = execution_agent._simulate_successful_execution(valid_signal) | |
assert result['status'] == 'filled' | |
assert result['success'] == True | |
assert result['error'] is None | |
assert result['order_id'] is not None | |
assert result['price'] > 0 | |
assert result['total_value'] > 0 | |
def test_simulate_failed_execution(self, execution_agent, valid_signal): | |
"""Test failed execution simulation""" | |
result = execution_agent._simulate_failed_execution(valid_signal) | |
assert result['status'] == 'rejected' | |
assert result['success'] == False | |
assert result['error'] is not None | |
assert result['order_id'] is None | |
assert result['price'] == 0 | |
assert result['total_value'] == 0 | |
def test_generate_execution_result(self, execution_agent, valid_signal): | |
"""Test execution result generation""" | |
# Test successful result | |
success_result = execution_agent._generate_execution_result(valid_signal, True) | |
assert success_result['status'] == 'filled' | |
assert success_result['success'] == True | |
assert success_result['order_id'] is not None | |
# Test failed result | |
failed_result = execution_agent._generate_execution_result(valid_signal, False, "Test error") | |
assert failed_result['status'] == 'rejected' | |
assert failed_result['success'] == False | |
assert failed_result['error'] == "Test error" | |
assert failed_result['order_id'] is None | |
def test_execution_delay(self, execution_agent, valid_signal): | |
"""Test that execution delay is applied""" | |
start_time = time.time() | |
with patch('time.sleep') as mock_sleep: | |
execution_agent._execute_order(valid_signal) | |
mock_sleep.assert_called_once() | |
# Check that sleep was called with the correct delay | |
call_args = mock_sleep.call_args[0][0] | |
expected_delay = execution_agent.execution_delay / 1000.0 | |
assert abs(call_args - expected_delay) < 0.001 | |
def test_success_rate_simulation(self, execution_agent, valid_signal): | |
"""Test success rate simulation""" | |
# Set success rate to 0.0 (should always fail) | |
execution_agent.success_rate = 0.0 | |
with patch('random.random', return_value=0.5): # Always above 0.0 | |
result = execution_agent._execute_order(valid_signal) | |
assert result['success'] == False | |
# Set success rate to 1.0 (should always succeed) | |
execution_agent.success_rate = 1.0 | |
with patch('random.random', return_value=0.5): # Always below 1.0 | |
result = execution_agent._execute_order(valid_signal) | |
assert result['success'] == True | |
def test_error_handling_in_execution(self, execution_agent, valid_signal): | |
"""Test error handling during execution""" | |
# Mock _simulate_successful_execution to raise an exception | |
with patch.object(execution_agent, '_simulate_successful_execution', side_effect=Exception("Test error")): | |
result = execution_agent._execute_order(valid_signal) | |
assert result['success'] == False | |
assert "Test error" in result['error'] | |
def test_get_execution_statistics(self, execution_agent): | |
"""Test execution statistics retrieval""" | |
stats = execution_agent.get_execution_statistics() | |
expected_keys = [ | |
'total_orders', 'successful_orders', 'failed_orders', | |
'success_rate', 'average_execution_time', 'total_commission' | |
] | |
for key in expected_keys: | |
assert key in stats | |
# Check default values | |
assert stats['total_orders'] == 0 | |
assert stats['successful_orders'] == 0 | |
assert stats['failed_orders'] == 0 | |
assert stats['success_rate'] == 0.0 | |
assert stats['average_execution_time'] == 0.0 | |
assert stats['total_commission'] == 0.0 | |
def test_price_slippage_simulation(self, execution_agent, valid_signal): | |
"""Test price slippage simulation""" | |
# Mock random.uniform to return a known slippage value | |
with patch('random.uniform', return_value=0.001): # 0.1% slippage | |
result = execution_agent._simulate_successful_execution(valid_signal) | |
# Price should be slightly different from original | |
original_price = valid_signal['price'] | |
executed_price = result['price'] | |
# Should be within 0.2% of original price | |
price_diff = abs(executed_price - original_price) / original_price | |
assert price_diff <= 0.002 | |
def test_commission_calculation_edge_cases(self, execution_agent): | |
"""Test commission calculation edge cases""" | |
# Test with zero quantity | |
zero_signal = {'action': 'buy', 'quantity': 0} | |
commission_zero = execution_agent._calculate_commission(zero_signal) | |
assert commission_zero == 1.0 # Only base commission | |
# Test with very large quantity | |
large_signal = {'action': 'sell', 'quantity': 10000} | |
commission_large = execution_agent._calculate_commission(large_signal) | |
expected_large = 1.0 + (10000 * 0.01) | |
assert commission_large == expected_large | |
def test_order_id_uniqueness(self, execution_agent): | |
"""Test that order IDs are unique""" | |
order_ids = set() | |
for _ in range(100): | |
order_id = execution_agent._generate_order_id() | |
order_ids.add(order_id) | |
# All order IDs should be unique | |
assert len(order_ids) == 100 |