summaryrefslogtreecommitdiff
path: root/test_trade_queue.py
blob: 70a795af739554386df0a86ad2d4e679add74159 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
from decimal import Decimal
import unittest
from datetime import datetime

from trade_queue import FIFOQueue


class TestFIFOQueue(unittest.TestCase):
    def setUp(self):
        """
        Set up a FIFOQueue instance and some test trades.
        """
        self.queue = FIFOQueue()
        self.queue.add(Decimal(10.0), Decimal(100.0), "2025-04-14")
        self.queue.add(Decimal(20.0), Decimal(200.0), "2025-04-15")
        self.queue.add(Decimal(30.0), Decimal(300.0), "2025-04-16")

    def test_add(self):
        """
        Test adding trades to the queue.
        """
        tq = self.queue.get_copy()
        self.assertEqual(len(tq), 3)  # There should be 3 trades in the queue
        self.assertEqual(tq[0].amount, 10.0)  # Check the first trade's amount
        self.assertEqual(tq[1].date, "2025-04-15")  # Check the second trade's date

    def test_remove_exact_amount(self):
        """
        Test removing an exact amount from the queue.
        """
        trades = self.queue.remove_coins(10.0)
        self.assertEqual(len(trades), 1)  # One trade should be returned
        self.assertEqual(trades[0].amount, 10.0)  # Amount should match the request
        tq = self.queue.get_copy()
        self.assertEqual(len(tq), 2)  # Two trades should remain in the queue

    def test_remove_partial_trade(self):
        """
        Test removing an amount that partially consumes a trade.
        """
        trades = self.queue.remove_coins(5.0)
        self.assertEqual(len(trades), 1)  # One partial trade should be returned
        self.assertEqual(trades[0].amount, 5.0)  # Amount should match the request
        tq = self.queue.get_copy()
        self.assertEqual(tq[0].amount, 5.0)  # Remaining trade amount should update

    def test_remove_multiple_trades(self):
        """
        Test removing an amount that spans multiple trades.
        """
        trades = self.queue.remove_coins(25.0)
        self.assertEqual(len(trades), 2)  # Two trades should be returned
        # The first trade should be fully consumed
        self.assertEqual(trades[0].amount, 10.0)
        # The second trade should be partially consumed
        self.assertEqual(trades[1].amount, 15.0)
        tq = self.queue.get_copy()
        self.assertEqual(tq[0].amount, 5.0)  # Remaining trade in queue should update

    def test_remove_insufficient_amount(self):
        """
        Test trying to remove more than is available in the queue.
        """
        with self.assertRaises(ValueError):
            self.queue.remove_coins(100.0)  # This should raise an exception

    def test_remove_negative_amount(self):
        """
        Test trying to remove a negative amount.
        """
        with self.assertRaises(ValueError):
            self.queue.remove_coins(-5.0)  # This should raise an exception

    def test_get_remaining_amount_initial(self):
        """
        Test the remaining amount in the queue after adding trades.
        """
        # Total of all amounts: 10 + 20 + 30
        self.assertEqual(self.queue.get_remaining_amount(), 60.0)

    def test_get_remaining_amount_after_removal(self):
        """
        Test the remaining amount after removing some assets.
        """
        self.queue.remove_coins(15.0)  # Remove 15 assets
        # Remaining: 60 - 15
        self.assertEqual(self.queue.get_remaining_amount(), 45.0)

    def test_get_remaining_amount_empty_queue(self):
        """
        Test the remaining amount in an empty queue.
        """
        empty_queue = FIFOQueue()  # New empty queue
        self.assertEqual(empty_queue.get_remaining_amount(), 0.0)  # No trades in queue

    def test_get_remaining_amount_partial_removal(self):
        """
        Test the remaining amount after partially consuming a trade.
        """
        self.queue.remove_coins(5.0)  # Remove 5 assets, leaving 5 in the first trade
        self.assertEqual(self.queue.get_remaining_amount(), 55.0)  # Remaining: 60 - 5

    def test_get_remaining_amount_full_removal(self):
        """
        Test the remaining amount after removing all trades.
        """
        self.queue.remove_coins(60.0)  # Remove all assets
        self.assertEqual(self.queue.get_remaining_amount(), 0.0)  # Remaining: 0

    def test_remove_partial_trade_correct_cost(self):
        """
        Test removing a partial trade and ensure the correct cost is calculated.
        """
        trades = self.queue.remove_coins(4.0)  # Remove 4 COIN from the first trade
        self.assertEqual(len(trades), 1)  # Only one trade should be returned

        # Coin-cost needs to stay constant
        self.assertEqual(trades[0].price_per_coin, 10)
        self.assertEqual(trades[0].amount, 4.0)  # Check the removed amount
        # Total cost should be proportional: (100 * 5 / 10)
        self.assertEqual(trades[0].total_cost, 40.0)

        tq = self.queue.get_copy()
        # Original total cost remains unchanged
        self.assertEqual(tq[0].price_per_coin, 10)
        # Remaining amount in the first trade should be updated
        self.assertEqual(tq[0].amount, 6.0)
        # Original total cost remains unchanged
        self.assertEqual(tq[0].total_cost, 60.0)


if __name__ == "__main__":
    unittest.main()