From 5db883906af22eeb086174d1fcfae149f6978ed4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 13 Dec 2019 06:52:33 +0100 Subject: [PATCH 01/25] Try to verify available amount on the exchange --- freqtrade/freqtradebot.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 0595e0d35..ac73f6d65 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -889,6 +889,27 @@ class FreqtradeBot: # TODO: figure out how to handle partially complete sell orders return False + def _safe_sell_amount(self, pair: str, amount: float) -> float: + """ + Get sellable amount. + Should be trade.amount - but will fall back to the available amount if necessary. + This should cover cases where get_real_amount() was not able to update the amount + for whatever reason. + :param pair: pair - used for logging + :param amount: amount we expect to be available + :return: amount to sell + :raise: DependencyException: if available balance is not within 2% of the available amount. + """ + wallet_amount = self.wallets.get_free(pair) + logger.info(f"Amounts: {wallet_amount} - {amount}") + if wallet_amount > amount: + return amount + elif wallet_amount > amount * 0.98: + logger.info(f"{pair} - Falling back to wallet-amount.") + return wallet_amount + else: + raise DependencyException("Not enough amount to sell.") + def execute_sell(self, trade: Trade, limit: float, sell_reason: SellType) -> None: """ Executes a limit sell for the given trade and limit @@ -919,10 +940,12 @@ class FreqtradeBot: # Emergencysells (default to market!) ordertype = self.strategy.order_types.get("emergencysell", "market") + amount = self._safe_sell_amount(trade.pair, trade.amount) + # Execute sell and update trade record order = self.exchange.sell(pair=str(trade.pair), ordertype=ordertype, - amount=trade.amount, rate=limit, + amount=amount, rate=limit, time_in_force=self.strategy.order_time_in_force['sell'] ) From 04257d8ecc357dea23face544f8e979834a20e19 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 13 Dec 2019 07:06:54 +0100 Subject: [PATCH 02/25] Add tests for safe_sell_amount --- freqtrade/freqtradebot.py | 2 +- tests/test_freqtradebot.py | 76 +++++++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index ac73f6d65..3ebe89a71 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -901,7 +901,7 @@ class FreqtradeBot: :raise: DependencyException: if available balance is not within 2% of the available amount. """ wallet_amount = self.wallets.get_free(pair) - logger.info(f"Amounts: {wallet_amount} - {amount}") + logger.debug(f"{pair} - Wallet: {wallet_amount} - Trade-amount: {amount}") if wallet_amount > amount: return amount elif wallet_amount > amount * 0.98: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index efab64a6a..9c2fd9ddc 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -883,7 +883,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: 'freqtrade.freqtradebot.FreqtradeBot', get_target_bid=get_bid, _get_min_pair_stake_amount=MagicMock(return_value=1) - ) + ) buy_mm = MagicMock(return_value={'id': limit_buy_order['id']}) mocker.patch.multiple( 'freqtrade.exchange.Exchange', @@ -2314,6 +2314,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.cancel_order', side_effect=InvalidOrderException()) + mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=300)) sellmock = MagicMock() patch_exchange(mocker) mocker.patch.multiple( @@ -2591,7 +2592,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple( - sell_flag=False, sell_type=SellType.NONE)) + sell_flag=False, sell_type=SellType.NONE)) freqtrade.create_trades() trade = Trade.query.first() @@ -2631,6 +2632,77 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke assert trade.sell_reason == SellType.SELL_SIGNAL.value +def test_sell_not_enough_balance(default_conf, limit_buy_order, + fee, mocker, caplog) -> None: + patch_RPCManager(mocker) + patch_exchange(mocker) + mocker.patch.multiple( + 'freqtrade.exchange.Exchange', + get_ticker=MagicMock(return_value={ + 'bid': 0.00002172, + 'ask': 0.00002173, + 'last': 0.00002172 + }), + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + get_fee=fee, + ) + + freqtrade = FreqtradeBot(default_conf) + patch_get_signal(freqtrade) + freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) + + freqtrade.create_trades() + + trade = Trade.query.first() + amnt = trade.amount + trade.update(limit_buy_order) + patch_get_signal(freqtrade, value=(False, True)) + mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=trade.amount * 0.985)) + + assert freqtrade.handle_trade(trade) is True + assert log_has_re(r'.*Falling back to wallet-amount.', caplog) + assert trade.amount != amnt + + +def test__safe_sell_amount(default_conf, caplog, mocker): + patch_RPCManager(mocker) + patch_exchange(mocker) + amount = 95.33 + amount_wallet = 95.29 + mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet)) + trade = Trade( + pair='LTC/ETH', + amount=amount, + exchange='binance', + open_rate=0.245441, + open_order_id="123456" + ) + freqtrade = FreqtradeBot(default_conf) + patch_get_signal(freqtrade) + + assert freqtrade._safe_sell_amount(trade.pair, trade.amount) == amount_wallet + assert log_has_re(r'.*Falling back to wallet-amount.', caplog) + + +def test__safe_sell_amount_error(default_conf, caplog, mocker): + patch_RPCManager(mocker) + patch_exchange(mocker) + amount = 95.33 + amount_wallet = 91.29 + mocker.patch('freqtrade.wallets.Wallets.get_free', MagicMock(return_value=amount_wallet)) + trade = Trade( + pair='LTC/ETH', + amount=amount, + exchange='binance', + open_rate=0.245441, + open_order_id="123456" + ) + freqtrade = FreqtradeBot(default_conf) + patch_get_signal(freqtrade) + with pytest.raises(DependencyException, match=r"Not enough amount to sell."): + assert freqtrade._safe_sell_amount(trade.pair, trade.amount) + + def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) From e72c6a0d94d810b2976c4a6729291fe51602db9d Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 18 Dec 2019 20:02:15 +0100 Subject: [PATCH 03/25] use only first part of the currency to get wallet-amount (!!) --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4a48bba04..5656268c8 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -903,7 +903,7 @@ class FreqtradeBot: :return: amount to sell :raise: DependencyException: if available balance is not within 2% of the available amount. """ - wallet_amount = self.wallets.get_free(pair) + wallet_amount = self.wallets.get_free(pair.split('/')[0]) logger.debug(f"{pair} - Wallet: {wallet_amount} - Trade-amount: {amount}") if wallet_amount > amount: return amount From 6507a26cc160fdfe99a58fbd92a8e0a197f4edef Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 18 Dec 2019 20:16:53 +0100 Subject: [PATCH 04/25] Fix some tests after merge --- freqtrade/freqtradebot.py | 2 +- tests/test_freqtradebot.py | 18 ++++++++++++++---- tests/test_integration.py | 2 +- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5656268c8..a51ab0dbf 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -898,7 +898,7 @@ class FreqtradeBot: Should be trade.amount - but will fall back to the available amount if necessary. This should cover cases where get_real_amount() was not able to update the amount for whatever reason. - :param pair: pair - used for logging + :param pair: Pair we're trying to sell :param amount: amount we expect to be available :return: amount to sell :raise: DependencyException: if available balance is not within 2% of the available amount. diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 300d0ad32..cd5c92199 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1682,6 +1682,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock time.sleep(0.01) # Race condition fix trade.update(limit_buy_order) assert trade.is_open is True + freqtrade.wallets.update() patch_get_signal(freqtrade, value=(False, True)) assert freqtrade.handle_trade(trade) is True @@ -2549,6 +2550,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, trade = Trade.query.first() trade.update(limit_buy_order) + freqtrade.wallets.update() patch_get_signal(freqtrade, value=(False, True)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -2579,6 +2581,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, trade = Trade.query.first() trade.update(limit_buy_order) + freqtrade.wallets.update() patch_get_signal(freqtrade, value=(False, True)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -2639,6 +2642,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke trade = Trade.query.first() trade.update(limit_buy_order) + freqtrade.wallets.update() patch_get_signal(freqtrade, value=(False, True)) assert freqtrade.handle_trade(trade) is True assert trade.sell_reason == SellType.SELL_SIGNAL.value @@ -2676,7 +2680,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, assert trade.amount != amnt -def test__safe_sell_amount(default_conf, caplog, mocker): +def test__safe_sell_amount(default_conf, fee, caplog, mocker): patch_RPCManager(mocker) patch_exchange(mocker) amount = 95.33 @@ -2687,7 +2691,9 @@ def test__safe_sell_amount(default_conf, caplog, mocker): amount=amount, exchange='binance', open_rate=0.245441, - open_order_id="123456" + open_order_id="123456", + fee_open=fee.return_value, + fee_close=fee.return_value, ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2696,7 +2702,7 @@ def test__safe_sell_amount(default_conf, caplog, mocker): assert log_has_re(r'.*Falling back to wallet-amount.', caplog) -def test__safe_sell_amount_error(default_conf, caplog, mocker): +def test__safe_sell_amount_error(default_conf, fee, caplog, mocker): patch_RPCManager(mocker) patch_exchange(mocker) amount = 95.33 @@ -2707,7 +2713,9 @@ def test__safe_sell_amount_error(default_conf, caplog, mocker): amount=amount, exchange='binance', open_rate=0.245441, - open_order_id="123456" + open_order_id="123456", + fee_open=fee.return_value, + fee_close=fee.return_value, ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) @@ -2775,6 +2783,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, mocker) -> trade = Trade.query.first() trade.update(limit_buy_order) + freqtrade.wallets.update() patch_get_signal(freqtrade, value=(True, True)) assert freqtrade.handle_trade(trade) is False @@ -3512,6 +3521,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order time.sleep(0.01) # Race condition fix trade.update(limit_buy_order) + freqtrade.wallets.update() assert trade.is_open is True patch_get_signal(freqtrade, value=(False, True)) diff --git a/tests/test_integration.py b/tests/test_integration.py index 728e96d55..2daf13db6 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -71,7 +71,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, ) mocker.patch("freqtrade.strategy.interface.IStrategy.should_sell", should_sell_mock) wallets_mock = mocker.patch("freqtrade.wallets.Wallets.update", MagicMock()) - mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=1)) + mocker.patch("freqtrade.wallets.Wallets.get_free", MagicMock(return_value=1000)) freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade.strategy.order_types['stoploss_on_exchange'] = True From 039dfc302ca2c8497aea27282f3ab20f5a0c4343 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 01:34:31 +0300 Subject: [PATCH 05/25] No need to convert pair name --- freqtrade/freqtradebot.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e21d89cd3..a9ce14bef 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -351,7 +351,6 @@ class FreqtradeBot: :param pair: pair for which we want to create a LIMIT_BUY :return: None """ - pair_s = pair.replace('_', '/') stake_currency = self.config['stake_currency'] fiat_currency = self.config.get('fiat_display_currency', None) time_in_force = self.strategy.order_time_in_force['buy'] @@ -362,10 +361,10 @@ class FreqtradeBot: # Calculate amount buy_limit_requested = self.get_target_bid(pair) - min_stake_amount = self._get_min_pair_stake_amount(pair_s, buy_limit_requested) + min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested) if min_stake_amount is not None and min_stake_amount > stake_amount: logger.warning( - f"Can't open a new trade for {pair_s}: stake amount " + f"Can't open a new trade for {pair}: stake amount " f"is too small ({stake_amount} < {min_stake_amount})" ) return False @@ -388,7 +387,7 @@ class FreqtradeBot: if float(order['filled']) == 0: logger.warning('Buy %s order with time in force %s for %s is %s by %s.' ' zero amount is fulfilled.', - order_tif, order_type, pair_s, order_status, self.exchange.name) + order_tif, order_type, pair, order_status, self.exchange.name) return False else: # the order is partially fulfilled @@ -396,7 +395,7 @@ class FreqtradeBot: # if the order is fulfilled fully or partially logger.warning('Buy %s order with time in force %s for %s is %s by %s.' ' %s amount fulfilled out of %s (%s remaining which is canceled).', - order_tif, order_type, pair_s, order_status, self.exchange.name, + order_tif, order_type, pair, order_status, self.exchange.name, order['filled'], order['amount'], order['remaining'] ) stake_amount = order['cost'] @@ -413,7 +412,7 @@ class FreqtradeBot: self.rpc.send_msg({ 'type': RPCMessageType.BUY_NOTIFICATION, 'exchange': self.exchange.name.capitalize(), - 'pair': pair_s, + 'pair': pair, 'limit': buy_limit_filled_price, 'order_type': order_type, 'stake_amount': stake_amount, From b6d1c5b17aca2e21d6cde9fd41513a5daf3aa0a2 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 01:44:51 +0300 Subject: [PATCH 06/25] _get_trade_stake_amount() is not private --- freqtrade/freqtradebot.py | 4 ++-- freqtrade/rpc/rpc.py | 2 +- tests/test_freqtradebot.py | 18 +++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a9ce14bef..9bd7e94fc 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -204,7 +204,7 @@ class FreqtradeBot: return used_rate - def _get_trade_stake_amount(self, pair) -> Optional[float]: + def get_trade_stake_amount(self, pair) -> Optional[float]: """ Check if stake amount can be fulfilled with the available balance for the stake currency @@ -309,7 +309,7 @@ class FreqtradeBot: self.dataprovider.ohlcv(_pair, self.strategy.ticker_interval)) if buy and not sell and len(Trade.get_open_trades()) < self.config['max_open_trades']: - stake_amount = self._get_trade_stake_amount(_pair) + stake_amount = self.get_trade_stake_amount(_pair) if not stake_amount: continue diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index d6d442df5..35c312743 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -462,7 +462,7 @@ class RPC: raise RPCException(f'position for {pair} already open - id: {trade.id}') # gen stake amount - stakeamount = self._freqtrade._get_trade_stake_amount(pair) + stakeamount = self._freqtrade.get_trade_stake_amount(pair) # execute buy if self._freqtrade.execute_buy(pair, stakeamount, price): diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 13f1277b9..1d6cce7fa 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -136,7 +136,7 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None: freqtrade = FreqtradeBot(default_conf) - result = freqtrade._get_trade_stake_amount('ETH/BTC') + result = freqtrade.get_trade_stake_amount('ETH/BTC') assert result == default_conf['stake_amount'] @@ -147,7 +147,7 @@ def test_get_trade_stake_amount_no_stake_amount(default_conf, mocker) -> None: freqtrade = FreqtradeBot(default_conf) with pytest.raises(DependencyException, match=r'.*stake amount.*'): - freqtrade._get_trade_stake_amount('ETH/BTC') + freqtrade.get_trade_stake_amount('ETH/BTC') def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, @@ -170,25 +170,25 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, patch_get_signal(freqtrade) # no open trades, order amount should be 'balance / max_open_trades' - result = freqtrade._get_trade_stake_amount('ETH/BTC') + result = freqtrade.get_trade_stake_amount('ETH/BTC') assert result == default_conf['stake_amount'] / conf['max_open_trades'] # create one trade, order amount should be 'balance / (max_open_trades - num_open_trades)' freqtrade.execute_buy('ETH/BTC', result) - result = freqtrade._get_trade_stake_amount('LTC/BTC') + result = freqtrade.get_trade_stake_amount('LTC/BTC') assert result == default_conf['stake_amount'] / (conf['max_open_trades'] - 1) # create 2 trades, order amount should be None freqtrade.execute_buy('LTC/BTC', result) - result = freqtrade._get_trade_stake_amount('XRP/BTC') + result = freqtrade.get_trade_stake_amount('XRP/BTC') assert result is None # set max_open_trades = None, so do not trade conf['max_open_trades'] = 0 freqtrade = FreqtradeBot(conf) - result = freqtrade._get_trade_stake_amount('NEO/BTC') + result = freqtrade.get_trade_stake_amount('NEO/BTC') assert result is None @@ -214,8 +214,8 @@ def test_edge_overrides_stake_amount(mocker, edge_conf) -> None: edge_conf['dry_run_wallet'] = 999.9 freqtrade = FreqtradeBot(edge_conf) - assert freqtrade._get_trade_stake_amount('NEO/BTC') == (999.9 * 0.5 * 0.01) / 0.20 - assert freqtrade._get_trade_stake_amount('LTC/BTC') == (999.9 * 0.5 * 0.01) / 0.21 + assert freqtrade.get_trade_stake_amount('NEO/BTC') == (999.9 * 0.5 * 0.01) / 0.20 + assert freqtrade.get_trade_stake_amount('LTC/BTC') == (999.9 * 0.5 * 0.01) / 0.21 def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf) -> None: @@ -570,7 +570,7 @@ def test_create_trades_limit_reached(default_conf, ticker, limit_buy_order, patch_get_signal(freqtrade) assert not freqtrade.create_trades() - assert freqtrade._get_trade_stake_amount('ETH/BTC') is None + assert freqtrade.get_trade_stake_amount('ETH/BTC') is None def test_create_trades_no_pairs_let(default_conf, ticker, limit_buy_order, fee, From 86f269304099489279934f5622205bdad159d01e Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 01:54:12 +0300 Subject: [PATCH 07/25] cosmetics --- freqtrade/freqtradebot.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9bd7e94fc..7827f29af 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -299,17 +299,17 @@ class FreqtradeBot: buycount = 0 # running get_signal on historical data fetched - for _pair in whitelist: - if self.strategy.is_pair_locked(_pair): - logger.info(f"Pair {_pair} is currently locked.") + for pair in whitelist: + if self.strategy.is_pair_locked(pair): + logger.info(f"Pair {pair} is currently locked.") continue (buy, sell) = self.strategy.get_signal( - _pair, self.strategy.ticker_interval, - self.dataprovider.ohlcv(_pair, self.strategy.ticker_interval)) + pair, self.strategy.ticker_interval, + self.dataprovider.ohlcv(pair, self.strategy.ticker_interval)) if buy and not sell and len(Trade.get_open_trades()) < self.config['max_open_trades']: - stake_amount = self.get_trade_stake_amount(_pair) + stake_amount = self.get_trade_stake_amount(pair) if not stake_amount: continue @@ -320,11 +320,11 @@ class FreqtradeBot: get('check_depth_of_market', {}) if (bidstrat_check_depth_of_market.get('enabled', False)) and\ (bidstrat_check_depth_of_market.get('bids_to_ask_delta', 0) > 0): - if self._check_depth_of_market_buy(_pair, bidstrat_check_depth_of_market): - buycount += self.execute_buy(_pair, stake_amount) + if self._check_depth_of_market_buy(pair, bidstrat_check_depth_of_market): + buycount += self.execute_buy(pair, stake_amount) continue - buycount += self.execute_buy(_pair, stake_amount) + buycount += self.execute_buy(pair, stake_amount) return buycount > 0 From 243bcb23680f5989dce55c268240b074de9448fd Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 02:25:43 +0300 Subject: [PATCH 08/25] Make _check_available_stake_amount() a separate method --- freqtrade/freqtradebot.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7827f29af..24d250ffe 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -206,9 +206,8 @@ class FreqtradeBot: def get_trade_stake_amount(self, pair) -> Optional[float]: """ - Check if stake amount can be fulfilled with the available balance - for the stake currency - :return: float: Stake Amount + Calculate stake amount for the trade + :return: float: Stake amount """ if self.edge: return self.edge.stake_amount( @@ -220,16 +219,24 @@ class FreqtradeBot: else: stake_amount = self.config['stake_amount'] - available_amount = self.wallets.get_free(self.config['stake_currency']) - if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: open_trades = len(Trade.get_open_trades()) if open_trades >= self.config['max_open_trades']: logger.warning("Can't open a new trade: max number of trades is reached") return None + available_amount = self.wallets.get_free(self.config['stake_currency']) return available_amount / (self.config['max_open_trades'] - open_trades) - # Check if stake_amount is fulfilled + return self._check_available_stake_amount(stake_amount) + + def _check_available_stake_amount(self, stake_amount) -> float: + """ + Check if stake amount can be fulfilled with the available balance + for the stake currency + :return: float: Stake amount + """ + available_amount = self.wallets.get_free(self.config['stake_currency']) + if available_amount < stake_amount: raise DependencyException( f"Available balance ({available_amount} {self.config['stake_currency']}) is " From abaeab89aadee4f8a6ee3a29d381fa58261fe0b1 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 02:36:32 +0300 Subject: [PATCH 09/25] Make _calculate_unlimited_stake_amount() a separate method --- freqtrade/freqtradebot.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 24d250ffe..7b111e2d4 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -220,15 +220,22 @@ class FreqtradeBot: stake_amount = self.config['stake_amount'] if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: - open_trades = len(Trade.get_open_trades()) - if open_trades >= self.config['max_open_trades']: - logger.warning("Can't open a new trade: max number of trades is reached") - return None - available_amount = self.wallets.get_free(self.config['stake_currency']) - return available_amount / (self.config['max_open_trades'] - open_trades) + return self._calculate_unlimited_stake_amount() return self._check_available_stake_amount(stake_amount) + def _calculate_unlimited_stake_amount(self) -> Optional[float]: + """ + Calculate stake amount for "unlimited" stake amount + :return: None if max number of trades reached + """ + open_trades = len(Trade.get_open_trades()) + if open_trades >= self.config['max_open_trades']: + logger.warning("Can't open a new trade: max number of trades is reached") + return None + available_amount = self.wallets.get_free(self.config['stake_currency']) + return available_amount / (self.config['max_open_trades'] - open_trades) + def _check_available_stake_amount(self, stake_amount) -> float: """ Check if stake amount can be fulfilled with the available balance From ef92fd775c0863530caae52e9b0b170c3fcc7b77 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 02:53:41 +0300 Subject: [PATCH 10/25] Align behavior: check for available in all cases: edge, unlimited and fixed --- freqtrade/freqtradebot.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 7b111e2d4..2f4803cc5 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -210,7 +210,7 @@ class FreqtradeBot: :return: float: Stake amount """ if self.edge: - return self.edge.stake_amount( + stake_amount = self.edge.stake_amount( pair, self.wallets.get_free(self.config['stake_currency']), self.wallets.get_total(self.config['stake_currency']), @@ -218,9 +218,8 @@ class FreqtradeBot: ) else: stake_amount = self.config['stake_amount'] - - if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: - return self._calculate_unlimited_stake_amount() + if stake_amount == constants.UNLIMITED_STAKE_AMOUNT: + stake_amount = self._calculate_unlimited_stake_amount() return self._check_available_stake_amount(stake_amount) @@ -236,7 +235,7 @@ class FreqtradeBot: available_amount = self.wallets.get_free(self.config['stake_currency']) return available_amount / (self.config['max_open_trades'] - open_trades) - def _check_available_stake_amount(self, stake_amount) -> float: + def _check_available_stake_amount(self, stake_amount: Optional[float]) -> Optional[float]: """ Check if stake amount can be fulfilled with the available balance for the stake currency @@ -244,7 +243,7 @@ class FreqtradeBot: """ available_amount = self.wallets.get_free(self.config['stake_currency']) - if available_amount < stake_amount: + if stake_amount is not None and available_amount < stake_amount: raise DependencyException( f"Available balance ({available_amount} {self.config['stake_currency']}) is " f"lower than stake amount ({stake_amount} {self.config['stake_currency']})" From ed9cb4219df4305e5c3aa1133b45ca0637b9b6d4 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 02:58:23 +0300 Subject: [PATCH 11/25] Make mypy happy --- freqtrade/freqtradebot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 2f4803cc5..4ec01cb60 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -209,6 +209,7 @@ class FreqtradeBot: Calculate stake amount for the trade :return: float: Stake amount """ + stake_amount: Optional[float] if self.edge: stake_amount = self.edge.stake_amount( pair, From 8eeabd2372b26987975a4dd772157e14de7a0044 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 03:22:50 +0300 Subject: [PATCH 12/25] Move warning to create_trades() --- freqtrade/freqtradebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4ec01cb60..44a83db1a 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -231,7 +231,6 @@ class FreqtradeBot: """ open_trades = len(Trade.get_open_trades()) if open_trades >= self.config['max_open_trades']: - logger.warning("Can't open a new trade: max number of trades is reached") return None available_amount = self.wallets.get_free(self.config['stake_currency']) return available_amount / (self.config['max_open_trades'] - open_trades) @@ -324,7 +323,8 @@ class FreqtradeBot: if buy and not sell and len(Trade.get_open_trades()) < self.config['max_open_trades']: stake_amount = self.get_trade_stake_amount(pair) - if not stake_amount: + if stake_amount is None: + logger.warning("Can't open a new trade: max number of trades is reached") continue logger.info(f"Buy signal found: about create a new trade with stake_amount: " From 3dbd83e35a7529215ef609e3090aa9d78475ecb4 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 03:46:42 +0300 Subject: [PATCH 13/25] Introduce get_free_open_trades() method --- freqtrade/freqtradebot.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 44a83db1a..14d67b942 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -136,7 +136,7 @@ class FreqtradeBot: self.process_maybe_execute_sells(trades) # Then looking for buy opportunities - if len(trades) < self.config['max_open_trades']: + if self.get_free_open_trades(): self.process_maybe_execute_buys() # Check and handle any timed out open orders @@ -173,6 +173,14 @@ class FreqtradeBot: """ return [(pair, self.config['ticker_interval']) for pair in pairs] + def get_free_open_trades(self): + """ + Return the number of free open trades slots or 0 if + max number of open trades reached + """ + open_trades = len(Trade.get_open_trades()) + return max(0, self.config['max_open_trades'] - open_trades) + def get_target_bid(self, pair: str, tick: Dict = None) -> float: """ Calculates bid target between current ask price and last price @@ -229,11 +237,11 @@ class FreqtradeBot: Calculate stake amount for "unlimited" stake amount :return: None if max number of trades reached """ - open_trades = len(Trade.get_open_trades()) - if open_trades >= self.config['max_open_trades']: + free_open_trades = self.get_free_open_trades() + if not free_open_trades: return None available_amount = self.wallets.get_free(self.config['stake_currency']) - return available_amount / (self.config['max_open_trades'] - open_trades) + return available_amount / free_open_trades def _check_available_stake_amount(self, stake_amount: Optional[float]) -> Optional[float]: """ @@ -321,12 +329,12 @@ class FreqtradeBot: pair, self.strategy.ticker_interval, self.dataprovider.ohlcv(pair, self.strategy.ticker_interval)) - if buy and not sell and len(Trade.get_open_trades()) < self.config['max_open_trades']: - stake_amount = self.get_trade_stake_amount(pair) - if stake_amount is None: + if buy and not sell: + if not self.get_free_open_trades(): logger.warning("Can't open a new trade: max number of trades is reached") continue + stake_amount = self.get_trade_stake_amount(pair) logger.info(f"Buy signal found: about create a new trade with stake_amount: " f"{stake_amount} ...") From d6ca562b0378a289b160372d1a4a7df0d2da2d67 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 04:05:03 +0300 Subject: [PATCH 14/25] Make mypy happy and handle hypothetical case when stake_amount == 0 --- freqtrade/freqtradebot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 14d67b942..9c18d2d95 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -335,6 +335,10 @@ class FreqtradeBot: continue stake_amount = self.get_trade_stake_amount(pair) + if not stake_amount: + logger.warning("Stake amount is 0, ignoring trade") + continue + logger.info(f"Buy signal found: about create a new trade with stake_amount: " f"{stake_amount} ...") From 5c39ebd0a0d9dea455762086d78a3ea5cbf3c995 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 13:59:40 +0300 Subject: [PATCH 15/25] Adjust logging --- freqtrade/freqtradebot.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9c18d2d95..1cc7f32f4 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -331,12 +331,12 @@ class FreqtradeBot: if buy and not sell: if not self.get_free_open_trades(): - logger.warning("Can't open a new trade: max number of trades is reached") + logger.debug("Can't open a new trade: max number of trades is reached") continue stake_amount = self.get_trade_stake_amount(pair) if not stake_amount: - logger.warning("Stake amount is 0, ignoring trade") + logger.debug("Stake amount is 0, ignoring possible trade for {pair}.") continue logger.info(f"Buy signal found: about create a new trade with stake_amount: " From d1c45cf3f869968523f5b84e7949efd8130e166d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Dec 2019 13:07:51 +0100 Subject: [PATCH 16/25] Update analysis documentation to include kernel installation --- docs/data-analysis.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 115ce1916..8786a1199 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -8,6 +8,27 @@ You can analyze the results of backtests and trading history easily using Jupyte * Don't forget to start a Jupyter notebook server from within your conda or venv environment or use [nb_conda_kernels](https://github.com/Anaconda-Platform/nb_conda_kernels)* * Copy the example notebook before use so your changes don't get clobbered with the next freqtrade update. +### Using virtual environment with System jupyter + +Sometimes it can be desired to use a system-wide installation of Jupyter notebook, and use a jupyter kernel from the virtual environment. +This prevents you from installing the full jupyter suite multiple times per system, and provides an easy way to switch between tasks (freqtrade / other analytics tasks). + +For this to work, first activate your virtual environment and run the following commands: + +``` bash +# Activate virtual environment +source .env/bin/activate + +pip install ipykernel +ipython kernel install --user --name=freqtrade +# Restart jupyter (lab / notebook) +# select kernel "freqtrade" in the notebook +``` + +!!! Note + This section is provided for completeness, the Freqtrade Team won't provide full support for problems with this setup and will recommend to install Jupyter in the virtual environment directly, as that is the easiest way to get up and running. For help with this setup please refer to the [Project Jupyter](https://jupyter.org/) [documentation](https://jupyter.org/documentation) or [help channels](https://jupyter.org/community). + + ## Fine print Some tasks don't work especially well in notebooks. For example, anything using asynchronous execution is a problem for Jupyter. Also, freqtrade's primary entry point is the shell cli, so using pure python in a notebook bypasses arguments that provide required objects and parameters to helper functions. You may need to set those values or create expected objects manually. From 304d15e23694d1dc3ac1f85760dbc3af9996710a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Dec 2019 19:35:42 +0100 Subject: [PATCH 17/25] Small corrections --- docs/data-analysis.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/data-analysis.md b/docs/data-analysis.md index 8786a1199..fc4693b17 100644 --- a/docs/data-analysis.md +++ b/docs/data-analysis.md @@ -8,7 +8,7 @@ You can analyze the results of backtests and trading history easily using Jupyte * Don't forget to start a Jupyter notebook server from within your conda or venv environment or use [nb_conda_kernels](https://github.com/Anaconda-Platform/nb_conda_kernels)* * Copy the example notebook before use so your changes don't get clobbered with the next freqtrade update. -### Using virtual environment with System jupyter +### Using virtual environment with system-wide Jupyter installation Sometimes it can be desired to use a system-wide installation of Jupyter notebook, and use a jupyter kernel from the virtual environment. This prevents you from installing the full jupyter suite multiple times per system, and provides an easy way to switch between tasks (freqtrade / other analytics tasks). @@ -26,7 +26,7 @@ ipython kernel install --user --name=freqtrade ``` !!! Note - This section is provided for completeness, the Freqtrade Team won't provide full support for problems with this setup and will recommend to install Jupyter in the virtual environment directly, as that is the easiest way to get up and running. For help with this setup please refer to the [Project Jupyter](https://jupyter.org/) [documentation](https://jupyter.org/documentation) or [help channels](https://jupyter.org/community). + This section is provided for completeness, the Freqtrade Team won't provide full support for problems with this setup and will recommend to install Jupyter in the virtual environment directly, as that is the easiest way to get jupyter notebooks up and running. For help with this setup please refer to the [Project Jupyter](https://jupyter.org/) [documentation](https://jupyter.org/documentation) or [help channels](https://jupyter.org/community). ## Fine print From df7ceb4ccbbfd35fe7eaec2d37dea273b89a4873 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 29 Dec 2019 19:51:47 +0100 Subject: [PATCH 18/25] Fix misinformation in /status table --- freqtrade/rpc/rpc.py | 2 +- tests/rpc/test_rpc.py | 2 +- tests/rpc/test_rpc_telegram.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 35c312743..0a79d350b 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -142,7 +142,7 @@ class RPC: def _rpc_status_table(self, stake_currency, fiat_display_currency: str) -> Tuple[List, List]: trades = Trade.get_open_trades() if not trades: - raise RPCException('no active order') + raise RPCException('no active trade') else: trades_list = [] for trade in trades: diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 3b897572c..c5bb0ca2c 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -113,7 +113,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: rpc = RPC(freqtradebot) freqtradebot.state = State.RUNNING - with pytest.raises(RPCException, match=r'.*no active order*'): + with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_status_table(default_conf['stake_currency'], 'USD') freqtradebot.create_trades() diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index f8b9ca8ab..ddbc35bd5 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -275,13 +275,13 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: # Status table is also enabled when stopped telegram._status_table(update=update, context=MagicMock()) assert msg_mock.call_count == 1 - assert 'no active order' in msg_mock.call_args_list[0][0][0] + assert 'no active trade' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() freqtradebot.state = State.RUNNING telegram._status_table(update=update, context=MagicMock()) assert msg_mock.call_count == 1 - assert 'no active order' in msg_mock.call_args_list[0][0][0] + assert 'no active trade' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() # Create some test data From 20a132651f460b3ab00f4e4032173b8c58eb7bba Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2019 07:34:49 +0000 Subject: [PATCH 19/25] Bump ccxt from 1.21.12 to 1.21.23 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.21.12 to 1.21.23. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md) - [Commits](https://github.com/ccxt/ccxt/compare/1.21.12...1.21.23) Signed-off-by: dependabot-preview[bot] --- requirements-common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-common.txt b/requirements-common.txt index 9d0fd4756..add1ea0fd 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,6 +1,6 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs -ccxt==1.21.12 +ccxt==1.21.23 SQLAlchemy==1.3.12 python-telegram-bot==12.2.0 arrow==0.15.4 From de23f3928de5639a01efe424e5040354196c59e8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Dec 2019 09:56:42 +0100 Subject: [PATCH 20/25] Add trailing_only_offset to template and sample --- freqtrade/templates/base_strategy.py.j2 | 1 + freqtrade/templates/sample_strategy.py | 1 + 2 files changed, 2 insertions(+) diff --git a/freqtrade/templates/base_strategy.py.j2 b/freqtrade/templates/base_strategy.py.j2 index 73a4c7a5a..32573ec9e 100644 --- a/freqtrade/templates/base_strategy.py.j2 +++ b/freqtrade/templates/base_strategy.py.j2 @@ -47,6 +47,7 @@ class {{ strategy }}(IStrategy): # Trailing stoploss trailing_stop = False + # trailing_only_offset_is_reached = False # trailing_stop_positive = 0.01 # trailing_stop_positive_offset = 0.0 # Disabled / not configured diff --git a/freqtrade/templates/sample_strategy.py b/freqtrade/templates/sample_strategy.py index 02bf24e7e..228e56b82 100644 --- a/freqtrade/templates/sample_strategy.py +++ b/freqtrade/templates/sample_strategy.py @@ -48,6 +48,7 @@ class SampleStrategy(IStrategy): # Trailing stoploss trailing_stop = False + # trailing_only_offset_is_reached = False # trailing_stop_positive = 0.01 # trailing_stop_positive_offset = 0.0 # Disabled / not configured From fb3a53b8af27c49eb4c8a26c271437a581dd187e Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Dec 2019 14:28:34 +0100 Subject: [PATCH 21/25] Use ExchangeResolver for edge_cli too --- freqtrade/optimize/edge_cli.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index ea5cc663d..4944f1dbb 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -12,8 +12,7 @@ from freqtrade import constants from freqtrade.configuration import (TimeRange, remove_credentials, validate_config_consistency) from freqtrade.edge import Edge -from freqtrade.exchange import Exchange -from freqtrade.resolvers import StrategyResolver +from freqtrade.resolvers import StrategyResolver, ExchangeResolver logger = logging.getLogger(__name__) @@ -33,7 +32,7 @@ class EdgeCli: # Reset keys for edge remove_credentials(self.config) self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT - self.exchange = Exchange(self.config) + self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) self.strategy = StrategyResolver.load_strategy(self.config) validate_config_consistency(self.config) From 20abf67779a1c9f778e6339df0abd0af63433154 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Dec 2019 14:29:36 +0100 Subject: [PATCH 22/25] Add Debug "code" for randomly failing test --- tests/test_utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index 4cf7b5f23..8545eb817 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -448,6 +448,9 @@ def test_create_datadir(caplog, mocker): # Ensure that caplog is empty before starting ... # Should prevent random failures. caplog.clear() + # Added assert here to analyze random test-failures ... + assert len(caplog.record_tuples) == 0 + cud = mocker.patch("freqtrade.utils.create_userdata_dir", MagicMock()) csf = mocker.patch("freqtrade.utils.copy_sample_files", MagicMock()) args = [ From 024aa3ab6b6e5e0ba11bf7cfe0035cb83423bbf3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Dec 2019 14:57:26 +0100 Subject: [PATCH 23/25] Move exceptions to seperate module --- freqtrade/__init__.py | 31 ------------------------------- freqtrade/exceptions.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 31 deletions(-) create mode 100644 freqtrade/exceptions.py diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 83fee0b0d..e1f65d4fe 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -11,34 +11,3 @@ if __version__ == 'develop': except Exception: # git not available, ignore pass - - -class DependencyException(Exception): - """ - Indicates that an assumed dependency is not met. - This could happen when there is currently not enough money on the account. - """ - - -class OperationalException(Exception): - """ - Requires manual intervention and will usually stop the bot. - This happens when an exchange returns an unexpected error during runtime - or given configuration is invalid. - """ - - -class InvalidOrderException(Exception): - """ - This is returned when the order is not valid. Example: - If stoploss on exchange order is hit, then trying to cancel the order - should return this exception. - """ - - -class TemporaryError(Exception): - """ - Temporary network or exchange related error. - This could happen when an exchange is congested, unavailable, or the user - has networking problems. Usually resolves itself after a time. - """ diff --git a/freqtrade/exceptions.py b/freqtrade/exceptions.py new file mode 100644 index 000000000..80d45dd86 --- /dev/null +++ b/freqtrade/exceptions.py @@ -0,0 +1,31 @@ + + +class DependencyException(Exception): + """ + Indicates that an assumed dependency is not met. + This could happen when there is currently not enough money on the account. + """ + + +class OperationalException(Exception): + """ + Requires manual intervention and will usually stop the bot. + This happens when an exchange returns an unexpected error during runtime + or given configuration is invalid. + """ + + +class InvalidOrderException(Exception): + """ + This is returned when the order is not valid. Example: + If stoploss on exchange order is hit, then trying to cancel the order + should return this exception. + """ + + +class TemporaryError(Exception): + """ + Temporary network or exchange related error. + This could happen when an exchange is congested, unavailable, or the user + has networking problems. Usually resolves itself after a time. + """ From 1ffda29fd2aeb95cffedfaded274c4d07e404436 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Dec 2019 15:02:17 +0100 Subject: [PATCH 24/25] Adjust improts to new exception location --- freqtrade/configuration/check_exchange.py | 4 ++-- freqtrade/configuration/config_validation.py | 3 ++- freqtrade/configuration/configuration.py | 5 +++-- freqtrade/configuration/deprecated_settings.py | 2 +- freqtrade/configuration/directory_operations.py | 2 +- freqtrade/configuration/load_config.py | 2 +- freqtrade/data/history.py | 6 ++++-- freqtrade/edge/__init__.py | 4 ++-- freqtrade/exchange/binance.py | 4 ++-- freqtrade/exchange/common.py | 2 +- freqtrade/exchange/exchange.py | 4 ++-- freqtrade/exchange/kraken.py | 2 +- freqtrade/freqtradebot.py | 6 +++--- freqtrade/loggers.py | 2 +- freqtrade/main.py | 2 +- freqtrade/optimize/__init__.py | 4 ++-- freqtrade/optimize/backtesting.py | 2 +- freqtrade/optimize/hyperopt.py | 6 +++--- freqtrade/optimize/hyperopt_interface.py | 6 ++---- freqtrade/pairlist/VolumePairList.py | 2 +- freqtrade/pairlist/pairlistmanager.py | 5 +++-- freqtrade/persistence.py | 2 +- freqtrade/plot/plot_utils.py | 2 +- freqtrade/resolvers/hyperopt_resolver.py | 2 +- freqtrade/resolvers/iresolver.py | 2 +- freqtrade/resolvers/strategy_resolver.py | 2 +- freqtrade/rpc/rpc.py | 2 +- freqtrade/utils.py | 2 +- freqtrade/worker.py | 4 ++-- tests/edge/test_edge.py | 2 +- tests/exchange/test_binance.py | 4 ++-- tests/exchange/test_exchange.py | 4 ++-- tests/optimize/test_backtesting.py | 4 ++-- tests/optimize/test_hyperopt.py | 2 +- tests/pairlist/test_pairlist.py | 2 +- tests/rpc/test_rpc.py | 4 ++-- tests/strategy/test_strategy.py | 2 +- tests/test_configuration.py | 2 +- tests/test_directory_operations.py | 2 +- tests/test_freqtradebot.py | 10 +++++----- tests/test_main.py | 2 +- tests/test_persistence.py | 3 ++- tests/test_plotting.py | 6 +++--- tests/test_utils.py | 2 +- 44 files changed, 74 insertions(+), 70 deletions(-) diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index c739de692..0076b1c5d 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -1,9 +1,9 @@ import logging from typing import Any, Dict -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.exchange import (available_exchanges, get_exchange_bad_reason, - is_exchange_known_ccxt, is_exchange_bad, + is_exchange_bad, is_exchange_known_ccxt, is_exchange_officially_supported) from freqtrade.state import RunMode diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 068364884..43eead46a 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -4,7 +4,8 @@ from typing import Any, Dict from jsonschema import Draft4Validator, validators from jsonschema.exceptions import ValidationError, best_match -from freqtrade import constants, OperationalException +from freqtrade import constants +from freqtrade.exceptions import OperationalException from freqtrade.state import RunMode logger = logging.getLogger(__name__) diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index f73b52c10..a8b7638c8 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -7,15 +7,16 @@ from copy import deepcopy from pathlib import Path from typing import Any, Callable, Dict, List, Optional -from freqtrade import OperationalException, constants +from freqtrade import constants from freqtrade.configuration.check_exchange import check_exchange from freqtrade.configuration.deprecated_settings import process_temporary_deprecated_settings from freqtrade.configuration.directory_operations import (create_datadir, create_userdata_dir) from freqtrade.configuration.load_config import load_config_file +from freqtrade.exceptions import OperationalException from freqtrade.loggers import setup_logging from freqtrade.misc import deep_merge_dicts, json_load -from freqtrade.state import RunMode, TRADING_MODES, NON_UTIL_MODES +from freqtrade.state import NON_UTIL_MODES, TRADING_MODES, RunMode logger = logging.getLogger(__name__) diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index b1e3535a3..260aae419 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -5,7 +5,7 @@ Functions to handle deprecated settings import logging from typing import Any, Dict -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException logger = logging.getLogger(__name__) diff --git a/freqtrade/configuration/directory_operations.py b/freqtrade/configuration/directory_operations.py index 556f27742..43a209483 100644 --- a/freqtrade/configuration/directory_operations.py +++ b/freqtrade/configuration/directory_operations.py @@ -3,7 +3,7 @@ import shutil from pathlib import Path from typing import Any, Dict, Optional -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.constants import USER_DATA_FILES logger = logging.getLogger(__name__) diff --git a/freqtrade/configuration/load_config.py b/freqtrade/configuration/load_config.py index 7a3ca1798..19179c6c3 100644 --- a/freqtrade/configuration/load_config.py +++ b/freqtrade/configuration/load_config.py @@ -6,7 +6,7 @@ import logging import sys from typing import Any, Dict -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException logger = logging.getLogger(__name__) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 4c5c0521f..30d168f78 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -16,10 +16,12 @@ from typing import Any, Dict, List, Optional, Tuple import arrow from pandas import DataFrame -from freqtrade import OperationalException, misc +from freqtrade import misc from freqtrade.configuration import TimeRange from freqtrade.data.converter import parse_ticker_dataframe, trades_to_ohlcv -from freqtrade.exchange import Exchange, timeframe_to_minutes, timeframe_to_seconds +from freqtrade.exceptions import OperationalException +from freqtrade.exchange import (Exchange, timeframe_to_minutes, + timeframe_to_seconds) logger = logging.getLogger(__name__) diff --git a/freqtrade/edge/__init__.py b/freqtrade/edge/__init__.py index 9ad2485ef..19d65d9d7 100644 --- a/freqtrade/edge/__init__.py +++ b/freqtrade/edge/__init__.py @@ -8,12 +8,12 @@ import numpy as np import utils_find_1st as utf1st from pandas import DataFrame -from freqtrade import constants, OperationalException +from freqtrade import constants from freqtrade.configuration import TimeRange from freqtrade.data import history +from freqtrade.exceptions import OperationalException from freqtrade.strategy.interface import SellType - logger = logging.getLogger(__name__) diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index b5507981f..96f72fcf5 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -4,8 +4,8 @@ from typing import Dict import ccxt -from freqtrade import (DependencyException, InvalidOrderException, - OperationalException, TemporaryError) +from freqtrade.exceptions import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.exchange import Exchange logger = logging.getLogger(__name__) diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index ed30b95c7..b38ed35a3 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -1,6 +1,6 @@ import logging -from freqtrade import DependencyException, TemporaryError +from freqtrade.exceptions import DependencyException, TemporaryError logger = logging.getLogger(__name__) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 01e84c06e..3ef32db62 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -17,9 +17,9 @@ import ccxt.async_support as ccxt_async from ccxt.base.decimal_to_precision import ROUND_DOWN, ROUND_UP from pandas import DataFrame -from freqtrade import (DependencyException, InvalidOrderException, - OperationalException, TemporaryError) from freqtrade.data.converter import parse_ticker_dataframe +from freqtrade.exceptions import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.exchange.common import BAD_EXCHANGES, retrier, retrier_async from freqtrade.misc import deep_merge_dicts diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index f548489bc..9bcd9cc1f 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -4,7 +4,7 @@ from typing import Dict import ccxt -from freqtrade import OperationalException, TemporaryError +from freqtrade.exceptions import OperationalException, TemporaryError from freqtrade.exchange import Exchange from freqtrade.exchange.exchange import retrier diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 90eb7beba..f33a8cd03 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -12,17 +12,17 @@ from typing import Any, Dict, List, Optional, Tuple import arrow from requests.exceptions import RequestException -from freqtrade import (DependencyException, InvalidOrderException, __version__, - constants, persistence) +from freqtrade import __version__, constants, persistence from freqtrade.configuration import validate_config_consistency from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge +from freqtrade.exceptions import DependencyException, InvalidOrderException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date +from freqtrade.pairlist.pairlistmanager import PairListManager from freqtrade.persistence import Trade from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.rpc import RPCManager, RPCMessageType -from freqtrade.pairlist.pairlistmanager import PairListManager from freqtrade.state import State from freqtrade.strategy.interface import IStrategy, SellType from freqtrade.wallets import Wallets diff --git a/freqtrade/loggers.py b/freqtrade/loggers.py index 27f16ecc3..c69388430 100644 --- a/freqtrade/loggers.py +++ b/freqtrade/loggers.py @@ -5,7 +5,7 @@ from logging import Formatter from logging.handlers import RotatingFileHandler, SysLogHandler from typing import Any, Dict, List -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException logger = logging.getLogger(__name__) diff --git a/freqtrade/main.py b/freqtrade/main.py index 7afaeb1a2..62d2fc05d 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -13,8 +13,8 @@ if sys.version_info < (3, 6): import logging from typing import Any, List -from freqtrade import OperationalException from freqtrade.configuration import Arguments +from freqtrade.exceptions import OperationalException logger = logging.getLogger('freqtrade') diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 1f2f588ef..34760372f 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -1,11 +1,11 @@ import logging from typing import Any, Dict -from freqtrade import DependencyException, constants, OperationalException +from freqtrade import constants +from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.state import RunMode from freqtrade.utils import setup_utils_configuration - logger = logging.getLogger(__name__) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index a8fe90a06..9bd0327e0 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -12,11 +12,11 @@ from typing import Any, Dict, List, NamedTuple, Optional from pandas import DataFrame from tabulate import tabulate -from freqtrade import OperationalException from freqtrade.configuration import (TimeRange, remove_credentials, validate_config_consistency) from freqtrade.data import history from freqtrade.data.dataprovider import DataProvider +from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds from freqtrade.misc import file_dump_json from freqtrade.persistence import Trade diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 48f883ac5..a8f1a71ef 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -22,13 +22,13 @@ from joblib import (Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects) from pandas import DataFrame -from freqtrade import OperationalException from freqtrade.data.history import get_timerange, trim_dataframe +from freqtrade.exceptions import OperationalException from freqtrade.misc import plural, round_dict from freqtrade.optimize.backtesting import Backtesting # Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules -from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F4 -from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F4 +from freqtrade.optimize.hyperopt_interface import IHyperOpt # noqa: F401 +from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F401 from freqtrade.resolvers.hyperopt_resolver import (HyperOptLossResolver, HyperOptResolver) diff --git a/freqtrade/optimize/hyperopt_interface.py b/freqtrade/optimize/hyperopt_interface.py index 856f3eee7..d7d917c19 100644 --- a/freqtrade/optimize/hyperopt_interface.py +++ b/freqtrade/optimize/hyperopt_interface.py @@ -4,17 +4,15 @@ This module defines the interface to apply for hyperopt """ import logging import math - from abc import ABC -from typing import Dict, Any, Callable, List +from typing import Any, Callable, Dict, List from skopt.space import Categorical, Dimension, Integer, Real -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes from freqtrade.misc import round_dict - logger = logging.getLogger(__name__) diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index 2df9ba691..4ac9935ba 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -8,7 +8,7 @@ import logging from datetime import datetime from typing import Dict, List -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.pairlist.IPairList import IPairList logger = logging.getLogger(__name__) diff --git a/freqtrade/pairlist/pairlistmanager.py b/freqtrade/pairlist/pairlistmanager.py index 1530710d2..55828c6ef 100644 --- a/freqtrade/pairlist/pairlistmanager.py +++ b/freqtrade/pairlist/pairlistmanager.py @@ -4,11 +4,12 @@ Static List provider Provides lists as configured in config.json """ -from cachetools import TTLCache, cached import logging from typing import Dict, List -from freqtrade import OperationalException +from cachetools import TTLCache, cached + +from freqtrade.exceptions import OperationalException from freqtrade.pairlist.IPairList import IPairList from freqtrade.resolvers import PairListResolver diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 993b68bc7..75116f1e3 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -16,7 +16,7 @@ from sqlalchemy.orm.scoping import scoped_session from sqlalchemy.orm.session import sessionmaker from sqlalchemy.pool import StaticPool -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException logger = logging.getLogger(__name__) diff --git a/freqtrade/plot/plot_utils.py b/freqtrade/plot/plot_utils.py index 8de0eb9e7..9eff08396 100644 --- a/freqtrade/plot/plot_utils.py +++ b/freqtrade/plot/plot_utils.py @@ -1,6 +1,6 @@ from typing import Any, Dict -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.state import RunMode from freqtrade.utils import setup_utils_configuration diff --git a/freqtrade/resolvers/hyperopt_resolver.py b/freqtrade/resolvers/hyperopt_resolver.py index c26fd09f2..ddf461252 100644 --- a/freqtrade/resolvers/hyperopt_resolver.py +++ b/freqtrade/resolvers/hyperopt_resolver.py @@ -7,8 +7,8 @@ import logging from pathlib import Path from typing import Dict -from freqtrade import OperationalException from freqtrade.constants import DEFAULT_HYPEROPT_LOSS, USERPATH_HYPEROPTS +from freqtrade.exceptions import OperationalException from freqtrade.optimize.hyperopt_interface import IHyperOpt from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.resolvers import IResolver diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index e3c0d1ad0..5a844097c 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -9,7 +9,7 @@ import logging from pathlib import Path from typing import Any, Dict, Generator, List, Optional, Tuple, Type, Union -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException logger = logging.getLogger(__name__) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 4fd5c586a..9e64f38df 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -11,9 +11,9 @@ from inspect import getfullargspec from pathlib import Path from typing import Dict, Optional -from freqtrade import OperationalException from freqtrade.constants import (REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGY) +from freqtrade.exceptions import OperationalException from freqtrade.resolvers import IResolver from freqtrade.strategy.interface import IStrategy diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 0a79d350b..c187dae4f 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -11,7 +11,7 @@ from typing import Any, Dict, List, Optional, Tuple import arrow from numpy import NAN, mean -from freqtrade import DependencyException, TemporaryError +from freqtrade.exceptions import DependencyException, TemporaryError from freqtrade.misc import shorten_date from freqtrade.persistence import Trade from freqtrade.rpc.fiat_convert import CryptoToFiatConverter diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 5a5662e4b..45520ecf7 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -11,7 +11,6 @@ import rapidjson from colorama import init as colorama_init from tabulate import tabulate -from freqtrade import OperationalException from freqtrade.configuration import (Configuration, TimeRange, remove_credentials) from freqtrade.configuration.directory_operations import (copy_sample_files, @@ -20,6 +19,7 @@ from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGY from freqtrade.data.history import (convert_trades_to_ohlcv, refresh_backtest_ohlcv_data, refresh_backtest_trades_data) +from freqtrade.exceptions import OperationalException from freqtrade.exchange import (available_exchanges, ccxt_exchanges, market_is_active, symbol_is_pair) from freqtrade.misc import plural, render_template diff --git a/freqtrade/worker.py b/freqtrade/worker.py index 8e4be9d43..22651d269 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -8,9 +8,9 @@ from typing import Any, Callable, Dict, Optional import sdnotify -from freqtrade import (OperationalException, TemporaryError, __version__, - constants) +from freqtrade import __version__, constants from freqtrade.configuration import Configuration +from freqtrade.exceptions import OperationalException, TemporaryError from freqtrade.freqtradebot import FreqtradeBot from freqtrade.rpc import RPCMessageType from freqtrade.state import State diff --git a/tests/edge/test_edge.py b/tests/edge/test_edge.py index 3a866c0a8..ef1280fa4 100644 --- a/tests/edge/test_edge.py +++ b/tests/edge/test_edge.py @@ -10,7 +10,7 @@ import numpy as np import pytest from pandas import DataFrame, to_datetime -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.edge import Edge, PairInfo from freqtrade.strategy.interface import SellType diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 7720a7d2e..0a12c1cb1 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -4,8 +4,8 @@ from unittest.mock import MagicMock import ccxt import pytest -from freqtrade import (DependencyException, InvalidOrderException, - OperationalException, TemporaryError) +from freqtrade.exceptions import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from tests.conftest import get_patched_exchange diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 2f95e4e01..cb40bdbd9 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -11,8 +11,8 @@ import ccxt import pytest from pandas import DataFrame -from freqtrade import (DependencyException, InvalidOrderException, - OperationalException, TemporaryError) +from freqtrade.exceptions import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.exchange import Binance, Exchange, Kraken from freqtrade.exchange.common import API_RETRY_COUNT from freqtrade.exchange.exchange import (market_is_active, symbol_is_pair, diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 8a27c591f..4e2fd01cf 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -10,13 +10,14 @@ import pandas as pd import pytest from arrow import Arrow -from freqtrade import DependencyException, OperationalException, constants +from freqtrade import constants from freqtrade.configuration import TimeRange from freqtrade.data import history from freqtrade.data.btanalysis import evaluate_result_multi from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.data.history import get_timerange +from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.optimize import setup_configuration, start_backtesting from freqtrade.optimize.backtesting import Backtesting from freqtrade.state import RunMode @@ -25,7 +26,6 @@ from freqtrade.strategy.interface import SellType from tests.conftest import (get_args, log_has, log_has_re, patch_exchange, patched_configuration_load_config_file) - ORDER_TYPES = [ { 'buy': 'limit', diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index fb492be35..473d760ac 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -9,7 +9,7 @@ import pytest from arrow import Arrow from filelock import Timeout -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.data.converter import parse_ticker_dataframe from freqtrade.data.history import load_tickerdata_file from freqtrade.optimize import setup_configuration, start_hyperopt diff --git a/tests/pairlist/test_pairlist.py b/tests/pairlist/test_pairlist.py index 21929de2b..ac4cbc813 100644 --- a/tests/pairlist/test_pairlist.py +++ b/tests/pairlist/test_pairlist.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock, PropertyMock import pytest -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.constants import AVAILABLE_PAIRLISTS from freqtrade.resolvers import PairListResolver from freqtrade.pairlist.pairlistmanager import PairListManager diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index c5bb0ca2c..31632bd70 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -7,13 +7,13 @@ from unittest.mock import ANY, MagicMock, PropertyMock import pytest from numpy import isnan -from freqtrade import DependencyException, TemporaryError from freqtrade.edge import PairInfo +from freqtrade.exceptions import DependencyException, TemporaryError from freqtrade.persistence import Trade from freqtrade.rpc import RPC, RPCException from freqtrade.rpc.fiat_convert import CryptoToFiatConverter from freqtrade.state import State -from tests.conftest import patch_get_signal, get_patched_freqtradebot +from tests.conftest import get_patched_freqtradebot, patch_get_signal # Functions for recurrent object patching diff --git a/tests/strategy/test_strategy.py b/tests/strategy/test_strategy.py index 10b9f3466..d3977ae44 100644 --- a/tests/strategy/test_strategy.py +++ b/tests/strategy/test_strategy.py @@ -8,7 +8,7 @@ from pathlib import Path import pytest from pandas import DataFrame -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.resolvers import StrategyResolver from freqtrade.strategy.interface import IStrategy from tests.conftest import log_has, log_has_re diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 6564417b3..ee3d23131 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -10,7 +10,6 @@ from unittest.mock import MagicMock import pytest from jsonschema import ValidationError -from freqtrade import OperationalException from freqtrade.configuration import (Arguments, Configuration, check_exchange, remove_credentials, validate_config_consistency) @@ -20,6 +19,7 @@ from freqtrade.configuration.deprecated_settings import ( process_temporary_deprecated_settings) from freqtrade.configuration.load_config import load_config_file from freqtrade.constants import DEFAULT_DB_DRYRUN_URL, DEFAULT_DB_PROD_URL +from freqtrade.exceptions import OperationalException from freqtrade.loggers import _set_loggers, setup_logging from freqtrade.state import RunMode from tests.conftest import (log_has, log_has_re, diff --git a/tests/test_directory_operations.py b/tests/test_directory_operations.py index db41e2da2..889338a64 100644 --- a/tests/test_directory_operations.py +++ b/tests/test_directory_operations.py @@ -4,10 +4,10 @@ from unittest.mock import MagicMock import pytest -from freqtrade import OperationalException from freqtrade.configuration.directory_operations import (copy_sample_files, create_datadir, create_userdata_dir) +from freqtrade.exceptions import OperationalException from tests.conftest import log_has, log_has_re diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 2f8c786fd..3f5b7bd54 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -11,9 +11,9 @@ import arrow import pytest import requests -from freqtrade import (DependencyException, InvalidOrderException, - OperationalException, TemporaryError, constants) -from freqtrade.constants import MATH_CLOSE_PREC +from freqtrade.constants import MATH_CLOSE_PREC, UNLIMITED_STAKE_AMOUNT +from freqtrade.exceptions import (DependencyException, InvalidOrderException, + OperationalException, TemporaryError) from freqtrade.freqtradebot import FreqtradeBot from freqtrade.persistence import Trade from freqtrade.rpc import RPCMessageType @@ -163,7 +163,7 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf, ticker, ) conf = deepcopy(default_conf) - conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT + conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT conf['max_open_trades'] = 2 freqtrade = FreqtradeBot(conf) @@ -564,7 +564,7 @@ def test_create_trades_limit_reached(default_conf, ticker, limit_buy_order, get_fee=fee, ) default_conf['max_open_trades'] = 0 - default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT + default_conf['stake_amount'] = UNLIMITED_STAKE_AMOUNT freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) diff --git a/tests/test_main.py b/tests/test_main.py index 03e6a7ce9..83be01999 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -5,8 +5,8 @@ from unittest.mock import MagicMock, PropertyMock import pytest -from freqtrade import OperationalException from freqtrade.configuration import Arguments +from freqtrade.exceptions import OperationalException from freqtrade.freqtradebot import FreqtradeBot from freqtrade.main import main from freqtrade.state import State diff --git a/tests/test_persistence.py b/tests/test_persistence.py index b9a636e1a..6bd7971a7 100644 --- a/tests/test_persistence.py +++ b/tests/test_persistence.py @@ -6,7 +6,8 @@ import arrow import pytest from sqlalchemy import create_engine -from freqtrade import OperationalException, constants +from freqtrade import constants +from freqtrade.exceptions import OperationalException from freqtrade.persistence import Trade, clean_dry_run_db, init from tests.conftest import log_has diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 31502cafc..9934d2493 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -7,17 +7,17 @@ import plotly.graph_objects as go import pytest from plotly.subplots import make_subplots -from freqtrade import OperationalException from freqtrade.configuration import TimeRange from freqtrade.data import history from freqtrade.data.btanalysis import create_cum_profit, load_backtest_data +from freqtrade.exceptions import OperationalException from freqtrade.plot.plot_utils import start_plot_dataframe, start_plot_profit from freqtrade.plot.plotting import (add_indicators, add_profit, - load_and_plot_trades, generate_candlestick_graph, generate_plot_filename, generate_profit_graph, init_plotscript, - plot_profit, plot_trades, store_plot_file) + load_and_plot_trades, plot_profit, + plot_trades, store_plot_file) from freqtrade.strategy.default_strategy import DefaultStrategy from tests.conftest import get_args, log_has, log_has_re diff --git a/tests/test_utils.py b/tests/test_utils.py index 4cf7b5f23..2e2499337 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -4,7 +4,7 @@ from unittest.mock import MagicMock, PropertyMock import pytest -from freqtrade import OperationalException +from freqtrade.exceptions import OperationalException from freqtrade.state import RunMode from freqtrade.utils import (setup_utils_configuration, start_create_userdir, start_download_data, start_hyperopt_list, From 8e9a3e8fc8e5de762455ca7472c81e5ad15f8591 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Dec 2019 15:11:07 +0100 Subject: [PATCH 25/25] Capture FtBaseException at the outermost level --- freqtrade/exceptions.py | 28 +++++++++++++++++----------- freqtrade/main.py | 4 ++-- tests/test_main.py | 4 ++-- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/freqtrade/exceptions.py b/freqtrade/exceptions.py index 80d45dd86..2f05ddb57 100644 --- a/freqtrade/exceptions.py +++ b/freqtrade/exceptions.py @@ -1,21 +1,27 @@ -class DependencyException(Exception): +class FreqtradeException(Exception): + """ + Freqtrade base exception. Handled at the outermost level. + All other exception types are subclasses of this exception type. + """ + + +class OperationalException(FreqtradeException): + """ + Requires manual intervention and will stop the bot. + Most of the time, this is caused by an invalid Configuration. + """ + + +class DependencyException(FreqtradeException): """ Indicates that an assumed dependency is not met. This could happen when there is currently not enough money on the account. """ -class OperationalException(Exception): - """ - Requires manual intervention and will usually stop the bot. - This happens when an exchange returns an unexpected error during runtime - or given configuration is invalid. - """ - - -class InvalidOrderException(Exception): +class InvalidOrderException(FreqtradeException): """ This is returned when the order is not valid. Example: If stoploss on exchange order is hit, then trying to cancel the order @@ -23,7 +29,7 @@ class InvalidOrderException(Exception): """ -class TemporaryError(Exception): +class TemporaryError(FreqtradeException): """ Temporary network or exchange related error. This could happen when an exchange is congested, unavailable, or the user diff --git a/freqtrade/main.py b/freqtrade/main.py index 62d2fc05d..811e29864 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -4,6 +4,7 @@ Main Freqtrade bot script. Read the documentation to know what cli arguments you need. """ +from freqtrade.exceptions import FreqtradeException, OperationalException import sys # check min. python version if sys.version_info < (3, 6): @@ -14,7 +15,6 @@ import logging from typing import Any, List from freqtrade.configuration import Arguments -from freqtrade.exceptions import OperationalException logger = logging.getLogger('freqtrade') @@ -50,7 +50,7 @@ def main(sysargv: List[str] = None) -> None: except KeyboardInterrupt: logger.info('SIGINT received, aborting ...') return_code = 0 - except OperationalException as e: + except FreqtradeException as e: logger.error(str(e)) return_code = 2 except Exception: diff --git a/tests/test_main.py b/tests/test_main.py index 83be01999..76b1bf658 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -6,7 +6,7 @@ from unittest.mock import MagicMock, PropertyMock import pytest from freqtrade.configuration import Arguments -from freqtrade.exceptions import OperationalException +from freqtrade.exceptions import OperationalException, FreqtradeException from freqtrade.freqtradebot import FreqtradeBot from freqtrade.main import main from freqtrade.state import State @@ -96,7 +96,7 @@ def test_main_operational_exception(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.freqtradebot.FreqtradeBot.cleanup', MagicMock()) mocker.patch( 'freqtrade.worker.Worker._worker', - MagicMock(side_effect=OperationalException('Oh snap!')) + MagicMock(side_effect=FreqtradeException('Oh snap!')) ) patched_configuration_load_config_file(mocker, default_conf) mocker.patch('freqtrade.wallets.Wallets.update', MagicMock())