From 11d8f7d522e559618bb4f4efee062ffa1252f701 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 15 Apr 2018 19:38:58 +0200 Subject: [PATCH 01/30] add get_real_amount and tests --- freqtrade/freqtradebot.py | 47 +++++++ freqtrade/tests/test_freqtradebot.py | 193 +++++++++++++++++++++++++++ 2 files changed, 240 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 02d22a6dc..6d158deb0 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -362,6 +362,53 @@ class FreqtradeBot(object): return self.handle_trade(trade) return False + def get_real_amount(self, order: Trade) -> float: + """ + Get real amount for the trade + This is needed for exchanges which charge fees in target currency (binance) + """ + + trades = exchange.get_trades_for_order( + order.open_order_id, order.pair, order.open_date) + + if len(trades) == 0: + raise OperationalException("get_real_amount: no trade found") + amount = 0 + fee = 0 + for trade in trades: + amount += trade["amount"] + if "fee" in trade: + if order.pair.startswith(trade["fee"]["currency"]): + fee += trade["fee"]["cost"] + + # TODO: create order using amount_lots would be better + if amount != order.amount: + self.logger.warning("amount {} does not match amount {}".format(amount, order.amount)) + raise OperationalException("Half bought? Amounts don't match") + real_amount = amount - fee + return real_amount + + def maybe_update_real_amount(self, trade: Trade) -> bool: + """ + Updates trade-amount with real amount + :return: True if real-amount has been changed. + """ + if trade.is_open and trade.open_order_id is None: + # Trade is not open anymore + self.logger.warning("could not open trade amount - Trade is not open anymore") + return False + try: + new_amount = self.get_real_amount(trade) + except OperationalException as exception: + self.logger.warning("could not update trade amount: %s", exception) + return False + # updating amount + self.logger.info("Updating amount for Trade {} from {} to {}".format( + trade, trade.amount, new_amount)) + trade.amount = new_amount + Trade.session.flush() + return True + def handle_trade(self, trade: Trade) -> bool: """ Sells the current pair if the threshold is reached and updates the trade record. diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 8f727838e..cc306f11d 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1281,3 +1281,196 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, mocker) -> trade.update(limit_buy_order) patch_get_signal(mocker, value=(False, True)) assert freqtrade.handle_trade(trade) is True + + +def test_get_real_amount_quote(default_conf, trades_for_order, mocker): + """ + Test get_real_amount + """ + + rv = [{'info': {'id': 34567, + 'orderId': 123456, + 'price': '0.24544100', + 'qty': '8.00000000', + 'commission': '0.00800000', + 'commissionAsset': 'LTC', + 'time': 1521663363189, + 'isBuyer': True, + 'isMaker': False, + 'isBestMatch': True}, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/ETH', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 0.245441, + 'cost': 1.963528, + 'amount': 8.0, + 'fee': {'cost': 0.008, 'currency': 'LTC'}}] + mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=rv) + + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + trade = Trade( + pair='LTC/ETH', + amount=8, + exchange='binance', + open_order_id="123456" + ) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + # Amount - cost + assert freqtrade.get_real_amount(trade) == 7.992 + + +def test_get_real_amount_stake(default_conf, mocker): + """ + Test get_real_amount + """ + + rv = [{'info': {'id': 34567, + 'orderId': 123456, + 'price': '0.24544100', + 'qty': '8.00000000', + 'commission': '0.00800000', + 'commissionAsset': 'LTC', + 'time': 1521663363189, + 'isBuyer': True, + 'isMaker': False, + 'isBestMatch': True}, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/ETH', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 0.245441, + 'cost': 1.963528, + 'amount': 8.0, + 'fee': {'cost': 0.008, 'currency': 'ETH'}}] + mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=rv) + + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + trade = Trade( + pair='IOTA/ETH', + amount=8, + exchange='binance', + open_order_id="123456" + ) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + # Amount - cost + assert freqtrade.get_real_amount(trade) == 8 + + +def test_get_real_amount_BNB(default_conf, mocker): + """ + Test get_real_amount + """ + + rv = [{'info': {'id': 34567, + 'orderId': 123456, + 'price': '0.24544100', + 'qty': '8.00000000', + 'commission': '0.00800000', + 'commissionAsset': 'LTC', + 'time': 1521663363189, + 'isBuyer': True, + 'isMaker': False, + 'isBestMatch': True}, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/ETH', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 0.245441, + 'cost': 1.963528, + 'amount': 8.0, + 'fee': {'cost': 0.00094518, 'currency': 'BNB'}}] + mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=rv) + + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + trade = Trade( + pair='IOTA/ETH', + amount=8, + exchange='binance', + open_order_id="123456" + ) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + # Amount - cost + assert freqtrade.get_real_amount(trade) == 8 + + +def test_get_real_amount_multi(default_conf, mocker): + """ + Test get_real_amount + """ + + rv = [{'info': {'id': 34567, + 'orderId': 123456, + 'price': '0.24544100', + 'qty': '8.00000000', + 'commission': '0.00800000', + 'commissionAsset': 'LTC', + 'time': 1521663363189, + 'isBuyer': True, + 'isMaker': False, + 'isBestMatch': True}, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/ETH', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 0.245441, + '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}, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/ETH', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 0.245441, + 'cost': 1.963528, + 'amount': 4.0, + 'fee': {'cost': 0.004, 'currency': 'LTC'}}] + mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=rv) + + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + trade = Trade( + pair='LTC/ETH', + amount=8, + exchange='binance', + open_order_id="123456" + ) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + # Amount - cost + assert freqtrade.get_real_amount(trade) == 7.992 From c7d1a767f75457d2bef930a9b13edc7848c925b0 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 15 Apr 2018 19:39:11 +0200 Subject: [PATCH 02/30] add get_trades_for_order --- freqtrade/exchange/__init__.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 6406fe769..91196ca06 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -3,6 +3,7 @@ import logging from random import randint from typing import List, Dict, Any, Optional +from datetime import datetime import ccxt import arrow @@ -312,6 +313,25 @@ def get_order(order_id: str, pair: str) -> Dict: raise OperationalException(e) +def get_trades_for_order(order_id: str, pair: str, since: datetime) -> List: + if _CONF['dry_run']: + return [] + if not exchange_has('fetchMyTrades'): + return [] + try: + my_trades = _API.fetch_my_trades(pair, since.timestamp()) + matched_trades = [trade for trade in my_trades if trade['order'] == order_id] + + return matched_trades + + except ccxt.NetworkError as e: + raise NetworkException( + 'Could not get trades due to networking error. Message: {}'.format(e) + ) + except ccxt.BaseError as e: + raise OperationalException(e) + + def get_pair_detail_url(pair: str) -> str: try: url_base = _API.urls.get('www') @@ -350,3 +370,13 @@ def get_fee(symbol='ETH/BTC', type='', side='', amount=1, return _API.calculate_fee(symbol=symbol, type=type, side=side, amount=amount, price=price, takerOrMaker=taker_or_maker)['rate'] + + +def get_amount_lots(pair: str, amount: float) -> float: + """ + get buyable amount rounding, .. + """ + # validate that markets are loaded before trying to get fee + if _API.markets is None or len(_API.markets) == 0: + _API.load_markets() + return _API.amount_to_lots(pair, amount) From 1d43dc229b8bc22db8c077c0ed89242da2cbf66e Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 15 Apr 2018 19:56:33 +0200 Subject: [PATCH 03/30] refactor tests of get_real_amount --- freqtrade/tests/conftest.py | 71 +++++++++ freqtrade/tests/test_freqtradebot.py | 222 ++++++++------------------- 2 files changed, 132 insertions(+), 161 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 4a8e294b9..dbc10e28a 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -499,3 +499,74 @@ def result(): # that inserts a trade of some type and open-status # return the open-order-id # See tests in rpc/main that could use this + + +@pytest.fixture(scope="function") +def trades_for_order(): + 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}, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/ETH', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 0.245441, + 'cost': 1.963528, + 'amount': 8.0, + 'fee': {'cost': 0.008, 'currency': 'LTC'}}] + + +@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}, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/ETH', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 0.245441, + '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}, + 'timestamp': 1521663363189, + 'datetime': '2018-03-21T20:16:03.189Z', + 'symbol': 'LTC/ETH', + 'id': '34567', + 'order': '123456', + 'type': None, + 'side': 'buy', + 'price': 0.245441, + 'cost': 1.963528, + 'amount': 4.0, + 'fee': {'cost': 0.004, 'currency': 'LTC'}}] diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index cc306f11d..c9413cbe7 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1285,192 +1285,92 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, mocker) -> def test_get_real_amount_quote(default_conf, trades_for_order, mocker): """ - Test get_real_amount + Test get_real_amount - fee in quote currency """ - rv = [{'info': {'id': 34567, - 'orderId': 123456, - 'price': '0.24544100', - 'qty': '8.00000000', - 'commission': '0.00800000', - 'commissionAsset': 'LTC', - 'time': 1521663363189, - 'isBuyer': True, - 'isMaker': False, - 'isBestMatch': True}, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/ETH', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 0.245441, - 'cost': 1.963528, - 'amount': 8.0, - 'fee': {'cost': 0.008, 'currency': 'LTC'}}] - mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=rv) + mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=trades_for_order) patch_get_signal(mocker) patch_RPCManager(mocker) patch_coinmarketcap(mocker) mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + amount = sum(x['amount'] for x in trades_for_order) trade = Trade( pair='LTC/ETH', - amount=8, + amount=amount, exchange='binance', open_order_id="123456" ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) - # Amount - cost - assert freqtrade.get_real_amount(trade) == 7.992 + # Amount is reduced by "fee" + assert freqtrade.get_real_amount(trade) == amount - (amount * 0.001) -def test_get_real_amount_stake(default_conf, mocker): +def test_get_real_amount_stake(default_conf, trades_for_order, mocker): """ - Test get_real_amount + Test get_real_amount - fees in Stake currency """ - - rv = [{'info': {'id': 34567, - 'orderId': 123456, - 'price': '0.24544100', - 'qty': '8.00000000', - 'commission': '0.00800000', - 'commissionAsset': 'LTC', - 'time': 1521663363189, - 'isBuyer': True, - 'isMaker': False, - 'isBestMatch': True}, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/ETH', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 0.245441, - 'cost': 1.963528, - 'amount': 8.0, - 'fee': {'cost': 0.008, 'currency': 'ETH'}}] - mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=rv) - - patch_get_signal(mocker) - patch_RPCManager(mocker) - patch_coinmarketcap(mocker) - mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) - trade = Trade( - pair='IOTA/ETH', - amount=8, - exchange='binance', - open_order_id="123456" - ) - freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) - # Amount - cost - assert freqtrade.get_real_amount(trade) == 8 - - -def test_get_real_amount_BNB(default_conf, mocker): - """ - Test get_real_amount - """ - - rv = [{'info': {'id': 34567, - 'orderId': 123456, - 'price': '0.24544100', - 'qty': '8.00000000', - 'commission': '0.00800000', - 'commissionAsset': 'LTC', - 'time': 1521663363189, - 'isBuyer': True, - 'isMaker': False, - 'isBestMatch': True}, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/ETH', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 0.245441, - 'cost': 1.963528, - 'amount': 8.0, - 'fee': {'cost': 0.00094518, 'currency': 'BNB'}}] - mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=rv) - - patch_get_signal(mocker) - patch_RPCManager(mocker) - patch_coinmarketcap(mocker) - mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) - trade = Trade( - pair='IOTA/ETH', - amount=8, - exchange='binance', - open_order_id="123456" - ) - freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) - # Amount - cost - assert freqtrade.get_real_amount(trade) == 8 - - -def test_get_real_amount_multi(default_conf, mocker): - """ - Test get_real_amount - """ - - rv = [{'info': {'id': 34567, - 'orderId': 123456, - 'price': '0.24544100', - 'qty': '8.00000000', - 'commission': '0.00800000', - 'commissionAsset': 'LTC', - 'time': 1521663363189, - 'isBuyer': True, - 'isMaker': False, - 'isBestMatch': True}, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/ETH', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 0.245441, - '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}, - 'timestamp': 1521663363189, - 'datetime': '2018-03-21T20:16:03.189Z', - 'symbol': 'LTC/ETH', - 'id': '34567', - 'order': '123456', - 'type': None, - 'side': 'buy', - 'price': 0.245441, - 'cost': 1.963528, - 'amount': 4.0, - 'fee': {'cost': 0.004, 'currency': 'LTC'}}] - mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=rv) + trades_for_order[0]['fee']['currency'] = 'ETH' 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 = sum(x['amount'] for x in trades_for_order) trade = Trade( pair='LTC/ETH', - amount=8, + amount=amount, exchange='binance', open_order_id="123456" ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) - # Amount - cost - assert freqtrade.get_real_amount(trade) == 7.992 + # Amount does not change + assert freqtrade.get_real_amount(trade) == amount + + +def test_get_real_amount_BNB(default_conf, trades_for_order, mocker): + """ + Test get_real_amount - Fees in BNB + """ + + trades_for_order[0]['fee']['currency'] = 'BNB' + trades_for_order[0]['fee']['cost'] = 0.00094518 + + 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 = sum(x['amount'] for x in trades_for_order) + trade = Trade( + pair='LTC/ETH', + amount=amount, + exchange='binance', + open_order_id="123456" + ) + freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) + # Amount does not change + assert freqtrade.get_real_amount(trade) == amount + + +def test_get_real_amount_multi(default_conf, trades_for_order2, mocker): + """ + Test get_real_amount with split trades (multiple trades for this order) + """ + + 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_order2) + amount = float(sum(x['amount'] for x in trades_for_order2)) + trade = Trade( + pair='LTC/ETH', + amount=amount, + exchange='binance', + 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) From 02f0f226217c6646fac2526f8f8ef231514b56fe Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 15 Apr 2018 23:10:46 +0200 Subject: [PATCH 04/30] fix comment --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6d158deb0..bf1549e67 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -365,7 +365,7 @@ class FreqtradeBot(object): def get_real_amount(self, order: Trade) -> float: """ Get real amount for the trade - This is needed for exchanges which charge fees in target currency (binance) + This is needed for exchanges which charge fees in target currency (e.g. binance) """ trades = exchange.get_trades_for_order( From f69e8458f4f4105d5d5600b360c178ad886e5025 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 15 Apr 2018 23:11:01 +0200 Subject: [PATCH 05/30] Add tests for update_real_amount --- freqtrade/tests/test_freqtradebot.py | 50 ++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index c9413cbe7..85aa64e89 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1374,3 +1374,53 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, mocker): freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) # Amount is reduced by "fee" assert freqtrade.get_real_amount(trade) == amount - (amount * 0.001) + + +def test_maybe_update_real_amount(default_conf, trades_for_order, mocker): + """ + Test get_real_amount with split trades (multiple trades for this order) + """ + + 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.maybe_update_real_amount(trade) is True + assert trade.amount == amount - (amount * 0.001) + + +def test_maybe_update_real_amount_stake(default_conf, trades_for_order, mocker): + """ + Test get_real_amount with split trades (multiple trades for this order) + """ + trades_for_order[0]['fee']['currency'] = 'ETH' + + 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.maybe_update_real_amount(trade) is True + assert trade.amount == amount From 7f4c70827af47fa44dcee9a1274a620e39a5e6c8 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Mon, 16 Apr 2018 19:43:13 +0200 Subject: [PATCH 06/30] Test get_amount_lots --- freqtrade/tests/exchange/test_exchange.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index d7a4b35a9..1fbae3101 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -10,7 +10,8 @@ import pytest from freqtrade import OperationalException, DependencyException, NetworkException from freqtrade.exchange import init, validate_pairs, buy, sell, get_balance, get_balances, \ - get_ticker, get_ticker_history, cancel_order, get_name, get_fee, get_id, get_pair_detail_url + get_ticker, get_ticker_history, cancel_order, get_name, get_fee, get_id, get_pair_detail_url, \ + get_amount_lots import freqtrade.exchange as exchange from freqtrade.tests.conftest import log_has @@ -499,3 +500,10 @@ def test_get_fee(default_conf, mocker): }) mocker.patch('freqtrade.exchange._API', api_mock) assert get_fee() == 0.025 + + +def test_get_amount_lots(default_conf, mocker): + api_mock = MagicMock() + api_mock.amount_to_lots = MagicMock(return_value=1.0) + mocker.patch('freqtrade.exchange._API', api_mock) + assert get_amount_lots('LTC/BTC', 1.54) == 1 From a620aa83527285b8b7e0ce85c072700474cbaf1d Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 21 Apr 2018 19:47:08 +0200 Subject: [PATCH 07/30] add columns fee_open and fee_close, update value --- freqtrade/freqtradebot.py | 38 ++++++++++++++++++++++++++++---------- freqtrade/persistence.py | 10 ++++++---- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index bf1549e67..b6695ae50 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -316,11 +316,14 @@ class FreqtradeBot(object): ) ) # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL + fee = exchange.get_fee(symbol=pair, taker_or_maker='maker') trade = Trade( pair=pair, stake_amount=stake_amount, amount=amount, - fee=exchange.get_fee(taker_or_maker='maker'), + fee=fee, + fee_open=fee, + fee_close=fee, open_rate=buy_limit, open_date=datetime.utcnow(), exchange=exchange.get_id(), @@ -355,7 +358,19 @@ class FreqtradeBot(object): if trade.open_order_id: # Update trade with order values self.logger.info('Found open order for %s', trade) - trade.update(exchange.get_order(trade.open_order_id, trade.pair)) + order = exchange.get_order(trade.open_order_id, trade.pair) + # TODO: correct place here ?? + # Try update amount (binance-fix) + try: + new_amount = self.get_real_amount(trade) + if order['amount'] != new_amount: + order['amount'] = new_amount + trade.fee_open = 0 + + except OperationalException as exception: + self.logger.warning("could not update trade amount: %s", exception) + + trade.update(order) if trade.is_open and trade.open_order_id is None: # Check if we can sell our current pair @@ -374,18 +389,18 @@ class FreqtradeBot(object): if len(trades) == 0: raise OperationalException("get_real_amount: no trade found") amount = 0 - fee = 0 + fee_abs = 0 for trade in trades: amount += trade["amount"] if "fee" in trade: + # only applies if fee is in quote currency! if order.pair.startswith(trade["fee"]["currency"]): - fee += trade["fee"]["cost"] + fee_abs += trade["fee"]["cost"] - # TODO: create order using amount_lots would be better if amount != order.amount: self.logger.warning("amount {} does not match amount {}".format(amount, order.amount)) raise OperationalException("Half bought? Amounts don't match") - real_amount = amount - fee + real_amount = amount - fee_abs return real_amount def maybe_update_real_amount(self, trade: Trade) -> bool: @@ -403,10 +418,12 @@ class FreqtradeBot(object): self.logger.warning("could not update trade amount: %s", exception) return False # updating amount - self.logger.info("Updating amount for Trade {} from {} to {}".format( - trade, trade.amount, new_amount)) - trade.amount = new_amount - Trade.session.flush() + if trade.amount != new_amount: + self.logger.info("Updating amount for Trade {} from {} to {}".format( + trade, trade.amount, new_amount)) + trade.amount = new_amount + trade.fee_open = 0 # Fee was applied - set to 0 for buy + Trade.session.flush() return True def handle_trade(self, trade: Trade) -> bool: @@ -452,6 +469,7 @@ class FreqtradeBot(object): # Check if trade is still actually open if int(order['remaining']) == 0: + # self.maybe_update_real_amount(trade) continue if order['side'] == 'buy' and ordertime < timeoutthreashold: diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 55f53329a..6fea67c88 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -86,6 +86,8 @@ class Trade(_DECL_BASE): pair = Column(String, nullable=False) is_open = Column(Boolean, nullable=False, default=True) fee = Column(Float, nullable=False, default=0.0) + fee_open = Column(Float, nullable=False, default=0.0) + fee_close = Column(Float, nullable=False, default=0.0) open_rate = Column(Float) close_rate = Column(Float) close_profit = Column(Float) @@ -156,7 +158,7 @@ class Trade(_DECL_BASE): getcontext().prec = 8 buy_trade = (Decimal(self.amount) * Decimal(self.open_rate)) - fees = buy_trade * Decimal(fee or self.fee) + fees = buy_trade * Decimal(fee or self.fee_open) return float(buy_trade + fees) def calc_close_trade_price( @@ -177,7 +179,7 @@ class Trade(_DECL_BASE): return 0.0 sell_trade = (Decimal(self.amount) * Decimal(rate or self.close_rate)) - fees = sell_trade * Decimal(fee or self.fee) + fees = sell_trade * Decimal(fee or self.fee_close) return float(sell_trade - fees) def calc_profit( @@ -195,7 +197,7 @@ class Trade(_DECL_BASE): open_trade_price = self.calc_open_trade_price() close_trade_price = self.calc_close_trade_price( rate=(rate or self.close_rate), - fee=(fee or self.fee) + fee=(fee or self.fee_close) ) return float("{0:.8f}".format(close_trade_price - open_trade_price)) @@ -215,7 +217,7 @@ class Trade(_DECL_BASE): open_trade_price = self.calc_open_trade_price() close_trade_price = self.calc_close_trade_price( rate=(rate or self.close_rate), - fee=(fee or self.fee) + fee=(fee or self.fee_close) ) return float("{0:.8f}".format((close_trade_price / open_trade_price) - 1)) From 47748bc6f70f2cd97eff141646ade4e7522a7463 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 21 Apr 2018 19:55:48 +0200 Subject: [PATCH 08/30] adjust tests for fee_open and fee_close --- freqtrade/tests/test_persistence.py | 36 +++++++++++++++++++---------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index 97f2cdcfe..8db3a30bc 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -120,7 +120,8 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, exchange='bittrex', ) assert trade.open_order_id is None @@ -147,7 +148,8 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, exchange='bittrex', ) @@ -169,7 +171,8 @@ def test_calc_close_trade_price_exception(limit_buy_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, exchange='bittrex', ) @@ -182,7 +185,8 @@ def test_update_open_order(limit_buy_order): trade = Trade( pair='ETH/BTC', stake_amount=1.00, - fee=0.1, + fee_open=0.1, + fee_close=0.1, exchange='bittrex', ) @@ -204,7 +208,8 @@ def test_update_invalid_order(limit_buy_order): trade = Trade( pair='ETH/BTC', stake_amount=1.00, - fee=0.1, + fee_open=0.1, + fee_close=0.1, exchange='bittrex', ) limit_buy_order['type'] = 'invalid' @@ -216,7 +221,8 @@ def test_calc_open_trade_price(limit_buy_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, exchange='bittrex', ) trade.open_order_id = 'open_trade' @@ -233,7 +239,8 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, exchange='bittrex', ) trade.open_order_id = 'close_trade' @@ -254,7 +261,8 @@ def test_calc_profit(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, exchange='bittrex', ) trade.open_order_id = 'profit_percent' @@ -284,7 +292,8 @@ def test_calc_profit_percent(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, exchange='bittrex', ) trade.open_order_id = 'profit_percent' @@ -312,7 +321,8 @@ def test_clean_dry_run_db(default_conf, fee): pair='ETH/BTC', stake_amount=0.001, amount=123.0, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, open_rate=0.123, exchange='bittrex', open_order_id='dry_run_buy_12345' @@ -323,7 +333,8 @@ def test_clean_dry_run_db(default_conf, fee): pair='ETC/BTC', stake_amount=0.001, amount=123.0, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, open_rate=0.123, exchange='bittrex', open_order_id='dry_run_sell_12345' @@ -335,7 +346,8 @@ def test_clean_dry_run_db(default_conf, fee): pair='ETC/BTC', stake_amount=0.001, amount=123.0, - fee=fee.return_value, + fee_open=fee.return_value, + fee_close=fee.return_value, open_rate=0.123, exchange='bittrex', open_order_id='prod_buy_12345' From 06d230279c7b2b514459b0ee46d7da41fe2e73eb Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 21 Apr 2018 20:05:39 +0200 Subject: [PATCH 09/30] Fix tests --- freqtrade/tests/rpc/test_rpc_telegram.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 1c90db53d..41bd6c1c0 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -234,7 +234,7 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None: ) -def test_status(default_conf, update, mocker, ticker) -> None: +def test_status(default_conf, update, fee, mocker, ticker) -> None: """ Test _status() method """ @@ -249,7 +249,8 @@ def test_status(default_conf, update, mocker, ticker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_pair_detail_url=MagicMock() + get_pair_detail_url=MagicMock(), + get_fee=fee ) msg_mock = MagicMock() status_table = MagicMock() From ce90ee4ac2152ec6511bf394665e0e15fb6bcd31 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 21 Apr 2018 20:05:49 +0200 Subject: [PATCH 10/30] have backtesting use fee_open and fee_close --- freqtrade/optimize/backtesting.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 268d199e0..a839fab54 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -114,12 +114,14 @@ class Backtesting(object): stake_amount = args['stake_amount'] max_open_trades = args.get('max_open_trades', 0) + fee = exchange.get_fee() trade = Trade( open_rate=buy_row.close, open_date=buy_row.date, stake_amount=stake_amount, amount=stake_amount / buy_row.open, - fee=exchange.get_fee() + fee_open=fee, + fee_close=fee ) # calculate win/lose forwards from buy point From 990f8a996bec7acb449790b45e8dc22fcfbf19b8 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 21 Apr 2018 21:01:53 +0200 Subject: [PATCH 11/30] log in case of error --- freqtrade/freqtradebot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b6695ae50..1b3f6e080 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -364,6 +364,8 @@ class FreqtradeBot(object): try: new_amount = self.get_real_amount(trade) if order['amount'] != new_amount: + self.logger.info("Updating amount for Trade {} from {} to {}".format( + trade, order['amount'], new_amount)) order['amount'] = new_amount trade.fee_open = 0 From 573b6b8e1521896331b5645762d7a6aa41f66ce5 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sat, 21 Apr 2018 22:35:17 +0200 Subject: [PATCH 12/30] Remove unused line --- freqtrade/freqtradebot.py | 1 - 1 file changed, 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 1b3f6e080..70a87ede3 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -471,7 +471,6 @@ class FreqtradeBot(object): # Check if trade is still actually open if int(order['remaining']) == 0: - # self.maybe_update_real_amount(trade) continue if order['side'] == 'buy' and ordertime < timeoutthreashold: From be95d699d2afdd2d5ec6d29449b7968172edfd34 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 22 Apr 2018 09:13:02 +0200 Subject: [PATCH 13/30] only update if open_fee is set --- freqtrade/freqtradebot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 48281e6d2..b737f0e83 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -370,7 +370,8 @@ class FreqtradeBot(object): # Try update amount (binance-fix) try: new_amount = self.get_real_amount(trade) - if order['amount'] != new_amount: + # This may break if a exchange applies no fee (which appears highly unlikely) + if order['amount'] != new_amount and trade.fee_open != 0: logger.info("Updating amount for Trade {} from {} to {}".format( trade, order['amount'], new_amount)) order['amount'] = new_amount From f838ba2a9bf592cb3a1369f8ed6365517d539add Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 22 Apr 2018 10:04:30 +0200 Subject: [PATCH 14/30] remove fee column from bot --- freqtrade/freqtradebot.py | 1 - freqtrade/persistence.py | 1 - freqtrade/tests/test_freqtradebot.py | 12 ++++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b737f0e83..6acda804d 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -328,7 +328,6 @@ class FreqtradeBot(object): pair=pair, stake_amount=stake_amount, amount=amount, - fee=fee, fee_open=fee, fee_close=fee, open_rate=buy_limit, diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 6fea67c88..ed81ad2ec 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -85,7 +85,6 @@ class Trade(_DECL_BASE): exchange = Column(String, nullable=False) pair = Column(String, nullable=False) is_open = Column(Boolean, nullable=False, default=True) - fee = Column(Float, nullable=False, default=0.0) fee_open = Column(Float, nullable=False, default=0.0) fee_close = Column(Float, nullable=False, default=0.0) open_rate = Column(Float) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 800a536bc..1c22d97f9 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -812,7 +812,8 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fe exchange='bittrex', open_order_id='123456789', amount=90.99181073, - fee=0.0, + fee_open=0.0, + fee_close=0.0, stake_amount=1, open_date=arrow.utcnow().shift(minutes=-601).datetime, is_open=True @@ -851,7 +852,8 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, exchange='bittrex', open_order_id='123456789', amount=90.99181073, - fee=0.0, + fee_open=0.0, + fee_close=0.0, stake_amount=1, open_date=arrow.utcnow().shift(hours=-5).datetime, close_date=arrow.utcnow().shift(minutes=-601).datetime, @@ -890,7 +892,8 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old exchange='bittrex', open_order_id='123456789', amount=90.99181073, - fee=0.0, + fee_open=0.0, + fee_close=0.0, stake_amount=1, open_date=arrow.utcnow().shift(minutes=-601).datetime, is_open=True @@ -937,7 +940,8 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) - exchange='bittrex', open_order_id='123456789', amount=90.99181073, - fee=0.0, + fee_open=0.0, + fee_close=0.0, stake_amount=1, open_date=arrow.utcnow().shift(minutes=-601).datetime, is_open=True From a70958da4194e1f28d292e2e9584d3ddac1e6d2c Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 22 Apr 2018 11:05:23 +0200 Subject: [PATCH 15/30] test modify-logic --- freqtrade/tests/conftest.py | 2 +- freqtrade/tests/test_freqtradebot.py | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index b61ac137b..59f39cf4a 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -208,7 +208,7 @@ def markets_empty(): return MagicMock(return_value=[]) -@pytest.fixture +@pytest.fixture(scope='function') def limit_buy_order(): return { 'id': 'mocked_limit_buy', diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 1c22d97f9..a6867ce73 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -568,18 +568,32 @@ def test_process_maybe_execute_buy_exception(mocker, default_conf, caplog) -> No log_has('Unable to create trade:', caplog.record_tuples) -def test_process_maybe_execute_sell(mocker, default_conf) -> None: +def test_process_maybe_execute_sell(mocker, default_conf, limit_buy_order, caplog) -> None: """ Test process_maybe_execute_sell() method """ freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) - mocker.patch('freqtrade.freqtradebot.exchange.get_order', return_value=1) + mocker.patch('freqtrade.freqtradebot.exchange.get_order', return_value=limit_buy_order) + mocker.patch('freqtrade.freqtradebot.exchange.get_trades_for_order', return_value=[]) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', + return_value=limit_buy_order['amount']) trade = MagicMock() trade.open_order_id = '123' + 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) + + 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 # Assert we call handle_trade() if trade is feasible for execution From 93a7c4697772ee085220f1ae20d92519a53b247e Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Sun, 22 Apr 2018 19:37:24 +0200 Subject: [PATCH 16/30] optimize to only do network calls if necessary --- freqtrade/freqtradebot.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6acda804d..a3f2b2aa1 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -368,13 +368,14 @@ class FreqtradeBot(object): # TODO: correct place here ?? # Try update amount (binance-fix) try: - 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 and trade.fee_open != 0: - logger.info("Updating amount for Trade {} from {} to {}".format( - trade, order['amount'], new_amount)) - order['amount'] = new_amount - trade.fee_open = 0 + if trade.fee_open != 0: + 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("Updating amount for Trade {} from {} to {}".format( + trade, order['amount'], new_amount)) + order['amount'] = new_amount + trade.fee_open = 0 except OperationalException as exception: logger.warning("could not update trade amount: %s", exception) From f580fbb91d33ba0337e26b439591ec1e2030ec2f Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Mon, 23 Apr 2018 20:03:10 +0200 Subject: [PATCH 17/30] remove maybe_update_amount and tests --- freqtrade/freqtradebot.py | 24 +------------ freqtrade/tests/test_freqtradebot.py | 50 ---------------------------- 2 files changed, 1 insertion(+), 73 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a3f2b2aa1..7359aa580 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -375,6 +375,7 @@ class FreqtradeBot(object): logger.info("Updating 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: @@ -413,29 +414,6 @@ class FreqtradeBot(object): real_amount = amount - fee_abs return real_amount - def maybe_update_real_amount(self, trade: Trade) -> bool: - """ - Updates trade-amount with real amount - :return: True if real-amount has been changed. - """ - if trade.is_open and trade.open_order_id is None: - # Trade is not open anymore - logger.warning("could not open trade amount - Trade is not open anymore") - return False - try: - new_amount = self.get_real_amount(trade) - except OperationalException as exception: - logger.warning("could not update trade amount: %s", exception) - return False - # updating amount - if trade.amount != new_amount: - logger.info("Updating amount for Trade {} from {} to {}".format( - trade, trade.amount, new_amount)) - trade.amount = new_amount - trade.fee_open = 0 # Fee was applied - set to 0 for buy - Trade.session.flush() - return True - def handle_trade(self, trade: Trade) -> bool: """ Sells the current pair if the threshold is reached and updates the trade record. diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index a6867ce73..cd9d7966c 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1410,53 +1410,3 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, mocker): freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) # Amount is reduced by "fee" assert freqtrade.get_real_amount(trade) == amount - (amount * 0.001) - - -def test_maybe_update_real_amount(default_conf, trades_for_order, mocker): - """ - Test get_real_amount with split trades (multiple trades for this order) - """ - - 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.maybe_update_real_amount(trade) is True - assert trade.amount == amount - (amount * 0.001) - - -def test_maybe_update_real_amount_stake(default_conf, trades_for_order, mocker): - """ - Test get_real_amount with split trades (multiple trades for this order) - """ - trades_for_order[0]['fee']['currency'] = 'ETH' - - 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.maybe_update_real_amount(trade) is True - assert trade.amount == amount From d2608cbf13a05020d52e94638a322e486434cc61 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Mon, 23 Apr 2018 20:06:00 +0200 Subject: [PATCH 18/30] improve check when not to run --- freqtrade/freqtradebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7359aa580..c7094b332 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -365,10 +365,10 @@ class FreqtradeBot(object): # Update trade with order values logger.info('Found open order for %s', trade) order = exchange.get_order(trade.open_order_id, trade.pair) - # TODO: correct place here ?? # Try update amount (binance-fix) try: - if trade.fee_open != 0: + # Only run for closed trades + if trade.fee_open != 0 and not (order['status'] == 'open' or order['price'] is None): 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: From 9450b76414fff29e6f2790e6d1ad0a8ed2709a62 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Mon, 23 Apr 2018 20:08:58 +0200 Subject: [PATCH 19/30] improve style of import in test --- freqtrade/tests/exchange/test_exchange.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index 35976386c..e2129fdca 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -9,9 +9,9 @@ import ccxt import pytest from freqtrade import OperationalException, DependencyException, NetworkException -from freqtrade.exchange import init, validate_pairs, buy, sell, get_balance, get_balances, \ - get_ticker, get_ticker_history, cancel_order, get_name, get_fee, get_id, get_pair_detail_url, \ - get_amount_lots +from freqtrade.exchange import (init, validate_pairs, buy, sell, get_balance, get_balances, + get_ticker, get_ticker_history, cancel_order, get_name, get_fee, + get_id, get_pair_detail_url, get_amount_lots) import freqtrade.exchange as exchange from freqtrade.tests.conftest import log_has From 2968347062d6fd3d848bffd77effa157033eb78c Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Mon, 23 Apr 2018 20:32:46 +0200 Subject: [PATCH 20/30] fix flake8 --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c7094b332..c7eace40c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -368,7 +368,7 @@ class FreqtradeBot(object): # Try update amount (binance-fix) try: # Only run for closed trades - if trade.fee_open != 0 and not (order['status'] == 'open' or order['price'] is None): + 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: From 9e94778fd741de2dc636aa27d28eb17be2106fca Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Tue, 24 Apr 2018 19:42:41 +0200 Subject: [PATCH 21/30] simplify check for presence of list --- freqtrade/exchange/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index fc5159781..13bc0b3de 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -378,6 +378,6 @@ def get_amount_lots(pair: str, amount: float) -> float: get buyable amount rounding, .. """ # validate that markets are loaded before trying to get fee - if _API.markets is None or len(_API.markets) == 0: + if not _API.markets: _API.load_markets() return _API.amount_to_lots(pair, amount) From ab6589d573a743f0abba6fa3a6a00672fa66e1e5 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Tue, 24 Apr 2018 19:43:08 +0200 Subject: [PATCH 22/30] Fix comment and improve log message --- freqtrade/freqtradebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c7eace40c..a74481b9a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -367,12 +367,12 @@ class FreqtradeBot(object): order = exchange.get_order(trade.open_order_id, trade.pair) # Try update amount (binance-fix) try: - # Only run for closed trades + # 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("Updating amount for Trade {} from {} to {}".format( + 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 From f6ecd8e5140787e09f7c38c63812cf104a850a44 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Wed, 25 Apr 2018 08:51:31 +0200 Subject: [PATCH 23/30] Add pytest fixture for real_amount test --- freqtrade/tests/conftest.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 59f39cf4a..f1af74348 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -570,3 +570,19 @@ def trades_for_order2(): 'cost': 1.963528, 'amount': 4.0, 'fee': {'cost': 0.004, 'currency': 'LTC'}}] + + +@pytest.fixture +def buy_order_fee(): + return { + 'id': 'mocked_limit_buy_old', + 'type': 'limit', + 'side': 'buy', + 'pair': 'mocked', + 'datetime': str(arrow.utcnow().shift(minutes=-601).datetime), + 'price': 0.245441, + 'amount': 8.0, + 'remaining': 90.99181073, + 'status': 'open', + 'fee': None + } From 9c2115c9175b3607a297780927496574669f7f59 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Wed, 25 Apr 2018 08:52:08 +0200 Subject: [PATCH 24/30] refactor get_real_amount --- freqtrade/freqtradebot.py | 57 ++++++++++++++++---------- freqtrade/tests/test_freqtradebot.py | 60 ++++++++++++++++++++++------ 2 files changed, 83 insertions(+), 34 deletions(-) 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) From 98669a3d622bc7ad483ed20fe3b32b7b13f5aef1 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Wed, 25 Apr 2018 09:01:21 +0200 Subject: [PATCH 25/30] remove duplicate log entry, fix key-error --- freqtrade/freqtradebot.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5a435ae06..69d52a079 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -369,8 +369,6 @@ class FreqtradeBot(object): try: 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 @@ -392,11 +390,11 @@ class FreqtradeBot(object): """ order_amount = order['amount'] # Only run for closed orders - if trade.fee_open == 0 or not order['status'] == 'open': + if trade.fee_open == 0 or order['status'] == 'open': return order_amount # use fee from order-dict if possible - if order['fee']: + if 'fee' in order.keys() and 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", From 483415cd65d2baf0996fa5e1b74365b471493034 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Wed, 25 Apr 2018 09:03:32 +0200 Subject: [PATCH 26/30] Add fee entry to DRY_ORDER dict as defined by ccxt --- freqtrade/exchange/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 13bc0b3de..32404a2c2 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -134,7 +134,8 @@ def buy(pair: str, rate: float, amount: float) -> Dict: 'side': 'buy', 'remaining': 0.0, 'datetime': arrow.utcnow().isoformat(), - 'status': 'closed' + 'status': 'closed', + 'fee': None } return {'id': order_id} From 72c17e29c05de44956de7b82abe19e6e356b1405 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Wed, 25 Apr 2018 09:08:02 +0200 Subject: [PATCH 27/30] Add test for "no trades found" case --- freqtrade/tests/conftest.py | 2 +- freqtrade/tests/test_freqtradebot.py | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index f1af74348..85ce3b35d 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -583,6 +583,6 @@ def buy_order_fee(): 'price': 0.245441, 'amount': 8.0, 'remaining': 90.99181073, - 'status': 'open', + 'status': 'closed', 'fee': None } diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 45c6620df..8c3c1302a 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1344,6 +1344,32 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, ca caplog.record_tuples) +def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker): + """ + Test get_real_amount - fee in quote currency + """ + + mocker.patch('freqtrade.exchange.get_trades_for_order', return_value=[]) + + patch_get_signal(mocker) + patch_RPCManager(mocker) + patch_coinmarketcap(mocker) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + amount = buy_order_fee['amount'] + 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, buy_order_fee) == amount + assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, ' + 'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found', + caplog.record_tuples) + def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, caplog, mocker): """ Test get_real_amount - fees in Stake currency From 8bd9ed1543b2f5004e10eb557705be2fdd7ea9a5 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Wed, 25 Apr 2018 09:13:56 +0200 Subject: [PATCH 28/30] fix flake8 --- freqtrade/tests/test_freqtradebot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 8c3c1302a..4396993c3 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -1370,6 +1370,7 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker): 'open_rate=0.24544100, open_since=closed) failed: myTrade-Dict empty found', caplog.record_tuples) + def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, caplog, mocker): """ Test get_real_amount - fees in Stake currency From 2e1124af1a0417a5f2c1716794bc110ed7be9583 Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Wed, 25 Apr 2018 14:00:25 +0200 Subject: [PATCH 29/30] remove unnecessary .keys() --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 69d52a079..f1a64955e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -394,7 +394,7 @@ class FreqtradeBot(object): return order_amount # use fee from order-dict if possible - if 'fee' in order.keys() and order['fee']: + if 'fee' in order and 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", From 0987af910ef5a804e583e7ef69a7dd31b9f17f0d Mon Sep 17 00:00:00 2001 From: Matthias Voppichler Date: Wed, 25 Apr 2018 20:03:32 +0200 Subject: [PATCH 30/30] remove indicator name from comment --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index f1a64955e..0f344f30c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -287,7 +287,7 @@ class FreqtradeBot(object): if not whitelist: raise DependencyException('No currency pairs in whitelist') - # Pick pair based on StochRSI buy signals + # Pick pair based on buy signals for _pair in whitelist: (buy, sell) = self.analyze.get_signal(_pair, interval) if buy and not sell: