Files
crypto_trader/tests/unit/autopilot/test_intelligent_autopilot.py

176 lines
6.4 KiB
Python

"""Tests for intelligent autopilot functionality."""
import pytest
from decimal import Decimal
from unittest.mock import Mock, patch, AsyncMock
from src.core.database import OrderSide, OrderType
class TestPreFlightValidation:
"""Tests for pre-flight order validation."""
@pytest.fixture
def mock_autopilot(self):
"""Create mock autopilot with necessary attributes."""
from src.autopilot.intelligent_autopilot import IntelligentAutopilot
with patch.object(IntelligentAutopilot, '__init__', lambda x, *args, **kwargs: None):
autopilot = IntelligentAutopilot.__new__(IntelligentAutopilot)
autopilot.symbol = 'BTC/USD'
autopilot.paper_trading = True
autopilot.logger = Mock()
# Mock trading engine
autopilot.trading_engine = Mock()
autopilot.trading_engine.paper_trading = Mock()
autopilot.trading_engine.paper_trading.get_balance.return_value = Decimal('1000.0')
autopilot.trading_engine.paper_trading.get_positions.return_value = []
return autopilot
@pytest.mark.asyncio
async def test_can_execute_order_insufficient_funds(self, mock_autopilot):
"""Test that insufficient funds returns False."""
mock_autopilot.trading_engine.paper_trading.get_balance.return_value = Decimal('10.0')
can_execute, reason = await mock_autopilot._can_execute_order(
side=OrderSide.BUY,
quantity=Decimal('1.0'),
price=Decimal('100.0')
)
assert can_execute is False
assert 'Insufficient funds' in reason
@pytest.mark.asyncio
async def test_can_execute_order_sufficient_funds(self, mock_autopilot):
"""Test that sufficient funds returns True."""
mock_autopilot.trading_engine.paper_trading.get_balance.return_value = Decimal('1000.0')
can_execute, reason = await mock_autopilot._can_execute_order(
side=OrderSide.BUY,
quantity=Decimal('1.0'),
price=Decimal('100.0')
)
assert can_execute is True
assert reason == 'OK'
@pytest.mark.asyncio
async def test_can_execute_order_no_position_for_sell(self, mock_autopilot):
"""Test that SELL without position returns False."""
mock_autopilot.trading_engine.paper_trading.get_positions.return_value = []
can_execute, reason = await mock_autopilot._can_execute_order(
side=OrderSide.SELL,
quantity=Decimal('1.0'),
price=Decimal('100.0')
)
assert can_execute is False
assert 'No position to sell' in reason
@pytest.mark.asyncio
async def test_can_execute_order_minimum_value(self, mock_autopilot):
"""Test that order below minimum value returns False."""
can_execute, reason = await mock_autopilot._can_execute_order(
side=OrderSide.BUY,
quantity=Decimal('0.001'),
price=Decimal('0.10') # Order value = $0.0001
)
assert can_execute is False
assert 'below minimum' in reason
class TestSmartOrderTypeSelection:
"""Tests for smart order type selection."""
@pytest.fixture
def mock_autopilot(self):
"""Create mock autopilot for order type tests."""
from src.autopilot.intelligent_autopilot import IntelligentAutopilot
with patch.object(IntelligentAutopilot, '__init__', lambda x, *args, **kwargs: None):
autopilot = IntelligentAutopilot.__new__(IntelligentAutopilot)
autopilot.logger = Mock()
return autopilot
def test_strong_signal_uses_market(self, mock_autopilot):
"""Test that strong signals (>80%) use MARKET orders."""
order_type, price = mock_autopilot._determine_order_type_and_price(
side=OrderSide.BUY,
signal_strength=0.85,
current_price=Decimal('100.0'),
is_stop_loss=False
)
assert order_type == OrderType.MARKET
assert price is None
def test_normal_signal_uses_limit(self, mock_autopilot):
"""Test that normal signals use LIMIT orders."""
order_type, price = mock_autopilot._determine_order_type_and_price(
side=OrderSide.BUY,
signal_strength=0.65,
current_price=Decimal('100.0'),
is_stop_loss=False
)
assert order_type == OrderType.LIMIT
assert price is not None
# BUY limit should be below market
assert price < Decimal('100.0')
def test_stop_loss_uses_market(self, mock_autopilot):
"""Test that stop-loss exits use MARKET orders."""
order_type, price = mock_autopilot._determine_order_type_and_price(
side=OrderSide.SELL,
signal_strength=0.5,
current_price=Decimal('100.0'),
is_stop_loss=True
)
assert order_type == OrderType.MARKET
assert price is None
def test_take_profit_uses_limit(self, mock_autopilot):
"""Test that take-profit exits can use LIMIT orders."""
order_type, price = mock_autopilot._determine_order_type_and_price(
side=OrderSide.SELL,
signal_strength=0.6,
current_price=Decimal('100.0'),
is_stop_loss=False
)
assert order_type == OrderType.LIMIT
assert price is not None
# SELL limit should be above market
assert price > Decimal('100.0')
def test_buy_limit_price_discount(self, mock_autopilot):
"""Test BUY LIMIT price is 0.1% below market."""
order_type, price = mock_autopilot._determine_order_type_and_price(
side=OrderSide.BUY,
signal_strength=0.6,
current_price=Decimal('1000.00'),
is_stop_loss=False
)
# 0.1% discount = 999.00
expected = Decimal('999.00')
assert price == expected
def test_sell_limit_price_premium(self, mock_autopilot):
"""Test SELL LIMIT price is 0.1% above market."""
order_type, price = mock_autopilot._determine_order_type_and_price(
side=OrderSide.SELL,
signal_strength=0.6,
current_price=Decimal('1000.00'),
is_stop_loss=False
)
# 0.1% premium = 1001.00
expected = Decimal('1001.00')
assert price == expected