def test_op_add(self):
     inv1 = Inventory.from_string('17.00 USD')
     orig_inv1 = Inventory.from_string('17.00 USD')
     inv2 = Inventory.from_string('21.00 CAD')
     inv3 = inv1 + inv2
     self.assertEqual(Inventory.from_string('17.00 USD, 21.00 CAD'), inv3)
     self.assertEqual(orig_inv1, inv1)
 def test_update(self):
     inv1 = Inventory.from_string('11 USD')
     inv2 = Inventory.from_string('12 CAD')
     inv_updated = inv1.add_inventory(inv2)
     expect_updated = Inventory.from_string('11 USD, 12 CAD')
     self.assertEqual(expect_updated, inv_updated)
     self.assertEqual(expect_updated, inv1)
    def test_currency_pairs(self):
        inv = Inventory()
        self.assertEqual(set(), inv.currency_pairs())

        inv = Inventory.from_string('40 USD {1.01 CAD}, 40 USD')
        self.assertEqual(set([('USD', 'CAD'), ('USD', None)]), inv.currency_pairs())

        inv = Inventory.from_string('40 AAPL {1.01 USD}, 10 HOOL {2.02 USD}')
        self.assertEqual(set([('AAPL', 'USD'), ('HOOL', 'USD')]), inv.currency_pairs())
    def test_op_neg(self):
        inv = Inventory()
        inv.add_amount(A('10 USD'))
        ninv = -inv
        self.checkAmount(ninv, '-10', 'USD')

        pinv = Inventory.from_string('1.50 JPY, 1.51 USD, 1.52 CAD')
        ninv = Inventory.from_string('-1.50 JPY, -1.51 USD, -1.52 CAD')
        self.assertEqual(pinv, -ninv)
    def test_is_mixed(self):
        inv = Inventory.from_string('100 HOOL {250 USD}, 101 HOOL {251 USD}')
        self.assertFalse(inv.is_mixed())

        inv = Inventory.from_string('100 HOOL {250 USD}, -1 HOOL {251 USD}')
        self.assertTrue(inv.is_mixed())

        inv = Inventory.from_string('-2 HOOL {250 USD}, -1 HOOL {251 USD}')
        self.assertFalse(inv.is_mixed())
    def test_currencies(self):
        inv = Inventory()
        self.assertEqual(set(), inv.currencies())

        inv = Inventory.from_string('40 USD {1.01 CAD}, 40 USD')
        self.assertEqual({'USD'}, inv.currencies())

        inv = Inventory.from_string('40 AAPL {1.01 USD}, 10 HOOL {2.02 USD}')
        self.assertEqual({'AAPL', 'HOOL'}, inv.currencies())
    def test_op_eq(self):
        inv1 = Inventory.from_string('100 USD, 100 CAD')
        inv2 = Inventory.from_string('100 CAD, 100 USD')
        self.assertEqual(inv1, inv2)
        self.assertEqual(inv2, inv1)

        inv3 = Inventory.from_string('200 USD, 100 CAD')
        self.assertNotEqual(inv1, inv3)
        self.assertNotEqual(inv3, inv1)

        inv4 = Inventory.from_string('100 USD, 100 JPY')
        self.assertNotEqual(inv1, inv4)
        self.assertNotEqual(inv4, inv1)

        inv5 = Inventory.from_string('100 JPY, 100 USD')
        self.assertEqual(inv4, inv5)
 def test_get_units(self):
     inv = Inventory.from_string('40.50 JPY, 40.51 USD {1.01 CAD}, 40.52 CAD')
     self.assertEqual(inv.get_units('JPY'), A('40.50 JPY'))
     self.assertEqual(inv.get_units('USD'), A('40.51 USD'))
     self.assertEqual(inv.get_units('CAD'), A('40.52 CAD'))
     self.assertEqual(inv.get_units('AUD'), A('0 AUD'))
     self.assertEqual(inv.get_units('NZD'), A('0 NZD'))
    def test_is_small__dict(self):
        test_inv = Inventory.from_string('0.03 JPY, 0.003 USD')
        for inv in test_inv, -test_inv:
            # Test all four types of inequalities.
            self.assertTrue(inv.is_small({'JPY': D('0.05'), 'USD': D('0.005')}))
            self.assertFalse(inv.is_small({'JPY': D('0.005'), 'USD': D('0.0005')}))
            self.assertTrue(inv.is_small({'JPY': D('0.05'), 'USD': D('0.5')}))
            self.assertFalse(inv.is_small({'JPY': D('0.005'), 'USD': D('0.005')}))

            # Test border case and an epsilon under.
            self.assertTrue(inv.is_small({'JPY': D('0.03'), 'USD': D('0.003')}))
            self.assertFalse(inv.is_small({'JPY': D('0.02999999999999'),
                                           'USD': D('0.003')}))
            self.assertFalse(inv.is_small({'JPY': D('0.03'), 'USD': D('0.00299999')}))

            # Test missing precisions.
            self.assertFalse(inv.is_small({'JPY': D('0.05')}))
            self.assertFalse(inv.is_small({'USD': D('0.005')}))

            # Test extra precisions.
            self.assertTrue(inv.is_small({'JPY': D('0.05'),
                                          'USD': D('0.005'),
                                          'CAD': D('0.0005')}))

            # Test no precisions.
            self.assertFalse(inv.is_small({}))
 def test_is_small__value(self):
     test_inv = Inventory.from_string('1.50 JPY, 1.51 USD, 1.52 CAD')
     for inv in test_inv, -test_inv:
         self.assertFalse(inv.is_small(D('1.49')))
         self.assertFalse(inv.is_small(D('1.50')))
         self.assertTrue(inv.is_small(D('1.53')))
         self.assertTrue(inv.is_small(D('1.52')))
    def test_units1(self):
        inv = Inventory()
        self.assertEqual(inv.units(), Inventory.from_string(''))

        inv = Inventory.from_string('40.50 JPY, 40.51 USD {1.01 CAD}, 40.52 CAD')
        self.assertEqual(inv.units(),
                         Inventory.from_string('40.50 JPY, 40.51 USD, 40.52 CAD'))

        # Check that the same units coalesce.
        inv = Inventory.from_string('2 HOOL {400 USD}, 3 HOOL {410 USD}')
        self.assertEqual(inv.units(), Inventory.from_string('5 HOOL'))

        inv = Inventory.from_string('2 HOOL {400 USD}, -3 HOOL {410 USD}')
        self.assertEqual(inv.units(), Inventory.from_string('-1 HOOL'))
    def test_add_allow_negative(self):

        def check_allow_negative(inv):
            position_, _ = inv.add_amount(A('-11 USD'))
            self.assertFalse(position_.is_negative_at_cost())
            position_, _ = inv.add_amount(A('-11 USD'), A('1.10 CAD'))
            self.assertTrue(position_.is_negative_at_cost())
            position_, _ = inv.add_amount(A('-11 USD'), None, date(2012, 1, 1))
            self.assertTrue(position_.is_negative_at_cost())
            inv.add_amount(A('-11 USD'), A('1.10 CAD'))
            inv.add_amount(A('-11 USD'), None, date(2012, 1, 1))

        # Test adding to a position that does not exist.
        inv = Inventory()
        check_allow_negative(inv)

        # Test adding to a position that does exist.
        inv = Inventory.from_string(
            '10 USD, 10 USD {1.10 CAD}, 10 USD {1.10 CAD, 2012-01-01}')
        check_allow_negative(inv)
    def test_average(self):
        # Identity, no aggregation.
        inv = Inventory.from_string('40.50 JPY, 40.51 USD {1.01 CAD}, 40.52 CAD')
        self.assertEqual(inv.average(), inv)

        # Identity, no aggregation, with a mix of lots at cost and without cost.
        inv = Inventory.from_string('40 USD {1.01 CAD}, 40 USD')
        self.assertEqual(inv.average(), inv)

        # Aggregation.
        inv = Inventory.from_string('40 USD {1.01 CAD}, 40 USD {1.02 CAD}')
        self.assertEqual(inv.average(), Inventory.from_string('80.00 USD {1.015 CAD}'))

        # Aggregation, more units.
        inv = Inventory.from_string('2 HOOL {500 USD}, 3 HOOL {520 USD}, 4 HOOL {530 USD}')
        self.assertEqual(inv.average(), Inventory.from_string('9 HOOL {520 USD}'))
 def test_str(self):
     inv = Inventory.from_string('100.00 USD, 101.00 CAD')
     self.assertEqual('(100.00 USD, 101.00 CAD)', str(inv))
 def test_is_small__with_default(self):
     inv = Inventory.from_string('0.03 JPY')
     self.assertTrue(inv.is_small({'JPY': D('0.05')}))
     self.assertFalse(inv.is_small({'JPY': D('0.02')}))
     self.assertTrue(inv.is_small({}, {'JPY': D('0.05')}))
     self.assertFalse(inv.is_small({}, {'JPY': D('0.02')}))
 def test_units(self):
     inv = Inventory(self.POSITIONS_ALL_KINDS +
                     [position.from_string('50.00 CAD')])
     inv_cost = inv.units()
     self.assertEqual(Inventory.from_string('121.50 USD, 50.00 CAD'), inv_cost)
 def test_cost(self):
     inv = Inventory(self.POSITIONS_ALL_KINDS +
                     [position.from_string('50.00 CAD')])
     inv_cost = inv.cost()
     self.assertEqual(Inventory.from_string('40.50 USD, 139.10 CAD'), inv_cost)