diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a74481b9a..5a435ae06 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -367,16 +367,13 @@ class FreqtradeBot(object): order = exchange.get_order(trade.open_order_id, trade.pair) # Try update amount (binance-fix) try: - # Only run for closed orders - if trade.fee_open != 0 and not (order['status'] == 'open'): - new_amount = self.get_real_amount(trade) - # This may break if a exchange applies no fee (which appears highly unlikely) - if order['amount'] != new_amount: - logger.info("Applying fee to amount for Trade {} from {} to {} ".format( - trade, order['amount'], new_amount)) - order['amount'] = new_amount - # Fee was applied, so set to 0 - trade.fee_open = 0 + new_amount = self.get_real_amount(trade, order) + if order['amount'] != new_amount: + logger.info("Applying fee to amount for Trade {} from {} to {} ".format( + trade, order['amount'], new_amount)) + order['amount'] = new_amount + # Fee was applied, so set to 0 + trade.fee_open = 0 except OperationalException as exception: logger.warning("could not update trade amount: %s", exception) @@ -388,30 +385,46 @@ class FreqtradeBot(object): return self.handle_trade(trade) return False - def get_real_amount(self, order: Trade) -> float: + def get_real_amount(self, trade: Trade, order: Dict) -> float: """ Get real amount for the trade - This is needed for exchanges which charge fees in target currency (e.g. binance) + Necessary for exchanges which charge fees in base currency (e.g. binance) """ + order_amount = order['amount'] + # Only run for closed orders + if trade.fee_open == 0 or not order['status'] == 'open': + return order_amount - trades = exchange.get_trades_for_order( - order.open_order_id, order.pair, order.open_date) + # use fee from order-dict if possible + if order['fee']: + if trade.pair.startswith(order['fee']['currency']): + new_amount = order_amount - order['fee']['cost'] + logger.info("Applying fee on amount for %s (from %s to %s) from Order", + trade, order['amount'], new_amount) + return new_amount + + # Fallback to Trades + trades = exchange.get_trades_for_order(trade.open_order_id, trade.pair, trade.open_date) if len(trades) == 0: - raise OperationalException("get_real_amount: no trade found") + logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade) + return order_amount amount = 0 fee_abs = 0 - for trade in trades: - amount += trade["amount"] - if "fee" in trade: + for exectrade in trades: + amount += exectrade['amount'] + if "fee" in exectrade: # only applies if fee is in quote currency! - if order.pair.startswith(trade["fee"]["currency"]): - fee_abs += trade["fee"]["cost"] + if trade.pair.startswith(exectrade['fee']['currency']): + fee_abs += exectrade['fee']['cost'] - if amount != order.amount: - logger.warning("amount {} does not match amount {}".format(amount, order.amount)) + if amount != order_amount: + logger.warning("amount {} does not match amount {}".format(amount, trade.amount)) raise OperationalException("Half bought? Amounts don't match") real_amount = amount - fee_abs + if fee_abs != 0: + logger.info("Applying fee on amount for {} (from {} to {}) from Trades".format( + trade, order['amount'], real_amount)) return real_amount def handle_trade(self, trade: Trade) -> bool: diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index cd9d7966c..45c6620df 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -585,14 +585,12 @@ def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplo trade.open_fee = 0.001 assert not freqtrade.process_maybe_execute_sell(trade) # Test amount not modified by fee-logic - assert not log_has('Updating amount for Trade {} from 90.99181073 to 90.81'.format(trade), - caplog.record_tuples) + assert not log_has('Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format( + trade), caplog.record_tuples) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) # test amount modified by fee-logic assert not freqtrade.process_maybe_execute_sell(trade) - assert log_has('Updating amount for Trade {} from 90.99181073 to 90.81'.format(trade), - caplog.record_tuples) trade.is_open = True trade.open_order_id = None @@ -1319,7 +1317,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke assert freqtrade.handle_trade(trade) is True -def test_get_real_amount_quote(default_conf, trades_for_order, mocker): +def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, caplog, mocker): """ Test get_real_amount - fee in quote currency """ @@ -1335,14 +1333,18 @@ def test_get_real_amount_quote(default_conf, trades_for_order, mocker): pair='LTC/ETH', amount=amount, exchange='binance', + open_rate=0.245441, open_order_id="123456" ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) # Amount is reduced by "fee" - assert freqtrade.get_real_amount(trade) == amount - (amount * 0.001) + assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001) + 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.992) from Trades', + caplog.record_tuples) -def test_get_real_amount_stake(default_conf, trades_for_order, mocker): +def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, caplog, mocker): """ Test get_real_amount - fees in Stake currency """ @@ -1358,14 +1360,15 @@ def test_get_real_amount_stake(default_conf, trades_for_order, mocker): pair='LTC/ETH', amount=amount, exchange='binance', + open_rate=0.245441, open_order_id="123456" ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) # Amount does not change - assert freqtrade.get_real_amount(trade) == amount + assert freqtrade.get_real_amount(trade, buy_order_fee) == amount -def test_get_real_amount_BNB(default_conf, trades_for_order, mocker): +def test_get_real_amount_BNB(default_conf, trades_for_order, buy_order_fee, mocker): """ Test get_real_amount - Fees in BNB """ @@ -1383,14 +1386,15 @@ def test_get_real_amount_BNB(default_conf, trades_for_order, mocker): pair='LTC/ETH', amount=amount, exchange='binance', + open_rate=0.245441, open_order_id="123456" ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) # Amount does not change - assert freqtrade.get_real_amount(trade) == amount + assert freqtrade.get_real_amount(trade, buy_order_fee) == amount -def test_get_real_amount_multi(default_conf, trades_for_order2, mocker): +def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, caplog, mocker): """ Test get_real_amount with split trades (multiple trades for this order) """ @@ -1405,8 +1409,40 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, mocker): pair='LTC/ETH', amount=amount, exchange='binance', + open_rate=0.245441, open_order_id="123456" ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) # Amount is reduced by "fee" - assert freqtrade.get_real_amount(trade) == amount - (amount * 0.001) + assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001) + 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.992) from Trades', + caplog.record_tuples) + + +def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee, caplog, mocker): + """ + Test get_real_amount with split trades (multiple trades for this order) + """ + limit_buy_order = deepcopy(buy_order_fee) + limit_buy_order['fee'] = {'cost': 0.004, 'currency': 'LTC'} + + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order) + amount = float(sum(x['amount'] for x in trades_for_order)) + trade = Trade( + pair='LTC/ETH', + amount=amount, + exchange='binance', + open_rate=0.245441, + open_order_id="123456" + ) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + # Amount is reduced by "fee" + assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004 + 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) from Order', + caplog.record_tuples)