def test_add_amount__allow_negative(self): inv = Inventory() # Test adding positions of different types. position_, _ = inv.add_amount(A('-11 USD')) self.assertIsNone(position_) position_, _ = inv.add_amount(A('-11 USD'), Cost(D('1.10'), 'CAD', None, None)) self.assertIsNone(position_) position_, _ = inv.add_amount(A('-11 USD'), Cost(D('1.10'), 'CAD', date(2012, 1, 1), None)) self.assertIsNone(position_) # Check for reductions. invlist = list(inv) self.assertTrue(invlist[1].is_negative_at_cost()) self.assertTrue(invlist[2].is_negative_at_cost()) inv.add_amount(A('-11 USD'), Cost(D('1.10'), 'CAD', None, None)) inv.add_amount(A('-11 USD'), Cost(D('1.10'), 'CAD', date(2012, 1, 1), None)) self.assertEqual(3, len(inv)) # Test adding to a position that does exist. inv = I('10 USD, 10 USD {1.10 CAD}, 10 USD {1.10 CAD, 2012-01-01}') position_, _ = inv.add_amount(A('-11 USD')) self.assertEqual(position_, position.from_string('10 USD')) position_, _ = inv.add_amount(A('-11 USD'), Cost(D('1.10'), 'CAD', None, None)) self.assertEqual(position_, position.from_string('10 USD {1.10 CAD}')) position_, _ = inv.add_amount(A('-11 USD'), Cost(D('1.10'), 'CAD', date(2012, 1, 1), None)) self.assertEqual(position_, position.from_string('10 USD {1.10 CAD, 2012-01-01}'))
def test_reducing__sans_date__equal_amount(self): inv = inventory.Inventory() wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('10 HOOL {500 USD}')) wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('-10 HOOL {500 USD}')) self.assertTrue(inv.is_empty())
def test_get_pholding_market_value(self): posting = data.Posting('Account', position.from_string('100 MSFT {54.34 USD}'), A('60.00 USD'), None, None) self.assertEqual(A('6000.00 USD'), holdings.get_pholding_market_value(posting)) posting = data.Posting('Account', position.from_string('100 MSFT {54.34 USD}'), None, None, None) self.assertEqual(A('5434.00 USD'), holdings.get_pholding_market_value(posting)) posting = data.Posting('Account', position.from_string('1000.00 USD'), None, None, None) self.assertEqual(A('1000.00 USD'), holdings.get_pholding_market_value(posting)) with self.assertRaises(AssertionError): posting = data.Posting('Account', position.from_string('1000.00 USD'), A('60.00 USD'), None, None) holdings.get_pholding_market_value(posting) with self.assertRaises(AssertionError): posting = data.Posting('Account', position.from_string('1000.00 USD {1.25 CAD}'), A('60.00 USD'), None, None) holdings.get_pholding_market_value(posting)
def test_reducing__sans_date__over_amount(self): inv = inventory.Inventory() wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('10 HOOL {500 USD}')) with self.assertRaises(ValueError): wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('-13 HOOL {500 USD}'))
def test_reducing__with_diff_date__incomplete_amount(self): inv = inventory.Inventory() wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('10 HOOL {500 USD}')) with self.assertRaises(ValueError): wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('-7 HOOL {500 USD / 2014-02-11}'))
def test_various(self): pos = position.from_string('100.00 USD') rdr = self.get(pos) self.assertEqual('100.00 USD', rdr.format(pos)) pos = position.from_string('5 HOOL {500.23 USD}') rdr = self.get(pos) self.assertEqual('5 HOOL {500.23 USD}', rdr.format(pos))
def test_reducing__sans_date__incomplete_amount(self): inv = inventory.Inventory() wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('10 HOOL {500 USD}')) wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('-7 HOOL {500 USD}')) self.assertEqual( inventory.from_string('3 HOOL {500 USD / 2014-02-10}'), inv)
def test_augmenting__sans_date(self): inv = inventory.Inventory() wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('10 HOOL {500 USD}')) wash_calculator.book_position(inv, DD('2014-02-10'), position.from_string('7 HOOL {500 USD}')) self.assertEqual( inventory.from_string('17 HOOL {500 USD / 2014-02-10}'), inv)
def test_get_position(self): inv = Inventory(self.POSITIONS_ALL_KINDS) self.assertEqual( position.from_string('40.50 USD'), inv.get_position(Lot('USD', None, None))) self.assertEqual( position.from_string('40.50 USD {1.10 CAD}'), inv.get_position(Lot('USD', A('1.10 CAD'), None))) self.assertEqual( position.from_string('40.50 USD {1.10 CAD, 2012-01-01}'), inv.get_position(Lot('USD', A('1.10 CAD'), date(2012, 1, 1))))
def test_balance_incomplete_postings__fill2(self): entry, errors = self.get_incomplete_entry(""" 2013-02-23 * "Something" Liabilities:CreditCard -50 USD Liabilities:CreditCard -50 CAD Expenses:Restaurant """) self.assertFalse(errors) self.assertEqual(4, len(entry.postings)) self.assertEqual(entry.postings[2].account, 'Expenses:Restaurant') self.assertEqual(entry.postings[3].account, 'Expenses:Restaurant') self.assertEqual(entry.postings[2].position, position.from_string('50 USD')) self.assertEqual(entry.postings[3].position, position.from_string('50 CAD'))
def test_holding_to_position(self): test_holding = holdings.Holding( 'Assets:US:Checking', D('100'), 'MSFT', D('54.34'), 'USD', D('5434.00'), D('6000.00'), D('60'), datetime.date(2012, 5, 2)) actual_position = holdings.holding_to_position(test_holding) expected_position = position.from_string('100 MSFT {54.34 USD}') self.assertEqual(expected_position, actual_position) test_holding = holdings.Holding( 'Assets:US:Checking', D('100'), 'USD', None, None, None, None, None, datetime.date(2012, 5, 2)) actual_position = holdings.holding_to_position(test_holding) expected_position = position.from_string('100.00 USD') self.assertEqual(expected_position, actual_position)
def deserialise_posting(posting): """Parse JSON to a Beancount Posting.""" amount = posting.get("amount") unit_price = None total_price = None if amount: if "@@" in amount: amount, raw_total_price = amount.split("@@") total_price = A(raw_total_price) elif "@" in amount: amount, raw_unit_price = amount.split("@") unit_price = A(raw_unit_price) pos = position.from_string(amount) units = pos.units if re.search(r"{\s*}", amount): cost = data.CostSpec(MISSING, None, MISSING, None, None, False) else: cost = pos.cost else: units, cost = None, None if total_price is not None: try: num = total_price.number / units.number.copy_abs() except decimal.InvalidOperation: # if both units and total price is zero, set it to zero. num = ZERO unit_price = Amount(num, total_price.currency) return data.Posting(posting["account"], units, cost, unit_price, None, None)
def test_position(self): itypes = [('pos', position.Position)] irows = [(position.from_string(string), ) for string in self.input_amounts] atypes, arows = numberify.numberify_results(itypes, irows) self.assertEqual(self.expected_types, atypes) self.assertEqual(self.expected_rows, arows)
def test_render_posting_no_cost(self): pos = position.from_string('100 USD') str_posting = journal_text.render_posting( data.Posting('Assets:Something', pos.units, pos.cost, None, None, None), self.number_format) self.assertEqual(' Assets:Something 100 USD', str_posting)
def test_creating__with_date(self): inv = inventory.Inventory() wash_calculator.book_position( inv, DD('2014-02-10'), position.from_string('10 HOOL {500 USD / 2014-02-11}')) self.assertEqual( inventory.from_string('10 HOOL {500 USD / 2014-02-11}'), inv)
def test_render_posting_price(self): str_posting = journal_text.render_posting( data.Posting('Assets:Something', position.from_string('10 VHT'), A('45.32 USD'), None, None), self.number_format) self.assertEqual( ' Assets:Something 10 VHT @ 45.32 USD', str_posting)
def test_render_posting_cost_price(self): pos = position.from_string('10 VHT {45.32 USD}') str_posting = journal_text.render_posting( data.Posting('Assets:Something', pos.units, pos.cost, A('47.00 USD'), None, None), self.number_format) self.assertEqual( ' Assets:Something 10 VHT {45.32 USD} @ 47.00 USD', str_posting)
def test_balance_incomplete_postings__cost(self): entry, errors = self.get_incomplete_entry(""" 2013-02-23 * "Something" Assets:Invest 10 MSFT {43.23 USD} Assets:Cash """) self.assertFalse(errors) self.assertEqual(2, len(entry.postings)) self.assertEqual(entry.postings[1].account, 'Assets:Cash') self.assertEqual(entry.postings[1].position, position.from_string('-432.30 USD'))
def test_balance_incomplete_postings__fill1(self): entry, errors = self.get_incomplete_entry(""" option "booking_algorithm" "SIMPLE" 2013-02-23 * "Something" Liabilities:CreditCard -50 USD Expenses:Restaurant """) self.assertFalse(errors) self.assertEqual(2, len(entry.postings)) self.assertEqual(position.get_position(entry.postings[1]), position.from_string('50 USD'))
def test_precision(self): # Some display context. dcontext = display_context.DisplayContext() dcontext.update(D('111'), 'JPY') dcontext.update(D('1.111'), 'RGAGX') dcontext.update(D('1.11'), 'USD') dformat = dcontext.build() # Input data. itypes = [('number', Decimal), ('amount', amount.Amount), ('position', position.Position), ('inventory', inventory.Inventory)] irows = [[D(amt.split()[0]), A(amt), position.from_string(amt), inventory.from_string(amt)] for amt in ['123.45678909876 JPY', '1.67321232123 RGAGX', '5.67345434543 USD']] # First check with no explicit quantization. atypes, arows = numberify.numberify_results(itypes, irows) erows = [[D('123.45678909876'), None, None, D('123.45678909876'), None, None, D('123.45678909876'), None, None, D('123.45678909876')], [D('1.67321232123'), None, D('1.67321232123'), None, None, D('1.67321232123'), None, None, D('1.67321232123'), None], [D('5.67345434543'), D('5.67345434543'), None, None, D('5.67345434543'), None, None, D('5.67345434543'), None, None]] self.assertEqual(erows, arows) # Then compare with quantization. atypes, arows = numberify.numberify_results(itypes, irows, dformat) erows = [[D('123.45678909876'), None, None, D('123'), None, None, D('123'), None, None, D('123')], [D('1.67321232123'), None, D('1.673'), None, None, D('1.673'), None, None, D('1.673'), None], [D('5.67345434543'), D('5.67'), None, None, D('5.67'), None, None, D('5.67'), None, None]] self.assertEqual(erows, arows)
def test_balance_incomplete_postings__insert_rounding(self): entry, errors = self.get_incomplete_entry(""" option "account_rounding" "RoundingError" 2013-02-23 * "Something" Assets:Invest 1.245 RGAGX {43.23 USD} Assets:Cash -53.82 USD """) self.assertFalse(errors) self.assertEqual(3, len(entry.postings)) self.assertEqual(entry.postings[2].account, 'Equity:RoundingError') self.assertEqual(entry.postings[2].position, position.from_string('-0.00135 USD'))
def deserialise_posting(posting): """Parse JSON to a Beancount Posting.""" amount = posting.get('amount') price = None if amount: if '@' in amount: amount, raw_price = amount.split('@') price = A(raw_price) pos = position.from_string(amount) units, cost = pos.units, pos.cost else: units, cost = None, None return data.Posting(posting['account'], units, cost, price, None, None)
def test_holding_to_posting(self): test_holding = holdings.Holding( 'Assets:US:Checking', D('100'), 'MSFT', D('54.34'), 'USD', D('5434.00'), D('6000.00'), D('60'), datetime.date(2012, 5, 2)) posting = holdings.holding_to_posting(test_holding) self.assertTrue(isinstance(posting, data.Posting)) expected_position = position.from_string('100 MSFT {54.34 USD}') self.assertEqual(expected_position, position.Position(posting.units, posting.cost)) expected_price = A('60.00 USD') self.assertEqual(expected_price, posting.price)
def test_ctor_empty_len(self): # Test regular constructor. inv = Inventory() self.assertTrue(inv.is_empty()) self.assertEqual(0, len(inv)) inv = Inventory([position.from_string('100.00 USD'), position.from_string('101.00 USD')]) self.assertFalse(inv.is_empty()) self.assertEqual(1, len(inv)) inv = Inventory([position.from_string('100.00 USD'), position.from_string('100.00 CAD')]) self.assertFalse(inv.is_empty()) self.assertEqual(2, len(inv)) inv = Inventory() self.assertEqual(0, len(inv)) inv.add_amount(A('100 USD')) self.assertEqual(1, len(inv)) inv.add_amount(A('100 CAD')) self.assertEqual(2, len(inv))
def deserialise_posting(posting): """Parse JSON to a Beancount Posting.""" amount = posting.get("amount") price = None if amount: if "@" in amount: amount, raw_price = amount.split("@") price = A(raw_price) pos = position.from_string(amount) units = pos.units if re.search(r"{\s*}", amount): cost = data.CostSpec(MISSING, None, MISSING, None, None, False) else: cost = pos.cost else: units, cost = None, None return data.Posting(posting["account"], units, cost, price, None, None)
def deserialise_posting(posting): """Parse JSON to a Beancount Posting.""" amount = posting.get('amount') price = None if amount: if '@' in amount: amount, raw_price = amount.split('@') price = A(raw_price) pos = position.from_string(amount) units = pos.units if re.search(r'{\s*}', amount): cost = data.CostSpec( MISSING, None, MISSING, None, None, False ) else: cost = pos.cost else: units, cost = None, None return data.Posting(posting['account'], units, cost, price, None, None)
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_to_string_no_detail(self): pos = from_string('2.2 HOOL {532.43 USD, 2014-06-15}') self.assertEqual(('2.2 HOOL {532.43 USD}'), pos.to_string(detail=False))
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)
class TestInventory(unittest.TestCase): def checkAmount(self, inventory, number, currency): if isinstance(number, str): number = D(number) amount_ = amount.Amount(number, currency) inv_amount = inventory.get_units(amount_.currency) self.assertEqual(inv_amount, amount_) def test_from_string(self): inv = inventory.from_string('') self.assertEqual(Inventory(), inv) inv = inventory.from_string('10 USD') self.assertEqual( Inventory([Position(Lot("USD", None, None), D('10'))]), inv) inv = inventory.from_string(' 10.00 USD ') self.assertEqual( Inventory([Position(Lot("USD", None, None), D('10'))]), inv) inv = inventory.from_string('1 USD, 2 CAD') self.assertEqual( Inventory([Position(Lot("USD", None, None), D('1')), Position(Lot("CAD", None, None), D('2'))]), inv) inv = inventory.from_string('2.2 HOOL {532.43 USD}, 3.413 EUR') self.assertEqual( Inventory([Position(Lot("HOOL", A('532.43 USD'), None), D('2.2')), Position(Lot("EUR", None, None), D('3.413'))]), inv) inv = inventory.from_string( '2.2 HOOL {532.43 USD}, 2.3 HOOL {564.00 USD, 2015-07-14}, 3.413 EUR') self.assertEqual( Inventory([Position(Lot("HOOL", A('532.43 USD'), None), D('2.2')), Position(Lot("HOOL", A('564.00 USD'), datetime.date(2015, 7, 14)), D('2.3')), Position(Lot("EUR", None, None), D('3.413'))]), inv) inv = inventory.from_string( '1.1 HOOL {500.00 # 11.00 USD}, 100 CAD') self.assertEqual( Inventory([Position(Lot("HOOL", A('510.00 USD'), None), D('1.1')), Position(Lot("CAD", None, None), D('100'))]), inv) def test_ctor_empty_len(self): # Test regular constructor. inv = Inventory() self.assertTrue(inv.is_empty()) self.assertEqual(0, len(inv)) inv = Inventory([position.from_string('100.00 USD'), position.from_string('101.00 USD')]) self.assertFalse(inv.is_empty()) self.assertEqual(1, len(inv)) inv = Inventory([position.from_string('100.00 USD'), position.from_string('100.00 CAD')]) self.assertFalse(inv.is_empty()) self.assertEqual(2, len(inv)) inv = Inventory() self.assertEqual(0, len(inv)) inv.add_amount(A('100 USD')) self.assertEqual(1, len(inv)) inv.add_amount(A('100 CAD')) self.assertEqual(2, len(inv)) 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_copy(self): inv = Inventory() inv.add_amount(A('100.00 USD')) self.checkAmount(inv, '100', 'USD') # Test copying. inv2 = copy.copy(inv) inv2.add_amount(A('50.00 USD')) self.checkAmount(inv2, '150', 'USD') # Check that the original object is not modified. self.checkAmount(inv, '100', 'USD') 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_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_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__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_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_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_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_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')) POSITIONS_ALL_KINDS = [ position.from_string('40.50 USD'), position.from_string('40.50 USD {1.10 CAD}'), position.from_string('40.50 USD {1.10 CAD, 2012-01-01}')] 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) 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_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_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_get_position(self): inv = Inventory(self.POSITIONS_ALL_KINDS) self.assertEqual( position.from_string('40.50 USD'), inv.get_position(Lot('USD', None, None))) self.assertEqual( position.from_string('40.50 USD {1.10 CAD}'), inv.get_position(Lot('USD', A('1.10 CAD'), None))) self.assertEqual( position.from_string('40.50 USD {1.10 CAD, 2012-01-01}'), inv.get_position(Lot('USD', A('1.10 CAD'), date(2012, 1, 1)))) def test_add(self): inv = Inventory() inv.add_amount(A('100.00 USD')) self.checkAmount(inv, '100', 'USD') # Add some amount inv.add_amount(A('25.01 USD')) self.checkAmount(inv, '125.01', 'USD') # Subtract some amount. inv.add_amount(A('-12.73 USD')) self.checkAmount(inv, '112.28', 'USD') # Subtract some to be negative (should be allowed if no lot). inv.add_amount(A('-120 USD')) self.checkAmount(inv, '-7.72', 'USD') # Subtract some more. inv.add_amount(A('-1 USD')) self.checkAmount(inv, '-8.72', 'USD') # Add to above zero again inv.add_amount(A('18.72 USD')) self.checkAmount(inv, '10', 'USD') def test_add__booking(self): inv = Inventory() _, booking = inv.add_amount(A('100.00 USD')) self.assertEqual(Booking.CREATED, booking) _, booking = inv.add_amount(A('20.00 USD')) self.assertEqual(Booking.AUGMENTED, booking) _, booking = inv.add_amount(A('-20 USD')) self.assertEqual(Booking.REDUCED, booking) _, booking = inv.add_amount(A('-100 USD')) self.assertEqual(Booking.REDUCED, booking) def test_add_multi_currency(self): inv = Inventory() inv.add_amount(A('100 USD')) inv.add_amount(A('100 CAD')) self.checkAmount(inv, '100', 'USD') self.checkAmount(inv, '100', 'CAD') inv.add_amount(A('25 USD')) self.checkAmount(inv, '125', 'USD') self.checkAmount(inv, '100', 'CAD') def test_add_withlots(self): # Testing the strict case where everything matches, with only a cost. inv = Inventory() inv.add_amount(A('50 HOOL'), A('700 USD')) self.checkAmount(inv, '50', 'HOOL') inv.add_amount(A('-40 HOOL'), A('700 USD')) self.checkAmount(inv, '10', 'HOOL') position_, _ = inv.add_amount(A('-12 HOOL'), A('700 USD')) self.assertTrue(position_.is_negative_at_cost()) # Testing the strict case where everything matches, a cost and a lot-date. inv = Inventory() inv.add_amount(A('50 HOOL'), A('700 USD'), date(2000, 1, 1)) self.checkAmount(inv, '50', 'HOOL') inv.add_amount(A('-40 HOOL'), A('700 USD'), date(2000, 1, 1)) self.checkAmount(inv, '10', 'HOOL') position_, _ = inv.add_amount(A('-12 HOOL'), A('700 USD'), date(2000, 1, 1)) self.assertTrue(position_.is_negative_at_cost()) 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_add_position(self): inv = Inventory() for pos in self.POSITIONS_ALL_KINDS: inv.add_position(pos) self.assertEqual(Inventory(self.POSITIONS_ALL_KINDS), inv) 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_sum_inventories(self): inv1 = Inventory() inv1.add_amount(A('10 USD')) inv2 = Inventory() inv2.add_amount(A('20 CAD')) inv2.add_amount(A('55 HOOL')) inv1 + inv2
def test_empty(self): inv = inventory.Inventory() wash_calculator.book_position(inv, DD('2014-02-03'), position.from_string('0 HOOL {500 USD}')) self.assertTrue(inv.is_empty())