def _portfolio_data(nodes, date, ledger, include_children): """ Turn a portfolio of tree nodes into querytable-style data. Args: nodes: Account tree nodes. date: Date. Return: types: Tuples of column names and types as strings. rows: Dictionaries of row data by column names. """ operating_currency = ledger.options["operating_currency"][0] acct_type = ("account", str(str)) bal_type = ("balance", str(Decimal)) alloc_type = ("allocation %", str(Decimal)) types = [acct_type, bal_type, alloc_type] rows = [] for node in nodes: row = {} row["account"] = node.name balance = cost_or_value(node.balance_children, date) if include_children else cost_or_value( node.balance, date) if operating_currency in balance: row["balance"] = balance[operating_currency] rows.append(row) portfolio_total = sum(row['balance'] for row in rows) for row in rows: if "balance" in row: row["allocation %"] = round( (row["balance"] / portfolio_total) * 100, 1) return types, rows
def _serialize_account_node(node, date): children = [ _serialize_account_node(account, date) for account in node.children ] return { 'account': node.name, 'balance_children': cost_or_value(node.balance_children, date), 'balance': cost_or_value(node.balance, date), 'children': children, }
def _serialize_account_node(node, date): children = [ _serialize_account_node(account, date) for account in node.children ] return { "account": node.name, "balance_children": cost_or_value(node.balance_children, date), "balance": cost_or_value(node.balance, date), "children": children, }
def _serialize_account_node(node, date): children = [ _serialize_account_node(account, date) for account in node.children ] return { 'account': node.name, 'balance_children': cost_or_value(node.balance_children, date), 'balance': cost_or_value(node.balance, date), 'children': children, }
def serialise(self, end: datetime.date): """Serialise the account. Args: end: A date to use for cost conversions. """ children = [child.serialise(end) for child in self.children] return { "account": self.name, "balance_children": cost_or_value(self.balance_children, end), "balance": cost_or_value(self.balance, end), "children": children, }
def _portfolio_data(self, nodes): """ Turn a portfolio of tree nodes into querytable-style data. Args: nodes: Account tree nodes. Return: types: Tuples of column names and types as strings. rows: Dictionaries of row data by column names. """ operating_currency = self.ledger.options["operating_currency"][0] acct_type = ("account", str(str)) bal_type = ("balance", str(Decimal)) alloc_type = ("allocation", str(Decimal)) types = [acct_type, bal_type, alloc_type] rows = [] portfolio_total = ZERO for node in nodes: row = {} row["account"] = node.name balance = cost_or_value(node.balance) if operating_currency in balance: balance_dec = balance[operating_currency] portfolio_total += balance_dec row["balance"] = balance_dec rows.append(row) for row in rows: if "balance" in row: row["allocation"] = round( (row["balance"] / portfolio_total) * 100, 2) return types, rows
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 end_date_exclusive in self.ledger.interval_ends(interval): end_date_inclusive = end_date_exclusive - datetime.timedelta( days=1) while txn and txn.date < end_date_exclusive: for posting in txn.postings: if posting.account.startswith(types): inventory.add_position(posting) txn = next(transactions, None) yield { "date": end_date_exclusive, "balance": cost_or_value(inventory, end_date_inclusive), }
def linechart(self, account_name): """The balance of an account. Args: account_name: A string. Returns: A list of dicts for all dates on which the balance of the given account has changed containing the balance (in units) of the account at that date. """ real_account = realization.get_or_create(self.ledger.root_account, account_name) postings = realization.get_postings(real_account) journal = realization.iterate_with_balance(postings) # When the balance for a commodity just went to zero, it will be # missing from the 'balance' so keep track of currencies that last had # a balance. last_currencies = None for entry, _, change, balance in journal: if change.is_empty(): continue balance = inv_to_dict(cost_or_value(balance, entry.date)) currencies = set(balance.keys()) if last_currencies: for currency in last_currencies - currencies: balance[currency] = 0 last_currencies = currencies yield {"date": entry.date, "balance": balance}
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 = CounterInventory() 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": cost_or_value(inventory, date)}
def interval_totals(self, interval, accounts): """Renders totals for account (or accounts) in the intervals. Args: interval: An interval. accounts: A single account (str) or a tuple of accounts. """ for begin, end in pairwise(self.ledger.interval_ends(interval)): inventory = CounterInventory() entries = iter_entry_dates(self.ledger.entries, begin, end) for entry in filter_type(entries, Transaction): for posting in entry.postings: if posting.account.startswith(accounts): inventory.add_position(posting) yield { 'date': begin, 'balance': cost_or_value(inventory, end), 'budgets': self.ledger.budgets.calculate_children(accounts, begin, end), }
def interval_totals(self, interval, accounts): """Renders totals for account (or accounts) in the intervals. Args: interval: An interval. accounts: A single account (str) or a tuple of accounts. """ for begin, end in pairwise(self.ledger.interval_ends(interval)): inventory = CounterInventory() entries = iter_entry_dates(self.ledger.entries, begin, end) for entry in filter_type(entries, Transaction): for posting in entry.postings: if posting.account.startswith(accounts): inventory.add_position(posting) yield { "date": begin, "balance": cost_or_value(inventory, end), "budgets": self.ledger.budgets.calculate_children( accounts, begin, end ), }
def linechart(self, account_name): """The balance of an account. Args: account_name: A string. Returns: A list of dicts for all dates on which the balance of the given account has changed containing the balance (in units) of the account at that date. """ real_account = realization.get_or_create(self.ledger.root_account, account_name) postings = realization.get_postings(real_account) journal = realization.iterate_with_balance(postings) # When the balance for a commodity just went to zero, it will be # missing from the 'balance' field but appear in the 'change' field. # Use 0 for those commodities. for entry, _, change, balance in journal: if change.is_empty(): continue if g.conversion == 'units': bal = {curr: 0 for curr in list(change.currencies())} bal.update({ p.units.currency: p.units.number for p in balance.reduce(convert.get_units) }) else: bal = { p.units.currency: p.units.number for p in cost_or_value(balance, entry.date) } yield { 'date': entry.date, 'balance': bal, }
def linechart(self, account_name): """The balance of an account. Args: account_name: A string. Returns: A list of dicts for all dates on which the balance of the given account has changed containing the balance (in units) of the account at that date. """ real_account = realization.get_or_create(self.ledger.root_account, account_name) postings = realization.get_postings(real_account) journal = realization.iterate_with_balance(postings) # When the balance for a commodity just went to zero, it will be # missing from the 'balance' field but appear in the 'change' field. # Use 0 for those commodities. for entry, _, change, balance in journal: if change.is_empty(): continue if g.conversion == 'units': bal = {curr: 0 for curr in list(change.currencies())} bal.update({ p.units.currency: p.units.number for p in balance.reduce(convert.get_units) }) else: bal = { p.units.currency: p.units.number for p in cost_or_value(balance, entry.date) } yield { 'date': entry.date, 'balance': bal, }
def _inventory_cost_or_value(inventory, date): """Renders an inventory at cost or value to a currency -> amount dict.""" inventory = cost_or_value(inventory, date) return {p.units.currency: p.units.number for p in inventory}
def cost_or_value(self, node, date, include_children): if include_children: return cost_or_value(node.balance_children, date) return cost_or_value(node.balance, date)