Separately account unrealised gains and unrealised losses

This commit is contained in:
RunasSudo 2020-07-22 02:41:37 +10:00
parent 7fe942df5f
commit 0d9f9a096e
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
3 changed files with 57 additions and 31 deletions

View File

@ -15,7 +15,7 @@ oci_account: OCI # Other Comprehensive Income
separate_pandl: [] separate_pandl: []
# These accounts will automatically be populated on reports # These accounts will automatically be populated on reports
unrealized_gains: 'OCI:Unrealized Gains' unrealized_gains: ['OCI:Unrealized Gains', 'OCI: Unrealized Losses']
accumulated_oci: 'Equity:Accumulated Other Comprehensive Income' accumulated_oci: 'Equity:Accumulated Other Comprehensive Income'
current_year_oci: 'Equity:Current Year Other Comprehensive Income' current_year_oci: 'Equity:Current Year Other Comprehensive Income'

View File

@ -47,39 +47,54 @@ def trial_balance(ledger, date, pstart, commodity, label=None):
for account in set(list(r_date.keys()) + list(r_pstart.keys())): for account in set(list(r_date.keys()) + list(r_pstart.keys())):
if account in r_pstart: if account in r_pstart:
tb_date.balances[account.name] = tb_date.get_balance(account) + r_pstart[account].postings[0].amount for trn in r_pstart[account]:
if r_pstart[account].postings[1].account.is_income or r_pstart[account].postings[1].account.is_expense: # Update/accumulate trial balances
tb_date.balances[config['retained_earnings']] = tb_date.get_balance(ledger.get_account(config['retained_earnings'])) - r_pstart[account].postings[0].amount tb_date.balances[account.name] = tb_date.get_balance(account) + trn.postings[0].amount
elif r_pstart[account].postings[1].account.is_oci:
tb_date.balances[config['accumulated_oci']] = tb_date.get_balance(ledger.get_account(config['accumulated_oci'])) - r_pstart[account].postings[0].amount
else:
tb_date.balances[config['unrealized_gains']] = tb_date.get_balance(ledger.get_account(config['accumulated_oci'])) - r_pstart[account].postings[0].amount
# Reversing entry if trn.postings[1].account.is_income or trn.postings[1].account.is_expense:
trn_reversal = r_pstart[account].reverse(None, pstart, '<Reversal of Unrealized Gains>') tb_date.balances[config['retained_earnings']] = tb_date.get_balance(ledger.get_account(config['retained_earnings'])) - trn.postings[0].amount
ledger.transactions.insert(0, trn_reversal) elif trn.postings[1].account.is_oci:
tb_date.balances[config['accumulated_oci']] = tb_date.get_balance(ledger.get_account(config['accumulated_oci'])) - trn.postings[0].amount
else:
tb_date.balances[trn.postings[1].account.name] = tb_date.get_balance(trn.postings[1].account) - trn.postings[0].amount
tb_date.balances[account.name] = tb_date.get_balance(account) + trn_reversal.postings[0].amount # Reversing entry
tb_date.balances[config['unrealized_gains']] = tb_date.get_balance(ledger.get_account(config['unrealized_gains'])) - trn_reversal.postings[0].amount trn_reversal = trn.reverse(None, pstart, '<Reversal of {}>'.format(trn.description[1:-1]))
ledger.transactions.insert(0, trn_reversal)
tb_date.balances[account.name] = tb_date.get_balance(account) + trn_reversal.postings[0].amount
tb_date.balances[trn_reversal.postings[1].account.name] = tb_date.get_balance(trn_reversal.postings[1].account) - trn_reversal.postings[0].amount
if account in r_date: if account in r_date:
tb_date.balances[account.name] = tb_date.get_balance(account) + r_date[account].postings[0].amount for trn in r_date[account]:
tb_date.balances[config['unrealized_gains']] = tb_date.get_balance(ledger.get_account(config['unrealized_gains'])) - r_date[account].postings[0].amount # Update trial balances
tb_date.balances[account.name] = tb_date.get_balance(account) + trn.postings[0].amount
tb_date.balances[trn.postings[1].account.name] = tb_date.get_balance(trn.postings[1].account) - trn.postings[0].amount
return tb_date return tb_date
# Adjust (in place) a trial balance for unrealized gains without accumulating OCI # Adjust (in place) a trial balance for unrealized gains without accumulating OCI
def _add_unrealized_gains(tb, commodity): def _add_unrealized_gains(tb, commodity):
results = {} results = {}
unrealized_gain_account = tb.ledger.get_account(config['unrealized_gains']) unrealized_gain_account = tb.ledger.get_account(config['unrealized_gains'][0])
unrealized_loss_account = tb.ledger.get_account(config['unrealized_gains'][1])
for account in list(tb.ledger.accounts.values()): for account in list(tb.ledger.accounts.values()):
if not account.is_market: if not account.is_market:
continue continue
total_cost = tb.get_balance(account).exchange(commodity, True) unrealized_gain = Amount(0, commodity)
total_market = tb.get_balance(account).exchange(commodity, False, tb.date, tb.ledger) unrealized_loss = Amount(0, commodity)
unrealized_gain = total_market - total_cost
for amount in tb.get_balance(account).amounts:
amt_cost = amount.exchange(commodity, True)
amt_market = amount.exchange(commodity, False, date=tb.date, ledger=tb.ledger)
amt_gain = amt_market - amt_cost
if amt_gain > 0:
unrealized_gain += amt_gain
if amt_gain < 0:
unrealized_loss += amt_gain
if unrealized_gain != 0: if unrealized_gain != 0:
transaction = Transaction(tb.ledger, None, tb.date, '<Unrealized Gains>') transaction = Transaction(tb.ledger, None, tb.date, '<Unrealized Gains>')
@ -87,7 +102,15 @@ def _add_unrealized_gains(tb, commodity):
transaction.postings.append(Posting(transaction, unrealized_gain_account, -unrealized_gain)) transaction.postings.append(Posting(transaction, unrealized_gain_account, -unrealized_gain))
tb.ledger.transactions.append(transaction) tb.ledger.transactions.append(transaction)
results[account] = transaction results[account] = results.get(account, []) + [transaction]
if unrealized_loss != 0:
transaction = Transaction(tb.ledger, None, tb.date, '<Unrealized Losses>')
transaction.postings.append(Posting(transaction, account, unrealized_loss))
transaction.postings.append(Posting(transaction, unrealized_loss_account, -unrealized_loss))
tb.ledger.transactions.append(transaction)
results[account] = results.get(account, []) + [transaction]
return tb, results return tb, results

