Merge pull request #8901 from freqtrade/fix/8841

Avoid false-triggering "dust-eating" logic for rebuys
This commit is contained in:
Matthias 2023-07-13 18:06:33 +02:00 committed by GitHub
commit 061e930eb1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 65 additions and 2 deletions

View File

@ -1939,6 +1939,7 @@ class FreqtradeBot(LoggingMixin):
""" """
Applies the fee to amount (either from Order or from Trades). Applies the fee to amount (either from Order or from Trades).
Can eat into dust if more than the required asset is available. 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, Can't happen in Futures mode - where Fees are always in settlement currency,
never in base currency. never in base currency.
""" """
@ -1948,6 +1949,10 @@ class FreqtradeBot(LoggingMixin):
# check against remaining amount! # check against remaining amount!
amount_ = trade.amount - 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_: if fee_abs != 0 and self.wallets.get_free(trade_base_currency) >= amount_:
# Eat into dust if we own more than base currency # Eat into dust if we own more than base currency
logger.info(f"Fee amount for {trade} was in base currency - " logger.info(f"Fee amount for {trade} was in base currency - "
@ -1977,7 +1982,11 @@ class FreqtradeBot(LoggingMixin):
# Init variables # Init variables
order_amount = safe_value_fallback(order, 'filled', 'amount') order_amount = safe_value_fallback(order, 'filled', 'amount')
# Only run for closed orders # 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 return None
trade_base_currency = self.exchange.get_pair_base_currency(trade.pair) trade_base_currency = self.exchange.get_pair_base_currency(trade.pair)

View File

@ -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, 8.0, None),
(8.0, 0.1, 7.9, 0.1), (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): amount, fee_abs, wallet, amount_exp):
walletmock = mocker.patch('freqtrade.wallets.Wallets.update') walletmock = mocker.patch('freqtrade.wallets.Wallets.update')
mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=wallet) 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 # Amount is kept as is
assert freqtrade.apply_fee_conditional(trade, 'LTC', amount, fee_abs, order) == amount_exp assert freqtrade.apply_fee_conditional(trade, 'LTC', amount, fee_abs, order) == amount_exp
assert walletmock.call_count == 1 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", [ @pytest.mark.parametrize("delta, is_high_delta", [