def test_no_corresponding_buy_for_sell(accountant, google_service): """Test that if there is no corresponding buy for a sell, the entire sell counts as profit""" history = [ Trade( timestamp=1476979735, location=Location.KRAKEN, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal(1), rate=FVal('2519.62'), fee=FVal('0.02'), fee_currency=A_EUR, link=None, ) ] accounting_history_process( accountant=accountant, start_ts=1436979735, end_ts=1519693374, history_list=history, ) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('2519.62'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-0.02'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_buying_selling_eth_before_daofork(accountant, google_service): history3 = [ Trade( timestamp=1446979735, # 11/08/2015 location=Location.EXTERNAL, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(1450), rate=FVal('0.2315893'), fee=None, fee_currency=None, link=None, ), Trade( # selling ETH prefork should also reduce our ETC amount timestamp=1461021812, # 18/04/2016 (taxable) location=Location.KRAKEN, base_asset=A_ETH, # cryptocompare hourly ETC/EUR price: 7.88 quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal(50), rate=FVal('7.88'), fee=FVal('0.5215'), fee_currency=A_EUR, link=None, ), Trade( # selling ETC after the fork timestamp=1481979135, # 17/12/2016 location=Location.KRAKEN, base_asset=A_ETC, # cryptocompare hourly ETC/EUR price: 7.88 quote_asset=A_EUR, trade_type=TradeType.SELL, # not-taxable -- considered bought with ETH so after year amount=FVal(550), rate=FVal('1.78'), fee=FVal('0.9375'), fee_currency=A_EUR, link=None, ), Trade( # selling ETH after the fork timestamp=1482138141, # 19/12/2016 location=Location.KRAKEN, base_asset=A_ETH, # cryptocompare hourly ETH/EUR price: 7.45 quote_asset=A_EUR, trade_type=TradeType.SELL, # not-taxable -- after 1 year amount=FVal(10), rate=FVal('7.45'), fee=FVal('0.12'), fee_currency=A_EUR, link=None, ), ] accounting_history_process(accountant, 1436979735, 1495751688, history3) no_message_errors(accountant.msg_aggregator) # make sure that the intermediate ETH sell before the fork reduced our ETC assert accountant.pots[0].cost_basis.get_calculated_asset_amount('ETC') == FVal(850) assert accountant.pots[0].cost_basis.get_calculated_asset_amount('ETH') == FVal(1390) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('382.4205350'), free=FVal('923.8099920')), AccountingEventType.FEE: PNL(taxable=FVal('-1.579'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_selling_crypto_bought_with_crypto(accountant, google_service): history = [ Trade( timestamp=1446979735, location=Location.EXTERNAL, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(82), rate=FVal('268.678317859'), fee=None, fee_currency=None, link=None, ), Trade( timestamp=1449809536, # cryptocompare hourly BTC/EUR price: 386.175 location=Location.POLONIEX, base_asset=A_XMR, # cryptocompare hourly XMR/EUR price: 0.39665 quote_asset=A_BTC, trade_type=TradeType.BUY, amount=FVal(375), rate=FVal('0.0010275'), fee=FVal('0.9375'), fee_currency=A_XMR, link=None, ), Trade( timestamp= 1458070370, # cryptocompare hourly rate XMR/EUR price: 1.0443027675 location=Location.KRAKEN, base_asset=A_XMR, quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal(45), rate=FVal('1.0443027675'), fee=FVal('0.117484061344'), fee_currency=A_XMR, link=None, ), ] accounting_history_process(accountant, 1436979735, 1495751688, history) no_message_errors(accountant.msg_aggregator) # Make sure buying XMR with BTC also creates a BTC sell sells = accountant.pots[0].cost_basis.get_events(A_BTC).spends assert len(sells) == 1 assert sells[0].timestamp == 1449809536 assert sells[0].amount.is_close(FVal('0.3853125')) assert sells[0].rate.is_close(FVal('386.03406326')) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('74.3118704999540625'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-0.419658351381311222'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_no_taxfree_period(accountant, google_service): accounting_history_process(accountant, 1436979735, 1519693374, history5) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('265253.1283582327833875'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-0.238868129979988140934107'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_simple_accounting(accountant, google_service): accounting_history_process(accountant, 1436979735, 1495751688, history1) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('559.6947154'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-0.23886813'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_nocrypto2crypto(accountant, google_service): accounting_history_process(accountant, 1436979735, 1519693374, history5) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=ZERO, free=FVal('264693.433642820')), AccountingEventType.FEE: PNL(taxable=FVal('-1.1708853227087498964'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_fees_count_in_cost_basis(accountant, google_service): """Make sure that asset amounts used in fees are reduced.""" history = [ Trade( timestamp=1609537953, location=Location.KRAKEN, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=ONE, rate=FVal('598.26'), fee=ONE, fee_currency=A_EUR, link=None, ), Trade( # PNL: 0.5 * 1862.06 - 0.5 * 599.26 -> 631.4 # fee: -0.5 * 1862.06 + 0.5 * 1862.06 - 0.5 * 599.26 -> -299.63 timestamp=1624395186, location=Location.KRAKEN, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('0.5'), rate=FVal('1862.06'), fee=FVal('0.5'), fee_currency=A_ETH, link=None, ), Trade( timestamp=1625001464, location=Location.KRAKEN, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('0.5'), rate=FVal('1837.31'), fee=None, fee_currency=None, link=None, ), ] accounting_history_process( accountant=accountant, start_ts=1436979735, end_ts=1625001466, history_list=history, ) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('1550.055'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-300.630'), free=ZERO), }) assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_ETH) is None warnings = accountant.msg_aggregator.consume_warnings() assert len(warnings) == 0 check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_kfee_price_in_accounting(accountant, google_service): """ Test that KFEEs are correctly handled during accounting KFEE price is fixed at $0.01 """ history = [ LedgerAction( identifier=0, timestamp=Timestamp(1539713238), # 178.615 EUR/ETH action_type=LedgerActionType.INCOME, location=Location.KRAKEN, amount=FVal(1), asset=A_ETH, rate=None, rate_asset=None, link=None, notes='', ), LedgerAction( identifier=0, timestamp=Timestamp(1539713238), # 0.8612 USD/EUR. 1 KFEE = $0.01 so 8.612 EUR action_type=LedgerActionType.INCOME, location=Location.KRAKEN, amount=FVal(1000), asset=A_KFEE, rate=None, rate_asset=None, link=None, notes='', ), Trade( timestamp=1609537953, location=Location.KRAKEN, # 0.89 USDT/EUR -> PNL: 20 * 0.89 - 0.02*178.615 -> 14.2277 base_asset=A_ETH, quote_asset=A_USDT, trade_type=TradeType.SELL, amount=FVal('0.02'), rate=FVal(1000), fee=FVal(30), # KFEE should not be taken into account fee_currency=A_KFEE, link=None, ), ] accounting_history_process( accountant, start_ts=1539713238, end_ts=1624395187, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=ZERO, free=FVal('14.2277')), AccountingEventType.LEDGER_ACTION: PNL(taxable=FVal('187.227'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_big_taxfree_period(accountant, google_service): accounting_history_process(accountant, 1436979735, 1519693374, history5) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=ZERO, free=FVal('265253.1283582327833875')), AccountingEventType.FEE: PNL( taxable=FVal('-1.170885322708749896'), free=FVal('0.932017192728761755465893'), ), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_fees_in_received_asset(accountant, google_service): """ Test the sell trade where the fee is nominated in the asset received. We had a bug where the PnL report said that there was no documented acquisition. """ history = [ LedgerAction( identifier=0, timestamp=Timestamp(1539713238), # 178.615 EUR/ETH action_type=LedgerActionType.INCOME, location=Location.BINANCE, amount=ONE, asset=A_ETH, rate=None, rate_asset=None, link=None, notes='', ), Trade( # Sell 0.02 ETH for USDT with rate 1000 USDT/ETH and 0.10 USDT fee # So acquired 20 USDT for 0.02 ETH + 0.10 USDT # So acquired 20 USDT for 0.02 * 598.26 + 0.10 * 0.89 -> 12.0542 EUR # So paid 12.0542/20 -> 0.60271 EUR/USDT timestamp=1609537953, # 0.89 EUR/USDT location=Location.BINANCE, base_asset=A_ETH, # 598.26 EUR/ETH quote_asset=A_USDT, trade_type=TradeType.SELL, amount=FVal('0.02'), rate=FVal(1000), fee=FVal('0.10'), fee_currency=A_USDT, link=None, ), ] accounting_history_process( accountant, start_ts=1539713238, end_ts=1624395187, history_list=history, ) no_message_errors(accountant.msg_aggregator) assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_USDT.identifier).is_close('19.90') # noqa: E501 expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=ZERO, free=FVal('14.2277')), AccountingEventType.FEE: PNL(taxable=FVal('-0.060271'), free=ZERO), AccountingEventType.LEDGER_ACTION: PNL(taxable=FVal('178.615'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_receiving_value_from_tx(accountant, google_service): """ Test that receiving a transaction that provides value works fine """ addr2 = make_ethereum_address() tx_hash = '0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a' history = [ HistoryBaseEntry( event_identifier=tx_hash, sequence_index=0, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=make_ethereum_address(), asset=A_ETH, balance=Balance(amount=FVal('1.5')), notes=f'Received 1.5 ETH from {addr2}', event_type=HistoryEventType.RECEIVE, event_subtype=HistoryEventSubType.NONE, counterparty=addr2, ) ] accounting_history_process( accountant, start_ts=0, end_ts=1640493376, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRANSACTION_EVENT: PNL(taxable=FVal('242.385'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_taxable_ledger_action_setting(accountant, expected, google_service): """Test that ledger actions respect the taxable setting""" history = [ LedgerAction( identifier=1, timestamp=1476979735, action_type=LedgerActionType.INCOME, location=Location.EXTERNAL, amount=FVal(1), # 578.505 EUR/BTC from mocked prices asset=A_BTC, rate=None, rate_asset=None, link=None, notes=None, ), LedgerAction( identifier=2, timestamp=1491062063, action_type=LedgerActionType.AIRDROP, location=Location.EXTERNAL, amount=FVal(10), # 47.865 EUR/ETH from mocked prices asset=A_ETH, rate=None, rate_asset=None, link='foo', notes='boo', ), LedgerAction( identifier=3, timestamp=1501062063, action_type=LedgerActionType.LOSS, location=Location.BLOCKCHAIN, amount=FVal(2), # 175.44 EUR/ETH from mocked prices asset=A_ETH, rate=FVal(400), # but should use the given rate of 400 EUR rate_asset=A_EUR, link='goo', notes='hoo', ), LedgerAction( # include a non taxed ledger action too identifier=4, timestamp=1501062064, action_type=LedgerActionType.EXPENSE, location=Location.BLOCKCHAIN, amount=FVal(1), asset=A_ETH, rate=FVal(400), rate_asset=A_EUR, link='goo2', notes='hoo2', ), ] accounting_history_process( accountant=accountant, start_ts=1436979735, end_ts=1519693374, history_list=history, ) expected_pnls = PnlTotals() if expected != 0: expected_pnls[AccountingEventType.LEDGER_ACTION] = PNL(taxable=FVal(expected), free=ZERO) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_kraken_staking_events(accountant, google_service): """ Test that staking events from kraken are correctly processed """ history = [ HistoryBaseEntry( event_identifier='XXX', sequence_index=0, timestamp=1640493374000, location=Location.KRAKEN, location_label='Kraken 1', asset=A_ETH2, balance=Balance( amount=FVal(0.0000541090), usd_value=FVal(0.212353475950), ), notes=None, event_type=HistoryEventType.STAKING, event_subtype=HistoryEventSubType.REWARD, ), HistoryBaseEntry( event_identifier='YYY', sequence_index=0, timestamp=1636638550000, location=Location.KRAKEN, location_label='Kraken 1', asset=A_ETH2, balance=Balance( amount=FVal(0.0000541090), usd_value=FVal(0.212353475950), ), notes=None, event_type=HistoryEventType.STAKING, event_subtype=HistoryEventSubType.REWARD, ) ] _, events = accounting_history_process( accountant, start_ts=1636638549, end_ts=1640493376, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.STAKING: PNL(taxable=FVal('0.471505826'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service) assert len(events) == 2 expected_pnls = [FVal('0.25114638241'), FVal('0.22035944359')] for idx, event in enumerate(events): assert event.pnl.taxable == expected_pnls[idx] assert event.type == AccountingEventType.STAKING
def handle_prefork_asset_acquisitions( cost_basis: 'CostBasisCalculator', location: Location, timestamp: Timestamp, asset: Asset, amount: FVal, price: Price, starting_index: int, ) -> List['ProcessedAccountingEvent']: """ Calculate the prefork asset acquisitions, meaning how is the acquisition of ETC pre ETH fork handled etc. TODO: This should change for https://github.com/rotki/rotki/issues/1610 Returns the acquisition events to append to the pot """ acquisitions = [] if asset == A_ETH and timestamp < ETH_DAO_FORK_TS: acquisitions = [(A_ETC, 'Prefork acquisition for ETC')] elif asset == A_BTC and timestamp < BTC_BCH_FORK_TS: # Acquiring BTC before the BCH fork provides equal amount of BCH and BSV acquisitions = [ (A_BCH, 'Prefork acquisition for BCH'), (A_BSV, 'Prefork acquisition for BSV'), ] elif asset == A_BCH and timestamp < BCH_BSV_FORK_TS: # Acquiring BCH before the BSV fork provides equal amount of BSV acquisitions = [(A_BSV, 'Prefork acquisition for BSV')] events = [] for acquisition in acquisitions: event = ProcessedAccountingEvent( type=AccountingEventType.PREFORK_ACQUISITION, notes=acquisition[1], location=location, timestamp=timestamp, asset=acquisition[0], taxable_amount=amount, free_amount=ZERO, price=price, pnl=PNL(), cost_basis=None, index=starting_index, ) cost_basis.obtain_asset(event) events.append(event) starting_index += 1 return events
def calculate_pnl( self, count_entire_amount_spend: bool, count_cost_basis_pnl: bool, ) -> PNL: """Calculate PnL for this event and return it. Only called for events that should have PnL counted If count_entire_amount_spend is True then the entire amount is counted as a spend. Which means an expense (negative pnl). If count_cost_basis_pnl is True then the PnL between buying the asset amount and spending it is calculated and added to PnL. """ self.count_entire_amount_spend = count_entire_amount_spend self.count_cost_basis_pnl = count_cost_basis_pnl taxable_bought_cost = taxfree_bought_cost = ZERO taxable_value = self.taxable_amount * self.price free_value = self.free_amount * self.price self.pnl = PNL() if count_entire_amount_spend: # for fees and other types we also need to consider the entire amount as spent self.pnl -= PNL(taxable=taxable_value + free_value, free=ZERO) if self.asset.is_fiat() or count_cost_basis_pnl is False: return self.pnl # no need to calculate spending pnl if asset is fiat if self.cost_basis is not None: taxable_bought_cost = self.cost_basis.taxable_bought_cost taxfree_bought_cost = self.cost_basis.taxfree_bought_cost self.pnl += PNL( taxable=taxable_value - taxable_bought_cost, free=free_value - taxfree_bought_cost, ) return self.pnl
def test_ignored_assets(accountant, google_service): history = history1 + [ Trade( timestamp=1476979735, location=Location.KRAKEN, base_asset=A_DASH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(10), rate=FVal('9.76775956284'), fee=FVal('0.0011'), fee_currency=A_DASH, link=None, ), Trade( timestamp=1496979735, location=Location.KRAKEN, base_asset=A_DASH, quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal(5), rate=FVal('128.09'), fee=FVal('0.015'), fee_currency=A_EUR, link=None, ), ] accounting_history_process(accountant, 1436979735, 1519693374, history) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('559.6947154127833875'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-0.238868129979988140934107'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_gas_fees_after_year(accountant, google_service): """ Test that for an expense like gas fees after year the "selling" part is tax free PnL, and the expense part is taxable pnl. """ tx_hash = '0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a' history = [ LedgerAction( identifier=0, timestamp=Timestamp(1539713238), # 178.615 EUR/ETH action_type=LedgerActionType. GIFT, # gift so not counting as income location=Location.KRAKEN, amount=FVal(1), asset=A_ETH, rate=None, rate_asset=None, link=None, notes='', ), HistoryBaseEntry( event_identifier=tx_hash, sequence_index=0, timestamp=1640493374000, # 4072.51 EUR/ETH location=Location.BLOCKCHAIN, location_label=make_ethereum_address(), asset=A_ETH, balance=Balance(amount=FVal('0.01')), notes='Burned 0.01 ETH in gas', event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_GAS, ) ] accounting_history_process( accountant, start_ts=0, end_ts=1640493376, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRANSACTION_EVENT: PNL(taxable=FVal('-40.7251'), free=FVal('38.93895')), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_eth2_staking(accountant, google_service): """Test that ethereum 2 staking is accounted for properly""" history = [ ValidatorDailyStats( validator_index=1, timestamp=1607727600, # ETH price: 449.68 ETH/EUR start_amount=FVal('32'), end_amount=FVal('32.05'), pnl=FVal('0.05'), # 0.05 * 449.68 = 22.484 ), ValidatorDailyStats( validator_index=1, timestamp=1607814000, # ETH price: 469.82 ETH/EUR start_amount=FVal('32.05'), end_amount=FVal('32.045'), pnl=FVal( '-0.005' ), # -0.005 * 469.82 + 0.005 * 469.82 - 0.005*449.68 = -2.2484 ), ValidatorDailyStats( validator_index=1, timestamp=1607900400, # ETH price: 486.57 ETH/EUR start_amount=FVal('32.045'), end_amount=FVal('32.085'), pnl=FVal('0.04'), # 0.04 * 486.57 = 19.4628 ), ValidatorDailyStats( validator_index=2, timestamp=1607900400, start_amount=FVal('32'), end_amount=FVal('32.045'), pnl=FVal('0.045'), # 0.045 * 486.57 = 21.89565 ), ] accounting_history_process( accountant, start_ts=1606727600, end_ts=1640493376, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ # 22.484 - 2.2484 + 19.4628 + 21.89565 AccountingEventType.STAKING: PNL(taxable=FVal('61.59405'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_include_gas_costs(accountant, google_service): addr1 = '0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12' tx_hash = '0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a' history = [ Trade( timestamp=1539388574, location=Location.EXTERNAL, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(10), rate=FVal('168.7'), fee=None, fee_currency=None, link=None, ), HistoryBaseEntry( event_identifier=tx_hash, sequence_index=0, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=addr1, asset=A_ETH, balance=Balance(amount=FVal('0.000030921')), notes=f'Burned 0.000030921 ETH in gas from {addr1}', event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_GAS, ) ] accounting_history_process(accountant, start_ts=1436979735, end_ts=1619693374, history_list=history) # noqa: E501 no_message_errors(accountant.msg_aggregator) expected = ZERO expected_pnls = PnlTotals() if accountant.pots[0].settings.include_gas_costs: expected = FVal('-0.0052163727') expected_pnls[AccountingEventType.TRANSACTION_EVENT] = PNL( taxable=expected, free=ZERO) check_pnls_and_csv(accountant, expected_pnls, google_service)
def deserialize_from_db(cls: Type[T], timestamp: Timestamp, stringified_json: str) -> T: """May raise: - DeserializationError if something is wrong with reading this from the DB """ try: data = json.loads(stringified_json) except json.decoder.JSONDecodeError as e: raise DeserializationError( f'Could not decode processed accounting event json from the DB due to {str(e)}', ) from e try: pnl_taxable = deserialize_fval(data['pnl_taxable'], name='pnl_taxable', location='processed event decoding') # noqa: E501 pnl_free = deserialize_fval(data['pnl_free'], name='pnl_free', location='processed event decoding') # noqa: E501 if data['cost_basis'] is None: cost_basis = None else: cost_basis = CostBasisInfo.deserialize(data['cost_basis']) event = cls( type=AccountingEventType.deserialize(data['type']), notes=data['notes'], location=Location.deserialize(data['location']), timestamp=timestamp, asset=Asset(data['asset']), free_amount=deserialize_fval(data['free_amount'], name='free_amount', location='processed event decoding'), # noqa: E501 taxable_amount=deserialize_fval(data['taxable_amount'], name='taxable_amount', location='processed event decoding'), # noqa: E501 price=deserialize_price(data['price']), pnl=PNL(free=pnl_free, taxable=pnl_taxable), cost_basis=cost_basis, index=data['index'], extra_data=data['extra_data'], ) event.count_cost_basis_pnl = data['count_cost_basis_pnl'] event.count_entire_amount_spend = data['count_entire_amount_spend'] return event except KeyError as e: raise DeserializationError(f'Could not decode processed accounting event json from the DB due to missing key {str(e)}') from e # noqa: E501
def test_margin_events_affect_gained_lost_amount(accountant, google_service): history = [ Trade( timestamp=1476979735, location=Location.KRAKEN, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(5), rate=FVal('578.505'), fee=FVal('0.0012'), fee_currency=A_BTC, link=None, ), Trade( # 2519.62 - 0.02 - ((0.0012*578.505)/5 + 578.505) timestamp=1476979735, location=Location.KRAKEN, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal(1), rate=FVal('2519.62'), fee=FVal('0.02'), fee_currency=A_EUR, link=None, ), ] history += [ MarginPosition( location=Location.POLONIEX, # BTC/EUR: 810.49 open_time=1484438400, # 15/01/2017 close_time=1484629704, # 17/01/2017 profit_loss=FVal('-0.5'), pl_currency=A_BTC, fee=FVal('0.001'), fee_currency=A_BTC, link='1', notes='margin1', ), MarginPosition( location=Location.POLONIEX, # BTC/EUR: 979.39 open_time=1487116800, # 15/02/2017 close_time=1487289600, # 17/02/2017 profit_loss=FVal('0.25'), pl_currency=A_BTC, fee=FVal('0.001'), fee_currency=A_BTC, link='2', notes='margin2', ) ] accounting_history_process( accountant=accountant, start_ts=1436979735, end_ts=1519693374, history_list=history, ) no_message_errors(accountant.msg_aggregator) assert accountant.pots[0].cost_basis.get_calculated_asset_amount( 'BTC').is_close('3.7468') expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('1940.9761588'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-1.87166029184'), free=ZERO), AccountingEventType.MARGIN_POSITION: PNL(taxable=FVal('-44.47442060'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_ignored_transactions(accountant, google_service): addr1 = '0x2B888954421b424C5D3D9Ce9bB67c9bD47537d12' tx_hash = '0x5cc0e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde7a' ignored_tx_hash = '0x1000e6e62753551313412492296d5e57bea0a9d1ce507cc96aa4aa076c5bde11' accountant.db.add_to_ignored_action_ids( action_type=ActionType.ETHEREUM_TRANSACTION, identifiers=[ignored_tx_hash], ) history = [ Trade( timestamp=1539388574, location=Location.EXTERNAL, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(10), rate=FVal('168.7'), fee=None, fee_currency=None, link=None, ), HistoryBaseEntry( identifier= 'uniqueid1', # should normally be given by DB at write time event_identifier=tx_hash, sequence_index=0, timestamp=1569924574000, location=Location.BLOCKCHAIN, location_label=addr1, asset=A_ETH, balance=Balance(amount=FVal('0.000030921')), notes=f'Burned 0.000030921 ETH in gas from {addr1}', event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_GAS, ), HistoryBaseEntry( identifier= 'uniqueid2', # should normally be given by DB at write time event_identifier=ignored_tx_hash, sequence_index=0, timestamp=1569934574000, location=Location.BLOCKCHAIN, location_label=addr1, asset=A_ETH, balance=Balance(amount=FVal('0.000040921')), notes=f'Burned 0.000040921 ETH in gas from {addr1}', event_type=HistoryEventType.SPEND, event_subtype=HistoryEventSubType.FEE, counterparty=CPT_GAS, ) ] _, events = accounting_history_process(accountant, start_ts=1436979735, end_ts=1619693374, history_list=history) # noqa: E501 assert len(events) == 3 no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals({ AccountingEventType.TRANSACTION_EVENT: PNL(taxable=FVal('-0.0052163727'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def test_ledger_actions_accounting(accountant, google_service): """Test for accounting for ledger actions Makes sure that Ledger actions are processed in accounting, range is respected and that they contribute to the "bought" amount per asset and that also if a rate is given then that is used instead of the queried price """ history = [LedgerAction( # before range - read only for amount not profit identifier=1, timestamp=1435979735, # 0.1 EUR per ETH action_type=LedgerActionType.INCOME, location=Location.EXTERNAL, asset=A_ETH, amount=AssetAmount(FVal(1)), rate=None, rate_asset=None, link=None, notes=None, ), LedgerAction( identifier=2, timestamp=1437279735, # 250 EUR per BTC action_type=LedgerActionType.INCOME, location=Location.BLOCKCHAIN, asset=A_BTC, amount=AssetAmount(FVal(1)), rate=FVal('400'), rate_asset=A_EUR, link='foo', notes='we give a rate here', ), LedgerAction( identifier=3, timestamp=1447279735, # 0.4 EUR per XMR action_type=LedgerActionType.DIVIDENDS_INCOME, location=Location.KRAKEN, asset=A_XMR, amount=AssetAmount(FVal(10)), rate=None, rate_asset=None, link=None, notes=None, ), LedgerAction( identifier=4, timestamp=1457279735, # 1 EUR per ETH action_type=LedgerActionType.EXPENSE, location=Location.EXTERNAL, asset=A_ETH, amount=AssetAmount(FVal('0.1')), rate=None, rate_asset=None, link=None, notes=None, ), LedgerAction( identifier=5, timestamp=1467279735, # 420 EUR per BTC action_type=LedgerActionType.LOSS, location=Location.EXTERNAL, asset=A_BTC, amount=AssetAmount(FVal('0.1')), rate=FVal(500), rate_asset=A_USD, link='foo2', notes='we give a rate here', ), LedgerAction( # after range and should be completely ignored identifier=6, timestamp=1529693374, action_type=LedgerActionType.EXPENSE, location=Location.EXTERNAL, asset=A_ETH, amount=AssetAmount(FVal('0.5')), rate=FVal(400), rate_asset=A_EUR, link='foo3', notes='we give a rate here too but doesnt matter', )] accounting_history_process( accountant=accountant, start_ts=1436979735, end_ts=1519693374, history_list=history, ) assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_BTC).is_close('0.9') assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_ETH).is_close('0.9') assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_XMR).is_close('10') expected_pnls = PnlTotals({ # 400 + 0.4*10 - 1*0.1 + 1*0.1 - 1*0.01 - 0.1*500*0.9004 + 0.1*500*0.9004 - 0.1* 400 AccountingEventType.LEDGER_ACTION: PNL(taxable=FVal('363.99'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def assert_csv_export( accountant: 'Accountant', expected_pnls: PnlTotals, google_service: Optional['GoogleService'] = None, ) -> None: """Test the contents of the csv export match the actual result If google_service exists then it's also uploaded to a sheet to check the formular rendering """ csvexporter = accountant.csvexporter if len(accountant.pots[0].processed_events) == 0: return # nothing to do for no events as no csv is generated with tempfile.TemporaryDirectory() as tmpdirname: tmpdir = Path(tmpdirname) # first make sure we export without formulas csvexporter.settings = csvexporter.settings._replace( pnl_csv_with_formulas=False) accountant.csvexporter.export( events=accountant.pots[0].processed_events, pnls=accountant.pots[0].pnls, directory=tmpdir, ) calculated_pnls = PnlTotals() expected_csv_data = [] with open(tmpdir / FILENAME_ALL_CSV, newline='') as csvfile: reader = csv.DictReader(csvfile) for row in reader: expected_csv_data.append(row) if row['type'] == '': continue # have summaries and reached the end event_type = AccountingEventType.deserialize(row['type']) taxable = FVal(row['pnl_taxable']) free = FVal(row['pnl_free']) if taxable != ZERO or free != ZERO: calculated_pnls[event_type] += PNL(taxable=taxable, free=free) assert_pnl_totals_close(expected_pnls, calculated_pnls) # export with formulas and summary csvexporter.settings = csvexporter.settings._replace( pnl_csv_with_formulas=True, pnl_csv_have_summary=True) # noqa: E501 accountant.csvexporter.export( events=accountant.pots[0].processed_events, pnls=accountant.pots[0].pnls, directory=tmpdir, ) index = CSV_INDEX_OFFSET at_summaries = False to_upload_data = [] with open(tmpdir / FILENAME_ALL_CSV, newline='') as csvfile: reader = csv.DictReader(csvfile) for row in reader: to_upload_data.append(row) if at_summaries: _check_summaries_row(row, accountant) continue if row['type'] == '': at_summaries = True continue # have summaries and reached the end if row['pnl_taxable'] != '0': value = f'G{index}*H{index}' if row['type'] == AccountingEventType.TRADE and 'Amount out' in row[ 'notes']: assert row['pnl_taxable'] == f'={value}-J{index}' elif row['type'] == AccountingEventType.FEE: assert row[ 'pnl_taxable'] == f'={value}+{value}-J{index}' if row['pnl_free'] != '0': value = f'F{index}*H{index}' if row['type'] == AccountingEventType.TRADE and 'Amount out' in row[ 'notes']: assert row['pnl_free'] == f'={value}-L{index}' elif row['type'] == AccountingEventType.FEE: assert row['pnl_free'] == f'={value}+{value}-:{index}' index += 1 if google_service is not None: upload_csv_and_check( service=google_service, csv_data=to_upload_data, expected_csv_data=expected_csv_data, expected_pnls=expected_pnls, )
def test_buying_selling_btc_before_bchfork(accountant, google_service): history = [ Trade( timestamp=1491593374, # 04/07/2017 location=Location.EXTERNAL, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal('6.5'), rate=FVal('1128.905'), fee=FVal('0.55'), fee_currency=A_EUR, link=None, ), Trade( # selling BTC prefork should also reduce the BCH equivalent -- taxable timestamp=1500595200, # 21/07/2017 location=Location.EXTERNAL, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('0.5'), rate=FVal('2380.835'), fee=FVal('0.15'), fee_currency=A_EUR, link=None, ), Trade( # selling BCH after the fork -- taxable timestamp=1512693374, # 08/12/2017 location=Location.KRAKEN, base_asset=A_BCH, # cryptocompare hourly BCH/EUR price: 995.935 quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('2.1'), rate=FVal('995.935'), fee=FVal('0.26'), fee_currency=A_EUR, link=None, ), Trade( timestamp=1514937600, # 03/01/2018 location=Location.KRAKEN, base_asset=A_BTC, # cryptocompare hourly BCH/EUR price: 995.935 quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('1.2'), rate=FVal('12404.88'), fee=FVal('0.52'), fee_currency=A_EUR, link=None, ), ] accounting_history_process(accountant, 1436979735, 1519693374, history) no_message_errors(accountant.msg_aggregator) amount_bch = FVal(3.9) amount_btc = FVal(4.8) buys = accountant.pots[0].cost_basis.get_events(A_BCH).acquisitions assert len(buys) == 1 assert buys[0].remaining_amount == amount_bch assert buys[0].timestamp == 1491593374 assert buys[0].rate.is_close('1128.98961538') assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_BCH) == amount_bch assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_BTC) == amount_btc expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('13877.57646153846153846153846'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-1.48'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
if expected != ZERO: expected_pnls[AccountingEventType.ASSET_MOVEMENT] = PNL( taxable=expected, free=ZERO) # noqa: E501 check_pnls_and_csv(accountant, expected_pnls, google_service) @pytest.mark.parametrize('mocked_price_queries', [prices]) @pytest.mark.parametrize('db_settings, expected', [ ( { 'calculate_past_cost_basis': False, 'taxfree_after_period': -1 }, PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('2292.44'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-0.01'), free=ZERO), }), ), ( { 'calculate_past_cost_basis': True, 'taxfree_after_period': -1 }, PnlTotals({ AccountingEventType.TRADE: PNL(taxable=FVal('1755.083364282'), free=ZERO), AccountingEventType.FEE: PNL(taxable=FVal('-0.01'), free=ZERO), }),
def test_buying_selling_bch_before_bsvfork(accountant, google_service): history = [ Trade( # 6.5 BTC 6.5 BCH 6.5 BSV timestamp=1491593374, # 04/07/2017 location=Location.EXTERNAL, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal('6.5'), rate=FVal('1128.905'), fee=FVal('0.55'), fee_currency=A_EUR, link=None, ), Trade( # selling BTC prefork should also reduce the BCH and BSV equivalent -- taxable # 6 BTC 6 BCH 6 BSV timestamp=1500595200, # 21/07/2017 location=Location.EXTERNAL, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('0.5'), rate=FVal('2380.835'), fee=FVal('0.15'), fee_currency=A_EUR, link=None, ), Trade( # selling BCH after the fork should also reduce BSV equivalent -- taxable # 6 BTC 3.9 BCH 3.9 BSV timestamp=1512693374, # 08/12/2017 location=Location.KRAKEN, base_asset=A_BCH, # cryptocompare hourly BCH/EUR price: 995.935 quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('2.1'), rate=FVal('995.935'), fee=FVal('0.26'), fee_currency=A_EUR, link=None, ), Trade( # 4.8 BTC 3.9 BCH 3.9 BSV timestamp=1514937600, # 03/01/2018 location=Location.KRAKEN, base_asset=A_BTC, # cryptocompare hourly BCH/EUR price: 995.935 quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('1.2'), rate=FVal('12404.88'), fee=FVal('0.52'), fee_currency=A_EUR, link=None, ), Trade( # buying BCH before the BSV fork should increase BSV equivalent # 4.8 BTC 4.9 BCH 4.9 BSV timestamp=1524937600, location=Location.KRAKEN, base_asset=A_BCH, # cryptocompare hourly BCH/EUR price: 1146.98 quote_asset=A_EUR, trade_type=TradeType.BUY, amount=ONE, rate=FVal('1146.98'), fee=FVal('0.52'), fee_currency=A_EUR, link=None, ), Trade( # selling BCH before the BSV fork should decrease the BSV equivalent # 4.8 BTC 4.6 BCH 4.6 BSV timestamp=1525937600, location=Location.KRAKEN, base_asset=A_BCH, # cryptocompare hourly BCH/EUR price: 1146.98 quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('0.3'), rate=FVal('1272.05'), fee=FVal('0.52'), fee_currency=A_EUR, link=None, ), Trade( # selling BCH after the BSV fork should not affect the BSV equivalent # 4.8 BTC 4.1 BCH 4.6 BSV timestamp=1552304352, location=Location.KRAKEN, base_asset=A_BCH, # cryptocompare hourly BCH/EUR price: 114.27 quote_asset=A_EUR, trade_type=TradeType.SELL, amount=FVal('0.5'), rate=FVal('114.27'), fee=FVal('0.52'), fee_currency=A_EUR, link=None, ), ] accounting_history_process(accountant, 1436979735, 1569693374, history) no_message_errors(accountant.msg_aggregator) amount_btc = FVal(4.8) amount_bch = FVal(4.1) amount_bsv = FVal(4.6) bch_buys = accountant.pots[0].cost_basis.get_events(A_BCH).acquisitions assert len(bch_buys) == 2 assert sum(x.remaining_amount for x in bch_buys) == amount_bch assert bch_buys[0].timestamp == 1491593374 assert bch_buys[1].timestamp == 1524937600 bsv_buys = accountant.pots[0].cost_basis.get_events(A_BSV).acquisitions assert len(bsv_buys) == 2 assert sum(x.remaining_amount for x in bsv_buys) == amount_bsv assert bsv_buys[0].timestamp == 1491593374 assert bsv_buys[1].timestamp == 1524937600 assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_BCH) == amount_bch assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_BTC) == amount_btc assert accountant.pots[0].cost_basis.get_calculated_asset_amount(A_BSV) == amount_bsv expected_pnls = PnlTotals({ AccountingEventType.TRADE: PNL( taxable=FVal('13877.57646153846153846153846'), free=FVal('-464.4416923076923076923076920'), ), AccountingEventType.FEE: PNL(taxable=FVal('-3.04'), free=ZERO), }) check_pnls_and_csv(accountant, expected_pnls, google_service)
def add_spend( self, event_type: AccountingEventType, notes: str, location: Location, timestamp: Timestamp, asset: Asset, amount: FVal, taxable: bool, given_price: Optional[Price] = None, taxable_amount_ratio: FVal = ONE, count_entire_amount_spend: bool = True, count_cost_basis_pnl: bool = True, extra_data: Optional[Dict[str, Any]] = None, ) -> Tuple[FVal, FVal]: """Add an asset spend event for the pot and count it in PnL if needed If a custom price for the asset should be used it can be passed here via given_price. Price is always in profit currency during accounting. If taxable_ratio is given then this is how we initialize the taxable and free amounts in the case of missing cost basis. By default it's all taxable. If count_entire_amount_spend is True then the entire amount is counted as a spend. Which means an expense (negative pnl). If count_cost_basis_pnl is True then we also count any profit/loss the asset may have had compared to when it was acquired. Returns (free, taxable) amounts. """ if amount == ZERO: # do nothing for zero spends return ZERO, ZERO if asset.is_fiat() and event_type != AccountingEventType.FEE: taxable = False handle_prefork_asset_spends( cost_basis=self.cost_basis, asset=asset, amount=amount, timestamp=timestamp, ) if given_price is not None: price = given_price else: price = self.get_rate_in_profit_currency( asset=asset, timestamp=timestamp, ) if asset == A_KFEE: count_cost_basis_pnl = False taxable = False spend_cost = None if count_cost_basis_pnl: spend_cost = self.cost_basis.spend_asset( location=location, timestamp=timestamp, asset=asset, amount=amount, rate=price, taxable_spend=taxable, ) taxable_amount = taxable_amount_ratio * amount free_amount = amount - taxable_amount if spend_cost: taxable_amount = spend_cost.taxable_amount free_amount = amount - spend_cost.taxable_amount spend_event = ProcessedAccountingEvent( type=event_type, notes=notes, location=location, timestamp=timestamp, asset=asset, taxable_amount=taxable_amount, free_amount=free_amount, price=price, pnl=PNL(), # filled out later cost_basis=spend_cost, index=len(self.processed_events), ) if extra_data: spend_event.extra_data = extra_data # count profit/losses if we are inside the query period if timestamp >= self.query_start_ts and taxable: self.pnls[event_type] += spend_event.calculate_pnl( count_entire_amount_spend=count_entire_amount_spend, count_cost_basis_pnl=count_cost_basis_pnl, ) self._add_processed_event(spend_event) return free_amount, taxable_amount
def add_acquisition( self, # pylint: disable=unused-argument event_type: AccountingEventType, notes: str, location: Location, timestamp: Timestamp, asset: Asset, amount: FVal, taxable: bool, given_price: Optional[Price] = None, extra_data: Optional[Dict] = None, **kwargs: Any, # to be able to consume args given by add_asset_change_event ) -> None: """Add an asset acquisition event for the pot and count it in PnL if needed. If a custom price for the asset should be used it can be passed here via given_price. Price is always in profit currency during accounting.""" if amount == ZERO: # do nothing for zero acquisitions return if given_price is not None: price = given_price else: price = self.get_rate_in_profit_currency(asset=asset, timestamp=timestamp) prefork_events = handle_prefork_asset_acquisitions( cost_basis=self.cost_basis, location=location, timestamp=timestamp, asset=asset, amount=amount, price=price, starting_index=len(self.processed_events), ) for prefork_event in prefork_events: self._add_processed_event(prefork_event) event = ProcessedAccountingEvent( type=event_type, notes=notes, location=location, timestamp=timestamp, asset=asset, taxable_amount=amount, free_amount=ZERO, price=price, pnl=PNL(), # filled out later cost_basis=None, index=len(self.processed_events), ) if extra_data: event.extra_data = extra_data self.cost_basis.obtain_asset(event) # count profit/losses if we are inside the query period if timestamp >= self.query_start_ts and taxable: self.pnls[event_type] += event.calculate_pnl( count_entire_amount_spend=False, count_cost_basis_pnl=True, ) self._add_processed_event(event)
def test_assets_movements_not_accounted_for(accountant, expected, google_service): # asset_movements_list partially copied from # rotkehlchen/tests/integration/test_end_to_end_tax_report.py history = [ Trade( timestamp=1446979735, location=Location.EXTERNAL, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(82), rate=FVal('268.678317859'), fee=None, fee_currency=None, link=None, ), Trade( timestamp=1446979735, location=Location.EXTERNAL, base_asset=A_ETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(1450), rate=FVal('0.2315893'), fee=None, fee_currency=None, link=None, ), AssetMovement( # before query period -- 8.915 * 0.001 = 8.915e-3 location=Location.KRAKEN, category=AssetMovementCategory.WITHDRAWAL, address=None, transaction_id=None, timestamp=Timestamp(1479510304), # 18/11/2016, asset=A_ETH, # cryptocompare hourly ETH/EUR: 8.915 amount=FVal('95'), fee_asset=A_ETH, fee=Fee(FVal('0.001')), link='krakenid1', ), AssetMovement( # 0.00029*1964.685 = 0.56975865 location=Location.POLONIEX, address='foo', transaction_id='0xfoo', category=AssetMovementCategory.WITHDRAWAL, timestamp=Timestamp(1495969504), # 28/05/2017, asset=A_BTC, # cryptocompare hourly BTC/EUR: 1964.685 amount=FVal('8.5'), fee_asset=A_BTC, fee=Fee(FVal('0.00029')), link='poloniexid1', ), ] accounting_history_process( accountant=accountant, start_ts=1436979735, end_ts=1519693374, history_list=history, ) no_message_errors(accountant.msg_aggregator) expected_pnls = PnlTotals() if expected != ZERO: expected_pnls[AccountingEventType.ASSET_MOVEMENT] = PNL( taxable=expected, free=ZERO) # noqa: E501 check_pnls_and_csv(accountant, expected_pnls, google_service)