TradingAgents/tests/portfolio/test_orders.py

220 lines
7.5 KiB
Python

"""
Tests for order classes.
"""
import unittest
from decimal import Decimal
from datetime import datetime
from tradingagents.portfolio import (
MarketOrder,
LimitOrder,
StopLossOrder,
TakeProfitOrder,
OrderStatus,
OrderSide,
create_order_from_dict,
)
from tradingagents.portfolio.exceptions import (
InvalidOrderError,
InvalidPriceError,
InvalidQuantityError,
)
class TestMarketOrder(unittest.TestCase):
"""Test cases for MarketOrder."""
def test_create_buy_order(self):
"""Test creating a buy market order."""
order = MarketOrder('AAPL', Decimal('100'))
self.assertEqual(order.ticker, 'AAPL')
self.assertEqual(order.quantity, Decimal('100'))
self.assertTrue(order.is_buy)
self.assertFalse(order.is_sell)
self.assertEqual(order.side, OrderSide.BUY)
self.assertEqual(order.status, OrderStatus.PENDING)
def test_create_sell_order(self):
"""Test creating a sell market order."""
order = MarketOrder('AAPL', Decimal('-50'))
self.assertEqual(order.quantity, Decimal('-50'))
self.assertFalse(order.is_buy)
self.assertTrue(order.is_sell)
self.assertEqual(order.side, OrderSide.SELL)
def test_zero_quantity_rejected(self):
"""Test that zero quantity is rejected."""
with self.assertRaises(InvalidQuantityError):
MarketOrder('AAPL', Decimal('0'))
def test_can_execute(self):
"""Test that market orders can always execute."""
order = MarketOrder('AAPL', Decimal('100'))
self.assertTrue(order.can_execute(Decimal('150.00')))
self.assertTrue(order.can_execute(Decimal('100.00')))
self.assertTrue(order.can_execute(Decimal('200.00')))
def test_mark_executed(self):
"""Test marking an order as executed."""
order = MarketOrder('AAPL', Decimal('100'))
order.mark_executed(Decimal('100'), Decimal('150.00'))
self.assertEqual(order.status, OrderStatus.EXECUTED)
self.assertEqual(order.filled_quantity, Decimal('100'))
self.assertEqual(order.filled_price, Decimal('150.00'))
self.assertIsNotNone(order.executed_at)
self.assertTrue(order.is_filled)
def test_partial_fill(self):
"""Test partial order fill."""
order = MarketOrder('AAPL', Decimal('100'))
order.mark_executed(Decimal('50'), Decimal('150.00'))
self.assertEqual(order.status, OrderStatus.PARTIALLY_FILLED)
self.assertTrue(order.is_partially_filled)
self.assertFalse(order.is_filled)
def test_cannot_execute_twice(self):
"""Test that executed order cannot be executed again."""
order = MarketOrder('AAPL', Decimal('100'))
order.mark_executed(Decimal('100'), Decimal('150.00'))
with self.assertRaises(InvalidOrderError):
order.mark_executed(Decimal('100'), Decimal('151.00'))
def test_cancel_order(self):
"""Test cancelling an order."""
order = MarketOrder('AAPL', Decimal('100'))
order.cancel()
self.assertEqual(order.status, OrderStatus.CANCELLED)
def test_cannot_cancel_executed_order(self):
"""Test that executed orders cannot be cancelled."""
order = MarketOrder('AAPL', Decimal('100'))
order.mark_executed(Decimal('100'), Decimal('150.00'))
with self.assertRaises(InvalidOrderError):
order.cancel()
class TestLimitOrder(unittest.TestCase):
"""Test cases for LimitOrder."""
def test_create_buy_limit_order(self):
"""Test creating a buy limit order."""
order = LimitOrder('AAPL', Decimal('100'), limit_price=Decimal('150.00'))
self.assertEqual(order.limit_price, Decimal('150.00'))
self.assertTrue(order.is_buy)
def test_missing_limit_price_rejected(self):
"""Test that limit orders require limit_price."""
with self.assertRaises(InvalidOrderError):
LimitOrder('AAPL', Decimal('100'))
def test_buy_limit_can_execute(self):
"""Test buy limit order execution logic."""
order = LimitOrder('AAPL', Decimal('100'), limit_price=Decimal('150.00'))
# Can execute at or below limit
self.assertTrue(order.can_execute(Decimal('150.00')))
self.assertTrue(order.can_execute(Decimal('149.00')))
# Cannot execute above limit
self.assertFalse(order.can_execute(Decimal('151.00')))
def test_sell_limit_can_execute(self):
"""Test sell limit order execution logic."""
order = LimitOrder('AAPL', Decimal('-100'), limit_price=Decimal('150.00'))
# Can execute at or above limit
self.assertTrue(order.can_execute(Decimal('150.00')))
self.assertTrue(order.can_execute(Decimal('151.00')))
# Cannot execute below limit
self.assertFalse(order.can_execute(Decimal('149.00')))
class TestStopLossOrder(unittest.TestCase):
"""Test cases for StopLossOrder."""
def test_create_stop_loss_order(self):
"""Test creating a stop-loss order."""
order = StopLossOrder('AAPL', Decimal('-100'), stop_price=Decimal('145.00'))
self.assertEqual(order.stop_price, Decimal('145.00'))
def test_stop_loss_trigger_for_long_position(self):
"""Test stop-loss trigger for closing long position."""
order = StopLossOrder('AAPL', Decimal('-100'), stop_price=Decimal('145.00'))
# Triggers at or below stop price
self.assertTrue(order.can_execute(Decimal('145.00')))
self.assertTrue(order.can_execute(Decimal('144.00')))
# Does not trigger above stop price
self.assertFalse(order.can_execute(Decimal('146.00')))
class TestTakeProfitOrder(unittest.TestCase):
"""Test cases for TakeProfitOrder."""
def test_create_take_profit_order(self):
"""Test creating a take-profit order."""
order = TakeProfitOrder('AAPL', Decimal('-100'), target_price=Decimal('160.00'))
self.assertEqual(order.target_price, Decimal('160.00'))
def test_take_profit_trigger_for_long_position(self):
"""Test take-profit trigger for closing long position."""
order = TakeProfitOrder('AAPL', Decimal('-100'), target_price=Decimal('160.00'))
# Triggers at or above target price
self.assertTrue(order.can_execute(Decimal('160.00')))
self.assertTrue(order.can_execute(Decimal('161.00')))
# Does not trigger below target price
self.assertFalse(order.can_execute(Decimal('159.00')))
class TestOrderSerialization(unittest.TestCase):
"""Test order serialization and deserialization."""
def test_market_order_to_dict(self):
"""Test market order serialization."""
order = MarketOrder('AAPL', Decimal('100'))
data = order.to_dict()
self.assertEqual(data['ticker'], 'AAPL')
self.assertEqual(data['quantity'], '100')
self.assertEqual(data['order_type'], 'market')
def test_limit_order_to_dict(self):
"""Test limit order serialization."""
order = LimitOrder('AAPL', Decimal('100'), limit_price=Decimal('150.00'))
data = order.to_dict()
self.assertEqual(data['limit_price'], '150.00')
def test_create_order_from_dict(self):
"""Test creating order from dictionary."""
order = MarketOrder('AAPL', Decimal('100'))
data = order.to_dict()
restored = create_order_from_dict(data)
self.assertIsInstance(restored, MarketOrder)
self.assertEqual(restored.ticker, order.ticker)
self.assertEqual(restored.quantity, order.quantity)
if __name__ == '__main__':
unittest.main()