def deserialize_asset_amount_force_positive( amount: AcceptableFValInitInput) -> AssetAmount: """Acts exactly like deserialize_asset_amount but also forces the number to be positive Is needed for some places like some exchanges that list the withdrawal amounts as negative numbers because it's a withdrawal""" result = deserialize_asset_amount(amount) if result < ZERO: result = AssetAmount(abs(result)) return result
def _deserialize_trade(self, raw_result: List[Any]) -> Trade: """Process a trade result from Bitfinex and deserialize it. The base and quote assets are instantiated using the `fee_currency_symbol` (from raw_result[10]) over the pair (from raw_result[1]). Known pairs format: 'tETHUST', 'tETH:UST'. Can raise DeserializationError. Schema reference in: https://docs.bitfinex.com/reference#rest-auth-trades """ amount = deserialize_asset_amount(raw_result[4]) trade_type = TradeType.BUY if amount >= ZERO else TradeType.SELL bfx_pair = self._process_bfx_pair(raw_result[1]) if bfx_pair not in self.pair_bfx_symbols_map: raise DeserializationError( f'Could not deserialize bitfinex trade pair {raw_result[1]}. ' f'Raw trade: {raw_result}', ) bfx_base_asset_symbol, bfx_quote_asset_symbol = self.pair_bfx_symbols_map[bfx_pair] try: base_asset = asset_from_bitfinex( bitfinex_name=bfx_base_asset_symbol, currency_map=self.currency_map, ) quote_asset = asset_from_bitfinex( bitfinex_name=bfx_quote_asset_symbol, currency_map=self.currency_map, ) fee_asset = asset_from_bitfinex( bitfinex_name=raw_result[10], currency_map=self.currency_map, ) except (UnknownAsset, UnsupportedAsset) as e: asset_tag = 'Unknown' if isinstance(e, UnknownAsset) else 'Unsupported' raise DeserializationError( f'{asset_tag} {e.asset_name} found while processing trade pair due to: {str(e)}', ) from e trade = Trade( timestamp=Timestamp(int(raw_result[2] / 1000)), location=Location.BITFINEX, pair=TradePair(f'{base_asset.identifier}_{quote_asset.identifier}'), trade_type=trade_type, amount=AssetAmount(abs(amount)), rate=deserialize_price(raw_result[5]), fee=Fee(abs(deserialize_fee(raw_result[9]))), fee_currency=fee_asset, link=str(raw_result[0]), notes='', ) return trade
def add_ledger_action( self, action: LedgerAction, profit_loss_in_profit_currency: FVal, ) -> None: if not self.create_csv: return self.ledger_actions_csv.append({ 'time': self.timestamp_to_date(action.timestamp), 'type': str(action.action_type), 'location': str(action.location), 'asset': str(action.asset), 'amount': str(action.amount), f'profit_loss_in_{self.profit_currency.symbol}': profit_loss_in_profit_currency, }) paid_asset: Optional[Asset] received_asset: Optional[Asset] if action.is_profitable(): paid_in_profit_currency = ZERO paid_in_asset = ZERO paid_asset = None received_asset = action.asset received_in_asset = action.amount received_in_profit_currency = profit_loss_in_profit_currency else: paid_in_profit_currency = profit_loss_in_profit_currency paid_in_asset = action.amount paid_asset = action.asset received_asset = None received_in_asset = AssetAmount(ZERO) received_in_profit_currency = ZERO self.add_to_allevents( event_type=EV_LEDGER_ACTION, location=action.location, paid_in_profit_currency=paid_in_profit_currency, paid_asset=paid_asset, paid_in_asset=paid_in_asset, received_asset=received_asset, received_in_asset=received_in_asset, taxable_received_in_profit_currency=received_in_profit_currency, total_received_in_profit_currency=received_in_profit_currency, timestamp=action.timestamp, link=action.link, notes=action.notes, )
def trade_from_conversion(trade_a: Dict[str, Any], trade_b: Dict[str, Any]) -> Optional[Trade]: """Turn information from a conversion into a trade Mary raise: - UnknownAsset due to Asset instantiation - DeserializationError due to unexpected format of dict entries - KeyError due to dict entires missing an expected entry """ # Check that the status is complete if trade_a['status'] != 'completed': return None # Trade b will represent the asset we are converting to if trade_b['amount']['amount'].startswith('-'): trade_a, trade_b = trade_b, trade_a timestamp = deserialize_timestamp_from_date(trade_a['updated_at'], 'iso8601', 'coinbase') trade_type = deserialize_trade_type('sell') tx_amount = AssetAmount(abs(deserialize_asset_amount(trade_a['amount']['amount']))) tx_asset = asset_from_coinbase(trade_a['amount']['currency'], time=timestamp) native_amount = deserialize_asset_amount(trade_b['amount']['amount']) native_asset = asset_from_coinbase(trade_b['amount']['currency'], time=timestamp) amount = tx_amount # The rate is how much you get/give in quotecurrency if you buy/sell 1 unit of base currency rate = Price(native_amount / tx_amount) # Obtain fee amount in the native currency using data from both trades amount_after_fee = deserialize_asset_amount(trade_b['native_amount']['amount']) amount_before_fee = deserialize_asset_amount(trade_a['native_amount']['amount']) # amount_after_fee + amount_before_fee is a negative amount and the fee needs to be positive conversion_native_fee_amount = abs(amount_after_fee + amount_before_fee) if ZERO not in (tx_amount, conversion_native_fee_amount, amount_before_fee): # We have the fee amount in the native currency. To get it in the # converted asset we have to get the rate asset_native_rate = tx_amount / abs(amount_before_fee) fee_amount = Fee(conversion_native_fee_amount / asset_native_rate) else: fee_amount = Fee(ZERO) fee_asset = asset_from_coinbase(trade_a['amount']['currency'], time=timestamp) return Trade( timestamp=timestamp, location=Location.COINBASE, # in coinbase you are buying/selling tx_asset for native_asset base_asset=tx_asset, quote_asset=native_asset, trade_type=trade_type, amount=amount, rate=rate, fee=fee_amount, fee_currency=fee_asset, link=str(trade_a['trade']['id']), )
def add_ledger_action( self, action: LedgerAction, profit_loss_in_profit_currency: FVal, ) -> None: if not self.create_csv: return self.ledger_actions_csv.append({ 'time': timestamp_to_date(action.timestamp, formatstr='%d/%m/%Y %H:%M:%S'), 'type': str(action.action_type), 'location': str(action.location), 'asset': str(action.asset), 'amount': str(action.amount), f'profit_loss_in_{self.profit_currency.identifier}': profit_loss_in_profit_currency, }) paid_asset: Union[EmptyStr, Asset] received_asset: Union[EmptyStr, Asset] if action.is_profitable(): paid_in_profit_currency = ZERO paid_in_asset = ZERO paid_asset = S_EMPTYSTR received_asset = action.asset received_in_asset = action.amount received_in_profit_currency = profit_loss_in_profit_currency else: paid_in_profit_currency = profit_loss_in_profit_currency paid_in_asset = action.amount paid_asset = action.asset received_asset = S_EMPTYSTR received_in_asset = AssetAmount(ZERO) received_in_profit_currency = ZERO self.add_to_allevents( event_type=EV_LEDGER_ACTION, location=action.location, paid_in_profit_currency=paid_in_profit_currency, paid_asset=paid_asset, paid_in_asset=paid_in_asset, received_asset=received_asset, received_in_asset=received_in_asset, taxable_received_in_profit_currency=received_in_profit_currency, timestamp=action.timestamp, )
def _read_asset_movements(self, filepath: str) -> List[AssetMovement]: """Reads a csv account type report and extracts the AssetMovements""" with open(filepath, newline='') as csvfile: reader = csv.DictReader(csvfile) movements = [] for row in reader: try: if row['type'] in ('withdrawal', 'deposit'): timestamp = deserialize_timestamp_from_date( row['time'], 'iso8601', 'coinbasepro', ) amount = deserialize_asset_amount(row['amount']) if row['type'] == 'withdrawal': # For withdrawals the withdraw amount is negative amount = AssetAmount(amount * FVal('-1')) asset = Asset(row['amount/balance unit']) movements.append( AssetMovement( location=Location.COINBASEPRO, category=deserialize_asset_movement_category( row['type']), timestamp=timestamp, asset=asset, amount=amount, fee_asset=asset, # I don't see any fee in deposit withdrawals in coinbasepro fee=Fee(ZERO), link=str(row['transfer id']), )) except UnknownAsset as e: self.msg_aggregator.add_warning( f'Found unknown Coinbasepro asset {e.asset_name}. ' f'Ignoring its deposit/withdrawal.', ) continue except (DeserializationError, KeyError) as e: msg = str(e) if isinstance(e, KeyError): msg = f'Missing key entry for {msg}.' self.msg_aggregator.add_error( 'Failed to deserialize a Coinbasepro deposit/withdrawal. ' 'Check logs for details. Ignoring it.', ) log.error( 'Error processing a coinbasepro deposit/withdrawal.', raw_asset_movement=row, error=msg, ) continue return movements
def _deserialize_transaction(grant_id: int, rawtx: Dict[str, Any]) -> LedgerAction: """May raise: - DeserializationError - KeyError - UnknownAsset """ timestamp = deserialize_timestamp_from_date( date=rawtx['timestamp'], formatstr='%Y-%m-%dT%H:%M:%S', location='Gitcoin API', skip_milliseconds=True, ) asset = get_gitcoin_asset(symbol=rawtx['asset'], token_address=rawtx['token_address']) raw_amount = deserialize_int_from_str(symbol=rawtx['amount'], location='gitcoin api') amount = asset_normalized_value(raw_amount, asset) if amount == ZERO: raise ZeroGitcoinAmount() # let's use gitcoin's calculated rate for now since they include it in the response usd_value = Price( ZERO) if rawtx['usd_value'] is None else deserialize_price( rawtx['usd_value']) # noqa: E501 rate = Price(ZERO) if usd_value == ZERO else Price(usd_value / amount) raw_txid = rawtx['tx_hash'] tx_type, tx_id = process_gitcoin_txid(key='tx_hash', entry=rawtx) # until we figure out if we can use it https://github.com/gitcoinco/web/issues/9255#issuecomment-874537144 # noqa: E501 clr_round = _calculate_clr_round(timestamp, rawtx) notes = f'Gitcoin grant {grant_id} event' if not clr_round else f'Gitcoin grant {grant_id} event in clr_round {clr_round}' # noqa: E501 return LedgerAction( identifier=1, # whatever -- does not end up in the DB timestamp=timestamp, action_type=LedgerActionType.DONATION_RECEIVED, location=Location.GITCOIN, amount=AssetAmount(amount), asset=asset, rate=rate, rate_asset=A_USD, link=raw_txid, notes=notes, extra_data=GitcoinEventData( tx_id=tx_id, grant_id=grant_id, clr_round=clr_round, tx_type=tx_type, ), )
def test_export_import_db(data_dir, username): """Create a DB, write some data and then after export/import confirm it's there""" msg_aggregator = MessagesAggregator() data = DataHandler(data_dir, msg_aggregator) data.unlock(username, '123', create_new=True) data.set_fiat_balances({A_EUR: AssetAmount(FVal('10'))}) encoded_data, _ = data.compress_and_encrypt_db('123') # The server would return them decoded encoded_data = encoded_data.decode() data.decompress_and_decrypt_db('123', encoded_data) fiat_balances = data.get_fiat_balances() assert len(fiat_balances) == 1 assert int(fiat_balances[A_EUR]) == 10
def test_get_trades_from_tx_swaps_1(): """Single swap""" trades = get_trades_from_tx_swaps(TEST_SWAPS_TX_1) expected_trades = [ AMMTrade( trade_type=TradeType.BUY, base_asset=EthereumToken('AMPL'), quote_asset=EthereumToken('USDC'), amount=AssetAmount(FVal('14285.153512382')), rate=Price(FVal('1.151914744569445582155783413')), trade_index=0, swaps=TEST_SWAPS_TX_1, ), ] assert trades == expected_trades
def test_get_trades_from_tx_swaps_2(): """Two swaps that can be aggregated""" trades = get_trades_from_tx_swaps(TEST_SWAPS_TX_2) expected_trades = [ AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_USDC, amount=AssetAmount(FVal('8.05955457343053505')), rate=Price(FVal('1538.698513176199118305140044')), trade_index=0, swaps=TEST_SWAPS_TX_2, ), ] assert trades == expected_trades
def test_data_set_fiat_balances(data_dir, username): msg_aggregator = MessagesAggregator() data = DataHandler(data_dir, msg_aggregator) data.unlock(username, '123', create_new=True) amount_eur = AssetAmount(FVal('100')) amount_cny = AssetAmount(FVal('500')) data.set_fiat_balances({A_EUR: amount_eur}) data.set_fiat_balances({A_CNY: amount_cny}) balances = data.get_fiat_balances() assert len(balances) == 2 assert FVal(balances[A_EUR]) == amount_eur assert FVal(balances[A_CNY]) == amount_cny data.set_fiat_balances({A_EUR: ZERO}) balances = data.get_fiat_balances() assert len(balances) == 1 assert FVal(balances[A_CNY]) == amount_cny # also check that all the fiat assets in the fiat table are in # all_assets.json for fiat_asset in FIAT_CURRENCIES: assert fiat_asset.is_fiat()
def test_get_trades_from_tx_swaps_1(): """Single swap""" trades = get_trades_from_tx_swaps(TEST_SWAPS_TX_1) expected_trades = [ AMMTrade( trade_type=TradeType.BUY, base_asset=A_AMPL, quote_asset=A_USDC, amount=AssetAmount(FVal('14285.153512382')), rate=Price(FVal('0.8681198020203941533693340847')), trade_index=0, swaps=TEST_SWAPS_TX_1, ), ] assert trades == expected_trades
def test_get_trades_from_tx_swaps_2(): """Two swaps that can be aggregated""" trades = get_trades_from_tx_swaps(TEST_SWAPS_TX_2) expected_trades = [ AMMTrade( trade_type=TradeType.BUY, base_asset=EthereumToken('WETH'), quote_asset=EthereumToken('USDC'), amount=AssetAmount(FVal('8.05955457343053505')), rate=Price(FVal('0.0006498998936028010652666316885')), trade_index=0, swaps=TEST_SWAPS_TX_2, ), ] assert trades == expected_trades
def trade_from_bittrex(bittrex_trade: Dict[str, Any]) -> Trade: """Turn a bittrex trade returned from bittrex trade history to our common trade history format Throws: - UnknownAsset/UnsupportedAsset due to bittrex_pair_to_world() - DeserializationError due to unexpected format of dict entries - KeyError due to dict entries missing an expected entry """ amount = AssetAmount( deserialize_asset_amount(bittrex_trade['Quantity']) - deserialize_asset_amount(bittrex_trade['QuantityRemaining']), ) timestamp = deserialize_timestamp_from_bittrex_date( bittrex_trade['TimeStamp']) rate = deserialize_price(bittrex_trade['PricePerUnit']) order_type = deserialize_trade_type(bittrex_trade['OrderType']) bittrex_price = deserialize_price(bittrex_trade['Price']) fee = deserialize_fee(bittrex_trade['Commission']) pair = bittrex_pair_to_world(bittrex_trade['Exchange']) quote_currency = get_pair_position_asset(pair, 'second') log.debug( 'Processing bittrex Trade', sensitive_log=True, amount=amount, rate=rate, order_type=order_type, price=bittrex_price, fee=fee, bittrex_pair=bittrex_trade['Exchange'], pair=pair, ) return Trade( timestamp=timestamp, location=Location.BITTREX, pair=pair, trade_type=order_type, amount=amount, rate=rate, fee=fee, fee_currency=quote_currency, link=str(bittrex_trade['OrderUuid']), )
def mock_exchange_data_in_db(exchange_locations, rotki) -> None: db = rotki.data.db for exchange_location in exchange_locations: db.add_trades([Trade( timestamp=Timestamp(1), location=exchange_location, base_asset=A_BTC, quote_asset=A_ETH, trade_type=TradeType.BUY, amount=AssetAmount(FVal(1)), rate=Price(FVal(1)), fee=Fee(FVal('0.1')), fee_currency=A_ETH, link='foo', notes='boo', )]) db.update_used_query_range(name=f'{str(exchange_location)}_trades', start_ts=0, end_ts=9999) # noqa: E501 db.update_used_query_range(name=f'{str(exchange_location)}_margins', start_ts=0, end_ts=9999) # noqa: E501 db.update_used_query_range(name=f'{str(exchange_location)}_asset_movements', start_ts=0, end_ts=9999) # noqa: E501
def _deserialize_trade( self, raw_trade: Dict[str, Any], ) -> Trade: """Process a trade user transaction from Bitstamp and deserialize it. Can raise DeserializationError. """ timestamp = deserialize_timestamp_from_bitstamp_date(raw_trade['datetime']) trade_pair_data = self._get_trade_pair_data_from_transaction(raw_trade) base_asset_amount = deserialize_asset_amount( raw_trade[trade_pair_data.base_asset_symbol], ) quote_asset_amount = deserialize_asset_amount( raw_trade[trade_pair_data.quote_asset_symbol], ) rate = deserialize_price(raw_trade[trade_pair_data.pair]) fee_currency = trade_pair_data.quote_asset if base_asset_amount >= ZERO: trade_type = TradeType.BUY else: if quote_asset_amount < 0: raise DeserializationError( f'Unexpected bitstamp trade format. Both base and quote ' f'amounts are negative: {raw_trade}', ) trade_type = TradeType.SELL trade = Trade( timestamp=timestamp, location=Location.BITSTAMP, base_asset=trade_pair_data.base_asset, quote_asset=trade_pair_data.quote_asset, trade_type=trade_type, amount=AssetAmount(abs(base_asset_amount)), rate=rate, fee=deserialize_fee(raw_trade['fee']), fee_currency=fee_currency, link=str(raw_trade['id']), notes='', ) return trade
def assert_blockfi_trades_import_results(rotki: Rotkehlchen): """A utility function to help assert on correctness of importing trades data from blockfi""" trades = rotki.data.db.get_trades() warnings = rotki.msg_aggregator.consume_warnings() errors = rotki.msg_aggregator.consume_errors() assert len(errors) == 0 assert len(warnings) == 0 expected_trades = [Trade( timestamp=Timestamp(1612051199), location=Location.BLOCKFI, base_asset=symbol_to_asset_or_token('USDC'), quote_asset=symbol_to_asset_or_token('LTC'), trade_type=TradeType.BUY, amount=AssetAmount(FVal('6404.6')), rate=Price(FVal('151.6283999982779809352223797')), fee=None, fee_currency=None, link='', notes='One Time', )] assert trades == expected_trades
def test_deserialize_v2_trade_sell(mock_kucoin): raw_result = { 'symbol': 'BCHSV-USDT', 'tradeId': '601da995e0ee8b00063a075c', 'orderId': '601da9950c92050006bd45c5', 'counterOrderId': '601da9950c92050006bd457d', 'side': 'sell', 'liquidity': 'taker', 'forceTaker': True, 'price': '37624.4', 'size': '0.0013', 'funds': '48.91172', 'fee': '0.034238204', 'feeRate': '0.0007', 'feeCurrency': 'USDT', 'stop': '', 'tradeType': 'TRADE', 'type': 'market', 'createdAt': 1612556794259, } expected_trade = Trade( timestamp=Timestamp(1612556794), location=Location.KUCOIN, base_asset=A_BSV, quote_asset=A_USDT, trade_type=TradeType.SELL, amount=AssetAmount(FVal('0.0013')), rate=Price(FVal('37624.4')), fee=Fee(FVal('0.034238204')), fee_currency=A_USDT, link='601da995e0ee8b00063a075c', notes='', ) trade = mock_kucoin._deserialize_trade( raw_result=raw_result, case=KucoinCase.TRADES, ) assert trade == expected_trade
def test_deserialize_v2_trade_buy(mock_kucoin): raw_result = { 'symbol': 'KCS-USDT', 'tradeId': '601da9faf1297d0007efd712', 'orderId': '601da9fa0c92050006bd83be', 'counterOrderId': '601bad620c9205000642300f', 'side': 'buy', 'liquidity': 'taker', 'forceTaker': True, 'price': 1000, 'size': '0.2', 'funds': 200, 'fee': '0.14', 'feeRate': '0.0007', 'feeCurrency': 'USDT', 'stop': '', 'tradeType': 'TRADE', 'type': 'market', 'createdAt': 1612556794259, } expected_trade = Trade( timestamp=Timestamp(1612556794), location=Location.KUCOIN, base_asset=A_KCS, quote_asset=A_USDT, trade_type=TradeType.BUY, amount=AssetAmount(FVal('0.2')), rate=Price(FVal('1000')), fee=Fee(FVal('0.14')), fee_currency=A_USDT, link='601da9faf1297d0007efd712', notes='', ) trade = mock_kucoin._deserialize_trade( raw_result=raw_result, case=KucoinCase.TRADES, ) assert trade == expected_trade
def trade_from_bitmex(bitmex_trade: Dict) -> MarginPosition: """Turn a bitmex trade returned from bitmex trade history to our common trade history format. This only returns margin positions as bitmex only deals in margin trading. May raise: - KeyError - DeserializationError """ close_time = iso8601ts_to_timestamp(bitmex_trade['transactTime']) profit_loss = AssetAmount( satoshis_to_btc(deserialize_asset_amount(bitmex_trade['amount']))) currency = bitmex_to_world(bitmex_trade['currency']) fee = deserialize_fee(bitmex_trade['fee']) notes = bitmex_trade['address'] assert currency == A_BTC, 'Bitmex trade should only deal in BTC' log.debug( 'Processing Bitmex Trade', sensitive_log=True, timestamp=close_time, profit_loss=profit_loss, currency=currency, fee=fee, notes=notes, ) return MarginPosition( location=Location.BITMEX, open_time=None, close_time=close_time, profit_loss=profit_loss, pl_currency=currency, fee=fee, fee_currency=A_BTC, notes=notes, link=str(bitmex_trade['transactID']), )
def trade_from_bitcoinde(raw_trade: Dict) -> Trade: try: timestamp = deserialize_timestamp_from_date( raw_trade['successfully_finished_at'], 'iso8601', 'bitcoinde', ) except KeyError: # For very old trades (2013) bitcoin.de does not return 'successfully_finished_at' timestamp = deserialize_timestamp_from_date( raw_trade['trade_marked_as_paid_at'], 'iso8601', 'bitcoinde', ) trade_type = deserialize_trade_type(raw_trade['type']) tx_amount = AssetAmount(FVal(raw_trade['amount_currency_to_trade'])) native_amount = FVal(raw_trade['volume_currency_to_pay']) tx_asset, native_asset = bitcoinde_pair_to_world(raw_trade['trading_pair']) pair = TradePair(f'{tx_asset.identifier}_{native_asset.identifier}') amount = tx_amount rate = Price(native_amount / tx_amount) fee_amount = deserialize_fee(raw_trade['fee_currency_to_pay']) fee_asset = Asset('EUR') return Trade( timestamp=timestamp, location=Location.BITCOINDE, pair=pair, trade_type=trade_type, amount=amount, rate=rate, fee=fee_amount, fee_currency=fee_asset, link=str(raw_trade['trade_id']), )
token0=A_WBTC, token1=A_WETH, amount0_in=AssetAmount(FVal('0.01562514')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('0.205421420618533148')), ), ], ).serialize() BALANCER_TEST_ADDR3_EXPECTED_HISTORY_POOL1 = (BalancerPoolEventsBalance( address=BALANCER_TEST_ADDR3, pool_address_token=BALANCER_TEST_ADDR3_POOL1, profit_loss_amounts=[ AssetAmount(FVal('-0.039312851799093402')), AssetAmount(FVal('0.744372160905819159')), ], usd_profit_loss=FVal('-0.76584117161052920880190053'), events=[ BalancerEvent( tx_hash= '0xb9dff9df4e3838c75d354d62c4596d94e5eb8904e07cee07a3b7ffa611c05544', log_index=331, address=BALANCER_TEST_ADDR3, timestamp=Timestamp(1597144247), event_type=BalancerBPTEventType.MINT, pool_address_token=BALANCER_TEST_ADDR3_POOL1, lp_balance=Balance( amount=FVal('0.042569019597126949'), usd_value=FVal('19.779488662371895'),
def get_balancer_test_addr2_expected_trades(): """In a function since the new(unknown) assets needs to have been loaded in the DB""" A_WCRES = EthereumToken.initialize( # noqa: N806 address=string_to_ethereum_address( '0xa0afAA285Ce85974c3C881256cB7F225e3A1178a'), decimals=18, symbol='wCRES', ) return [ AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_AAVE, amount=AssetAmount(FVal('1.616934038985744521')), rate=Price(FVal('6.963972908793392530935439799')), trade_index=1, swaps=[ AMMSwap( tx_hash= '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589', # noqa: E501 log_index=24, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546' ), # noqa: E501 timestamp=Timestamp(1607008178), location=Location.BALANCER, token0=A_AAVE, token1=A_WETH, amount0_in=AssetAmount(FVal('11.260284842802604032')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('1.616934038985744521')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_AAVE, quote_asset=A_WETH, amount=AssetAmount(FVal('11.260286362820602094')), rate=Price(FVal('0.1416068599966922676173010716')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0x3c457da9b541ae39a7dc781ab04a03938b98b5649512aec2a2d32635c9bbf589', # noqa: E501 log_index=18, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x70985E557aE0CD6dC88189a532e54FbC61927BAd' ), # noqa: E501 timestamp=Timestamp(1607008178), location=Location.BALANCER, token0=A_WETH, token1=A_AAVE, amount0_in=AssetAmount(FVal('1.594533794502600192')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('11.260286362820602094')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_SYN, amount=AssetAmount(FVal('1.352902561458047718')), rate=Price(FVal('724.4303350385182691258363763')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0x5e235216cb03e4eb234014f5ccf3efbfddd40c4576424e2a8204f1d12b96ed35', # noqa: E501 log_index=143, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x8982E9bBf7AC6A49c434aD81D2fF8e16895318e5' ), # noqa: E501 timestamp=Timestamp(1607008218), location=Location.BALANCER, token0=A_SYN, token1=A_WETH, amount0_in=AssetAmount(FVal('980.08365587152306176')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('1.352902561458047718')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_WCRES, amount=AssetAmount(FVal('0.205709519074945018')), rate=Price(FVal('232.7409943164679514496089589')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0xf54be824b4619777f1db0e3da91b0cd52f6dba730c95a75644e2b085e6ab9824', # noqa: E501 log_index=300, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x10996eC4f3E7A1b314EbD966Fa8b1ad0fE0f8307' ), # noqa: E501 timestamp=Timestamp(1607009877), location=Location.BALANCER, token0=A_WCRES, token1=A_WETH, amount0_in=AssetAmount(FVal('47.87703800986513408')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('0.205709519074945018')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_API3, quote_asset=A_WETH, amount=AssetAmount(FVal('295.881648100500428692')), rate=Price(FVal('0.003346787723157288562491614498')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0xfed4e15051e3ce4dc0d2816f719701e5920e40bf41614b5feaa3c5a6a0186c03', # noqa: E501 log_index=22, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x997c0fc9578a8194EFDdE2E0cD7aa6A69cFCD7c1' ), # noqa: E501 timestamp=Timestamp(1607010888), location=Location.BALANCER, token0=A_WETH, token1=A_API3, amount0_in=AssetAmount(FVal('0.990253067370299904')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('295.881648100500428692')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_MFT, amount=AssetAmount(FVal('0.686544199299304057')), rate=Price(FVal('243775.0324093115004367119900')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0xf0147c4b81098676c08ae20ae5bf8f8b60d0ad79eec484f3f93ac6ab49a3c51c', # noqa: E501 log_index=97, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x2Eb6CfbFFC8785Cd0D9f2d233d0a617bF4269eeF' ), # noqa: E501 timestamp=Timestamp(1607015059), location=Location.BALANCER, token0=A_MFT, token1=A_WETH, amount0_in=AssetAmount(FVal('167362.334434612660404224')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('0.686544199299304057')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_AAVE, amount=AssetAmount(FVal('3.055412574642681758')), rate=Price(FVal('6.916116208273240607778771150')), trade_index=1, swaps=[ AMMSwap( tx_hash= '0x67c0e9a0fdd002d0b9d1cca0c8e4ca4d30435bbf57bbf0091396275efaea414b', # noqa: E501 log_index=37, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x0E552307659E70bF61f918f96AA880Cdec40d7E2' ), # noqa: E501 timestamp=Timestamp(1607015339), location=Location.BALANCER, token0=A_AAVE, token1=A_WETH, amount0_in=AssetAmount(FVal('21.131588430448123904')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('3.055412574642681758')), ), ], ), AMMTrade( trade_type=TradeType.BUY, base_asset=A_AAVE, quote_asset=A_WETH, amount=AssetAmount(FVal('21.131588567541018817')), rate=Price(FVal('0.1435213742524287826717337545')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0x67c0e9a0fdd002d0b9d1cca0c8e4ca4d30435bbf57bbf0091396275efaea414b', # noqa: E501 log_index=31, address=string_to_ethereum_address( '0x029f388aC4D5C8BfF490550ce0853221030E822b' ), # noqa: E501 from_address=string_to_ethereum_address( '0x0000000000007F150Bd6f54c40A34d7C3d5e9f56' ), # noqa: E501 to_address=string_to_ethereum_address( '0x7c90a3cd7Ec80dd2F633ed562480AbbEEd3bE546' ), # noqa: E501 timestamp=Timestamp(1607015339), location=Location.BALANCER, token0=A_WETH, token1=A_AAVE, amount0_in=AssetAmount(FVal('3.0328346313504')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('21.131588567541018817')), ), ], ), ]
to_address=ETH_ADDRESS1, value=FVal('12323'), gas=FVal('5000000'), gas_price=FVal('2100000000'), gas_used=FVal('1900000'), input_data=DUMMY_HASH, nonce=3, ), ] margin_history = [ MarginPosition( # before query period -- BTC/EUR: 422.90 location=Location.POLONIEX, open_time=Timestamp(1463184190), # 14/05/2016 close_time=Timestamp(1464393600), # 28/05/2016 profit_loss=AssetAmount(FVal(0.05)), pl_currency=A_BTC, fee=Fee(ZERO), fee_currency=A_BTC, link='1', notes='margin1', ), MarginPosition( # before query period -- BTC/EUR: 542.87 location=Location.POLONIEX, open_time=Timestamp(1472428800), # 29/08/2016 close_time=Timestamp(1473897600), # 15/09/2016 profit_loss=AssetAmount(FVal('-0.042')), pl_currency=A_BTC, fee=Fee(FVal('0.0001')), fee_currency=A_BTC, link='2',
def test_get_trade_with_1_token_pool( rotkehlchen_api_server, ethereum_accounts, # pylint: disable=unused-argument rotki_premium_credentials, # pylint: disable=unused-argument start_with_valid_premium, # pylint: disable=unused-argument ): """ Test the special case of a swap within an 1 token pool. This can probably happen if the controller has since removed tokens from the pool. """ rotki = rotkehlchen_api_server.rest_api.rotkehlchen setup = setup_balances( rotki, ethereum_accounts=ethereum_accounts, btc_accounts=None, original_queries=['zerion', 'logs', 'blocknobytime'], ) with ExitStack() as stack: # patch ethereum/etherscan to not autodetect tokens setup.enter_ethereum_patches(stack) response = requests.get( api_url_for(rotkehlchen_api_server, 'balancertradeshistoryresource'), json={ 'from_timestamp': 1621358338, 'to_timestamp': 1621358340, }, ) result = assert_proper_response_with_result(response) db_trades = rotki.data.db.get_amm_swaps() assert len(db_trades) == 29 address_trades = result[BALANCER_TEST_ADDR4] assert len(address_trades) == 1 assert address_trades[0] == AMMTrade( trade_type=TradeType.BUY, base_asset=A_WETH, quote_asset=A_WBTC, amount=AssetAmount(FVal('0.205421420618533148')), rate=Price(FVal('0.07606382992071615428519015532')), trade_index=0, swaps=[ AMMSwap( tx_hash= '0x4f9e0d8aa660a5d3db276a1ade038f7027f29838dd22d5276571d2e4ea7131ae', # noqa: E501 log_index=84, address=string_to_ethereum_address( BALANCER_TEST_ADDR4), # noqa: E501 from_address=string_to_ethereum_address( '0xFD3dFB524B2dA40c8a6D703c62BE36b5D8540626' ), # noqa: E501 to_address=string_to_ethereum_address( '0x582818356331877553F3E9Cf9557b48e5DdbD54a' ), # noqa: E501 timestamp=Timestamp(1621358339), location=Location.BALANCER, token0=A_WBTC, token1=A_WETH, amount0_in=AssetAmount(FVal('0.01562514')), amount1_in=AssetAmount(ZERO), amount0_out=AssetAmount(ZERO), amount1_out=AssetAmount(FVal('0.205421420618533148')), ), ], ).serialize()
def _consume_cointracking_entry(self, csv_row: Dict[str, Any]) -> None: """Consumes a cointracking entry row from the CSV and adds it into the database Can raise: - DeserializationError if something is wrong with the format of the expected values - UnsupportedCointrackingEntry if importing of this entry is not supported. - IndexError if the CSV file is corrupt - KeyError if the an expected CSV key is missing - UnknownAsset if one of the assets founds in the entry are not supported """ row_type = csv_row['Type'] timestamp = deserialize_timestamp_from_date( date=csv_row['Date'], formatstr='%d.%m.%Y %H:%M:%S', location='cointracking.info', ) notes = csv_row['Comment'] location = exchange_row_to_location(csv_row['Exchange']) fee = Fee(ZERO) fee_currency = A_USD # whatever (used only if there is no fee) if csv_row['Fee'] != '': fee = deserialize_fee(csv_row['Fee']) fee_currency = Asset(csv_row['Cur.Fee']) if row_type in ('Gift/Tip', 'Trade', 'Income'): base_asset = Asset(csv_row['Cur.Buy']) quote_asset = None if csv_row['Cur.Sell'] == '' else Asset( csv_row['Cur.Sell']) if quote_asset is None and row_type not in ('Gift/Tip', 'Income'): raise DeserializationError( 'Got a trade entry with an empty quote asset') if quote_asset is None: # Really makes no difference as this is just a gift and the amount is zero quote_asset = A_USD pair = TradePair( f'{base_asset.identifier}_{quote_asset.identifier}') base_amount_bought = deserialize_asset_amount(csv_row['Buy']) if csv_row['Sell'] != '-': quote_amount_sold = deserialize_asset_amount(csv_row['Sell']) else: quote_amount_sold = AssetAmount(ZERO) rate = Price(quote_amount_sold / base_amount_bought) trade = Trade( timestamp=timestamp, location=location, pair=pair, trade_type=TradeType. BUY, # It's always a buy during cointracking import amount=base_amount_bought, rate=rate, fee=fee, fee_currency=fee_currency, link='', notes=notes, ) self.db.add_trades([trade]) elif row_type == 'Deposit' or row_type == 'Withdrawal': category = deserialize_asset_movement_category(row_type.lower()) if category == AssetMovementCategory.DEPOSIT: amount = deserialize_asset_amount(csv_row['Buy']) asset = Asset(csv_row['Cur.Buy']) else: amount = deserialize_asset_amount_force_positive( csv_row['Sell']) asset = Asset(csv_row['Cur.Sell']) asset_movement = AssetMovement( location=location, category=category, address=None, transaction_id=None, timestamp=timestamp, asset=asset, amount=amount, fee=fee, fee_asset=fee_currency, link='', ) self.db.add_asset_movements([asset_movement]) else: raise UnsupportedCointrackingEntry( f'Unknown entrype type "{row_type}" encountered during cointracking ' f'data import. Ignoring entry', )
def check_result_of_history_creation( start_ts: Timestamp, end_ts: Timestamp, trade_history: List[Trade], margin_history: List[Trade], loan_history: Dict, asset_movements: List[AssetMovement], eth_transactions: List[EthereumTransaction], ) -> Dict[str, Any]: """This function offers Some simple assertions on the result of the created history. The entire processing part of the history is mocked away by this checking function""" assert start_ts == 0, 'should be same as given to process_history' assert end_ts == TEST_END_TS, 'should be same as given to process_history' # TODO: Add more assertions/check for each action # OR instead do it in tests for conversion of actions(trades, loans, deposits e.t.c.) # from exchange to our format for each exchange assert len(trade_history) == 9 assert trade_history[0].location == 'kraken' assert trade_history[0].pair == 'ETH_EUR' assert trade_history[0].trade_type == TradeType.BUY assert trade_history[1].location == 'kraken' assert trade_history[1].pair == 'BTC_EUR' assert trade_history[1].trade_type == TradeType.BUY assert trade_history[2].location == 'bittrex' assert trade_history[2].pair == 'LTC_BTC' assert trade_history[2].trade_type == TradeType.BUY assert trade_history[3].location == 'bittrex' assert trade_history[3].pair == 'LTC_ETH' assert trade_history[3].trade_type == TradeType.SELL assert trade_history[4].location == 'binance' assert trade_history[4].pair == 'ETH_BTC' assert trade_history[4].trade_type == TradeType.BUY assert trade_history[5].location == 'binance' assert trade_history[5].pair == 'RDN_ETH' assert trade_history[5].trade_type == TradeType.SELL assert trade_history[6].location == 'poloniex' assert trade_history[6].pair == 'ETH_BTC' assert trade_history[6].trade_type == TradeType.SELL assert trade_history[7].location == 'poloniex' assert trade_history[7].pair == 'ETH_BTC' assert trade_history[7].trade_type == TradeType.BUY assert trade_history[8].location == 'poloniex' assert trade_history[8].pair == 'XMR_ETH' assert trade_history[8].trade_type == TradeType.BUY assert len(loan_history) == 2 assert loan_history[0].currency == A_ETH assert loan_history[0].earned == AssetAmount(FVal('0.00000001')) assert loan_history[1].currency == A_BTC assert loan_history[1].earned == AssetAmount(FVal('0.00000005')) assert len(asset_movements) == 8 assert asset_movements[0].exchange == 'kraken' assert asset_movements[0].category == 'deposit' assert asset_movements[0].asset == A_BTC assert asset_movements[1].exchange == 'kraken' assert asset_movements[1].category == 'deposit' assert asset_movements[1].asset == A_ETH assert asset_movements[2].exchange == 'kraken' assert asset_movements[2].category == 'withdrawal' assert asset_movements[2].asset == A_BTC assert asset_movements[3].exchange == 'kraken' assert asset_movements[3].category == 'withdrawal' assert asset_movements[3].asset == A_ETH assert asset_movements[4].exchange == 'poloniex' assert asset_movements[4].category == 'withdrawal' assert asset_movements[4].asset == A_BTC assert asset_movements[5].exchange == 'poloniex' assert asset_movements[5].category == 'withdrawal' assert asset_movements[5].asset == A_ETH assert asset_movements[6].exchange == 'poloniex' assert asset_movements[6].category == 'deposit' assert asset_movements[6].asset == A_BTC assert asset_movements[7].exchange == 'poloniex' assert asset_movements[7].category == 'deposit' assert asset_movements[7].asset == A_ETH # The history creation for these is not yet tested assert len(margin_history) == 0 assert len(eth_transactions) == 0 return {}
# Method: `_calculate_events_balances` LP_1_EVENTS = [ LiquidityPoolEvent( tx_hash= '0xa9ce328d0e2d2fa8932890bfd4bc61411abd34a4aaa48fc8b853c873a55ea824', log_index=263, address=TEST_ADDRESS_1, timestamp=Timestamp(1604273256), event_type=EventType.MINT, pool_address=deserialize_ethereum_address( "0x55111baD5bC368A2cb9ecc9FBC923296BeDb3b89"), # noqa: E501 token0=TOKEN_BASED, token1=ASSET_WETH, amount0=AssetAmount(FVal('605.773209925184996494')), amount1=AssetAmount(FVal('1.106631443395672732')), usd_price=Price(FVal('872.4689300619698095220125311431804')), lp_amount=AssetAmount(FVal('1.220680531244355402')), ), LiquidityPoolEvent( tx_hash= '0x27ddad4f187e965a3ee37257b75d297ff79b2663fd0a2d8d15f7efaccf1238fa', log_index=66, address=TEST_ADDRESS_1, timestamp=Timestamp(1604283808), event_type=EventType.BURN, pool_address=deserialize_ethereum_address( "0x55111baD5bC368A2cb9ecc9FBC923296BeDb3b89"), # noqa: E501 token0=TOKEN_BASED, token1=ASSET_WETH,
def test_ledger_actions_accounting(accountant): """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 """ ledger_actions_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', ) ] result = accounting_history_process( accountant=accountant, start_ts=1436979735, end_ts=1519693374, history_list=[], ledger_actions_list=ledger_actions_history, ) assert accountant.events.cost_basis.get_calculated_asset_amount( A_BTC).is_close('0.9') assert accountant.events.cost_basis.get_calculated_asset_amount( A_ETH).is_close('0.9') assert accountant.events.cost_basis.get_calculated_asset_amount( A_XMR).is_close('10') # 400 * 1 + 0.4 * 10 - 1 * 0.1 - 500 * 0.9004 * 0.1 = 358.88 expected_pnl = '358.88' assert FVal(result['overview']['ledger_actions_profit_loss']).is_close( expected_pnl) assert FVal(result['overview']['total_profit_loss']).is_close(expected_pnl) assert FVal( result['overview']['total_taxable_profit_loss']).is_close(expected_pnl)
def check_result_of_history_creation( start_ts: Timestamp, end_ts: Timestamp, trade_history: List[Union[Trade, MarginPosition]], loan_history: List[Loan], asset_movements: List[AssetMovement], eth_transactions: List[EthereumTransaction], ) -> Dict[str, Any]: """This function offers some simple assertions on the result of the created history. The entire processing part of the history is mocked away by this checking function""" if history_start_ts is None: assert start_ts == 0, 'if no start_ts is given it should be zero' else: assert start_ts == history_start_ts, 'should be same as given to process_history' if history_end_ts is not None: assert end_ts == history_end_ts, 'should be same as given to process_history' # TODO: Add more assertions/check for each action # OR instead do it in tests for conversion of actions(trades, loans, deposits e.t.c.) # from exchange to our format for each exchange assert len(trade_history) == 11 assert isinstance(trade_history[0], Trade) assert trade_history[0].location == Location.KRAKEN assert trade_history[0].pair == 'ETH_EUR' assert trade_history[0].trade_type == TradeType.BUY assert isinstance(trade_history[1], Trade) assert trade_history[1].location == Location.KRAKEN assert trade_history[1].pair == 'BTC_EUR' assert trade_history[1].trade_type == TradeType.BUY assert isinstance(trade_history[2], Trade) assert trade_history[2].location == Location.BITTREX assert trade_history[2].pair == 'LTC_BTC' assert trade_history[2].trade_type == TradeType.BUY assert isinstance(trade_history[3], Trade) assert trade_history[3].location == Location.BITTREX assert trade_history[3].pair == 'LTC_ETH' assert trade_history[3].trade_type == TradeType.SELL assert isinstance(trade_history[4], MarginPosition) assert trade_history[4].profit_loss == FVal('0.05') assert isinstance(trade_history[5], Trade) assert trade_history[5].location == Location.BINANCE assert trade_history[5].pair == 'ETH_BTC' assert trade_history[5].trade_type == TradeType.BUY assert isinstance(trade_history[6], Trade) assert trade_history[6].location == Location.BINANCE assert trade_history[6].pair == 'RDN_ETH' assert trade_history[6].trade_type == TradeType.SELL assert isinstance(trade_history[7], Trade) assert trade_history[7].location == Location.POLONIEX assert trade_history[7].pair == 'ETH_BTC' assert trade_history[7].trade_type == TradeType.SELL assert isinstance(trade_history[8], Trade) assert trade_history[8].location == Location.POLONIEX assert trade_history[8].pair == 'ETH_BTC' assert trade_history[8].trade_type == TradeType.BUY assert isinstance(trade_history[9], Trade) assert trade_history[9].location == Location.POLONIEX assert trade_history[9].pair == 'XMR_ETH' assert trade_history[9].trade_type == TradeType.BUY assert isinstance(trade_history[10], MarginPosition) assert trade_history[10].profit_loss == FVal('5E-9') assert len(loan_history) == 2 assert loan_history[0].currency == A_ETH assert loan_history[0].earned == AssetAmount(FVal('0.00000001')) assert loan_history[1].currency == A_BTC assert loan_history[1].earned == AssetAmount(FVal('0.00000005')) assert len(asset_movements) == 11 assert asset_movements[0].location == Location.POLONIEX assert asset_movements[0].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[0].asset == A_BTC assert asset_movements[1].location == Location.POLONIEX assert asset_movements[1].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[1].asset == A_ETH assert asset_movements[2].location == Location.POLONIEX assert asset_movements[2].category == AssetMovementCategory.DEPOSIT assert asset_movements[2].asset == A_BTC assert asset_movements[3].location == Location.POLONIEX assert asset_movements[3].category == AssetMovementCategory.DEPOSIT assert asset_movements[3].asset == A_ETH assert asset_movements[4].location == Location.BITMEX assert asset_movements[4].category == AssetMovementCategory.DEPOSIT assert asset_movements[4].asset == A_BTC assert asset_movements[5].location == Location.BITMEX assert asset_movements[5].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[5].asset == A_BTC assert asset_movements[6].location == Location.BITMEX assert asset_movements[6].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[6].asset == A_BTC assert asset_movements[7].location == Location.KRAKEN assert asset_movements[7].category == AssetMovementCategory.DEPOSIT assert asset_movements[7].asset == A_BTC assert asset_movements[8].location == Location.KRAKEN assert asset_movements[8].category == AssetMovementCategory.DEPOSIT assert asset_movements[8].asset == A_ETH assert asset_movements[9].location == Location.KRAKEN assert asset_movements[9].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[9].asset == A_BTC assert asset_movements[10].location == Location.KRAKEN assert asset_movements[10].category == AssetMovementCategory.WITHDRAWAL assert asset_movements[10].asset == A_ETH # The history creation for these is not yet tested assert len(eth_transactions) == 3 assert eth_transactions[0].block_number == 54092 assert eth_transactions[0].tx_hash == hexstring_to_bytes(TX_HASH_STR1) assert eth_transactions[0].from_address == ETH_ADDRESS1 assert eth_transactions[0].to_address == '' assert eth_transactions[0].value == FVal('11901464239480000000000000') assert eth_transactions[0].input_data == MOCK_INPUT_DATA assert eth_transactions[1].block_number == 54093 assert eth_transactions[1].tx_hash == hexstring_to_bytes(TX_HASH_STR2) assert eth_transactions[1].from_address == ETH_ADDRESS2 assert eth_transactions[1].to_address == ETH_ADDRESS1 assert eth_transactions[1].value == FVal('40000300') assert eth_transactions[1].input_data == MOCK_INPUT_DATA assert eth_transactions[2].block_number == 54094 assert eth_transactions[2].tx_hash == hexstring_to_bytes(TX_HASH_STR3) assert eth_transactions[2].from_address == ETH_ADDRESS3 assert eth_transactions[2].to_address == ETH_ADDRESS1 assert eth_transactions[2].value == FVal('500520300') assert eth_transactions[2].input_data == MOCK_INPUT_DATA return {}