From 039dfc302ca2c8497aea27282f3ab20f5a0c4343 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sat, 28 Dec 2019 01:34:31 +0300 Subject: [PATCH 01/11] 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 02/11] _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 03/11] 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 04/11] 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 05/11] 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 06/11] 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 07/11] 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 08/11] 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 09/11] 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 10/11] 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 11/11] 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: "