from typing import List, Sequence from .finance_fx_revaluation_types import ForeignBalance, FxRevaluation, RevaluedBalance from .money_add import add_money, subtract_money from .money_amount import money from .money_convert import convert_money from .money_convert_types import ExchangeRate def fx_revaluation( balances: Sequence[ForeignBalance], rates: Sequence[ExchangeRate], functional_currency: str ) -> FxRevaluation: """Revalue foreign-currency balances at the closing rates and report the unrealised gain (positive) or loss (negative) on each and in total. """ net = money(0, functional_currency) lines: List[RevaluedBalance] = [] for item in balances: foreign = item.balance.currency if foreign == functional_currency: raise ValueError("account %s is already in %s" % (item.account, functional_currency)) if item.booked_value.currency != functional_currency: raise ValueError("booked value of account %s must be in %s" % (item.account, functional_currency)) matching = [r for r in rates if r.base == foreign and r.quote == functional_currency] if len(matching) == 0: raise ValueError("no period-end rate from %s to %s" % (foreign, functional_currency)) if len(matching) > 1: raise ValueError("more than one period-end rate from %s to %s" % (foreign, functional_currency)) revalued = convert_money(item.balance, matching[0], "half-up") gain_loss = subtract_money(revalued, item.booked_value) lines.append( RevaluedBalance( account=item.account, balance=item.balance, booked_value=item.booked_value, revalued_value=revalued, gain_loss=gain_loss, ) ) net = add_money(net, gain_loss) return FxRevaluation(lines=lines, net_gain_loss=net)