def test_history_creation_remote_errors( rotkehlchen_server_with_exchanges, accountant, ): """Test that during history creation, remote errors are detected and errors are returned""" server = rotkehlchen_server_with_exchanges rotki = server.rotkehlchen rotki.accountant = accountant kraken = rotki.exchange_manager.connected_exchanges['kraken'] kraken.random_trade_data = False kraken.random_ledgers_data = False kraken.remote_errors = True ( accountant_patch, polo_patch, binance_patch, bittrex_patch, bitmex_patch, ) = mock_history_processing_and_exchanges(rotki, remote_errors=True) with accountant_patch, polo_patch, binance_patch, bittrex_patch, bitmex_patch: response = server.process_trade_history(start_ts='0', end_ts=str(TEST_END_TS)) # The history processing is completely mocked away and omitted in this test. # because it is only for the history creation not its processing. # For history processing tests look at test_accounting.py and # test_accounting_events.py assert 'invalid JSON' in response['message'] assert 'Binance' in response['message'] assert 'Bittrex' in response['message'] assert 'Bitmex' in response['message'] assert 'Kraken' in response['message'] assert 'Poloniex' in response['message'] assert response['result'] == {}
def test_edit_trades(rotkehlchen_api_server_with_exchanges): """Test that editing a trade via the trades endpoint works as expected""" rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) # Query trades of all exchanges to get them saved in the DB with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for(rotkehlchen_api_server_with_exchanges, "exchangetradesresource")) assert_proper_response(response) # Simply get all trades without any filtering response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), ) assert_proper_response(response) data = response.json() # get the binance trades original_binance_trades = [ t for t in data['result'] if t['location'] == 'binance' ] for trade in original_binance_trades: # edit two fields of each binance trade trade['amount'] = '1337.5' trade['notes'] = 'edited trade' response = requests.patch( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json=trade, ) assert_proper_response(response) data = response.json() assert data['message'] == '' # check that the returned trade is edited _check_trade_is_edited(original_trade=trade, result_trade=data['result']) # Finally also query binance trades to see they are edited response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={'location': 'binance'}, ) assert_proper_response(response) data = response.json() assert data['message'] == '' assert len(data['result']) == 2 # only 2 binance trades for idx, trade in enumerate(data['result']): _check_trade_is_edited(original_trade=original_binance_trades[idx], result_trade=trade)
def test_history_creation( rotkehlchen_server_with_exchanges, accountant, ): """This is a big test that contacts all exchange mocks and returns mocked trades and other data from exchanges in order to create the accounting history for a specific period and see that rotkehlchen handles the creation of that history correctly""" server = rotkehlchen_server_with_exchanges rotki = server.rotkehlchen rotki.accountant = accountant kraken = rotki.exchange_manager.connected_exchanges['kraken'] kraken.random_trade_data = False kraken.random_ledgers_data = False ( accountant_patch, polo_patch, binance_patch, bittrex_patch, bitmex_patch, ) = mock_history_processing_and_exchanges(rotki) with accountant_patch, polo_patch, binance_patch, bittrex_patch, bitmex_patch: response = server.process_trade_history(start_ts='0', end_ts=str(TEST_END_TS)) # The history processing is completely mocked away and omitted in this test. # because it is only for the history creation not its processing. # For history processing tests look at test_accounting.py and # test_accounting_events.py assert response['message'] == '' assert response['result'] == {} # And now make sure that warnings have also been generated for the query of # the unsupported/unknown assets warnings = rotki.msg_aggregator.consume_warnings() assert len(warnings) == 13 assert 'kraken trade with unknown asset IDONTEXISTTOO' in warnings[0] assert 'unknown kraken asset IDONTEXIST. Ignoring its deposit/withdrawals query' in warnings[ 1] msg = 'unknown kraken asset IDONTEXISTEITHER. Ignoring its deposit/withdrawals query' assert msg in warnings[2] assert 'poloniex trade with unknown asset NOEXISTINGASSET' in warnings[3] assert 'poloniex trade with unsupported asset BALLS' in warnings[4] assert 'withdrawal of unknown poloniex asset IDONTEXIST' in warnings[5] assert 'withdrawal of unsupported poloniex asset DIS' in warnings[6] assert 'deposit of unknown poloniex asset IDONTEXIST' in warnings[7] assert 'deposit of unsupported poloniex asset EBT' in warnings[8] assert 'poloniex loan with unsupported asset BDC' in warnings[9] assert 'poloniex loan with unknown asset NOTEXISTINGASSET' in warnings[10] assert 'bittrex trade with unsupported asset PTON' in warnings[11] assert 'bittrex trade with unknown asset IDONTEXIST' in warnings[12] errors = rotki.msg_aggregator.consume_errors() assert len(errors) == 3 assert 'kraken trade with unprocessable pair IDONTEXISTZEUR' in errors[0] assert 'kraken trade with unprocessable pair %$#%$#%$#%$#%$#%' in errors[1] assert 'bittrex trade with unprocessable pair %$#%$#%#$%' in errors[2]
def test_query_trade_history(rotkehlchen_server, function_scope_bittrex, function_scope_poloniex): """Just test that querying an exchange's trade history via the server api works Here we only have bittrex and poloniex registered """ rotki = rotkehlchen_server.rotkehlchen exchanges = rotki.exchange_manager.connected_exchanges exchanges['bittrex'] = function_scope_bittrex exchanges['poloniex'] = function_scope_poloniex exchanges['poloniex'].cache_ttl_secs = 0 exchanges['bittrex'].cache_ttl_secs = 0 _, polo_patch, _, bittrex_patch, _ = mock_history_processing_and_exchanges(rotki) # only poloniex with polo_patch: result = rotkehlchen_server.query_trade_history('poloniex', start_ts=0, end_ts=TEST_END_TS) assert len(result['result']) == 3 # And also test serialization of one trade trade = result['result'][0] assert trade['timestamp'] == 1539713117 assert trade['location'] == 'poloniex' assert trade['pair'] == 'ETH_BTC' assert trade['trade_type'] == 'sell' assert trade['amount'] == '1.40308443' assert trade['rate'] == '0.06935244' assert trade['fee'] == '0.0000973073287465092' assert trade['fee_currency'] == 'BTC' assert trade['link'] == '' assert trade['notes'] == '' # only bittrex with bittrex_patch: result = rotkehlchen_server.query_trade_history('bittrex', start_ts=0, end_ts=TEST_END_TS) assert len(result['result']) == 2 # all exchanges (both poloniex and bittrex) with polo_patch, bittrex_patch: result = rotkehlchen_server.query_trade_history('all', start_ts=0, end_ts=TEST_END_TS) assert len(result['result']) == 5 # also check that querying a non-registered exchange returns an error result = rotkehlchen_server.query_trade_history('binance', start_ts=0, end_ts=TEST_END_TS) msg = 'Exchange binance provided in query_trade_history is not registered yet for this user' assert result['message'] == msg # also check that querying an invalid exchange returns an error result = rotkehlchen_server.query_trade_history('dasdsd', start_ts=0, end_ts=TEST_END_TS) assert result['message'] == 'Unknown exchange dasdsd provided in query_trade_history'
def test_delete_trades(rotkehlchen_api_server_with_exchanges): """Test that deleting a trade via the trades endpoint works as expected""" rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) # Query trades of all exchanges to get them saved in the DB with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for(rotkehlchen_api_server_with_exchanges, "exchangetradesresource")) assert_proper_response(response) # Simply get all trades without any filtering response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), ) assert_proper_response(response) data = response.json() # get the poloniex trade ids poloniex_trade_ids = [ t['trade_id'] for t in data['result'] if t['location'] == 'poloniex' ] for trade_id in poloniex_trade_ids: # delete all poloniex trades response = requests.delete( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={'trade_id': trade_id}, ) assert_proper_response(response) data = response.json() assert data['message'] == '' assert data['result'] is True # Finally also query poloniex trades to see they no longer exist response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={'location': 'poloniex'}, ) assert_proper_response(response) data = response.json() assert data['message'] == '' assert len(data['result']) == 0
def test_exchange_query_trades(rotkehlchen_api_server_with_exchanges): """Test that using the exchange trades query endpoint works fine""" server = rotkehlchen_api_server_with_exchanges setup = mock_history_processing_and_exchanges(server.rest_api.rotkehlchen) # query trades of one specific exchange with setup.binance_patch: response = requests.get( api_url_for( server, "named_exchanges_trades_resource", name='binance', )) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert_binance_trades_result(json_data['result']) # query trades of all exchanges with setup.binance_patch, setup.polo_patch: response = requests.get(api_url_for(server, "exchangetradesresource")) assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert len( json_data['result']) == 2, 'only two exchanges should be registered' assert_binance_trades_result(json_data['result']['binance']) assert_poloniex_trades_result(json_data['result']['poloniex']) def assert_okay(response): """Helper function for DRY checking below assertions""" assert_proper_response(response) json_data = response.json() assert json_data['message'] == '' assert len(json_data['result'] ) == 2, 'only two exchanges should be registered' assert_binance_trades_result(json_data['result']['binance']) assert_poloniex_trades_result(json_data['result']['poloniex'], trades_to_check=(0, )) # and now query them in a specific time range excluding two of poloniex's trades data = {'from_timestamp': 1499865548, 'to_timestamp': 1539713118} with setup.binance_patch, setup.polo_patch: response = requests.get(api_url_for(server, "exchangetradesresource"), json=data) assert_okay(response) # do the same but with query args. This serves as test of from/to timestamp with query args with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for(server, "exchangetradesresource") + '?' + urlencode(data)) assert_okay(response)
def test_query_trades_over_limit(rotkehlchen_api_server_with_exchanges, start_with_valid_premium): """ Test querying the trades endpoint with trades over the limit limits the result if non premium """ rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) spam_trades = [ Trade(timestamp=x, location=Location.EXTERNAL, base_asset=A_BTC, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=FVal(x + 1), rate=FVal(1), fee=FVal(0), fee_currency=A_EUR, link='', notes='') for x in range(FREE_TRADES_LIMIT + 50) ] rotki.data.db.add_trades(spam_trades) # Check that we get all trades correctly even if we query two times for _ in range(2): with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), ) result = assert_proper_response_with_result(response) all_trades_num = FREE_TRADES_LIMIT + 50 + 5 # 5 = 3 polo and 2 binance if start_with_valid_premium: assert len(result['entries']) == all_trades_num assert result['entries_limit'] == -1 assert result['entries_found'] == all_trades_num else: assert len(result['entries']) == FREE_TRADES_LIMIT assert result['entries_limit'] == FREE_TRADES_LIMIT assert result['entries_found'] == all_trades_num
def test_exchange_query_trades_async(rotkehlchen_api_server_with_exchanges): """Test that using the exchange trades query endpoint works fine when called asynchronously""" server = rotkehlchen_api_server_with_exchanges setup = mock_history_processing_and_exchanges(server.rest_api.rotkehlchen) # async query trades of one specific exchange with setup.binance_patch: response = requests.get(api_url_for( server, "named_exchanges_trades_resource", name='binance', ), json={'async_query': True}) task_id = assert_ok_async_response(response) outcome = wait_for_async_task(rotkehlchen_api_server_with_exchanges, task_id) assert_binance_trades_result(outcome['result']) def assert_okay(response): """Helper function for DRY checking below assertions""" task_id = assert_ok_async_response(response) outcome = wait_for_async_task(rotkehlchen_api_server_with_exchanges, task_id) assert_binance_trades_result(outcome['result']['binance']) assert_poloniex_trades_result(outcome['result']['poloniex'], trades_to_check=(0, )) # query trades of all exchanges and in a specific range asynchronously data = { 'from_timestamp': 1499865548, 'to_timestamp': 1539713118, 'async_query': True } with setup.binance_patch, setup.polo_patch: response = requests.get(api_url_for(server, "exchangetradesresource"), json=data) assert_okay(response) # do the same but with query args. This serves as test of async query with query args with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for(server, "exchangetradesresource") + '?' + urlencode(data), ) assert_okay(response)
def test_query_messages(rotkehlchen_api_server_with_exchanges): """Test that querying the messages endpoint returns notifications for the user""" rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) # Query polo trades of to get them saved in the DB. This generates # warnings due to unsupported assets found during querying with setup.polo_patch: response = requests.get( api_url_for(rotkehlchen_api_server_with_exchanges, "tradesresource")) assert_proper_response(response) # and now query for the messages response = requests.get( api_url_for(rotkehlchen_api_server_with_exchanges, "messagesresource"), ) assert_proper_response(response) data = response.json() assert data['message'] == '' errors = data['result']['errors'] warnings = data['result']['warnings'] assert len(errors) == 0 assert len(warnings) == 2 assert warnings[ 0] == 'Found poloniex trade with unknown asset NOEXISTINGASSET. Ignoring it.' assert warnings[ 1] == 'Found poloniex trade with unsupported asset BALLS. Ignoring it.' # now query for the messages again and make sure that nothing is return, since # our previous query should have popped all the messages in the queue response = requests.get( api_url_for(rotkehlchen_api_server_with_exchanges, "messagesresource"), ) assert_proper_response(response) data = response.json() assert data['message'] == '' errors = data['result']['errors'] warnings = data['result']['warnings'] assert len(errors) == 0 assert len(warnings) == 0
def test_exchange_query_trades(rotkehlchen_api_server_with_exchanges): """Test that using the exchange trades query endpoint works fine""" async_query = random.choice([False, True]) server = rotkehlchen_api_server_with_exchanges setup = mock_history_processing_and_exchanges(server.rest_api.rotkehlchen) # query trades of one specific exchange with setup.binance_patch: response = requests.get( api_url_for( server, "tradesresource", ), json={ 'location': 'binance', 'async_query': async_query }, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server_with_exchanges, task_id) result = outcome['result'] else: result = assert_proper_response_with_result(response) assert result['entries_found'] > 0 assert result['entries_limit'] == FREE_TRADES_LIMIT assert_binance_trades_result([x['entry'] for x in result['entries']]) # query trades of all exchanges with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for(server, "tradesresource"), json={'async_query': async_query}, ) if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server_with_exchanges, task_id) result = outcome['result'] else: result = assert_proper_response_with_result(response) trades = result['entries'] assert_binance_trades_result([ x['entry'] for x in trades if x['entry']['location'] == 'binance' ]) # noqa: E501 assert_poloniex_trades_result([ x['entry'] for x in trades if x['entry']['location'] == 'poloniex' ]) # noqa: E501 def assert_okay(response): """Helper function for DRY checking below assertions""" if async_query: task_id = assert_ok_async_response(response) outcome = wait_for_async_task( rotkehlchen_api_server_with_exchanges, task_id) result = outcome['result'] else: result = assert_proper_response_with_result(response) trades = result['entries'] assert_binance_trades_result([ x['entry'] for x in trades if x['entry']['location'] == 'binance' ]) # noqa: E501 assert_poloniex_trades_result( trades=[ x['entry'] for x in trades if x['entry']['location'] == 'poloniex' ], trades_to_check=(2, ), ) # and now query them in a specific time range excluding two of poloniex's trades data = { 'from_timestamp': 1499865548, 'to_timestamp': 1539713118, 'async_query': async_query } with setup.binance_patch, setup.polo_patch: response = requests.get(api_url_for(server, "tradesresource"), json=data) assert_okay(response) # do the same but with query args. This serves as test of from/to timestamp with query args with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for(server, "tradesresource") + '?' + urlencode(data)) assert_okay(response)
def test_query_trades_associated_locations( rotkehlchen_api_server_with_exchanges): """Test that querying the trades endpoint works as expected when we have associated locations including associated exchanges and imported locations. """ rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) trades = [ Trade( timestamp=Timestamp(1596429934), location=Location.EXTERNAL, base_asset=A_WETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('1')), rate=Price(FVal('320')), fee=Fee(ZERO), fee_currency=A_EUR, link='', notes='', ), Trade( timestamp=Timestamp(1596429934), location=Location.KRAKEN, base_asset=A_WETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('1')), rate=Price(FVal('320')), fee=Fee(ZERO), fee_currency=A_EUR, link='', notes='', ), Trade( timestamp=Timestamp(1596429934), location=Location.BISQ, base_asset=A_WETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('1')), rate=Price(FVal('320')), fee=Fee(ZERO), fee_currency=A_EUR, link='', notes='', ), Trade( timestamp=Timestamp(1596429934), location=Location.BINANCE, base_asset=A_WETH, quote_asset=A_EUR, trade_type=TradeType.BUY, amount=AssetAmount(FVal('1')), rate=Price(FVal('320')), fee=Fee(ZERO), fee_currency=A_EUR, link='', notes='', ) ] # Add multiple entries for same exchange + connected exchange rotki.data.db.add_trades(trades) # Simply get all trades without any filtering with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), ) result = assert_proper_response_with_result(response) result = result['entries'] assert len( result ) == 9 # 3 polo, (2 + 1) binance trades, 1 kraken, 1 external, 1 BISQ expected_locations = ( Location.KRAKEN, Location.POLONIEX, Location.BINANCE, Location.BISQ, Location.EXTERNAL, ) returned_locations = {x['entry']['location'] for x in result} assert returned_locations == set(map(str, expected_locations)) response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json={ 'location': 'kraken', 'only_cache': True }, ) result = assert_proper_response_with_result(response) result = result['entries'] assert len(result) == 1 response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json={ 'location': 'binance', 'only_cache': True }, ) result = assert_proper_response_with_result(response) result = result['entries'] assert len(result) == 3 response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json={'location': 'nexo'}, ) result = assert_proper_response_with_result(response) result = result['entries'] assert len(result) == 0
def test_query_trades(rotkehlchen_api_server_with_exchanges, start_with_valid_premium): """Test that querying the trades endpoint works as expected Many similarities with test_exchanges.py::test_exchange_query_trades since those two endpoints got merged. """ rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) # Simply get all trades without any filtering with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), ) result = assert_proper_response_with_result(response) result = result['entries'] assert len(result) == 5 # 3 polo and 2 binance trades binance_ids = [ t['entry']['trade_id'] for t in result if t['entry']['location'] == 'binance' ] assert_binance_trades_result([ t['entry'] for t in result if t['entry']['location'] == 'binance' ]) # noqa: E501 assert_poloniex_trades_result([ t['entry'] for t in result if t['entry']['location'] == 'poloniex' ]) # noqa: E501 msg = 'should have no ignored trades at start' assert all(t['ignored_in_accounting'] is False for t in result), msg # now also try to ignore all binance trades for accounting response = requests.put( api_url_for( rotkehlchen_api_server_with_exchanges, 'ignoredactionsresource', ), json={ 'action_type': 'trade', 'action_ids': binance_ids }, ) result = assert_proper_response_with_result(response) assert set(result['trade']) == set(binance_ids) def assert_okay(response): """Helper function to run next query and its assertion twice""" result = assert_proper_response_with_result(response)['entries'] assert len(result) == 2 # only 2 binance trades assert_binance_trades_result([ t['entry'] for t in result if t['entry']['location'] == 'binance' ]) # noqa: E501 msg = 'binance trades should now be ignored for accounting' assert all(t['ignored_in_accounting'] is True for t in result if t['entry']['location'] == 'binance'), msg # noqa: E501 # Now filter by location with json body with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json={'location': 'binance'}, ) assert_okay(response) # Now filter by location with query params with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ) + '?location=binance', ) assert_okay(response) # Now filter by time with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json={ 'from_timestamp': 1512561942, 'to_timestamp': 1539713237 }, ) result = assert_proper_response_with_result(response)['entries'] assert len(result) == 3 # 1 binance trade and 2 poloniex trades assert_binance_trades_result( trades=[ t['entry'] for t in result if t['entry']['location'] == 'binance' ], trades_to_check=(0, ), ) assert_poloniex_trades_result( trades=[ t['entry'] for t in result if t['entry']['location'] == 'poloniex' ], trades_to_check=(1, 2), ) # filter by both time and location with setup.binance_patch, setup.polo_patch: data = { 'from_timestamp': 1512561942, 'to_timestamp': 1539713237, 'location': 'poloniex' } response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json=data, ) result = assert_proper_response_with_result(response)['entries'] assert len(result) == 2 # only 2/3 poloniex trades assert_poloniex_trades_result( trades=[ t['entry'] for t in result if t['entry']['location'] == 'poloniex' ], trades_to_check=(1, 2), ) # test pagination data = { 'location': 'poloniex', 'offset': 1, 'limit': 1, 'only_cache': True } response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json=data, ) result = assert_proper_response_with_result(response) assert result[ 'entries_limit'] == -1 if start_with_valid_premium else FREE_TRADES_LIMIT assert result['entries_total'] == 5 assert result['entries_found'] == 3 # for this filter result = result['entries'] assert len(result) == 1 # this filter and pagination assert_poloniex_trades_result( trades=[ t['entry'] for t in result if t['entry']['location'] == 'poloniex' ], trades_to_check=(1, ), ) def assert_order_by(order_by: str): """A helper to keep things DRY in the test""" data = { 'order_by_attribute': order_by, 'ascending': False, 'only_cache': True } response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json=data, ) result = assert_proper_response_with_result(response) assert result[ 'entries_limit'] == -1 if start_with_valid_premium else FREE_TRADES_LIMIT assert result['entries_total'] == 5 assert result['entries_found'] == 5 desc_result = result['entries'] assert len(desc_result) == 5 data = { 'order_by_attribute': order_by, 'ascending': True, 'only_cache': True } response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, 'tradesresource', ), json=data, ) result = assert_proper_response_with_result(response) assert result[ 'entries_limit'] == -1 if start_with_valid_premium else FREE_TRADES_LIMIT assert result['entries_total'] == 5 assert result['entries_found'] == 5 asc_result = result['entries'] assert len(asc_result) == 5 return desc_result, asc_result # test order by location desc_result, asc_result = assert_order_by('location') assert all(x['entry']['location'] == 'binance' for x in desc_result[:2]) assert all(x['entry']['location'] == 'poloniex' for x in desc_result[2:]) assert all(x['entry']['location'] == 'poloniex' for x in asc_result[:3]) assert all(x['entry']['location'] == 'binance' for x in asc_result[3:]) # test order by type desc_result, asc_result = assert_order_by('type') assert all(x['entry']['trade_type'] == 'sell' for x in desc_result[:2]) assert all(x['entry']['trade_type'] == 'buy' for x in desc_result[2:]) assert all(x['entry']['trade_type'] == 'buy' for x in asc_result[:3]) assert all(x['entry']['trade_type'] == 'sell' for x in asc_result[3:]) # test order by amount desc_result, asc_result = assert_order_by('amount') for idx, x in enumerate(desc_result): if idx < len(desc_result) - 1: assert FVal(x['entry']['amount']) >= FVal( desc_result[idx + 1]['entry']['amount']) for idx, x in enumerate(asc_result): if idx < len(asc_result) - 1: assert FVal(x['entry']['amount']) <= FVal( asc_result[idx + 1]['entry']['amount']) # test order by rate desc_result, asc_result = assert_order_by('rate') for idx, x in enumerate(desc_result): if idx < len(desc_result) - 1: assert FVal(x['entry']['rate']) >= FVal( desc_result[idx + 1]['entry']['rate']) for idx, x in enumerate(asc_result): if idx < len(asc_result) - 1: assert FVal(x['entry']['rate']) <= FVal( asc_result[idx + 1]['entry']['rate']) # test order by fee desc_result, asc_result = assert_order_by('fee') for idx, x in enumerate(desc_result): if idx < len(desc_result) - 1: assert FVal(x['entry']['fee']) >= FVal( desc_result[idx + 1]['entry']['fee']) for idx, x in enumerate(asc_result): if idx < len(asc_result) - 1: assert FVal(x['entry']['fee']) <= FVal( asc_result[idx + 1]['entry']['fee'])
def test_query_trades(rotkehlchen_api_server_with_exchanges): """Test that querying the trades endpoint works as expected""" rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) # Query trades of all exchanges to get them saved in the DB with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for(rotkehlchen_api_server_with_exchanges, "exchangetradesresource")) assert_proper_response(response) # Simply get all trades without any filtering response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), ) assert_proper_response(response) data = response.json() assert data['message'] == '' assert len(data['result']) == 5 # 3 polo and 2 binance trades assert_binance_trades_result( [t for t in data['result'] if t['location'] == 'binance']) assert_poloniex_trades_result( [t for t in data['result'] if t['location'] == 'poloniex']) def assert_okay(response): """Helper function to run next query and its assertion twice""" assert_proper_response(response) data = response.json() assert data['message'] == '' assert len(data['result']) == 2 # only 2 binance trades assert_binance_trades_result( [t for t in data['result'] if t['location'] == 'binance']) # Now filter by location with json body response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={'location': 'binance'}, ) assert_okay(response) # Now filter by location with query params response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ) + '?location=binance', ) assert_okay(response) # Now filter by time response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={ 'from_timestamp': 1512561942, 'to_timestamp': 1539713237 }, ) assert_proper_response(response) data = response.json() assert data['message'] == '' assert len(data['result']) == 3 # 1 binance trade and 2 poloniex trades assert_binance_trades_result( trades=[t for t in data['result'] if t['location'] == 'binance'], trades_to_check=(1, ), ) assert_poloniex_trades_result( trades=[t for t in data['result'] if t['location'] == 'poloniex'], trades_to_check=(0, 1), ) # and now filter by both time and location response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={ 'from_timestamp': 1512561942, 'to_timestamp': 1539713237, 'location': 'poloniex' }, ) assert_proper_response(response) data = response.json() assert data['message'] == '' assert len(data['result']) == 2 # only 2/3 poloniex trades assert_poloniex_trades_result( trades=[t for t in data['result'] if t['location'] == 'poloniex'], trades_to_check=(0, 1), )
def test_query_trades(rotkehlchen_api_server_with_exchanges): """Test that querying the trades endpoint works as expected Many similarities with test_exchanges.py::test_exchange_query_trades since those two endpoints got merged. """ rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) # Simply get all trades without any filtering with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), ) result = assert_proper_response_with_result(response) result = result['entries'] assert len(result) == 5 # 3 polo and 2 binance trades assert_binance_trades_result( [t for t in result if t['location'] == 'binance']) assert_poloniex_trades_result( [t for t in result if t['location'] == 'poloniex']) def assert_okay(response): """Helper function to run next query and its assertion twice""" result = assert_proper_response_with_result(response)['entries'] assert len(result) == 2 # only 2 binance trades assert_binance_trades_result( [t for t in result if t['location'] == 'binance']) # Now filter by location with json body with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={'location': 'binance'}, ) assert_okay(response) # Now filter by location with query params with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ) + '?location=binance', ) assert_okay(response) # Now filter by time with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={ 'from_timestamp': 1512561942, 'to_timestamp': 1539713237 }, ) result = assert_proper_response_with_result(response)['entries'] assert len(result) == 3 # 1 binance trade and 2 poloniex trades assert_binance_trades_result( trades=[t for t in result if t['location'] == 'binance'], trades_to_check=(0, ), ) assert_poloniex_trades_result( trades=[t for t in result if t['location'] == 'poloniex'], trades_to_check=(1, 2), ) # and now filter by both time and location with setup.binance_patch, setup.polo_patch: data = { 'from_timestamp': 1512561942, 'to_timestamp': 1539713237, 'location': 'poloniex' } response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json=data, ) result = assert_proper_response_with_result(response)['entries'] assert len(result) == 2 # only 2/3 poloniex trades assert_poloniex_trades_result( trades=[t for t in result if t['location'] == 'poloniex'], trades_to_check=(1, 2), )
def test_history_creation_corrupt_trades_cache( rotkehlchen_server_with_exchanges, accountant, accounting_data_dir, ): """ Tests for corrupt trade cache. Test that if cache is used and data are corrupt we revert to omitting cache and contacting the exchanges """ # Create the "broken" trades cache. Must have broken cache for all registered exchanges too historyfile_path = os.path.join(accounting_data_dir, TRADES_HISTORYFILE) with open(historyfile_path, 'w') as f: f.write( f'{{"start_time":0, "end_time": {TEST_END_TS}, "data": ' f'[{{"unknown_trade_key": "random_trade_value"}}]}}', ) with open(os.path.join(accounting_data_dir, 'poloniex_trades.json'), 'w') as f: f.write( f'{{"start_time":0, "end_time": {TEST_END_TS}, "data": ' f'{{"BTC_SJX": [{{"unknown_trade_key": "random_trade_value"}}]}}}}', ) with open(os.path.join(accounting_data_dir, 'kraken_trades.json'), 'w') as f: f.write( f'{{"start_time":0, "end_time": {TEST_END_TS}, "data": ' f'[{{"unknown_trade_key": "random_trade_value"}}]}}', ) with open(os.path.join(accounting_data_dir, 'binance_trades.json'), 'w') as f: f.write( f'{{"start_time":0, "end_time": {TEST_END_TS}, "data": ' f'[{{"unknown_trade_key": "random_trade_value"}}]}}', ) with open(os.path.join(accounting_data_dir, 'bittrex_trades.json'), 'w') as f: f.write( f'{{"start_time":0, "end_time": {TEST_END_TS}, "data": ' f'[{{"unknown_trade_key": "random_trade_value"}}]}}', ) with open(os.path.join(accounting_data_dir, LOANS_HISTORYFILE), 'w') as f: f.write( f'{{"start_time":0, "end_time": {TEST_END_TS}, "data": ' f'[{{"unknown_trade_key": "random_trade_value"}}]}}', ) with open(os.path.join(accounting_data_dir, ASSETMOVEMENTS_HISTORYFILE), 'w') as f: f.write( f'{{"start_time":0, "end_time": {TEST_END_TS}, "data": ' f'[]}}', ) with open(os.path.join(accounting_data_dir, ETHEREUM_TX_LOGFILE), 'w') as f: f.write( f'{{"start_time":0, "end_time": {TEST_END_TS}, "data": ' f'[]}}', ) server = rotkehlchen_server_with_exchanges rotki = server.rotkehlchen rotki.accountant = accountant kraken = rotki.exchange_manager.connected_exchanges['kraken'] kraken.random_trade_data = False kraken.random_ledgers_data = False ( accountant_patch, polo_patch, binance_patch, bittrex_patch, bitmex_patch, ) = mock_history_processing_and_exchanges(rotki) with accountant_patch, polo_patch, binance_patch, bittrex_patch, bitmex_patch: response = server.process_trade_history(start_ts='0', end_ts=str(TEST_END_TS)) # The history processing is completely mocked away and omitted in this test. # because it is only for the history creation not its processing. # For history processing tests look at test_accounting.py and # test_accounting_events.py assert response['message'] == '' assert response['result'] == {} # And now make sure that warnings/errors number did not change warnings = rotki.msg_aggregator.consume_warnings() assert len(warnings) == 13 errors = rotki.msg_aggregator.consume_errors() assert len(errors) == 3
def test_query_trades(rotkehlchen_api_server_with_exchanges): """Test that querying the trades endpoint works as expected Many similarities with test_exchanges.py::test_exchange_query_trades since those two endpoints got merged. """ rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) # Simply get all trades without any filtering with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), ) result = assert_proper_response_with_result(response) result = result['entries'] assert len(result) == 5 # 3 polo and 2 binance trades binance_ids = [t['entry']['trade_id'] for t in result if t['entry']['location'] == 'binance'] assert_binance_trades_result([t['entry'] for t in result if t['entry']['location'] == 'binance']) # noqa: E501 assert_poloniex_trades_result([t['entry'] for t in result if t['entry']['location'] == 'poloniex']) # noqa: E501 msg = 'should have no ignored trades at start' assert all(t['ignored_in_accounting'] is False for t in result), msg # now also try to ignore all binance trades for accounting response = requests.put( api_url_for( rotkehlchen_api_server_with_exchanges, 'ignoredactionsresource', ), json={'action_type': 'trade', 'action_ids': binance_ids}, ) result = assert_proper_response_with_result(response) assert result == {'trade': binance_ids} def assert_okay(response): """Helper function to run next query and its assertion twice""" result = assert_proper_response_with_result(response)['entries'] assert len(result) == 2 # only 2 binance trades assert_binance_trades_result([t['entry'] for t in result if t['entry']['location'] == 'binance']) # noqa: E501 msg = 'binance trades should now be ignored for accounting' assert all(t['ignored_in_accounting'] is True for t in result if t['entry']['location'] == 'binance'), msg # noqa: E501 # Now filter by location with json body with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={'location': 'binance'}, ) assert_okay(response) # Now filter by location with query params with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ) + '?location=binance', ) assert_okay(response) # Now filter by time with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={'from_timestamp': 1512561942, 'to_timestamp': 1539713237}, ) result = assert_proper_response_with_result(response)['entries'] assert len(result) == 3 # 1 binance trade and 2 poloniex trades assert_binance_trades_result( trades=[t['entry'] for t in result if t['entry']['location'] == 'binance'], trades_to_check=(0,), ) assert_poloniex_trades_result( trades=[t['entry'] for t in result if t['entry']['location'] == 'poloniex'], trades_to_check=(1, 2), ) # and now filter by both time and location with setup.binance_patch, setup.polo_patch: data = {'from_timestamp': 1512561942, 'to_timestamp': 1539713237, 'location': 'poloniex'} response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json=data, ) result = assert_proper_response_with_result(response)['entries'] assert len(result) == 2 # only 2/3 poloniex trades assert_poloniex_trades_result( trades=[t['entry'] for t in result if t['entry']['location'] == 'poloniex'], trades_to_check=(1, 2), )
def test_edit_trades(rotkehlchen_api_server_with_exchanges): """Test that editing a trade via the trades endpoint works as expected""" rotki = rotkehlchen_api_server_with_exchanges.rest_api.rotkehlchen setup = mock_history_processing_and_exchanges(rotki) # Simply get all trades without any filtering with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), ) assert_proper_response(response) trades = response.json()['result']['entries'] # get the binance trades original_binance_trades = [ t['entry'] for t in trades if t['entry']['location'] == 'binance' ] # get the poloniex trades original_poloniex_trades = [ t['entry'] for t in trades if t['entry']['location'] == 'poloniex' ] # Test that setting '0' as value for `fee` fails original_poloniex_trades[0]['fee'] = '0' response = requests.patch( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json=original_poloniex_trades[0], ) assert_error_response(response, contained_in_msg='fee cannot be zero') # Test that popping either of `fee` or `fee_currency` fails original_poloniex_trades[1].pop('fee', None) response = requests.patch( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json=original_poloniex_trades[1], ) assert_error_response( response, contained_in_msg='fee and fee_currency must be provided') # Test that setting both `fee` and `fee_currency` passes response = requests.patch( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json=original_poloniex_trades[2], ) assert_proper_response(response) for trade in original_binance_trades: # edit two fields of each binance trade trade['amount'] = '1337.5' trade['notes'] = 'edited trade' response = requests.patch( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json=trade, ) assert_proper_response(response) data = response.json() assert data['message'] == '' # check that the returned trade is edited _check_trade_is_edited(original_trade=trade, result_trade=data['result']) # Finally also query binance trades to see they are edited with setup.binance_patch, setup.polo_patch: response = requests.get( api_url_for( rotkehlchen_api_server_with_exchanges, "tradesresource", ), json={'location': 'binance'}, ) trades = assert_proper_response_with_result(response)['entries'] assert len(trades) == 2 # only 2 binance trades for idx, trade in enumerate(trades): _check_trade_is_edited(original_trade=original_binance_trades[idx], result_trade=trade['entry']) # noqa: E501