def add_loan_gain(self, gained_asset, gained_amount, fee_in_asset, lent_amount, open_time, close_time): timestamp = close_time rate = self.get_rate_in_profit_currency(gained_asset, timestamp) if gained_asset not in self.events: self.events[gained_asset] = Events(list(), list()) net_gain_amount = gained_amount - fee_in_asset gain_in_profit_currency = net_gain_amount * rate assert gain_in_profit_currency > 0, "Loan profit is negative. Should never happen" self.events[gained_asset].buys.append( BuyEvent(amount=net_gain_amount, timestamp=timestamp, rate=rate, fee_rate=0, cost=0)) # count profits if we are inside the query period if timestamp >= self.query_start_ts: self.loan_profit += gain_in_profit_currency self.csv_exporter.add_loan_profit( gained_asset=gained_asset, gained_amount=gained_amount, gain_in_profit_currency=gain_in_profit_currency, lent_amount=lent_amount, open_time=open_time, close_time=close_time, )
def add_margin_position(self, gained_asset, gained_amount, fee_in_asset, margin_notes, timestamp): rate = self.get_rate_in_profit_currency(gained_asset, timestamp) if gained_asset not in self.events: self.events[gained_asset] = Events(list(), list()) net_gain_amount = gained_amount - fee_in_asset gain_in_profit_currency = net_gain_amount * rate assert gain_in_profit_currency > 0, ( 'Margin profit is negative. Should never happen for the hacky way I use em now' ) self.events[gained_asset].buys.append( BuyEvent(amount=net_gain_amount, timestamp=timestamp, rate=rate, fee_rate=0, cost=0)) # count profits if we are inside the query period if timestamp >= self.query_start_ts: self.margin_positions_profit += gain_in_profit_currency self.csv_exporter.add_margin_position( margin_notes=margin_notes, gained_asset=gained_asset, net_gain_amount=net_gain_amount, gain_in_profit_currency=gain_in_profit_currency, timestamp=timestamp, )
def add_buy(self, bought_asset, bought_amount, paid_with_asset, trade_rate, trade_fee, fee_currency, timestamp, is_virtual=False): paid_with_asset_rate = self.get_rate_in_profit_currency( paid_with_asset, timestamp) buy_rate = paid_with_asset_rate * trade_rate fee_price_in_profit_currency = 0 if trade_fee != 0: fee_price_in_profit_currency = self.price_historian.query_historical_price( fee_currency, self.profit_currency, timestamp) self.handle_prefork_acquisitions(bought_asset=bought_asset, bought_amount=bought_amount, paid_with_asset=paid_with_asset, trade_rate=trade_rate, trade_fee=trade_fee, fee_currency=fee_currency, timestamp=timestamp) if bought_asset not in self.events: self.events[bought_asset] = Events(list(), list()) fee_cost = fee_price_in_profit_currency * trade_fee gross_cost = bought_amount * buy_rate cost = gross_cost + fee_cost self.events[bought_asset].buys.append( BuyEvent(amount=bought_amount, timestamp=timestamp, rate=buy_rate, fee_rate=fee_cost / bought_amount, cost=cost)) if logger.isEnabledFor(logging.DEBUG): logger.debug( 'Buying {} "{}" for {} "{}" ({} "{}" per "{}" or {} "{}" per ' '"{}") at {}'.format( bought_amount, bought_asset, bought_amount * trade_rate, paid_with_asset, trade_rate, paid_with_asset, bought_asset, buy_rate, self.profit_currency, bought_asset, tsToDate(timestamp, formatstr='%d/%m/%Y %H:%M:%S'))) self.csv_exporter.add_buy( bought_asset=bought_asset, rate=buy_rate, fee_cost=fee_cost, amount=bought_amount, gross_cost=gross_cost, cost=cost, paid_with_asset=paid_with_asset, paid_with_asset_rate=paid_with_asset_rate, timestamp=timestamp, is_virtual=is_virtual, )
def test_reduce_asset_amount(accountant): asset = 'BTC' events = accountant.events.events events[asset] = Events(list(), list()) events[asset].buys.append( BuyEvent( amount=FVal(1), timestamp=1446979735, # 08/11/2015 rate=FVal(268.1), fee_rate=FVal(0.0001), ), ) events['BTC'].buys.append( BuyEvent( amount=FVal(1), timestamp=1467378304, # 31/06/2016 rate=FVal(612.45), fee_rate=FVal(0.0019), ), ) events['BTC'].buys.append( BuyEvent( amount=FVal(3), # 25/10/2016 timestamp=1477378304, rate=FVal(603.415), fee_rate=FVal(0.0017), ), ) assert accountant.events.reduce_asset_amount(asset, FVal(1.5)) assert (len(accountant.events.events[asset].buys)) == 2, '1 buy should be used' remaining_amount = accountant.events.events[asset].buys[0].amount assert remaining_amount == FVal(0.5), '0.5 of 2nd buy should remain'
def test_search_buys_calculate_profit_1_buy_consumed_by_1_sell(accountant): """ Assert bought_cost is correct when 1 buy is completely consumed by 1 sell Regression test for part of https://github.com/rotkehlchenio/rotkehlchen/issues/223 """ asset = 'BTC' events = accountant.events.events events[asset] = Events(list(), list()) events[asset].buys.append( BuyEvent( amount=FVal(5), timestamp=1446979735, # 08/11/2015 rate=FVal(268.1), fee_rate=FVal(0.0001), ), ) ( taxable_amount, taxable_bought_cost, taxfree_bought_cost, ) = accountant.events.search_buys_calculate_profit( selling_amount=FVal(5), selling_asset=asset, timestamp=1467378304, # 31/06/2016 ) assert taxable_amount == 5, '5 out of 5 should be taxable (within a year)' assert taxfree_bought_cost.is_close(FVal('0')) assert taxable_bought_cost.is_close(FVal('1340.5005')) assert (len(accountant.events.events[asset].buys)) == 0, 'only buy should have been used'
def add_margin_position( self, gain_loss_asset: Asset, gain_loss_amount: FVal, fee_in_asset: Fee, margin_notes: str, timestamp: Timestamp, ) -> None: rate = self.get_rate_in_profit_currency(gain_loss_asset, timestamp) if gain_loss_asset not in self.events: self.events[gain_loss_asset] = Events(list(), list()) net_gain_loss_amount = gain_loss_amount - fee_in_asset gain_loss_in_profit_currency = net_gain_loss_amount * rate if net_gain_loss_amount > 0: self.events[gain_loss_asset].buys.append( BuyEvent( amount=net_gain_loss_amount, timestamp=timestamp, rate=rate, fee_rate=ZERO, ), ) elif net_gain_loss_amount < 0: result = self.reduce_asset_amount( asset=gain_loss_asset, amount=-gain_loss_amount, ) if not result: log.critical( f'No documented buy found for {gain_loss_asset} before ' f'{timestamp_to_date(timestamp, formatstr="%d/%m/%Y %H:%M:%S")}', ) # count profit/loss if we are inside the query period if timestamp >= self.query_start_ts: self.margin_positions_profit_loss += gain_loss_in_profit_currency log.debug( 'Accounting for margin position', sensitive_log=True, notes=margin_notes, gain_loss_asset=gain_loss_asset, net_gain_loss_amount=net_gain_loss_amount, gain_loss_in_profit_currency=gain_loss_in_profit_currency, timestamp=timestamp, ) self.csv_exporter.add_margin_position( margin_notes=margin_notes, gain_loss_asset=gain_loss_asset, net_gain_loss_amount=net_gain_loss_amount, gain_loss_in_profit_currency=gain_loss_in_profit_currency, timestamp=timestamp, )
def test_search_buys_calculate_profit_1_buy_used_by_2_sells_taxable(accountant): """ Make sure that when 1 buy is used by 2 sells bought cost is correct Regression test for taxable part of: https://github.com/rotkehlchenio/rotkehlchen/issues/223 """ asset = 'BTC' events = accountant.events.events events[asset] = Events(list(), list()) events[asset].buys.append( BuyEvent( amount=FVal(5), timestamp=1446979735, # 08/11/2015 rate=FVal(268.1), fee_rate=FVal(0.0001), ), ) ( taxable_amount, taxable_bought_cost, taxfree_bought_cost, ) = accountant.events.search_buys_calculate_profit( selling_amount=FVal(3), selling_asset=asset, timestamp=1467378304, # 31/06/2016 ) assert taxable_amount == 3, '3 out of 3 should be taxable (within a year)' assert taxfree_bought_cost.is_close(FVal('0')) assert taxable_bought_cost.is_close(FVal('804.3003')) assert (len(accountant.events.events[asset].buys)) == 1, 'whole buy was not used' remaining_amount = accountant.events.events[asset].buys[0].amount assert remaining_amount == FVal(2), '3 of 5 should have been consumed' # now eat up all the rest ( taxable_amount, taxable_bought_cost, taxfree_bought_cost, ) = accountant.events.search_buys_calculate_profit( selling_amount=FVal(2), selling_asset=asset, timestamp=1467378404, # bit after previous sell ) assert taxable_amount == 2, '2 out of 2 should be taxable (within a year)' assert taxfree_bought_cost.is_close(FVal('0')) assert taxable_bought_cost.is_close(FVal('536.2002')) assert (len(accountant.events.events[asset].buys)) == 0, 'the buy should have been used'
def add_loan_gain( self, gained_asset: Asset, gained_amount: FVal, fee_in_asset: Fee, lent_amount: FVal, open_time: Timestamp, close_time: Timestamp, ) -> None: timestamp = close_time rate = self.get_rate_in_profit_currency(gained_asset, timestamp) if gained_asset not in self.events: self.events[gained_asset] = Events(list(), list()) net_gain_amount = gained_amount - fee_in_asset gain_in_profit_currency = net_gain_amount * rate assert gain_in_profit_currency > 0, "Loan profit is negative. Should never happen" self.events[gained_asset].buys.append( BuyEvent( amount=net_gain_amount, timestamp=timestamp, rate=rate, fee_rate=ZERO, ), ) # count profits if we are inside the query period if timestamp >= self.query_start_ts: log.debug( 'Accounting for loan profit', sensitive_log=True, gained_asset=gained_asset, gained_amount=gained_amount, gain_in_profit_currency=gain_in_profit_currency, lent_amount=lent_amount, open_time=open_time, close_time=close_time, ) self.loan_profit += gain_in_profit_currency self.csv_exporter.add_loan_profit( gained_asset=gained_asset, gained_amount=gained_amount, gain_in_profit_currency=gain_in_profit_currency, lent_amount=lent_amount, open_time=open_time, close_time=close_time, )
def test_search_buys_calculate_profit_after_year(accountant): asset = 'BTC' events = accountant.events.events events[asset] = Events(list(), list()) events[asset].buys.append( BuyEvent( amount=FVal(5), timestamp=1446979735, # 08/11/2015 rate=FVal(268.1), fee_rate=FVal(0.0001), ), ) events['BTC'].buys.append( BuyEvent( amount=FVal(15), timestamp=1467378304, # 31/06/2016 rate=FVal(612.45), fee_rate=FVal(0.0019), ), ) events['BTC'].buys.append( BuyEvent( amount=FVal(3), # 25/10/2016 timestamp=1477378304, rate=FVal(603.415), fee_rate=FVal(0.0017), ), ) ( taxable_amount, taxable_bought_cost, taxfree_bought_cost, ) = accountant.events.search_buys_calculate_profit( selling_amount=FVal(8), selling_asset=asset, timestamp=1480683904, # 02/12/2016 ) assert taxable_amount == 3, '3 out of 8 should be taxable (within a year)' assert taxfree_bought_cost.is_close(FVal('1340.5005')) assert taxable_bought_cost.is_close(FVal('1837.3557')) assert (len(accountant.events.events[asset].buys)) == 2, 'first buy should have been used' remaining_amount = accountant.events.events[asset].buys[0].amount assert remaining_amount == FVal(12), '3 of 15 should have been consumed'
def add_margin_position( self, gain_loss_asset: Asset, gain_loss_amount: FVal, fee_in_asset: FVal, margin_notes: str, timestamp: Timestamp, ): rate = self.get_rate_in_profit_currency(gain_loss_asset, timestamp) if gain_loss_asset not in self.events: self.events[gain_loss_asset] = Events(list(), list()) net_gain_loss_amount = gain_loss_amount - fee_in_asset gain_loss_in_profit_currency = net_gain_loss_amount * rate if net_gain_loss_amount > 0: self.events[gain_loss_asset].buys.append( BuyEvent(amount=net_gain_loss_amount, timestamp=timestamp, rate=rate, fee_rate=0, cost=0)) # count profit/loss if we are inside the query period if timestamp >= self.query_start_ts: self.margin_positions_profit_loss += gain_loss_in_profit_currency log.debug( 'Accounting for margin position', sensitive_log=True, notes=margin_notes, gain_loss_asset=gain_loss_asset, net_gain_loss_amount=net_gain_loss_amount, gain_loss_in_profit_currency=gain_loss_in_profit_currency, timestamp=timestamp, ) self.csv_exporter.add_margin_position( margin_notes=margin_notes, gain_loss_asset=gain_loss_asset, net_gain_loss_amount=net_gain_loss_amount, gain_loss_in_profit_currency=gain_loss_in_profit_currency, timestamp=timestamp, )
def test_reduce_asset_amount_more_that_bought(accountant): asset = 'BTC' events = accountant.events.events events[asset] = Events(list(), list()) events[asset].buys.append( BuyEvent( amount=FVal(1), timestamp=1446979735, # 08/11/2015 rate=FVal(268.1), fee_rate=FVal(0.0001), ), ) events['BTC'].buys.append( BuyEvent( amount=FVal(1), timestamp=1467378304, # 31/06/2016 rate=FVal(612.45), fee_rate=FVal(0.0019), ), ) assert not accountant.events.reduce_asset_amount(asset, FVal(3)) assert (len(accountant.events.events[asset].buys)) == 0, 'all buys should be used'
def test_search_buys_calculate_profit_sell_more_than_bought_after_year(accountant): asset = 'BTC' events = accountant.events.events events[asset] = Events(list(), list()) events[asset].buys.append( BuyEvent( amount=FVal(1), timestamp=1446979735, # 08/11/2015 rate=FVal(268.1), fee_rate=FVal(0.0001), ), ) events['BTC'].buys.append( BuyEvent( amount=FVal(1), timestamp=1467378304, # 31/06/2016 rate=FVal(612.45), fee_rate=FVal(0.0019), ), ) ( taxable_amount, taxable_bought_cost, taxfree_bought_cost, ) = accountant.events.search_buys_calculate_profit( selling_amount=FVal(3), selling_asset=asset, timestamp=1523399409, # 10/04/2018 ) assert taxable_amount == 1, '1 out of 3 should be taxable (after a year)' assert taxfree_bought_cost.is_close(FVal('880.552')) assert taxable_bought_cost.is_close(FVal('0')) assert (len(accountant.events.events[asset].buys)) == 0, 'only buy should have been used'
def add_sell(self, selling_asset, selling_amount, receiving_asset, receiving_amount, gain_in_profit_currency, total_fee_in_profit_currency, trade_rate, rate_in_profit_currency, timestamp, loan_settlement=False, is_virtual=False): if selling_asset not in self.events: self.events[selling_asset] = Events(list(), list()) self.events[selling_asset].sells.append( SellEvent( amount=selling_amount, timestamp=timestamp, rate=rate_in_profit_currency, fee_rate=total_fee_in_profit_currency / selling_amount, gain=gain_in_profit_currency, )) debug_enabled = logger.isEnabledFor(logging.DEBUG) if debug_enabled: if loan_settlement: logger.debug( 'Loan Settlement Selling {} of "{}" for {} "{}" at {}'. format(selling_amount, selling_asset, gain_in_profit_currency, self.profit_currency, tsToDate(timestamp, formatstr='%d/%m/%Y %H:%M:%S'))) else: logger.debug( 'Selling {} of "{}" for {} "{}" ({} "{}" per "{}" or {} "{}" ' 'per "{}") for a gain of {} "{}" and a fee of {} "{} at {}' .format(selling_amount, selling_asset, receiving_amount, receiving_asset, trade_rate, receiving_asset, selling_asset, rate_in_profit_currency, self.profit_currency, selling_asset, gain_in_profit_currency, self.profit_currency, total_fee_in_profit_currency, self.profit_currency, tsToDate(timestamp, formatstr='%d/%m/%Y %H:%M:%S'))) # now search the buys for `paid_with_asset` and calculate profit/loss (taxable_amount, taxable_bought_cost, taxfree_bought_cost) = self.search_buys_calculate_profit( selling_amount, selling_asset, timestamp) general_profit_loss = 0 taxable_profit_loss = 0 # If we don't include crypto2crypto and we sell for crypto, stop here if receiving_asset not in FIAT_CURRENCIES and not self.include_crypto2crypto: return # calculate profit/loss if not loan_settlement or (loan_settlement and self.count_profit_for_settlements): taxable_gain = taxable_gain_for_sell( taxable_amount=taxable_amount, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, selling_amount=selling_amount, ) general_profit_loss = gain_in_profit_currency - ( taxfree_bought_cost + taxable_bought_cost + total_fee_in_profit_currency) taxable_profit_loss = taxable_gain - taxable_bought_cost # should never happen, should be stopped at the main loop assert timestamp <= self.query_end_ts, ( "Trade time > query_end_ts found in adding to sell event") # count profit/losses if we are inside the query period if timestamp >= self.query_start_ts: if loan_settlement: # If it's a loan settlement we are charged both the fee and the gain settlement_loss = gain_in_profit_currency + total_fee_in_profit_currency self.settlement_losses += settlement_loss if debug_enabled: logger.debug("Loan Settlement Loss: {} {}".format( settlement_loss, self.profit_currency)) elif debug_enabled: logger.debug("Taxable P/L: {} {} General P/L: {} {}".format( taxable_profit_loss, self.profit_currency, general_profit_loss, self.profit_currency, )) self.general_trade_profit_loss += general_profit_loss self.taxable_trade_profit_loss += taxable_profit_loss if loan_settlement: self.csv_exporter.add_loan_settlement( asset=selling_asset, amount=selling_amount, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, timestamp=timestamp, ) else: self.csv_exporter.add_sell( selling_asset=selling_asset, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, gain_in_profit_currency=gain_in_profit_currency, selling_amount=selling_amount, receiving_asset=receiving_asset, receiving_amount=receiving_amount, receiving_asset_rate_in_profit_currency=self. get_rate_in_profit_currency( receiving_asset, timestamp, ), taxable_amount=taxable_amount, taxable_bought_cost=taxable_bought_cost, timestamp=timestamp, is_virtual=is_virtual, )
def add_sell( self, selling_asset: Asset, selling_amount: FVal, receiving_asset: Optional[Asset], receiving_amount: Optional[FVal], gain_in_profit_currency: FVal, total_fee_in_profit_currency: Fee, trade_rate: FVal, rate_in_profit_currency: FVal, timestamp: Timestamp, loan_settlement: bool = False, is_virtual: bool = False, ) -> None: if selling_asset not in self.events: self.events[selling_asset] = Events(list(), list()) self.events[selling_asset].sells.append( SellEvent( amount=selling_amount, timestamp=timestamp, rate=rate_in_profit_currency, fee_rate=total_fee_in_profit_currency / selling_amount, gain=gain_in_profit_currency, ), ) self.handle_prefork_asset_sells(selling_asset, selling_amount, timestamp) if loan_settlement: log.debug( 'Loan Settlement Selling Event', sensitive_log=True, selling_amount=selling_amount, selling_asset=selling_asset, gain_in_profit_currency=gain_in_profit_currency, profit_currency=self.profit_currency, timestamp=timestamp, ) else: log.debug( 'Selling Event', sensitive_log=True, selling_amount=selling_amount, selling_asset=selling_asset, receiving_amount=receiving_amount, receiving_asset=receiving_asset, rate=trade_rate, rate_in_profit_currency=rate_in_profit_currency, profit_currency=self.profit_currency, gain_in_profit_currency=gain_in_profit_currency, fee_in_profit_currency=total_fee_in_profit_currency, timestamp=timestamp, ) # now search the buys for `paid_with_asset` and calculate profit/loss ( taxable_amount, taxable_bought_cost, taxfree_bought_cost, ) = self.search_buys_calculate_profit( selling_amount, selling_asset, timestamp, ) general_profit_loss = 0 taxable_profit_loss = 0 # If we don't include crypto2crypto and we sell for crypto, stop here if receiving_asset and not receiving_asset.is_fiat( ) and not self.include_crypto2crypto: return # calculate profit/loss if not loan_settlement or (loan_settlement and self.count_profit_for_settlements): taxable_gain = taxable_gain_for_sell( taxable_amount=taxable_amount, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, selling_amount=selling_amount, ) general_profit_loss = gain_in_profit_currency - ( taxfree_bought_cost + taxable_bought_cost + total_fee_in_profit_currency) taxable_profit_loss = taxable_gain - taxable_bought_cost # should never happen, should be stopped at the main loop assert timestamp <= self.query_end_ts, ( "Trade time > query_end_ts found in adding to sell event") # count profit/losses if we are inside the query period if timestamp >= self.query_start_ts: if loan_settlement: # If it's a loan settlement we are charged both the fee and the gain settlement_loss = gain_in_profit_currency + total_fee_in_profit_currency expected = rate_in_profit_currency * selling_amount + total_fee_in_profit_currency msg = ( f'Expected settlement loss mismatch. rate_in_profit_currency' f' ({rate_in_profit_currency}) * selling_amount' f' ({selling_amount}) + total_fee_in_profit_currency' f' ({total_fee_in_profit_currency}) != settlement_loss ' f'({settlement_loss})') assert expected == settlement_loss, msg self.settlement_losses += settlement_loss log.debug( 'Loan Settlement Loss', sensitive_log=True, settlement_loss=settlement_loss, profit_currency=self.profit_currency, ) else: log.debug( "After Sell Profit/Loss", sensitive_log=True, taxable_profit_loss=taxable_profit_loss, general_profit_loss=general_profit_loss, profit_currency=self.profit_currency, ) self.general_trade_profit_loss += general_profit_loss self.taxable_trade_profit_loss += taxable_profit_loss if loan_settlement: self.csv_exporter.add_loan_settlement( asset=selling_asset, amount=selling_amount, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, timestamp=timestamp, ) else: assert receiving_asset, 'Here receiving asset should have a value' self.csv_exporter.add_sell( selling_asset=selling_asset, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, gain_in_profit_currency=gain_in_profit_currency, selling_amount=selling_amount, receiving_asset=receiving_asset, receiving_amount=receiving_amount, receiving_asset_rate_in_profit_currency=self. get_rate_in_profit_currency( receiving_asset, timestamp, ), taxable_amount=taxable_amount, taxable_bought_cost=taxable_bought_cost, timestamp=timestamp, is_virtual=is_virtual, )
def add_buy(self, bought_asset, bought_amount, paid_with_asset, trade_rate, trade_fee, fee_currency, timestamp, is_virtual=False): paid_with_asset_rate = self.get_rate_in_profit_currency( paid_with_asset, timestamp) buy_rate = paid_with_asset_rate * trade_rate fee_price_in_profit_currency = 0 if trade_fee != 0: fee_price_in_profit_currency = self.price_historian.query_historical_price( fee_currency, self.profit_currency, timestamp) self.handle_prefork_acquisitions(bought_asset=bought_asset, bought_amount=bought_amount, paid_with_asset=paid_with_asset, trade_rate=trade_rate, trade_fee=trade_fee, fee_currency=fee_currency, timestamp=timestamp) if bought_asset not in self.events: self.events[bought_asset] = Events(list(), list()) fee_cost = fee_price_in_profit_currency * trade_fee gross_cost = bought_amount * buy_rate cost = gross_cost + fee_cost self.events[bought_asset].buys.append( BuyEvent(amount=bought_amount, timestamp=timestamp, rate=buy_rate, fee_rate=fee_cost / bought_amount, cost=cost)) log.debug( 'Buy Event', sensitive_log=True, bought_amount=bought_amount, bought_asset=bought_asset, paid_with_asset=paid_with_asset, rate=trade_rate, rate_in_profit_currency=buy_rate, profit_currency=self.profit_currency, timestamp=timestamp, ) self.csv_exporter.add_buy( bought_asset=bought_asset, rate=buy_rate, fee_cost=fee_cost, amount=bought_amount, gross_cost=gross_cost, cost=cost, paid_with_asset=paid_with_asset, paid_with_asset_rate=paid_with_asset_rate, timestamp=timestamp, is_virtual=is_virtual, )
def add_buy( self, bought_asset: Asset, bought_amount: FVal, paid_with_asset: Asset, trade_rate: FVal, fee_in_profit_currency: Fee, fee_currency: Asset, timestamp: Timestamp, is_virtual: bool = False, ) -> None: paid_with_asset_rate = self.get_rate_in_profit_currency( paid_with_asset, timestamp) buy_rate = paid_with_asset_rate * trade_rate self.handle_prefork_asset_buys( bought_asset=bought_asset, bought_amount=bought_amount, paid_with_asset=paid_with_asset, trade_rate=trade_rate, fee_in_profit_currency=fee_in_profit_currency, fee_currency=fee_currency, timestamp=timestamp, ) if bought_asset not in self.events: self.events[bought_asset] = Events(list(), list()) gross_cost = bought_amount * buy_rate cost_in_profit_currency = gross_cost + fee_in_profit_currency self.events[bought_asset].buys.append( BuyEvent( amount=bought_amount, timestamp=timestamp, rate=buy_rate, fee_rate=fee_in_profit_currency / bought_amount, ), ) log.debug( 'Buy Event', sensitive_log=True, bought_amount=bought_amount, bought_asset=bought_asset, paid_with_asset=paid_with_asset, rate=trade_rate, rate_in_profit_currency=buy_rate, profit_currency=self.profit_currency, timestamp=timestamp, ) if timestamp >= self.query_start_ts: self.csv_exporter.add_buy( bought_asset=bought_asset, rate=buy_rate, fee_cost=fee_in_profit_currency, amount=bought_amount, cost=cost_in_profit_currency, paid_with_asset=paid_with_asset, paid_with_asset_rate=paid_with_asset_rate, timestamp=timestamp, is_virtual=is_virtual, )
def add_sell( self, selling_asset, selling_amount, receiving_asset, receiving_amount, gain_in_profit_currency, total_fee_in_profit_currency, trade_rate, rate_in_profit_currency, timestamp, loan_settlement=False, is_virtual=False, ): if selling_asset not in self.events: self.events[selling_asset] = Events(list(), list()) self.events[selling_asset].sells.append( SellEvent( amount=selling_amount, timestamp=timestamp, rate=rate_in_profit_currency, fee_rate=total_fee_in_profit_currency / selling_amount, gain=gain_in_profit_currency, )) if loan_settlement: log.debug( 'Loan Settlement Selling Event', sensitive_log=True, selling_amount=selling_amount, selling_asset=selling_asset, gain_in_profit_currency=gain_in_profit_currency, profit_currency=self.profit_currency, timestamp=timestamp, ) else: log.debug( 'Selling Event', sensitive_log=True, selling_amount=selling_amount, selling_asset=selling_asset, receiving_amount=receiving_amount, receiving_asset=receiving_asset, rate=trade_rate, rate_in_profit_currency=rate_in_profit_currency, profit_currency=self.profit_currency, gain_in_profit_currency=gain_in_profit_currency, fee_in_profit_currency=total_fee_in_profit_currency, timestamp=timestamp, ) # now search the buys for `paid_with_asset` and calculate profit/loss (taxable_amount, taxable_bought_cost, taxfree_bought_cost) = self.search_buys_calculate_profit( selling_amount, selling_asset, timestamp) general_profit_loss = 0 taxable_profit_loss = 0 # If we don't include crypto2crypto and we sell for crypto, stop here if receiving_asset not in FIAT_CURRENCIES and not self.include_crypto2crypto: return # calculate profit/loss if not loan_settlement or (loan_settlement and self.count_profit_for_settlements): taxable_gain = taxable_gain_for_sell( taxable_amount=taxable_amount, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, selling_amount=selling_amount, ) general_profit_loss = gain_in_profit_currency - ( taxfree_bought_cost + taxable_bought_cost + total_fee_in_profit_currency) taxable_profit_loss = taxable_gain - taxable_bought_cost # should never happen, should be stopped at the main loop assert timestamp <= self.query_end_ts, ( "Trade time > query_end_ts found in adding to sell event") # count profit/losses if we are inside the query period if timestamp >= self.query_start_ts: if loan_settlement: # If it's a loan settlement we are charged both the fee and the gain settlement_loss = gain_in_profit_currency + total_fee_in_profit_currency self.settlement_losses += settlement_loss log.debug( 'Loan Settlement Loss', sensitive_log=True, settlement_loss=settlement_loss, profit_currency=self.profit_currency, ) else: log.debug( "After Sell Profit/Loss", sensitive_log=True, taxable_profit_loss=taxable_profit_loss, general_profit_loss=general_profit_loss, profit_currency=self.profit_currency, ) self.general_trade_profit_loss += general_profit_loss self.taxable_trade_profit_loss += taxable_profit_loss if loan_settlement: self.csv_exporter.add_loan_settlement( asset=selling_asset, amount=selling_amount, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, timestamp=timestamp, ) else: self.csv_exporter.add_sell( selling_asset=selling_asset, rate_in_profit_currency=rate_in_profit_currency, total_fee_in_profit_currency=total_fee_in_profit_currency, gain_in_profit_currency=gain_in_profit_currency, selling_amount=selling_amount, receiving_asset=receiving_asset, receiving_amount=receiving_amount, receiving_asset_rate_in_profit_currency=self. get_rate_in_profit_currency( receiving_asset, timestamp, ), taxable_amount=taxable_amount, taxable_bought_cost=taxable_bought_cost, timestamp=timestamp, is_virtual=is_virtual, )