diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ada9889a6..c86fb616b 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1448,13 +1448,16 @@ class FreqtradeBot: fee_cost, fee_currency, fee_rate = self.exchange.extract_cost_curr_rate(order) logger.info(f"Fee for Trade {trade} [{order.get('side')}]: " f"{fee_cost:.8g} {fee_currency} - rate: {fee_rate}") - - trade.update_fee(fee_cost, fee_currency, fee_rate, order.get('side', '')) - if trade_base_currency == fee_currency: - # Apply fee to amount - return self.apply_fee_conditional(trade, trade_base_currency, - amount=order_amount, fee_abs=fee_cost) - return order_amount + if fee_rate is None or fee_rate < 0.02: + # Reject all fees that report as > 2%. + # These are most likely caused by a parsing bug in ccxt + # due to multiple trades (https://github.com/ccxt/ccxt/issues/8025) + trade.update_fee(fee_cost, fee_currency, fee_rate, order.get('side', '')) + if trade_base_currency == fee_currency: + # Apply fee to amount + return self.apply_fee_conditional(trade, trade_base_currency, + amount=order_amount, fee_abs=fee_cost) + return order_amount return self.fee_detection_from_trades(trade, order, order_amount) def fee_detection_from_trades(self, trade: Trade, order: Dict, order_amount: float) -> float: diff --git a/tests/conftest.py b/tests/conftest.py index 079a521ed..5d358f015 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1588,16 +1588,7 @@ def fetch_trades_result(): @pytest.fixture(scope="function") def trades_for_order2(): - return [{'info': {'id': 34567, - 'orderId': 123456, - 'price': '0.24544100', - 'qty': '8.00000000', - 'commission': '0.00800000', - 'commissionAsset': 'LTC', - 'time': 1521663363189, - 'isBuyer': True, - 'isMaker': False, - 'isBestMatch': True}, + return [{'info': {}, 'timestamp': 1521663363189, 'datetime': '2018-03-21T20:16:03.189Z', 'symbol': 'LTC/ETH', @@ -1609,16 +1600,7 @@ def trades_for_order2(): 'cost': 1.963528, 'amount': 4.0, 'fee': {'cost': 0.004, 'currency': 'LTC'}}, - {'info': {'id': 34567, - 'orderId': 123456, - 'price': '0.24544100', - 'qty': '8.00000000', - 'commission': '0.00800000', - 'commissionAsset': 'LTC', - 'time': 1521663363189, - 'isBuyer': True, - 'isMaker': False, - 'isBestMatch': True}, + {'info': {}, 'timestamp': 1521663363189, 'datetime': '2018-03-21T20:16:03.189Z', 'symbol': 'LTC/ETH', @@ -1632,6 +1614,14 @@ def trades_for_order2(): 'fee': {'cost': 0.004, 'currency': 'LTC'}}] +@pytest.fixture(scope="function") +def trades_for_order3(trades_for_order2): + # Different fee currencies for each trade + trades_for_order = deepcopy(trades_for_order2) + trades_for_order[0]['fee'] = {'cost': 0.02, 'currency': 'BNB'} + return trades_for_order + + @pytest.fixture def buy_order_fee(): return { diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 6adef510f..459a09c0c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -3718,6 +3718,48 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).', caplog) + assert trade.fee_open == 0.001 + assert trade.fee_close == 0.001 + assert trade.fee_open_cost is not None + assert trade.fee_open_currency is not None + assert trade.fee_close_cost is None + assert trade.fee_close_currency is None + + +def test_get_real_amount_multi2(default_conf, trades_for_order3, buy_order_fee, caplog, fee, + mocker, markets): + # Different fee currency on both trades + mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order3) + amount = float(sum(x['amount'] for x in trades_for_order3)) + default_conf['stake_currency'] = 'ETH' + trade = Trade( + pair='LTC/ETH', + amount=amount, + exchange='binance', + fee_open=fee.return_value, + fee_close=fee.return_value, + open_rate=0.245441, + open_order_id="123456" + ) + # Fake markets entry to enable fee parsing + markets['BNB/ETH'] = markets['ETH/BTC'] + freqtrade = get_patched_freqtradebot(mocker, default_conf) + mocker.patch('freqtrade.exchange.Exchange.markets', PropertyMock(return_value=markets)) + mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', + return_value={'ask': 0.19, 'last': 0.2}) + + # Amount is reduced by "fee" + assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.0005) + assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + 'open_rate=0.24544100, open_since=closed) (from 8.0 to 7.996).', + caplog) + # Overall fee is average of both trade's fee + assert trade.fee_open == 0.001518575 + assert trade.fee_open_cost is not None + assert trade.fee_open_currency is not None + assert trade.fee_close_cost is None + assert trade.fee_close_currency is None + def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, fee, caplog, mocker): @@ -4264,7 +4306,7 @@ def test_update_closed_trades_without_assigned_fees(mocker, default_conf, fee): freqtrade = get_patched_freqtradebot(mocker, default_conf) def patch_with_fee(order): - order.update({'fee': {'cost': 0.1, 'rate': 0.2, + order.update({'fee': {'cost': 0.1, 'rate': 0.01, 'currency': order['symbol'].split('/')[0]}}) return order