refactor get_real_amount
This commit is contained in:
		| @@ -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: | ||||
|   | ||||
| @@ -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) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user