From db03a2410958fff66dd943f35bc46527976876f3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jun 2021 06:44:51 +0200 Subject: [PATCH] Add tests for fill methods --- freqtrade/exchange/exchange.py | 14 ++++---- tests/conftest.py | 34 ++++++++++++++++++ tests/exchange/test_exchange.py | 64 +++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 87798e612..ea3a7d7cd 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -608,22 +608,24 @@ class Exchange: """ if self.exchange_has('fetchL2OrderBook'): ob = self.fetch_l2_order_book(pair, 20) - book_entry_type = 'asks' if side == 'buy' else 'bids' + ob_type = 'asks' if side == 'buy' else 'bids' remaining_amount = amount filled_amount = 0 - for book_entry in ob[book_entry_type]: + for book_entry in ob[ob_type]: book_entry_price = book_entry[0] book_entry_coin_volume = book_entry[1] - book_entry_ref_currency_volume = book_entry_price * book_entry_coin_volume if remaining_amount > 0: - if remaining_amount < book_entry_ref_currency_volume: + if remaining_amount < book_entry_coin_volume: filled_amount += remaining_amount * book_entry_price else: - filled_amount += book_entry_ref_currency_volume * book_entry_price - remaining_amount -= book_entry_ref_currency_volume + filled_amount += book_entry_coin_volume * book_entry_price + remaining_amount -= book_entry_coin_volume else: break + else: + # If remaining_amount wasn't consumed completely (break was not called) + filled_amount += remaining_amount * book_entry_price forecast_avg_filled_price = filled_amount / amount return self.price_to_precision(pair, forecast_avg_filled_price) diff --git a/tests/conftest.py b/tests/conftest.py index 43a98647f..8ce41cf9f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1087,6 +1087,40 @@ def order_book_l2(): }) +@pytest.fixture +def order_book_l2_usd(): + return MagicMock(return_value={ + 'symbol': 'LTC/USDT', + 'bids': [ + [25.563, 49.269], + [25.562, 83.0], + [25.56, 106.0], + [25.559, 15.381], + [25.558, 29.299], + [25.557, 34.624], + [25.556, 10.0], + [25.555, 14.684], + [25.554, 45.91], + [25.553, 50.0] + ], + 'asks': [ + [25.566, 14.27], + [25.567, 48.484], + [25.568, 92.349], + [25.572, 31.48], + [25.573, 23.0], + [25.574, 20.0], + [25.575, 89.606], + [25.576, 262.016], + [25.577, 178.557], + [25.578, 78.614] + ], + 'timestamp': None, + 'datetime': None, + 'nonce': 2372149736 + }) + + @pytest.fixture def ohlcv_history_list(): return [ diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 73b8022d1..2c4ddacb4 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -947,6 +947,70 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name): assert order["symbol"] == "ETH/BTC" +@pytest.mark.parametrize("side,startprice,endprice", [ + ("buy", 25.563, 25.566), + ("sell", 25.566, 25.563) +]) +@pytest.mark.parametrize("exchange_name", EXCHANGES) +def test_create_dry_run_order_limit_fill(default_conf, mocker, side, startprice, endprice, + exchange_name, order_book_l2_usd): + default_conf['dry_run'] = True + exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) + mocker.patch.multiple('freqtrade.exchange.Exchange', + exchange_has=MagicMock(return_value=True), + fetch_l2_order_book=order_book_l2_usd, + ) + + order = exchange.create_dry_run_order( + pair='LTC/USDT', ordertype='limit', side=side, amount=1, rate=startprice) + assert 'id' in order + assert f'dry_run_{side}_' in order["id"] + assert order["side"] == side + assert order["type"] == "limit" + assert order["symbol"] == "LTC/USDT" + + order_closed = exchange.fetch_dry_run_order(order['id']) + assert order_book_l2_usd.call_count == 1 + assert order_closed['status'] == 'open' + assert not order['fee'] + + order_book_l2_usd.reset_mock() + order_closed['price'] = endprice + + order_closed = exchange.fetch_dry_run_order(order['id']) + assert order_closed['status'] == 'closed' + assert order['fee'] + + +@pytest.mark.parametrize("side,amount,endprice", [ + ("buy", 1, 25.566), + ("buy", 100, 25.5672), # Requires interpolation + ("buy", 1000, 25.575), # More than orderbook return + ("sell", 1, 25.563), + ("sell", 100, 25.5625), # Requires interpolation + ("sell", 1000, 25.5555), # More than orderbook return +]) +@pytest.mark.parametrize("exchange_name", EXCHANGES) +def test_create_dry_run_order_market_fill(default_conf, mocker, side, amount, endprice, + exchange_name, order_book_l2_usd): + default_conf['dry_run'] = True + exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) + mocker.patch.multiple('freqtrade.exchange.Exchange', + exchange_has=MagicMock(return_value=True), + fetch_l2_order_book=order_book_l2_usd, + ) + + order = exchange.create_dry_run_order( + pair='LTC/USDT', ordertype='market', side=side, amount=amount, rate=25.5) + assert 'id' in order + assert f'dry_run_{side}_' in order["id"] + assert order["side"] == side + assert order["type"] == "market" + assert order["symbol"] == "LTC/USDT" + assert order['status'] == 'closed' + assert round(order["average"], 4) == round(endprice, 4) + + @pytest.mark.parametrize("side", [ ("buy"), ("sell")