Separately account unrealised gains and unrealised losses
This commit is contained in:
parent
7fe942df5f
commit
0d9f9a096e
@ -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'
|
||||||
|
@ -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
|
if trn.postings[1].account.is_income or trn.postings[1].account.is_expense:
|
||||||
else:
|
tb_date.balances[config['retained_earnings']] = tb_date.get_balance(ledger.get_account(config['retained_earnings'])) - trn.postings[0].amount
|
||||||
tb_date.balances[config['unrealized_gains']] = tb_date.get_balance(ledger.get_account(config['accumulated_oci'])) - r_pstart[account].postings[0].amount
|
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
|
||||||
# Reversing entry
|
else:
|
||||||
trn_reversal = r_pstart[account].reverse(None, pstart, '<Reversal of Unrealized Gains>')
|
tb_date.balances[trn.postings[1].account.name] = tb_date.get_balance(trn.postings[1].account) - trn.postings[0].amount
|
||||||
ledger.transactions.insert(0, trn_reversal)
|
|
||||||
|
# Reversing entry
|
||||||
tb_date.balances[account.name] = tb_date.get_balance(account) + trn_reversal.postings[0].amount
|
trn_reversal = trn.reverse(None, pstart, '<Reversal of {}>'.format(trn.description[1:-1]))
|
||||||
tb_date.balances[config['unrealized_gains']] = tb_date.get_balance(ledger.get_account(config['unrealized_gains'])) - trn_reversal.postings[0].amount
|
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
|
||||||
|
|
||||||
|
@ -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):
|
||||||
|
Reference in New Issue
Block a user