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

176 lines
6.4 KiB
Python
Raw Normal View History

"""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