View File

@ -267,7 +267,7 @@ class Amount:
def __rsub__(self, other): def __rsub__(self, other):
return Amount(other - self.amount, self.commodity) return Amount(other - self.amount, self.commodity)
def exchange(self, commodity, is_cost, price=None): def exchange(self, commodity, is_cost, price=None, date=None, ledger=None):
if self.commodity.name == commodity.name: if self.commodity.name == commodity.name:
return Amount(self) return Amount(self)
@ -277,6 +277,16 @@ class Amount:
if price: if price:
return Amount(self.amount * price.amount, commodity) return Amount(self.amount * price.amount, commodity)
if date and ledger:
if any(p[1] == self.commodity.name for p in ledger.prices):
# This commodity has price information
# Measured at fair value
return self.exchange(commodity, is_cost, ledger.get_price(self.commodity, commodity, date))
else:
# This commodity has no price information
# Measured at historical cost
return self.exchange(commodity, True)
raise TypeError('Cannot exchange {} to {}'.format(self.commodity, commodity)) raise TypeError('Cannot exchange {} to {}'.format(self.commodity, commodity))
@property @property
@ -308,14 +318,7 @@ class Balance:
if is_cost or amount.commodity.name == commodity.name: if is_cost or amount.commodity.name == commodity.name:
result += amount.exchange(commodity, is_cost) result += amount.exchange(commodity, is_cost)
else: else:
if any(p[1] == amount.commodity.name for p in ledger.prices): result += amount.exchange(commodity, is_cost, date=date, ledger=ledger)
# This commodity has price information
# Measured at fair value
result += amount.exchange(commodity, is_cost, ledger.get_price(amount.commodity, commodity, date))
else:
# This commodity has no price information
# Measured at historical cost
result += amount.exchange(commodity, True)
return result return result
def __neg__(self): def __neg__(self):