diff --git a/gui/qt/history_list.py b/gui/qt/history_list.py index 63a0b4b9b..4c8090313 100644 --- a/gui/qt/history_list.py +++ b/gui/qt/history_list.py @@ -62,6 +62,7 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop): 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 + _('Capital Gains')]) self.update_headers(headers) def get_domain(self): @@ -91,6 +92,10 @@ class HistoryList(MyTreeWidget, AcceptFileDragDrop): for amount in [value, balance]: text = fx.historical_value_str(amount, date) entry.append(text) + # fixme: should use is_mine + if value < 0: + cg = self.wallet.capital_gain(tx_hash, self.parent.fx.timestamp_rate) + entry.append("%.2f"%cg if cg is not None else _('No data')) item = QTreeWidgetItem(entry) item.setIcon(0, icon) item.setToolTip(0, str(conf) + " confirmation" + ("s" if conf != 1 else "")) diff --git a/lib/exchange_rate.py b/lib/exchange_rate.py index e6a562474..5003721d0 100644 --- a/lib/exchange_rate.py +++ b/lib/exchange_rate.py @@ -493,3 +493,8 @@ class FxThread(ThreadJob): def historical_value_str(self, satoshis, d_t): rate = self.history_rate(d_t) return self.value_str(satoshis, rate) + + def timestamp_rate(self, timestamp): + from electrum.util import timestamp_to_datetime + date = timestamp_to_datetime(timestamp) + return self.history_rate(date) diff --git a/lib/wallet.py b/lib/wallet.py index d7b80a5c1..96a3b7605 100644 --- a/lib/wallet.py +++ b/lib/wallet.py @@ -962,6 +962,8 @@ class Abstract_Wallet(PrintError): 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) + if value < 0: + item['capital_gain'] = self.capital_gain(tx_hash, fx.timestamp_rate) out.append(item) return out @@ -1586,6 +1588,46 @@ class Abstract_Wallet(PrintError): children |= self.get_depending_transactions(other_hash) return children + def txin_value(self, txin): + txid = txin['prevout_hash'] + prev_n = txin['prevout_n'] + for address, d in self.txo[txid].items(): + for n, v, cb in d: + if n == prev_n: + return v + raise BaseException('unknown txin value') + + def capital_gain(self, txid, price_func): + """ + Difference between the fiat price of coins leaving the wallet because of transaction txid, + and the price of these coins when they entered the wallet. + price_func: function that returns the fiat price given a timestamp + """ + height, conf, timestamp = self.get_tx_height(txid) + tx = self.transactions[txid] + out_value = sum([ (value if not self.is_mine(address) else 0) for otype, address, value in tx.outputs() ]) + try: + return out_value/1e8 * (price_func(timestamp) - self.average_price(tx, price_func)) + except: + return None + + def average_price(self, tx, price_func): + """ average price of the inputs of a transaction """ + return sum(self.coin_price(txin, price_func) * self.txin_value(txin) for txin in tx.inputs()) / sum(self.txin_value(txin) for txin in tx.inputs()) + + def coin_price(self, coin, price_func): + """ fiat price of acquisition of coin """ + txid = coin['prevout_hash'] + tx = self.transactions[txid] + if all([self.is_mine(txin['address']) for txin in tx.inputs()]): + return self.average_price(tx, price_func) + elif all([ not self.is_mine(txin['address']) for txin in tx.inputs()]): + height, conf, timestamp = self.get_tx_height(txid) + return price_func(timestamp) + else: + # could be some coinjoin transaction.. + return None + class Simple_Wallet(Abstract_Wallet): # wallet with a single keystore