diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 562cdc2e0..1fdf79b68 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1939,6 +1939,7 @@ class FreqtradeBot(LoggingMixin): """ Applies the fee to amount (either from Order or from Trades). Can eat into dust if more than the required asset is available. + In case of trade adjustment orders, trade.amount will not have been adjusted yet. Can't happen in Futures mode - where Fees are always in settlement currency, never in base currency. """ @@ -1948,6 +1949,10 @@ class FreqtradeBot(LoggingMixin): # check against remaining amount! amount_ = trade.amount - amount + if trade.nr_of_successful_entries >= 1 and order_obj.ft_order_side == trade.entry_side: + # In case of rebuy's, trade.amount doesn't contain the amount of the last entry. + amount_ = trade.amount + amount + if fee_abs != 0 and self.wallets.get_free(trade_base_currency) >= amount_: # Eat into dust if we own more than base currency logger.info(f"Fee amount for {trade} was in base currency - " @@ -1977,7 +1982,11 @@ class FreqtradeBot(LoggingMixin): # Init variables order_amount = safe_value_fallback(order, 'filled', 'amount') # Only run for closed orders - if trade.fee_updated(order.get('side', '')) or order['status'] == 'open': + if ( + trade.fee_updated(order.get('side', '')) + or order['status'] == 'open' + or order_obj.ft_fee_base + ): return None trade_base_currency = self.exchange.get_pair_base_currency(trade.pair) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 3bfa5a127..742fdcace 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -5023,7 +5023,7 @@ def test_get_real_amount_in_point(default_conf_usdt, buy_order_fee, fee, mocker, (8.0, 0.1, 8.0, None), (8.0, 0.1, 7.9, 0.1), ]) -def test_apply_fee_conditional(default_conf_usdt, fee, mocker, +def test_apply_fee_conditional(default_conf_usdt, fee, mocker, caplog, amount, fee_abs, wallet, amount_exp): walletmock = mocker.patch('freqtrade.wallets.Wallets.update') mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=wallet) @@ -5048,6 +5048,60 @@ def test_apply_fee_conditional(default_conf_usdt, fee, mocker, # Amount is kept as is assert freqtrade.apply_fee_conditional(trade, 'LTC', amount, fee_abs, order) == amount_exp assert walletmock.call_count == 1 + if fee_abs != 0 and amount_exp is None: + assert log_has_re(r"Fee amount.*Eating.*dust\.", caplog) + + +@pytest.mark.parametrize('amount,fee_abs,wallet,amount_exp', [ + (8.0, 0.0, 16, None), + (8.0, 0.0, 0, None), + (8.0, 0.1, 8, 0.1), + (8.0, 0.1, 20, None), + (8.0, 0.1, 16.0, None), + (8.0, 0.1, 7.9, 0.1), + (8.0, 0.1, 12, 0.1), + (8.0, 0.1, 15.9, 0.1), +]) +def test_apply_fee_conditional_multibuy(default_conf_usdt, fee, mocker, caplog, + amount, fee_abs, wallet, amount_exp): + walletmock = mocker.patch('freqtrade.wallets.Wallets.update') + mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=wallet) + trade = Trade( + pair='LTC/ETH', + amount=amount, + exchange='binance', + open_rate=0.245441, + fee_open=fee.return_value, + fee_close=fee.return_value, + open_order_id="123456" + ) + # One closed order + order = Order( + ft_order_side='buy', + order_id='10', + ft_pair=trade.pair, + ft_is_open=False, + filled=amount, + status="closed" + ) + trade.orders.append(order) + # Add additional order - this should NOT eat into dust unless the wallet was bigger already. + order1 = Order( + ft_order_side='buy', + order_id='100', + ft_pair=trade.pair, + ft_is_open=True, + ) + trade.orders.append(order1) + + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + + walletmock.reset_mock() + # The new trade amount will be 2x amount - fee / wallet will have to be adapted to this. + assert freqtrade.apply_fee_conditional(trade, 'LTC', amount, fee_abs, order1) == amount_exp + assert walletmock.call_count == 1 + if fee_abs != 0 and amount_exp is None: + assert log_has_re(r"Fee amount.*Eating.*dust\.", caplog) @pytest.mark.parametrize("delta, is_high_delta", [