def balance_check(entries, options_map): errors = [] tracking_accounts = set() for entry in entries: if isinstance(entry, Open): if entry.meta.get('tracking', False): tracking_accounts.add(entry.account) asum = Inventory() bsum = Inventory() for entry in filter_txns(entries): for posting in entry.postings: if posting.account in tracking_accounts: continue components = posting.account.split(':') if components[0] in ('Assets', 'Liabilities'): asum.add_position(posting) elif components[0] in ('Income', 'Expenses'): bsum.add_position(posting) csum = asum.reduce(convert.get_weight) + bsum.reduce(convert.get_weight) if not csum.is_small(interpolate.infer_tolerances({}, options_map)): errors.append( BudgetBalanceError( { 'filename': '<budget_balance_check>', 'lineno': 0 }, f"On-budget accounts and budget total do not match: {asum} vs {-bsum}", None)) return entries, errors
def test_units1(self): inv = Inventory() self.assertEqual(inv.reduce(convert.get_units), I('')) inv = I('40.50 JPY, 40.51 USD {1.01 CAD}, 40.52 CAD') self.assertEqual(inv.reduce(convert.get_units), I('40.50 JPY, 40.51 USD, 40.52 CAD')) # Check that the same units coalesce. inv = I('2 HOOL {400 USD}, 3 HOOL {410 USD}') self.assertEqual(inv.reduce(convert.get_units), I('5 HOOL')) inv = I('2 HOOL {400 USD}, -3 HOOL {410 USD}') self.assertEqual(inv.reduce(convert.get_units), I('-1 HOOL'))
def compute_portfolio_values( price_map: prices.PriceMap, transactions: data.Entries) -> Tuple[List[Date], List[float]]: """Compute a serie of portfolio values over time.""" # Infer the list of required prices. currency_pairs = set() for entry in transactions: for posting in entry.postings: if posting.meta["category"] is Cat.ASSET: if posting.cost: currency_pairs.add( (posting.units.currency, posting.cost.currency)) first = lambda x: x[0] price_dates = sorted(itertools.chain( ((date, None) for pair in currency_pairs for date, _ in prices.get_all_prices(price_map, pair)), ((entry.date, entry) for entry in transactions)), key=first) # Iterate computing the balance. value_dates = [] value_values = [] balance = Inventory() for date, group in itertools.groupby(price_dates, key=first): # Update balances. for _, entry in group: if entry is None: continue for posting in entry.postings: if posting.meta["category"] is Cat.ASSET: balance.add_position(posting) # Convert to market value. value_balance = balance.reduce(convert.get_value, price_map, date) cost_balance = value_balance.reduce(convert.convert_position, "USD", price_map) pos = cost_balance.get_only_position() value = pos.units.number if pos else ZERO # Add one data point. value_dates.append(date) value_values.append(value) return value_dates, value_values
def net_worth(self, interval): """Compute net worth. Args: interval: A string for the interval. Returns: A list of dicts for all ends of the given interval containing the net worth (Assets + Liabilities) separately converted to all operating currencies. """ transactions = (entry for entry in self.ledger.entries if (isinstance(entry, Transaction) and entry.flag != flags.FLAG_UNREALIZED)) types = (self.ledger.options['name_assets'], self.ledger.options['name_liabilities']) txn = next(transactions, None) inventory = Inventory() for date in self.ledger.interval_ends(interval): while txn and txn.date < date: for posting in filter(lambda p: p.account.startswith(types), txn.postings): inventory.add_position(posting) txn = next(transactions, None) yield { 'date': date, 'balance': { currency: inventory.reduce(convert.convert_position, currency, self.ledger.price_map, date).get_currency_units(currency).number for currency in self.ledger.options['operating_currency'] } }
def test_cost(self): inv = Inventory(self.POSITIONS_ALL_KINDS + [P('50.00 CAD')]) inv_cost = inv.reduce(convert.get_cost) self.assertEqual(I('40.50 USD, 139.10 CAD'), inv_cost)
def test_units(self): inv = Inventory(self.POSITIONS_ALL_KINDS + [P('50.00 CAD')]) inv_cost = inv.reduce(convert.get_units) self.assertEqual(I('121.50 USD, 50.00 CAD'), inv_cost)
new_inventory = Inventory() do_round = True for position in inventory: # units_digits = get_digits(position.units.number) # cost_digits = get_digits(position.cost.number) units_digits = 4 cost_digits = 4 new_units_number = position.units.number * new_to_old_ratio new_cost_number = position.cost.number / new_to_old_ratio if do_round: new_units_number = round(new_units_number, units_digits) new_cost_number = round(new_cost_number, cost_digits) new_position = Position(units=Amount(new_units_number, new_currency), cost=Cost(number=new_cost_number, currency=position.cost.currency, date=position.cost.date, label=position.cost.label)) new_inventory.add_position(new_position) print(' %s %s' % (args.transfer_to, new_position)) print(' %s %s' % (args.account, -position)) print('New units: ', new_inventory.reduce(get_units)) print('New cost: ', new_inventory.reduce(get_cost)) elif args.transfer_to: for position in inventory: print(' %s %s' % (args.transfer_to, position)) print(' %s %s' % (args.account, -position)) else: for position in inventory: print(' %s %s' % (args.account, position))
def _row_children(self, rows, a): sum = Inventory() for sub in rows: if sub.startswith(a.account): sum.add_inventory(rows.get(sub, Inventory())) return -self._only_position(sum.reduce(convert.get_weight))