Пример #1
0
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'] == {}
Пример #2
0
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)
Пример #3
0
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]
Пример #4
0
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'
Пример #5
0
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
Пример #6
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)
Пример #7
0
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
Пример #8
0
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)
Пример #9
0
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
Пример #10
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)
Пример #11
0
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
Пример #12
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'])
Пример #13
0
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),
    )
Пример #14
0
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),
    )
Пример #15
0
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
Пример #16
0
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),
    )
Пример #17
0
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