electrum

Electrum Bitcoin wallet
git clone https://git.parazyd.org/electrum
Log | Files | Refs | Submodules

commit 0f16bcdc1ff5cda748f3079d7efd983b426277fb
parent b2c035024006f72efc711b37b39010067a5487cf
Author: ThomasV <thomasv@electrum.org>
Date:   Wed, 14 Feb 2018 10:42:09 +0100

Capital gains:
* Show acquisition price in history.
* Add summary to history command

Diffstat:
Mgui/qt/history_list.py | 23+++++++++++++----------
Mlib/exchange_rate.py | 10+++++++---
Mlib/wallet.py | 52++++++++++++++++++++++++++++++++++++++++++++++------
3 files changed, 66 insertions(+), 19 deletions(-)

diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py @@ -61,7 +61,8 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop): headers = ['', '', _('Date'), _('Description') , _('Amount'), _('Balance')] fx = self.parent.fx if fx and fx.show_history(): - headers.extend(['%s '%fx.ccy + _('Amount'), '%s '%fx.ccy + _('Balance')]) + headers.extend(['%s '%fx.ccy + _('Value')]) + headers.extend(['%s '%fx.ccy + _('Acquisition price')]) headers.extend(['%s '%fx.ccy + _('Capital Gains')]) self.editable_columns.extend([6]) self.update_headers(headers) @@ -89,20 +90,22 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop): label = self.wallet.get_label(tx_hash) entry = ['', tx_hash, status_str, label, v_str, balance_str] fiat_value = None - if fx and fx.show_history(): + if value is not None and fx and fx.show_history(): date = timestamp_to_datetime(time.time() if conf <= 0 else timestamp) fiat_value = self.wallet.get_fiat_value(tx_hash, fx.ccy) if not fiat_value: - value_str = fx.historical_value_str(value, date) + fiat_value = fx.historical_value(value, date) + fiat_default = True else: - value_str = str(fiat_value) + fiat_default = False + value_str = fx.format_fiat(fiat_value) entry.append(value_str) - balance_str = fx.historical_value_str(balance, date) - entry.append(balance_str) # fixme: should use is_mine - if value is not None and value < 0: - cg = self.wallet.capital_gain(tx_hash, fx.timestamp_rate, fx.ccy) - entry.append("%.2f"%cg if cg is not None else _('No data')) + if value < 0: + ap, lp = self.wallet.capital_gain(tx_hash, fx.timestamp_rate, fx.ccy) + cg = None if lp is None or ap is None else lp - ap + entry.append(fx.format_fiat(ap)) + entry.append(fx.format_fiat(cg)) item = QTreeWidgetItem(entry) item.setIcon(0, icon) item.setToolTip(0, str(conf) + " confirmation" + ("s" if conf != 1 else "")) @@ -116,7 +119,7 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop): if value and value < 0: item.setForeground(3, QBrush(QColor("#BC1E1E"))) item.setForeground(4, QBrush(QColor("#BC1E1E"))) - if fiat_value: + if not fiat_default: item.setForeground(6, QBrush(QColor("#1E1EFF"))) if tx_hash: item.setData(0, Qt.UserRole, tx_hash) diff --git a/lib/exchange_rate.py b/lib/exchange_rate.py @@ -506,10 +506,14 @@ class FxThread(ThreadJob): self.value_str(COIN / (10**(8 - decimal_point)), rate), self.ccy) def value_str(self, satoshis, rate): - if satoshis is None: # Can happen with incomplete history - return _("Unknown") - if rate: + if satoshis is not None and rate is not None: value = Decimal(satoshis) / COIN * Decimal(rate) + else: + value = None + return self.format_fiat(value) + + def format_fiat(self, value): + if value is not None: return "%s" % (self.ccy_amount_str(value, True)) return _("No data") diff --git a/lib/wallet.py b/lib/wallet.py @@ -943,10 +943,21 @@ class Abstract_Wallet(PrintError): return h2 + def balance_at_timestamp(self, domain, target_timestamp): + h = self.get_history(domain) + for tx_hash, height, conf, timestamp, value, balance in h: + if timestamp > target_timestamp: + return balance - value + # return last balance + return balance + def export_history(self, domain=None, from_timestamp=None, to_timestamp=None, fx=None, show_addresses=False): from .util import format_time, format_satoshis, timestamp_to_datetime h = self.get_history(domain) out = [] + init_balance = None + capital_gains = 0 + fiat_income = 0 for tx_hash, height, conf, timestamp, value, balance in h: if from_timestamp and timestamp < from_timestamp: continue @@ -960,6 +971,9 @@ class Abstract_Wallet(PrintError): 'value': format_satoshis(value, True) if value is not None else '--', 'balance': format_satoshis(balance) } + if init_balance is None: + init_balance = balance - value + end_balance = balance if item['height']>0: date_str = format_time(timestamp) if timestamp is not None else _("unverified") else: @@ -988,11 +1002,36 @@ class Abstract_Wallet(PrintError): item['output_addresses'] = output_addresses if fx is not None: date = timestamp_to_datetime(time.time() if conf <= 0 else timestamp) - item['fiat_value'] = fx.historical_value_str(value, date) - item['fiat_balance'] = fx.historical_value_str(balance, date) + fiat_value = self.get_fiat_value(tx_hash, fx.ccy) + if fiat_value is None: + fiat_value = fx.historical_value(value, date) + item['fiat_value'] = fx.format_fiat(fiat_value) if value < 0: - item['capital_gain'] = self.capital_gain(tx_hash, fx.timestamp_rate, fx.ccy) + ap, lp = self.capital_gain(tx_hash, fx.timestamp_rate, fx.ccy) + cg = None if lp is None or ap is None else lp - ap + item['acquisition_price'] = fx.format_fiat(ap) + item['capital_gain'] = fx.format_fiat(cg) + capital_gains += cg + else: + fiat_income += fiat_value out.append(item) + + if from_timestamp and to_timestamp: + summary = { + 'start_date': format_time(from_timestamp), + 'end_date': format_time(to_timestamp), + 'initial_balance': format_satoshis(init_balance), + 'final_balance': format_satoshis(end_balance), + 'capital_gains': fx.format_fiat(capital_gains), + 'fiat_income': fx.format_fiat(fiat_income) + } + if fx: + start_date = timestamp_to_datetime(from_timestamp) + end_date = timestamp_to_datetime(to_timestamp) + summary['initial_fiat_value'] = fx.format_fiat(fx.historical_value(init_balance, start_date)) + summary['final_fiat_value'] = fx.format_fiat(fx.historical_value(end_balance, end_date)) + out.append(summary) + return out def get_label(self, tx_hash): @@ -1644,11 +1683,12 @@ class Abstract_Wallet(PrintError): liquidation_price = None if p is None else out_value/Decimal(COIN) * p else: liquidation_price = - fiat_value - try: - return liquidation_price - out_value/Decimal(COIN) * self.average_price(tx, price_func, ccy) + acquisition_price = out_value/Decimal(COIN) * self.average_price(tx, price_func, ccy) except: - return None + acquisition_price = None + return acquisition_price, liquidation_price + def average_price(self, tx, price_func, ccy): """ average price of the inputs of a transaction """