From 762604300f26afb5712d0ac92f01a57048a63f9e Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 29 Dec 2019 04:17:49 +0300 Subject: [PATCH 01/31] Refactor create_trades() --- freqtrade/freqtradebot.py | 104 ++++++++++++++++++++------------------ 1 file changed, 54 insertions(+), 50 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 90eb7beba..d15624e9f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -294,65 +294,48 @@ class FreqtradeBot: # See also #2575 at github. return max(min_stake_amounts) / amount_reserve_percent - def create_trades(self) -> bool: + def create_trade(self, pair: str) -> bool: """ - Checks the implemented trading strategy for buy-signals, using the active pair whitelist. - If a pair triggers the buy_signal a new trade record gets created. - Checks pairs as long as the open trade count is below `max_open_trades`. - :return: True if at least one trade has been created. + Check the implemented trading strategy for buy-signals. + If the pair triggers the buy_signal a new trade record gets created. + :return: True if a trade has been created. """ - whitelist = copy.deepcopy(self.active_pair_whitelist) + logger.debug(f"create_trade for pair {pair}") - if not whitelist: - logger.info("Active pair whitelist is empty.") + if self.strategy.is_pair_locked(pair): + logger.info(f"Pair {pair} is currently locked.") return False - # Remove currently opened and latest pairs from whitelist - for trade in Trade.get_open_trades(): - if trade.pair in whitelist: - whitelist.remove(trade.pair) - logger.debug('Ignoring %s in pair whitelist', trade.pair) - - if not whitelist: - logger.info("No currency pair in active pair whitelist, " - "but checking to sell open trades.") - return False - - 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.") - continue + (buy, sell) = self.strategy.get_signal( + pair, self.strategy.ticker_interval, + self.dataprovider.ohlcv(pair, self.strategy.ticker_interval)) - (buy, sell) = self.strategy.get_signal( - pair, self.strategy.ticker_interval, - self.dataprovider.ohlcv(pair, self.strategy.ticker_interval)) + if buy and not sell: + if not self.get_free_open_trades(): + logger.debug("Can't open a new trade: max number of trades is reached") + return False - if buy and not sell: - if not self.get_free_open_trades(): - 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.debug("Stake amount is 0, ignoring possible trade for {pair}.") + return False - stake_amount = self.get_trade_stake_amount(pair) - if not stake_amount: - 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: " + f"{stake_amount} ...") - logger.info(f"Buy signal found: about create a new trade with stake_amount: " - f"{stake_amount} ...") + bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\ + 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): + return self.execute_buy(pair, stake_amount) + else: + return False - bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\ - 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) - continue - - buycount += self.execute_buy(pair, stake_amount) - - return buycount > 0 + return self.execute_buy(pair, stake_amount) + else: + return False def _check_depth_of_market_buy(self, pair: str, conf: Dict) -> bool: """ @@ -479,10 +462,31 @@ class FreqtradeBot: """ Tries to execute buy orders for trades in a safe way """ + result = False try: - # Create entity and execute trade - if not self.create_trades(): + whitelist = copy.deepcopy(self.active_pair_whitelist) + + if not whitelist: + logger.info("Active pair whitelist is empty.") + else: + # Remove currently opened and latest pairs from whitelist + for trade in Trade.get_open_trades(): + if trade.pair in whitelist: + whitelist.remove(trade.pair) + logger.debug('Ignoring %s in pair whitelist', trade.pair) + + if not whitelist: + logger.info("No currency pair in active pair whitelist, " + "but checking to sell open trades.") + else: + # Create entity and execute trade for each pair from whitelist + for pair in whitelist: + if self.create_trade(pair): + result = True + + if not result: logger.debug('Found no buy signals for whitelisted currencies. Trying again...') + except DependencyException as exception: logger.warning('Unable to create trade: %s', exception) From ce84f74528de4ee06977967a859722b68a1f6b31 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 29 Dec 2019 04:38:28 +0300 Subject: [PATCH 02/31] Adjust tests --- tests/rpc/test_rpc.py | 22 ++--- tests/rpc/test_rpc_apiserver.py | 8 +- tests/rpc/test_rpc_telegram.py | 22 ++--- tests/test_freqtradebot.py | 151 ++++++++++++++++---------------- tests/test_integration.py | 4 +- 5 files changed, 105 insertions(+), 102 deletions(-) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 3b897572c..a9f73e98d 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -41,7 +41,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_trade_status() - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() results = rpc._rpc_trade_status() assert { 'trade_id': 1, @@ -116,7 +116,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: with pytest.raises(RPCException, match=r'.*no active order*'): rpc._rpc_status_table(default_conf['stake_currency'], 'USD') - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') assert "Since" in headers @@ -162,7 +162,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -217,7 +217,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) @@ -231,7 +231,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, trade.close_date = datetime.utcnow() trade.is_open = False - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) @@ -299,7 +299,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, rpc = RPC(freqtradebot) # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) @@ -529,7 +529,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: msg = rpc._rpc_forcesell('all') assert msg == {'result': 'Created sell orders for all open trades.'} - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() msg = rpc._rpc_forcesell('all') assert msg == {'result': 'Created sell orders for all open trades.'} @@ -563,7 +563,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: assert cancel_order_mock.call_count == 1 assert trade.amount == filled_amount - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.filter(Trade.id == '2').first() amount = trade.amount # make an limit-buy open trade, if there is no 'filled', don't sell it @@ -582,7 +582,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: assert cancel_order_mock.call_count == 2 assert trade.amount == amount - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() # make an limit-sell open trade mocker.patch( 'freqtrade.exchange.Exchange.get_order', @@ -613,7 +613,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, rpc = RPC(freqtradebot) # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -649,7 +649,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: assert counts["current"] == 0 # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() counts = rpc._rpc_count() assert counts["current"] == 1 diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 36bb81e41..94af85349 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -267,7 +267,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets): assert rc.json["max"] == 1.0 # Create some test data - ftbot.create_trades() + ftbot.process_maybe_execute_buys() rc = client_get(client, f"{BASE_URI}/count") assert_response(rc) assert rc.json["current"] == 1.0 @@ -333,7 +333,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li assert len(rc.json) == 1 assert rc.json == {"error": "Error querying _profit: no closed trade"} - ftbot.create_trades() + ftbot.process_maybe_execute_buys() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade @@ -422,7 +422,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): assert_response(rc, 200) assert rc.json == [] - ftbot.create_trades() + ftbot.process_maybe_execute_buys() rc = client_get(client, f"{BASE_URI}/status") assert_response(rc) assert len(rc.json) == 1 @@ -552,7 +552,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): assert_response(rc, 502) assert rc.json == {"error": "Error querying _forcesell: invalid argument"} - ftbot.create_trades() + ftbot.process_maybe_execute_buys() rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index f8b9ca8ab..d9a7e2f5b 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -189,7 +189,7 @@ def test_status(default_conf, update, mocker, fee, ticker,) -> None: # Create some test data for _ in range(3): - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() telegram._status(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -236,7 +236,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: msg_mock.reset_mock() # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() # Trigger status while we have a fulfilled order for the open trade telegram._status(update=update, context=MagicMock()) @@ -285,7 +285,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: msg_mock.reset_mock() # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() telegram._status_table(update=update, context=MagicMock()) @@ -322,7 +322,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -352,7 +352,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, msg_mock.reset_mock() freqtradebot.config['max_open_trades'] = 2 # Add two other trades - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trades = Trade.query.all() for trade in trades: @@ -431,7 +431,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, msg_mock.reset_mock() # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade @@ -709,7 +709,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee, telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -764,7 +764,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() # Decrease the price and sell it mocker.patch.multiple( @@ -821,7 +821,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() rpc_mock.reset_mock() # /forcesell all @@ -971,7 +971,7 @@ def test_performance_handle(default_conf, update, ticker, fee, telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -1014,7 +1014,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: freqtradebot.state = State.RUNNING # Create some test data - freqtradebot.create_trades() + freqtradebot.process_maybe_execute_buys() msg_mock.reset_mock() telegram._count(update=update, context=MagicMock()) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 2f8c786fd..be6a9b392 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -247,7 +247,7 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf freqtrade.active_pair_whitelist = ['NEO/BTC'] patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) ############################################# @@ -287,7 +287,7 @@ def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee, freqtrade.active_pair_whitelist = ['NEO/BTC'] patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) ############################################# @@ -310,7 +310,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker, ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade is not None @@ -318,7 +318,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker, assert trade.is_open assert trade.open_date is not None - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.order_by(Trade.id.desc()).first() assert trade is not None @@ -462,7 +462,7 @@ def test_get_min_pair_stake_amount_real_data(mocker, default_conf) -> None: assert round(result, 8) == round(max(0.0001, 0.001 * 0.020405) / 0.9, 8) -def test_create_trades(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_create_trade(default_conf, ticker, limit_buy_order, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -476,7 +476,7 @@ def test_create_trades(default_conf, ticker, limit_buy_order, fee, mocker) -> No whitelist = deepcopy(default_conf['exchange']['pair_whitelist']) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.create_trade('ETH/BTC') trade = Trade.query.first() assert trade is not None @@ -494,8 +494,8 @@ def test_create_trades(default_conf, ticker, limit_buy_order, fee, mocker) -> No assert whitelist == default_conf['exchange']['pair_whitelist'] -def test_create_trades_no_stake_amount(default_conf, ticker, limit_buy_order, - fee, mocker) -> None: +def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, + fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) patch_wallet(mocker, free=default_conf['stake_amount'] * 0.5) @@ -509,11 +509,11 @@ def test_create_trades_no_stake_amount(default_conf, ticker, limit_buy_order, patch_get_signal(freqtrade) with pytest.raises(DependencyException, match=r'.*stake amount.*'): - freqtrade.create_trades() + freqtrade.create_trade('ETH/BTC') -def test_create_trades_minimal_amount(default_conf, ticker, limit_buy_order, - fee, mocker) -> None: +def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, + fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) buy_mock = MagicMock(return_value={'id': limit_buy_order['id']}) @@ -527,13 +527,13 @@ def test_create_trades_minimal_amount(default_conf, ticker, limit_buy_order, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.create_trade('ETH/BTC') rate, amount = buy_mock.call_args[1]['rate'], buy_mock.call_args[1]['amount'] assert rate * amount >= default_conf['stake_amount'] -def test_create_trades_too_small_stake_amount(default_conf, ticker, limit_buy_order, - fee, mocker) -> None: +def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_order, + fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) buy_mock = MagicMock(return_value={'id': limit_buy_order['id']}) @@ -549,11 +549,11 @@ def test_create_trades_too_small_stake_amount(default_conf, ticker, limit_buy_or patch_get_signal(freqtrade) - assert not freqtrade.create_trades() + assert not freqtrade.create_trade('ETH/BTC') -def test_create_trades_limit_reached(default_conf, ticker, limit_buy_order, - fee, markets, mocker) -> None: +def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, + fee, markets, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -569,12 +569,13 @@ def test_create_trades_limit_reached(default_conf, ticker, limit_buy_order, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - assert not freqtrade.create_trades() + assert not freqtrade.create_trade('ETH/BTC') assert freqtrade.get_trade_stake_amount('ETH/BTC') is None -def test_create_trades_no_pairs_let(default_conf, ticker, limit_buy_order, fee, - mocker, caplog) -> None: +def test_process_maybe_execute_buys_no_pairs_let(default_conf, ticker, + limit_buy_order, fee, + mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -588,14 +589,15 @@ def test_create_trades_no_pairs_let(default_conf, ticker, limit_buy_order, fee, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - assert freqtrade.create_trades() - assert not freqtrade.create_trades() - assert log_has("No currency pair in active pair whitelist, " - "but checking to sell open trades.", caplog) + freqtrade.process_maybe_execute_buys() + assert not log_has_re(r"No currency pair in active pair whitelist.*", caplog) + freqtrade.process_maybe_execute_buys() + assert log_has_re(r"No currency pair in active pair whitelist.*", caplog) -def test_create_trades_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, - mocker, caplog) -> None: +def test_process_maybe_execute_buys_no_pairs_in_whitelist(default_conf, ticker, + limit_buy_order, fee, + mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -608,11 +610,11 @@ def test_create_trades_no_pairs_in_whitelist(default_conf, ticker, limit_buy_ord freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - assert not freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() assert log_has("Active pair whitelist is empty.", caplog) -def test_create_trades_no_signal(default_conf, fee, mocker) -> None: +def test_create_trade_no_signal(default_conf, fee, mocker) -> None: default_conf['dry_run'] = True patch_RPCManager(mocker) @@ -628,7 +630,7 @@ def test_create_trades_no_signal(default_conf, fee, mocker) -> None: Trade.query = MagicMock() Trade.query.filter = MagicMock() - assert not freqtrade.create_trades() + assert not freqtrade.create_trade('ETH/BTC') @pytest.mark.parametrize("max_open", range(0, 5)) @@ -646,7 +648,7 @@ def test_create_trades_multiple_trades(default_conf, ticker, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trades = Trade.get_open_trades() assert len(trades) == max_open @@ -672,7 +674,8 @@ def test_create_trades_preopen(default_conf, ticker, fee, mocker) -> None: assert len(Trade.get_open_trades()) == 2 # Create 2 new trades using create_trades - assert freqtrade.create_trades() + assert freqtrade.create_trade('ETH/BTC') + assert freqtrade.create_trade('NEO/BTC') trades = Trade.get_open_trades() assert len(trades) == 4 @@ -1056,7 +1059,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, # should unset stoploss_order_id and return true # as a trade actually happened caplog.clear() - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1114,7 +1117,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.is_open = True trade.open_order_id = '12345' @@ -1149,7 +1152,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, patch_get_signal(freqtrade) freqtrade.strategy.order_types['stoploss_on_exchange'] = True - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() caplog.clear() freqtrade.create_stoploss_order(trade, 200, 199) @@ -1207,7 +1210,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1295,7 +1298,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c # setting stoploss_on_exchange_interval to 60 seconds freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60 patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1376,7 +1379,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1444,7 +1447,7 @@ def test_process_maybe_execute_buys(mocker, default_conf, caplog) -> None: caplog.set_level(logging.DEBUG) freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trades', MagicMock(return_value=False)) + mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade', MagicMock(return_value=False)) freqtrade.process_maybe_execute_buys() assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) @@ -1453,7 +1456,7 @@ def test_process_maybe_execute_buys_exception(mocker, default_conf, caplog) -> N freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch( - 'freqtrade.freqtradebot.FreqtradeBot.create_trades', + 'freqtrade.freqtradebot.FreqtradeBot.create_trade', MagicMock(side_effect=DependencyException) ) freqtrade.process_maybe_execute_buys() @@ -1674,7 +1677,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -1711,7 +1714,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, patch_get_signal(freqtrade, value=(True, True)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() # Buy and Sell triggering, so doing nothing ... trades = Trade.query.all() @@ -1720,7 +1723,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, # Buy is triggering, so buying ... patch_get_signal(freqtrade, value=(True, False)) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trades = Trade.query.all() nb_trades = len(trades) assert nb_trades == 1 @@ -1764,7 +1767,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order, patch_get_signal(freqtrade, value=(True, False)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.is_open = True @@ -1795,7 +1798,7 @@ def test_handle_trade_use_sell_signal( freqtrade = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.is_open = True @@ -1823,7 +1826,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, patch_get_signal(freqtrade) # Create trade and sell it - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -2183,7 +2186,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N patch_get_signal(freqtrade) # Create some test data - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -2231,7 +2234,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) patch_get_signal(freqtrade) # Create some test data - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -2281,7 +2284,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe patch_get_signal(freqtrade) # Create some test data - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -2339,7 +2342,7 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c freqtrade.strategy.order_types['stoploss_on_exchange'] = True patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() Trade.session = MagicMock() @@ -2382,7 +2385,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke patch_get_signal(freqtrade) # Create some test data - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -2432,7 +2435,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f patch_get_signal(freqtrade) # Create some test data - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trades = [trade] freqtrade.process_maybe_execute_sells(trades) @@ -2484,7 +2487,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee, patch_get_signal(freqtrade) # Create some test data - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -2546,7 +2549,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2577,7 +2580,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2608,7 +2611,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker patch_get_signal(freqtrade) freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple( sell_flag=False, sell_type=SellType.NONE)) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2638,7 +2641,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2667,7 +2670,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() amnt = trade.amount @@ -2735,7 +2738,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo patch_get_signal(freqtrade) # Create some test data - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -2754,7 +2757,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo # reinit - should buy other pair. caplog.clear() - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() assert log_has(f"Pair {trade.pair} is currently locked.", caplog) @@ -2779,7 +2782,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, mocker) -> patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2812,7 +2815,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, caplog, mocker) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert freqtrade.handle_trade(trade) is False @@ -2867,7 +2870,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2924,7 +2927,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2987,7 +2990,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -3046,7 +3049,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trade.update(limit_buy_order) @@ -3377,7 +3380,7 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order, fee, whitelist = deepcopy(default_conf['exchange']['pair_whitelist']) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade is not None @@ -3412,7 +3415,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o # Save state of current whitelist freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade is None @@ -3514,7 +3517,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trade = Trade.query.first() assert trade @@ -3604,7 +3607,7 @@ def test_process_i_am_alive(default_conf, mocker, caplog): @pytest.mark.usefixtures("init_persistence") -def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order): +def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order, caplog): default_conf['dry_run'] = True # Initialize to 2 times stake amount default_conf['dry_run_wallet'] = 0.002 @@ -3621,12 +3624,12 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order) patch_get_signal(bot) assert bot.wallets.get_free('BTC') == 0.002 - bot.create_trades() + bot.process_maybe_execute_buys() trades = Trade.query.all() assert len(trades) == 2 bot.config['max_open_trades'] = 3 - with pytest.raises( - DependencyException, - match=r"Available balance \(0 BTC\) is lower than stake amount \(0.001 BTC\)"): - bot.create_trades() + bot.process_maybe_execute_buys() + assert log_has_re(r"Unable to create trade: " + r"Available balance \(0 BTC\) is lower than stake amount \(0.001 BTC\)", + caplog) diff --git a/tests/test_integration.py b/tests/test_integration.py index 11dbca225..2c95bc6e3 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -80,7 +80,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, patch_get_signal(freqtrade) # Create some test data - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() wallets_mock.reset_mock() Trade.session = MagicMock() @@ -153,7 +153,7 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc patch_get_signal(freqtrade) # Create 4 trades - freqtrade.create_trades() + freqtrade.process_maybe_execute_buys() trades = Trade.query.all() assert len(trades) == 4 From 04f28ed9bcd337e6f0dbdddd0c1d51f62e67c613 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Sun, 29 Dec 2019 05:03:10 +0300 Subject: [PATCH 03/31] Refactor try/except: handle DependencyException for each pair separately --- freqtrade/freqtradebot.py | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d15624e9f..e47a4a85c 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -463,32 +463,32 @@ class FreqtradeBot: Tries to execute buy orders for trades in a safe way """ result = False - try: - whitelist = copy.deepcopy(self.active_pair_whitelist) + + whitelist = copy.deepcopy(self.active_pair_whitelist) + if not whitelist: + logger.info("Active pair whitelist is empty.") + else: + # Remove currently opened and latest pairs from whitelist + for trade in Trade.get_open_trades(): + if trade.pair in whitelist: + whitelist.remove(trade.pair) + logger.debug('Ignoring %s in pair whitelist', trade.pair) if not whitelist: - logger.info("Active pair whitelist is empty.") + logger.info("No currency pair in active pair whitelist, " + "but checking to sell open trades.") else: - # Remove currently opened and latest pairs from whitelist - for trade in Trade.get_open_trades(): - if trade.pair in whitelist: - whitelist.remove(trade.pair) - logger.debug('Ignoring %s in pair whitelist', trade.pair) - - if not whitelist: - logger.info("No currency pair in active pair whitelist, " - "but checking to sell open trades.") - else: - # Create entity and execute trade for each pair from whitelist - for pair in whitelist: + # Create entity and execute trade for each pair from whitelist + for pair in whitelist: + try: if self.create_trade(pair): result = True + except DependencyException as exception: + logger.warning('Unable to create trade: %s', exception) - if not result: - logger.debug('Found no buy signals for whitelisted currencies. Trying again...') - - except DependencyException as exception: - logger.warning('Unable to create trade: %s', exception) + if not result: + logger.debug("Found no buy signals for whitelisted currencies. " + "Trying again...") def process_maybe_execute_sells(self, trades: List[Any]) -> None: """ From 4c9295fe2d657fdf373601ea8d2350fb1990ebc8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 30 Dec 2019 14:00:34 +0100 Subject: [PATCH 04/31] Rename Bid-strategy helpervariable to something shorter avoids unnecessary wrapping... --- freqtrade/freqtradebot.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e47a4a85c..68091ed76 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -313,7 +313,7 @@ class FreqtradeBot: if buy and not sell: if not self.get_free_open_trades(): - logger.debug("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.") return False stake_amount = self.get_trade_stake_amount(pair) @@ -324,11 +324,10 @@ class FreqtradeBot: logger.info(f"Buy signal found: about create a new trade with stake_amount: " f"{stake_amount} ...") - bidstrat_check_depth_of_market = self.config.get('bid_strategy', {}).\ - 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): + bid_check_dom = self.config.get('bid_strategy', {}).get('check_depth_of_market', {}) + if ((bid_check_dom.get('enabled', False)) and + (bid_check_dom.get('bids_to_ask_delta', 0) > 0)): + if self._check_depth_of_market_buy(pair, bid_check_dom): return self.execute_buy(pair, stake_amount) else: return False From 4d56e3b36e02d2fb8bd41bde5cbf0fabc0cd7a28 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 30 Dec 2019 20:54:32 +0300 Subject: [PATCH 05/31] Address some comments made in the review --- freqtrade/freqtradebot.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 68091ed76..3bfa74005 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -296,8 +296,11 @@ class FreqtradeBot: def create_trade(self, pair: str) -> bool: """ - Check the implemented trading strategy for buy-signals. - If the pair triggers the buy_signal a new trade record gets created. + Check the implemented trading strategy for buy signals. + + If the pair triggers the buy signal a new trade record gets created + and the buy-order opening the trade gets issued towards the exchange. + :return: True if a trade has been created. """ logger.debug(f"create_trade for pair {pair}") @@ -467,7 +470,7 @@ class FreqtradeBot: if not whitelist: logger.info("Active pair whitelist is empty.") else: - # Remove currently opened and latest pairs from whitelist + # Remove pairs for currently opened trades from the whitelist for trade in Trade.get_open_trades(): if trade.pair in whitelist: whitelist.remove(trade.pair) From b00406a7eb0e73f2b104598441958782d4c95497 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 30 Dec 2019 21:09:35 +0300 Subject: [PATCH 06/31] Make process_maybe_execute_*() returning integers --- freqtrade/freqtradebot.py | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 3bfa74005..e6f51e5fa 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -460,11 +460,11 @@ class FreqtradeBot: return True - def process_maybe_execute_buys(self) -> None: + def process_maybe_execute_buys(self) -> int: """ Tries to execute buy orders for trades in a safe way """ - result = False + trades_created = 0 whitelist = copy.deepcopy(self.active_pair_whitelist) if not whitelist: @@ -483,39 +483,42 @@ class FreqtradeBot: # Create entity and execute trade for each pair from whitelist for pair in whitelist: try: - if self.create_trade(pair): - result = True + trades_created += self.create_trade(pair) except DependencyException as exception: logger.warning('Unable to create trade: %s', exception) - if not result: + if not trades_created: logger.debug("Found no buy signals for whitelisted currencies. " "Trying again...") - def process_maybe_execute_sells(self, trades: List[Any]) -> None: + return trades_created + + def process_maybe_execute_sells(self, trades: List[Any]) -> int: """ Tries to execute sell orders for trades in a safe way """ - result = False + trades_closed = 0 for trade in trades: try: self.update_trade_state(trade) if (self.strategy.order_types.get('stoploss_on_exchange') and self.handle_stoploss_on_exchange(trade)): - result = True + trades_closed += 1 continue # Check if we can sell our current pair if trade.open_order_id is None and self.handle_trade(trade): - result = True + trades_closed += 1 except DependencyException as exception: logger.warning('Unable to sell trade: %s', exception) # Updating wallets if any trade occured - if result: + if trades_closed: self.wallets.update() + return trades_closed + def get_real_amount(self, trade: Trade, order: Dict, order_amount: float = None) -> float: """ Get real amount for the trade From 84918ad42481a7558bd935a1639b29de5f317931 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 30 Dec 2019 22:08:36 +0300 Subject: [PATCH 07/31] Rename process_maybe_execute_sells() --> exit_positions() --- freqtrade/freqtradebot.py | 8 ++++---- tests/test_freqtradebot.py | 22 ++++++++++++---------- tests/test_integration.py | 6 ++++-- 3 files changed, 20 insertions(+), 16 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e6f51e5fa..695302bf9 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -132,8 +132,8 @@ class FreqtradeBot: self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist), self.strategy.informative_pairs()) - # First process current opened trades - self.process_maybe_execute_sells(trades) + # First process current opened trades (positions) + self.exit_positions(trades) # Then looking for buy opportunities if self.get_free_open_trades(): @@ -493,9 +493,9 @@ class FreqtradeBot: return trades_created - def process_maybe_execute_sells(self, trades: List[Any]) -> int: + def exit_positions(self, trades: List[Any]) -> int: """ - Tries to execute sell orders for trades in a safe way + Tries to execute sell orders for open trades (positions) """ trades_closed = 0 for trade in trades: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index be6a9b392..22110dd1c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -990,7 +990,7 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None trade.is_open = True trades = [trade] - freqtrade.process_maybe_execute_sells(trades) + freqtrade.exit_positions(trades) assert trade.stoploss_order_id == '13434334' assert stoploss_limit.call_count == 1 assert trade.is_open is True @@ -1463,7 +1463,7 @@ def test_process_maybe_execute_buys_exception(mocker, default_conf, caplog) -> N assert log_has('Unable to create trade: ', caplog) -def test_process_maybe_execute_sells(mocker, default_conf, limit_buy_order, caplog) -> None: +def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True)) @@ -1476,7 +1476,8 @@ def test_process_maybe_execute_sells(mocker, default_conf, limit_buy_order, capl trade.open_order_id = '123' trade.open_fee = 0.001 trades = [trade] - assert not freqtrade.process_maybe_execute_sells(trades) + n = freqtrade.exit_positions(trades) + assert n == 0 # Test amount not modified by fee-logic assert not log_has( 'Applying fee to amount for Trade {} from 90.99181073 to 90.81'.format(trade), caplog @@ -1484,11 +1485,11 @@ def test_process_maybe_execute_sells(mocker, default_conf, limit_buy_order, capl mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81) # test amount modified by fee-logic - assert not freqtrade.process_maybe_execute_sells(trades) + n = freqtrade.exit_positions(trades) + assert n == 0 -def test_process_maybe_execute_sells_exception(mocker, default_conf, - limit_buy_order, caplog) -> None: +def test_exit_positions_exception(mocker, default_conf, limit_buy_order, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.get_order', return_value=limit_buy_order) @@ -1502,7 +1503,8 @@ def test_process_maybe_execute_sells_exception(mocker, default_conf, 'freqtrade.freqtradebot.FreqtradeBot.update_trade_state', side_effect=DependencyException() ) - freqtrade.process_maybe_execute_sells(trades) + n = freqtrade.exit_positions(trades) + assert n == 0 assert log_has('Unable to sell trade: ', caplog) @@ -2391,7 +2393,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke assert trade trades = [trade] - freqtrade.process_maybe_execute_sells(trades) + freqtrade.exit_positions(trades) # Increase the price and sell it mocker.patch.multiple( @@ -2438,7 +2440,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f freqtrade.process_maybe_execute_buys() trade = Trade.query.first() trades = [trade] - freqtrade.process_maybe_execute_sells(trades) + freqtrade.exit_positions(trades) assert trade assert trade.stoploss_order_id == '123' assert trade.open_order_id is None @@ -2466,7 +2468,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f }) mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_limit_executed) - freqtrade.process_maybe_execute_sells(trades) + freqtrade.exit_positions(trades) assert trade.stoploss_order_id is None assert trade.is_open is False assert trade.sell_reason == SellType.STOPLOSS_ON_EXCHANGE.value diff --git a/tests/test_integration.py b/tests/test_integration.py index 2c95bc6e3..a73beeabe 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -90,7 +90,8 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, trade.stoploss_order_id = 3 trade.open_order_id = None - freqtrade.process_maybe_execute_sells(trades) + n = freqtrade.exit_positions(trades) + assert n == 2 assert should_sell_mock.call_count == 2 # Only order for 3rd trade needs to be cancelled @@ -170,7 +171,8 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc assert len(trades) == 5 bals = freqtrade.wallets.get_all_balances() - freqtrade.process_maybe_execute_sells(trades) + n = freqtrade.exit_positions(trades) + assert n == 1 trades = Trade.get_open_trades() # One trade sold assert len(trades) == 4 From fd7af587da29851de854fdd2be7a7d697cdcdbe2 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 30 Dec 2019 22:50:56 +0300 Subject: [PATCH 08/31] Rename process_maybe_execute_buys() --> enter_positions() --- freqtrade/freqtradebot.py | 6 +- tests/rpc/test_rpc.py | 22 +++--- tests/rpc/test_rpc_apiserver.py | 8 +-- tests/rpc/test_rpc_telegram.py | 25 +++---- tests/test_freqtradebot.py | 118 +++++++++++++++++--------------- tests/test_integration.py | 5 +- 6 files changed, 96 insertions(+), 88 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 695302bf9..c595150ac 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -137,7 +137,7 @@ class FreqtradeBot: # Then looking for buy opportunities if self.get_free_open_trades(): - self.process_maybe_execute_buys() + self.enter_positions() # Check and handle any timed out open orders self.check_handle_timedout() @@ -460,9 +460,9 @@ class FreqtradeBot: return True - def process_maybe_execute_buys(self) -> int: + def enter_positions(self) -> int: """ - Tries to execute buy orders for trades in a safe way + Tries to execute buy orders for new trades (positions) """ trades_created = 0 diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index a9f73e98d..3581e3d18 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -41,7 +41,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_trade_status() - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() results = rpc._rpc_trade_status() assert { 'trade_id': 1, @@ -116,7 +116,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: with pytest.raises(RPCException, match=r'.*no active order*'): rpc._rpc_status_table(default_conf['stake_currency'], 'USD') - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() result, headers = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') assert "Since" in headers @@ -162,7 +162,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee, rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter() # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() assert trade @@ -217,7 +217,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) @@ -231,7 +231,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, trade.close_date = datetime.utcnow() trade.is_open = False - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) @@ -299,7 +299,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, rpc = RPC(freqtradebot) # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade trade.update(limit_buy_order) @@ -529,7 +529,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: msg = rpc._rpc_forcesell('all') assert msg == {'result': 'Created sell orders for all open trades.'} - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() msg = rpc._rpc_forcesell('all') assert msg == {'result': 'Created sell orders for all open trades.'} @@ -563,7 +563,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: assert cancel_order_mock.call_count == 1 assert trade.amount == filled_amount - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.filter(Trade.id == '2').first() amount = trade.amount # make an limit-buy open trade, if there is no 'filled', don't sell it @@ -582,7 +582,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None: assert cancel_order_mock.call_count == 2 assert trade.amount == amount - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() # make an limit-sell open trade mocker.patch( 'freqtrade.exchange.Exchange.get_order', @@ -613,7 +613,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee, rpc = RPC(freqtradebot) # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() assert trade @@ -649,7 +649,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None: assert counts["current"] == 0 # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() counts = rpc._rpc_count() assert counts["current"] == 1 diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 94af85349..25c971bf7 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -267,7 +267,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets): assert rc.json["max"] == 1.0 # Create some test data - ftbot.process_maybe_execute_buys() + ftbot.enter_positions() rc = client_get(client, f"{BASE_URI}/count") assert_response(rc) assert rc.json["current"] == 1.0 @@ -333,7 +333,7 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, limit_buy_order, li assert len(rc.json) == 1 assert rc.json == {"error": "Error querying _profit: no closed trade"} - ftbot.process_maybe_execute_buys() + ftbot.enter_positions() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade @@ -422,7 +422,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets): assert_response(rc, 200) assert rc.json == [] - ftbot.process_maybe_execute_buys() + ftbot.enter_positions() rc = client_get(client, f"{BASE_URI}/status") assert_response(rc) assert len(rc.json) == 1 @@ -552,7 +552,7 @@ def test_api_forcesell(botclient, mocker, ticker, fee, markets): assert_response(rc, 502) assert rc.json == {"error": "Error querying _forcesell: invalid argument"} - ftbot.process_maybe_execute_buys() + ftbot.enter_positions() rc = client_post(client, f"{BASE_URI}/forcesell", data='{"tradeid": "1"}') diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index d9a7e2f5b..44e51514e 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -188,8 +188,8 @@ def test_status(default_conf, update, mocker, fee, ticker,) -> None: telegram = Telegram(freqtradebot) # Create some test data - for _ in range(3): - freqtradebot.process_maybe_execute_buys() + n = freqtradebot.enter_positions() + assert n == 1 telegram._status(update=update, context=MagicMock()) assert msg_mock.call_count == 1 @@ -236,7 +236,7 @@ def test_status_handle(default_conf, update, ticker, fee, mocker) -> None: msg_mock.reset_mock() # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() # Trigger status while we have a fulfilled order for the open trade telegram._status(update=update, context=MagicMock()) @@ -285,7 +285,7 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None: msg_mock.reset_mock() # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() telegram._status_table(update=update, context=MagicMock()) @@ -322,7 +322,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() assert trade @@ -352,7 +352,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, msg_mock.reset_mock() freqtradebot.config['max_open_trades'] = 2 # Add two other trades - freqtradebot.process_maybe_execute_buys() + n = freqtradebot.enter_positions() + assert n == 2 trades = Trade.query.all() for trade in trades: @@ -431,7 +432,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, msg_mock.reset_mock() # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() # Simulate fulfilled LIMIT_BUY order for trade @@ -709,7 +710,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee, telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() assert trade @@ -764,7 +765,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() # Decrease the price and sell it mocker.patch.multiple( @@ -821,7 +822,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() rpc_mock.reset_mock() # /forcesell all @@ -971,7 +972,7 @@ def test_performance_handle(default_conf, update, ticker, fee, telegram = Telegram(freqtradebot) # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() trade = Trade.query.first() assert trade @@ -1014,7 +1015,7 @@ def test_count_handle(default_conf, update, ticker, fee, mocker) -> None: freqtradebot.state = State.RUNNING # Create some test data - freqtradebot.process_maybe_execute_buys() + freqtradebot.enter_positions() msg_mock.reset_mock() telegram._count(update=update, context=MagicMock()) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 22110dd1c..f99b14286 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -247,7 +247,7 @@ def test_edge_overrides_stoploss(limit_buy_order, fee, caplog, mocker, edge_conf freqtrade.active_pair_whitelist = ['NEO/BTC'] patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) ############################################# @@ -287,7 +287,7 @@ def test_edge_should_ignore_strategy_stoploss(limit_buy_order, fee, freqtrade.active_pair_whitelist = ['NEO/BTC'] patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) ############################################# @@ -310,7 +310,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker, ) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade is not None @@ -318,7 +318,7 @@ def test_total_open_trades_stakes(mocker, default_conf, ticker, assert trade.is_open assert trade.open_date is not None - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.order_by(Trade.id.desc()).first() assert trade is not None @@ -573,9 +573,8 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order, assert freqtrade.get_trade_stake_amount('ETH/BTC') is None -def test_process_maybe_execute_buys_no_pairs_let(default_conf, ticker, - limit_buy_order, fee, - mocker, caplog) -> None: +def test_enter_positions_no_pairs_left(default_conf, ticker, limit_buy_order, fee, + mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -589,15 +588,16 @@ def test_process_maybe_execute_buys_no_pairs_let(default_conf, ticker, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + n = freqtrade.enter_positions() + assert n == 1 assert not log_has_re(r"No currency pair in active pair whitelist.*", caplog) - freqtrade.process_maybe_execute_buys() + n = freqtrade.enter_positions() + assert n == 0 assert log_has_re(r"No currency pair in active pair whitelist.*", caplog) -def test_process_maybe_execute_buys_no_pairs_in_whitelist(default_conf, ticker, - limit_buy_order, fee, - mocker, caplog) -> None: +def test_enter_positions_no_pairs_in_whitelist(default_conf, ticker, limit_buy_order, fee, + mocker, caplog) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -610,7 +610,8 @@ def test_process_maybe_execute_buys_no_pairs_in_whitelist(default_conf, ticker, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + n = freqtrade.enter_positions() + assert n == 0 assert log_has("Active pair whitelist is empty.", caplog) @@ -648,7 +649,8 @@ def test_create_trades_multiple_trades(default_conf, ticker, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + n = freqtrade.enter_positions() + assert n == max_open trades = Trade.get_open_trades() assert len(trades) == max_open @@ -1059,7 +1061,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog, # should unset stoploss_order_id and return true # as a trade actually happened caplog.clear() - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1117,7 +1119,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.is_open = True trade.open_order_id = '12345' @@ -1152,7 +1154,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee, patch_get_signal(freqtrade) freqtrade.strategy.order_types['stoploss_on_exchange'] = True - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() caplog.clear() freqtrade.create_stoploss_order(trade, 200, 199) @@ -1210,7 +1212,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1298,7 +1300,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c # setting stoploss_on_exchange_interval to 60 seconds freqtrade.strategy.order_types['stoploss_on_exchange_interval'] = 60 patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1379,7 +1381,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, freqtrade.active_pair_whitelist = freqtrade.edge.adjust(freqtrade.active_pair_whitelist) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.is_open = True trade.open_order_id = None @@ -1443,23 +1445,25 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, stop_price=0.00002344 * 0.99) -def test_process_maybe_execute_buys(mocker, default_conf, caplog) -> None: +def test_enter_positions(mocker, default_conf, caplog) -> None: caplog.set_level(logging.DEBUG) freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade', MagicMock(return_value=False)) - freqtrade.process_maybe_execute_buys() + n = freqtrade.enter_positions() + assert n == 0 assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) -def test_process_maybe_execute_buys_exception(mocker, default_conf, caplog) -> None: +def test_enter_positions_exception(mocker, default_conf, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch( 'freqtrade.freqtradebot.FreqtradeBot.create_trade', MagicMock(side_effect=DependencyException) ) - freqtrade.process_maybe_execute_buys() + n = freqtrade.enter_positions() + assert n == 0 assert log_has('Unable to create trade: ', caplog) @@ -1679,7 +1683,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -1702,7 +1706,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mock assert trade.close_date is not None -def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, mocker) -> None: +def test_handle_overlapping_signals(default_conf, ticker, limit_buy_order, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( @@ -1716,7 +1720,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, patch_get_signal(freqtrade, value=(True, True)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() # Buy and Sell triggering, so doing nothing ... trades = Trade.query.all() @@ -1725,7 +1729,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, fee, # Buy is triggering, so buying ... patch_get_signal(freqtrade, value=(True, False)) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trades = Trade.query.all() nb_trades = len(trades) assert nb_trades == 1 @@ -1769,7 +1773,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order, patch_get_signal(freqtrade, value=(True, False)) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.is_open = True @@ -1800,7 +1804,7 @@ def test_handle_trade_use_sell_signal( freqtrade = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.is_open = True @@ -1828,7 +1832,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, patch_get_signal(freqtrade) # Create trade and sell it - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -2188,7 +2192,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> N patch_get_signal(freqtrade) # Create some test data - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -2236,7 +2240,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) patch_get_signal(freqtrade) # Create some test data - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -2286,7 +2290,7 @@ def test_execute_sell_down_stoploss_on_exchange_dry_run(default_conf, ticker, fe patch_get_signal(freqtrade) # Create some test data - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -2344,7 +2348,7 @@ def test_execute_sell_sloe_cancel_exception(mocker, default_conf, ticker, fee, c freqtrade.strategy.order_types['stoploss_on_exchange'] = True patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() Trade.session = MagicMock() @@ -2387,7 +2391,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke patch_get_signal(freqtrade) # Create some test data - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -2437,7 +2441,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f patch_get_signal(freqtrade) # Create some test data - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trades = [trade] freqtrade.exit_positions(trades) @@ -2489,7 +2493,7 @@ def test_execute_sell_market_order(default_conf, ticker, fee, patch_get_signal(freqtrade) # Create some test data - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -2551,7 +2555,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2582,7 +2586,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2613,7 +2617,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, mocker patch_get_signal(freqtrade) freqtrade.strategy.stop_loss_reached = MagicMock(return_value=SellCheckTuple( sell_flag=False, sell_type=SellType.NONE)) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2643,7 +2647,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, mocke patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2672,7 +2676,7 @@ def test_sell_not_enough_balance(default_conf, limit_buy_order, patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() amnt = trade.amount @@ -2740,7 +2744,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo patch_get_signal(freqtrade) # Create some test data - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -2759,7 +2763,7 @@ def test_locked_pairs(default_conf, ticker, fee, ticker_sell_down, mocker, caplo # reinit - should buy other pair. caplog.clear() - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() assert log_has(f"Pair {trade.pair} is currently locked.", caplog) @@ -2784,7 +2788,7 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, mocker) -> patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2817,7 +2821,7 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, caplog, mocker) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert freqtrade.handle_trade(trade) is False @@ -2872,7 +2876,7 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2929,7 +2933,7 @@ def test_trailing_stop_loss_offset(default_conf, limit_buy_order, fee, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -2992,7 +2996,7 @@ def test_tsl_only_offset_reached(default_conf, limit_buy_order, fee, freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=False) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -3051,7 +3055,7 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order, patch_get_signal(freqtrade) freqtrade.strategy.min_roi_reached = MagicMock(return_value=True) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() trade.update(limit_buy_order) @@ -3382,7 +3386,7 @@ def test_order_book_depth_of_market(default_conf, ticker, limit_buy_order, fee, whitelist = deepcopy(default_conf['exchange']['pair_whitelist']) freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade is not None @@ -3417,7 +3421,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o # Save state of current whitelist freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade is None @@ -3519,7 +3523,7 @@ def test_order_book_ask_strategy(default_conf, limit_buy_order, limit_sell_order freqtrade = FreqtradeBot(default_conf) patch_get_signal(freqtrade) - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() trade = Trade.query.first() assert trade @@ -3626,12 +3630,14 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order, patch_get_signal(bot) assert bot.wallets.get_free('BTC') == 0.002 - bot.process_maybe_execute_buys() + n = bot.enter_positions() + assert n == 2 trades = Trade.query.all() assert len(trades) == 2 bot.config['max_open_trades'] = 3 - bot.process_maybe_execute_buys() + n = bot.enter_positions() + assert n == 0 assert log_has_re(r"Unable to create trade: " r"Available balance \(0 BTC\) is lower than stake amount \(0.001 BTC\)", caplog) diff --git a/tests/test_integration.py b/tests/test_integration.py index a73beeabe..1108969ad 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -80,7 +80,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee, patch_get_signal(freqtrade) # Create some test data - freqtrade.process_maybe_execute_buys() + freqtrade.enter_positions() wallets_mock.reset_mock() Trade.session = MagicMock() @@ -154,7 +154,8 @@ def test_forcebuy_last_unlimited(default_conf, ticker, fee, limit_buy_order, moc patch_get_signal(freqtrade) # Create 4 trades - freqtrade.process_maybe_execute_buys() + n = freqtrade.enter_positions() + assert n == 4 trades = Trade.query.all() assert len(trades) == 4 From a88464de3a5ffb4c6802040c6463d4079c5dc868 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 31 Dec 2019 07:01:58 +0100 Subject: [PATCH 09/31] Improve some test code --- tests/rpc/test_rpc_telegram.py | 10 ---------- tests/test_freqtradebot.py | 3 ++- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 44e51514e..9a39f2119 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -148,11 +148,6 @@ def test_status(default_conf, update, mocker, fee, ticker,) -> None: default_conf['telegram']['enabled'] = False default_conf['telegram']['chat_id'] = "123" - mocker.patch.multiple( - 'freqtrade.exchange.Exchange', - fetch_ticker=ticker, - get_fee=fee, - ) msg_mock = MagicMock() status_table = MagicMock() mocker.patch.multiple( @@ -184,13 +179,8 @@ def test_status(default_conf, update, mocker, fee, ticker,) -> None: ) freqtradebot = get_patched_freqtradebot(mocker, default_conf) - patch_get_signal(freqtradebot, (True, False)) telegram = Telegram(freqtradebot) - # Create some test data - n = freqtradebot.enter_positions() - assert n == 1 - telegram._status(update=update, context=MagicMock()) assert msg_mock.call_count == 1 diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index f99b14286..434633f5f 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1458,12 +1458,13 @@ def test_enter_positions(mocker, default_conf, caplog) -> None: def test_enter_positions_exception(mocker, default_conf, caplog) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch( + mock_ct = mocker.patch( 'freqtrade.freqtradebot.FreqtradeBot.create_trade', MagicMock(side_effect=DependencyException) ) n = freqtrade.enter_positions() assert n == 0 + assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) assert log_has('Unable to create trade: ', caplog) From 6ebb9017c7bc0b0d39d8f83d663a7218d776c48a Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 31 Dec 2019 07:03:57 +0100 Subject: [PATCH 10/31] Improve test enter_positions --- tests/test_freqtradebot.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 434633f5f..812ba4e3e 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1449,10 +1449,12 @@ def test_enter_positions(mocker, default_conf, caplog) -> None: caplog.set_level(logging.DEBUG) freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade', MagicMock(return_value=False)) + mock_ct = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trade', + MagicMock(return_value=False)) n = freqtrade.enter_positions() assert n == 0 assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) + assert mock_ct.call_count == 4 def test_enter_positions_exception(mocker, default_conf, caplog) -> None: From 9d518b9d29cdcf90ccdc8efc4117ccff0ba5c380 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 31 Dec 2019 07:05:21 +0100 Subject: [PATCH 11/31] Add comment and don't hardcode 4 in test --- tests/test_freqtradebot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 812ba4e3e..6527554be 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1454,7 +1454,8 @@ def test_enter_positions(mocker, default_conf, caplog) -> None: n = freqtrade.enter_positions() assert n == 0 assert log_has('Found no buy signals for whitelisted currencies. Trying again...', caplog) - assert mock_ct.call_count == 4 + # create_trade should be called once for every pair in the whitelist. + assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) def test_enter_positions_exception(mocker, default_conf, caplog) -> None: From 26a2395aebd0b1ed39aaadb63e674dcf64a592bc Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 31 Dec 2019 07:11:09 +0100 Subject: [PATCH 12/31] Include Pair name in exception log message --- freqtrade/freqtradebot.py | 2 +- tests/test_freqtradebot.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c595150ac..a642e6ea7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -485,7 +485,7 @@ class FreqtradeBot: try: trades_created += self.create_trade(pair) except DependencyException as exception: - logger.warning('Unable to create trade: %s', exception) + logger.warning('Unable to create trade for %s: %s', pair, exception) if not trades_created: logger.debug("Found no buy signals for whitelisted currencies. " diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 6527554be..cf395ef39 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1468,7 +1468,7 @@ def test_enter_positions_exception(mocker, default_conf, caplog) -> None: n = freqtrade.enter_positions() assert n == 0 assert mock_ct.call_count == len(default_conf['exchange']['pair_whitelist']) - assert log_has('Unable to create trade: ', caplog) + assert log_has('Unable to create trade for ETH/BTC: ', caplog) def test_exit_positions(mocker, default_conf, limit_buy_order, caplog) -> None: @@ -3642,6 +3642,6 @@ def test_sync_wallet_dry_run(mocker, default_conf, ticker, fee, limit_buy_order, bot.config['max_open_trades'] = 3 n = bot.enter_positions() assert n == 0 - assert log_has_re(r"Unable to create trade: " + assert log_has_re(r"Unable to create trade for XRP/BTC: " r"Available balance \(0 BTC\) is lower than stake amount \(0.001 BTC\)", caplog) From 0ea44b014347d3541bb1b125ee1ebda88e8464f7 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 2 Jan 2020 02:36:59 +0300 Subject: [PATCH 13/31] Fix message in setup.sh --- setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.sh b/setup.sh index c4b6e074a..fb5102e12 100755 --- a/setup.sh +++ b/setup.sh @@ -263,7 +263,7 @@ function install() { echo "-------------------------" echo "Run the bot !" echo "-------------------------" - echo "You can now use the bot by executing 'source .env/bin/activate; freqtrade'." + echo "You can now use the bot by executing 'source .env/bin/activate; freqtrade trade'." } function plot() { From 4475110df85265cd2ba1172ebe3bf9df592addaf Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 2 Jan 2020 02:53:25 +0300 Subject: [PATCH 14/31] Cosmetics in freqtradebot --- freqtrade/freqtradebot.py | 73 ++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c4991a31c..4a207b087 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -27,6 +27,7 @@ from freqtrade.state import State from freqtrade.strategy.interface import IStrategy, SellType from freqtrade.wallets import Wallets + logger = logging.getLogger(__name__) @@ -181,6 +182,43 @@ class FreqtradeBot: open_trades = len(Trade.get_open_trades()) return max(0, self.config['max_open_trades'] - open_trades) +# +# BUY / enter positions / open trades part +# + + def enter_positions(self) -> int: + """ + Tries to execute buy orders for new trades (positions) + """ + trades_created = 0 + + whitelist = copy.deepcopy(self.active_pair_whitelist) + if not whitelist: + logger.info("Active pair whitelist is empty.") + else: + # Remove pairs for currently opened trades from the whitelist + for trade in Trade.get_open_trades(): + if trade.pair in whitelist: + whitelist.remove(trade.pair) + logger.debug('Ignoring %s in pair whitelist', trade.pair) + + if not whitelist: + logger.info("No currency pair in active pair whitelist, " + "but checking to sell open trades.") + else: + # Create entity and execute trade for each pair from whitelist + for pair in whitelist: + try: + trades_created += self.create_trade(pair) + except DependencyException as exception: + logger.warning('Unable to create trade for %s: %s', pair, exception) + + if not trades_created: + logger.debug("Found no buy signals for whitelisted currencies. " + "Trying again...") + + return trades_created + def get_target_bid(self, pair: str, tick: Dict = None) -> float: """ Calculates bid target between current ask price and last price @@ -460,38 +498,9 @@ class FreqtradeBot: return True - def enter_positions(self) -> int: - """ - Tries to execute buy orders for new trades (positions) - """ - trades_created = 0 - - whitelist = copy.deepcopy(self.active_pair_whitelist) - if not whitelist: - logger.info("Active pair whitelist is empty.") - else: - # Remove pairs for currently opened trades from the whitelist - for trade in Trade.get_open_trades(): - if trade.pair in whitelist: - whitelist.remove(trade.pair) - logger.debug('Ignoring %s in pair whitelist', trade.pair) - - if not whitelist: - logger.info("No currency pair in active pair whitelist, " - "but checking to sell open trades.") - else: - # Create entity and execute trade for each pair from whitelist - for pair in whitelist: - try: - trades_created += self.create_trade(pair) - except DependencyException as exception: - logger.warning('Unable to create trade for %s: %s', pair, exception) - - if not trades_created: - logger.debug("Found no buy signals for whitelisted currencies. " - "Trying again...") - - return trades_created +# +# SELL / exit positions / close trades part +# def exit_positions(self, trades: List[Any]) -> int: """ From 21418e298828e78576fffae4b973fe5c3723079a Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 2 Jan 2020 03:16:18 +0300 Subject: [PATCH 15/31] Minor: fix comment --- freqtrade/freqtradebot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4a207b087..774e001c8 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -407,7 +407,7 @@ class FreqtradeBot: if price: buy_limit_requested = price else: - # Calculate amount + # Calculate price buy_limit_requested = self.get_target_bid(pair) min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested) From e89fa44680e795f5a52067406526e41c6a1ae756 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 2 Jan 2020 11:50:54 +0300 Subject: [PATCH 16/31] Arrange common section for update trade state methods --- freqtrade/freqtradebot.py | 170 +++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 83 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 774e001c8..d5d918585 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -183,7 +183,7 @@ class FreqtradeBot: return max(0, self.config['max_open_trades'] - open_trades) # -# BUY / enter positions / open trades part +# BUY / enter positions / open trades logic and methods # def enter_positions(self) -> int: @@ -499,7 +499,7 @@ class FreqtradeBot: return True # -# SELL / exit positions / close trades part +# SELL / exit positions / close trades logic and methods # def exit_positions(self, trades: List[Any]) -> int: @@ -528,87 +528,6 @@ class FreqtradeBot: return trades_closed - def get_real_amount(self, trade: Trade, order: Dict, order_amount: float = None) -> float: - """ - Get real amount for the trade - Necessary for exchanges which charge fees in base currency (e.g. binance) - """ - if order_amount is None: - order_amount = order['amount'] - # Only run for closed orders - if trade.fee_open == 0 or order['status'] == 'open': - return order_amount - - # use fee from order-dict if possible - if ('fee' in order and order['fee'] is not None and - (order['fee'].keys() >= {'currency', 'cost'})): - if (order['fee']['currency'] is not None and - order['fee']['cost'] is not None and - trade.pair.startswith(order['fee']['currency'])): - new_amount = order_amount - order['fee']['cost'] - logger.info("Applying fee on amount for %s (from %s to %s) from Order", - trade, order['amount'], new_amount) - return new_amount - - # Fallback to Trades - trades = self.exchange.get_trades_for_order(trade.open_order_id, trade.pair, - trade.open_date) - - if len(trades) == 0: - logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade) - return order_amount - amount = 0 - fee_abs = 0 - for exectrade in trades: - amount += exectrade['amount'] - if ("fee" in exectrade and exectrade['fee'] is not None and - (exectrade['fee'].keys() >= {'currency', 'cost'})): - # only applies if fee is in quote currency! - if (exectrade['fee']['currency'] is not None and - exectrade['fee']['cost'] is not None and - trade.pair.startswith(exectrade['fee']['currency'])): - fee_abs += exectrade['fee']['cost'] - - if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC): - logger.warning(f"Amount {amount} does not match amount {trade.amount}") - raise DependencyException("Half bought? Amounts don't match") - real_amount = amount - fee_abs - if fee_abs != 0: - logger.info(f"Applying fee on amount for {trade} " - f"(from {order_amount} to {real_amount}) from Trades") - return real_amount - - def update_trade_state(self, trade, action_order: dict = None): - """ - Checks trades with open orders and updates the amount if necessary - """ - # Get order details for actual price per unit - if trade.open_order_id: - # Update trade with order values - logger.info('Found open order for %s', trade) - try: - order = action_order or self.exchange.get_order(trade.open_order_id, trade.pair) - except InvalidOrderException as exception: - logger.warning('Unable to fetch order %s: %s', trade.open_order_id, exception) - return - # Try update amount (binance-fix) - try: - new_amount = self.get_real_amount(trade, order) - if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC): - order['amount'] = new_amount - # Fee was applied, so set to 0 - trade.fee_open = 0 - trade.recalc_open_trade_price() - - except DependencyException as exception: - logger.warning("Could not update trade amount: %s", exception) - - trade.update(order) - - # Updating wallets when order is closed - if not trade.is_open: - self.wallets.update() - def get_sell_rate(self, pair: str, refresh: bool) -> float: """ Get sell rate - either using get-ticker bid or first bid based on orderbook @@ -1047,3 +966,88 @@ class FreqtradeBot: # Send the message self.rpc.send_msg(msg) + +# +# Common update trade state methods +# + + def update_trade_state(self, trade, action_order: dict = None): + """ + Checks trades with open orders and updates the amount if necessary + """ + # Get order details for actual price per unit + if trade.open_order_id: + # Update trade with order values + logger.info('Found open order for %s', trade) + try: + order = action_order or self.exchange.get_order(trade.open_order_id, trade.pair) + except InvalidOrderException as exception: + logger.warning('Unable to fetch order %s: %s', trade.open_order_id, exception) + return + # Try update amount (binance-fix) + try: + new_amount = self.get_real_amount(trade, order) + if not isclose(order['amount'], new_amount, abs_tol=constants.MATH_CLOSE_PREC): + order['amount'] = new_amount + # Fee was applied, so set to 0 + trade.fee_open = 0 + trade.recalc_open_trade_price() + + except DependencyException as exception: + logger.warning("Could not update trade amount: %s", exception) + + trade.update(order) + + # Updating wallets when order is closed + if not trade.is_open: + self.wallets.update() + + def get_real_amount(self, trade: Trade, order: Dict, order_amount: float = None) -> float: + """ + Get real amount for the trade + Necessary for exchanges which charge fees in base currency (e.g. binance) + """ + if order_amount is None: + order_amount = order['amount'] + # Only run for closed orders + if trade.fee_open == 0 or order['status'] == 'open': + return order_amount + + # use fee from order-dict if possible + if ('fee' in order and order['fee'] is not None and + (order['fee'].keys() >= {'currency', 'cost'})): + if (order['fee']['currency'] is not None and + order['fee']['cost'] is not None and + trade.pair.startswith(order['fee']['currency'])): + new_amount = order_amount - order['fee']['cost'] + logger.info("Applying fee on amount for %s (from %s to %s) from Order", + trade, order['amount'], new_amount) + return new_amount + + # Fallback to Trades + trades = self.exchange.get_trades_for_order(trade.open_order_id, trade.pair, + trade.open_date) + + if len(trades) == 0: + logger.info("Applying fee on amount for %s failed: myTrade-Dict empty found", trade) + return order_amount + amount = 0 + fee_abs = 0 + for exectrade in trades: + amount += exectrade['amount'] + if ("fee" in exectrade and exectrade['fee'] is not None and + (exectrade['fee'].keys() >= {'currency', 'cost'})): + # only applies if fee is in quote currency! + if (exectrade['fee']['currency'] is not None and + exectrade['fee']['cost'] is not None and + trade.pair.startswith(exectrade['fee']['currency'])): + fee_abs += exectrade['fee']['cost'] + + if not isclose(amount, order_amount, abs_tol=constants.MATH_CLOSE_PREC): + logger.warning(f"Amount {amount} does not match amount {trade.amount}") + raise DependencyException("Half bought? Amounts don't match") + real_amount = amount - fee_abs + if fee_abs != 0: + logger.info(f"Applying fee on amount for {trade} " + f"(from {order_amount} to {real_amount}) from Trades") + return real_amount From 6fbdd6bee9520afc13450cb63c3fba4591537ce3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 09:51:24 +0100 Subject: [PATCH 17/31] Remove unused directory from user_data --- user_data/backtest_data/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 user_data/backtest_data/.gitkeep diff --git a/user_data/backtest_data/.gitkeep b/user_data/backtest_data/.gitkeep deleted file mode 100644 index e69de29bb..000000000 From 2c8e8d8ef65021fbb3ee3446768fe3e3fa34c36f Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 09:51:47 +0100 Subject: [PATCH 18/31] Align columns for btanalysis loading --- freqtrade/data/btanalysis.py | 2 +- tests/data/test_btanalysis.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 2fc931a9b..04b2ca980 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -47,7 +47,7 @@ def load_backtest_data(filename) -> pd.DataFrame: utc=True, infer_datetime_format=True ) - df['profitabs'] = df['close_rate'] - df['open_rate'] + df['profit'] = df['close_rate'] - df['open_rate'] df = df.sort_values("open_time").reset_index(drop=True) return df diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index 13711c63e..60d9c3ea5 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -20,7 +20,7 @@ def test_load_backtest_data(testdatadir): filename = testdatadir / "backtest-result_test.json" bt_data = load_backtest_data(filename) assert isinstance(bt_data, DataFrame) - assert list(bt_data.columns) == BT_DATA_COLUMNS + ["profitabs"] + assert list(bt_data.columns) == BT_DATA_COLUMNS + ["profit"] assert len(bt_data) == 179 # Test loading from string (must yield same result) From 9325880fe5c84973a6553f47993639042f574ac9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 10:38:59 +0100 Subject: [PATCH 19/31] Split config-validation requires --- freqtrade/configuration/config_validation.py | 11 +++++-- freqtrade/constants.py | 30 ++++++++++++-------- tests/test_configuration.py | 1 + 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 43eead46a..9afa39ca7 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -1,4 +1,5 @@ import logging +from copy import deepcopy from typing import Any, Dict from jsonschema import Draft4Validator, validators @@ -42,15 +43,21 @@ def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]: :param conf: Config in JSON format :return: Returns the config if valid, otherwise throw an exception """ + conf_schema = deepcopy(constants.CONF_SCHEMA) + if conf.get('runmode', RunMode.OTHER) in (RunMode.DRY_RUN, RunMode.LIVE): + conf_schema['required'] = constants.SCHEMA_TRADE_REQUIRED + else: + conf_schema['required'] = constants.SCHEMA_MINIMAL_REQUIRED + try: - FreqtradeValidator(constants.CONF_SCHEMA).validate(conf) + FreqtradeValidator(conf_schema).validate(conf) return conf except ValidationError as e: logger.critical( f"Invalid configuration. See config.json.example. Reason: {e}" ) raise ValidationError( - best_match(Draft4Validator(constants.CONF_SCHEMA).iter_errors(conf)).message + best_match(Draft4Validator(conf_schema).iter_errors(conf)).message ) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index d7c6249d5..b21279bb4 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -269,16 +269,22 @@ CONF_SCHEMA = { 'required': ['process_throttle_secs', 'allowed_risk', 'capital_available_percentage'] } }, - 'required': [ - 'exchange', - 'max_open_trades', - 'stake_currency', - 'stake_amount', - 'dry_run', - 'dry_run_wallet', - 'bid_strategy', - 'unfilledtimeout', - 'stoploss', - 'minimal_roi', - ] } + +SCHEMA_TRADE_REQUIRED = [ + 'exchange', + 'max_open_trades', + 'stake_currency', + 'stake_amount', + 'dry_run', + 'dry_run_wallet', + 'bid_strategy', + 'unfilledtimeout', + 'stoploss', + 'minimal_roi', +] + +SCHEMA_MINIMAL_REQUIRED = [ + 'exchange', + 'dry_run', +] diff --git a/tests/test_configuration.py b/tests/test_configuration.py index ee3d23131..531a88688 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -49,6 +49,7 @@ def test_load_config_missing_attributes(default_conf) -> None: conf = deepcopy(default_conf) conf.pop('stake_currency') + conf['runmode'] = RunMode.DRY_RUN with pytest.raises(ValidationError, match=r".*'stake_currency' is a required property.*"): validate_config_schema(conf) From 20fc3b7978d036da11a56df2b8c81622854e2a6e Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 10:41:10 +0100 Subject: [PATCH 20/31] validate config for utils too --- freqtrade/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 45520ecf7..9fe15aea6 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -12,7 +12,8 @@ from colorama import init as colorama_init from tabulate import tabulate from freqtrade.configuration import (Configuration, TimeRange, - remove_credentials) + remove_credentials, + validate_config_consistency) from freqtrade.configuration.directory_operations import (copy_sample_files, create_userdata_dir) from freqtrade.constants import USERPATH_HYPEROPTS, USERPATH_STRATEGY @@ -40,6 +41,7 @@ def setup_utils_configuration(args: Dict[str, Any], method: RunMode) -> Dict[str # Ensure we do not use Exchange credentials remove_credentials(config) + validate_config_consistency(config) return config From 22fcf7b4dc64c82058e4eb1349422ac83199c043 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 10:47:37 +0100 Subject: [PATCH 21/31] Allow empty stake currency in certain cases --- freqtrade/configuration/config_validation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 9afa39ca7..f7886eec2 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -48,6 +48,10 @@ def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]: conf_schema['required'] = constants.SCHEMA_TRADE_REQUIRED else: conf_schema['required'] = constants.SCHEMA_MINIMAL_REQUIRED + # Dynamically allow empty stake-currency + # Since the minimal config specifies this too. + # It's not allowed for Dry-run or live modes + conf_schema['properties']['stake_currency']['enum'] += [''] try: FreqtradeValidator(conf_schema).validate(conf) From 9382b38c41509e51495d48248b968d58bdf1e6df Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 10:56:00 +0100 Subject: [PATCH 22/31] Fix mypy error --- freqtrade/configuration/config_validation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index f7886eec2..6f56790f4 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -51,7 +51,7 @@ def validate_config_schema(conf: Dict[str, Any]) -> Dict[str, Any]: # Dynamically allow empty stake-currency # Since the minimal config specifies this too. # It's not allowed for Dry-run or live modes - conf_schema['properties']['stake_currency']['enum'] += [''] + conf_schema['properties']['stake_currency']['enum'] += [''] # type: ignore try: FreqtradeValidator(conf_schema).validate(conf) From f15e5e9d5715c233d49adbe692dd38215e71a769 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 2 Jan 2020 13:51:25 +0300 Subject: [PATCH 23/31] Add _notify_buy() --- freqtrade/freqtradebot.py | 42 +++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d5d918585..9570a80ef 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -400,8 +400,6 @@ class FreqtradeBot: :param pair: pair for which we want to create a LIMIT_BUY :return: None """ - 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'] if price: @@ -458,17 +456,6 @@ class FreqtradeBot: amount = order['amount'] buy_limit_filled_price = order['price'] - self.rpc.send_msg({ - 'type': RPCMessageType.BUY_NOTIFICATION, - 'exchange': self.exchange.name.capitalize(), - 'pair': pair, - 'limit': buy_limit_filled_price, - 'order_type': order_type, - 'stake_amount': stake_amount, - 'stake_currency': stake_currency, - 'fiat_currency': fiat_currency - }) - # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL fee = self.exchange.get_fee(symbol=pair, taker_or_maker='maker') trade = Trade( @@ -486,6 +473,8 @@ class FreqtradeBot: ticker_interval=timeframe_to_minutes(self.config['ticker_interval']) ) + self._notify_buy(trade, order_type) + # Update fees if order is closed if order_status == 'closed': self.update_trade_state(trade, order) @@ -498,6 +487,30 @@ class FreqtradeBot: return True + def _notify_buy(self, trade: Trade, order_type: str): + """ + Sends rpc notification when a buy occured. + """ + msg = { + 'type': RPCMessageType.BUY_NOTIFICATION, + 'exchange': self.exchange.name.capitalize(), + 'pair': trade.pair, + 'limit': trade.open_rate, + 'order_type': order_type, + 'stake_amount': trade.stake_amount, + } + + if 'stake_currency' in self.config and 'fiat_display_currency' in self.config: + stake_currency = self.config['stake_currency'] + fiat_currency = self.config['fiat_display_currency'] + msg.update({ + 'stake_currency': stake_currency, + 'fiat_currency': fiat_currency, + }) + + # Send the message + self.rpc.send_msg(msg) + # # SELL / exit positions / close trades logic and methods # @@ -952,10 +965,9 @@ class FreqtradeBot: 'profit_percent': profit_percent, 'sell_reason': trade.sell_reason, 'open_date': trade.open_date, - 'close_date': trade.close_date or datetime.utcnow() + 'close_date': trade.close_date or datetime.utcnow(), } - # For regular case, when the configuration exists if 'stake_currency' in self.config and 'fiat_display_currency' in self.config: stake_currency = self.config['stake_currency'] fiat_currency = self.config['fiat_display_currency'] From 88efa4065bdcb222091d9594c2cb86a5a8b03461 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 2 Jan 2020 13:56:16 +0300 Subject: [PATCH 24/31] Align the name of a variable to be same for buy and sell parts --- freqtrade/freqtradebot.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9570a80ef..5e6ffbfde 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -913,16 +913,16 @@ class FreqtradeBot: except InvalidOrderException: logger.exception(f"Could not cancel stoploss order {trade.stoploss_order_id}") - ordertype = self.strategy.order_types[sell_type] + order_type = self.strategy.order_types[sell_type] if sell_reason == SellType.EMERGENCY_SELL: # Emergencysells (default to market!) - ordertype = self.strategy.order_types.get("emergencysell", "market") + order_type = 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, + ordertype=order_type, amount=amount, rate=limit, time_in_force=self.strategy.order_time_in_force['sell'] ) @@ -938,7 +938,7 @@ class FreqtradeBot: # Lock pair for one candle to prevent immediate rebuys self.strategy.lock_pair(trade.pair, timeframe_to_next_date(self.config['ticker_interval'])) - self._notify_sell(trade, ordertype) + self._notify_sell(trade, order_type) def _notify_sell(self, trade: Trade, order_type: str): """ From a47a25ca8889667bfec6cd948a2773b471cff87a Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Thu, 2 Jan 2020 14:38:25 +0300 Subject: [PATCH 25/31] Refine passing msg params --- freqtrade/freqtradebot.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 5e6ffbfde..c18d9131f 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -498,16 +498,10 @@ class FreqtradeBot: 'limit': trade.open_rate, 'order_type': order_type, 'stake_amount': trade.stake_amount, + 'stake_currency': self.config['stake_currency'], + 'fiat_currency': self.config.get('fiat_display_currency', None), } - if 'stake_currency' in self.config and 'fiat_display_currency' in self.config: - stake_currency = self.config['stake_currency'] - fiat_currency = self.config['fiat_display_currency'] - msg.update({ - 'stake_currency': stake_currency, - 'fiat_currency': fiat_currency, - }) - # Send the message self.rpc.send_msg(msg) @@ -966,13 +960,12 @@ class FreqtradeBot: 'sell_reason': trade.sell_reason, 'open_date': trade.open_date, 'close_date': trade.close_date or datetime.utcnow(), + 'stake_currency': self.config['stake_currency'], } - if 'stake_currency' in self.config and 'fiat_display_currency' in self.config: - stake_currency = self.config['stake_currency'] + if 'fiat_display_currency' in self.config: fiat_currency = self.config['fiat_display_currency'] msg.update({ - 'stake_currency': stake_currency, 'fiat_currency': fiat_currency, }) From b48bf035f6758bedf5773ec9b1b1ed98ca3477ae Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 14:52:34 +0100 Subject: [PATCH 26/31] Add note about MacOS installation --- docs/installation.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/installation.md b/docs/installation.md index 27b7a94c5..f7dce8787 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -270,3 +270,19 @@ The easiest way is to download install Microsoft Visual Studio Community [here]( Now you have an environment ready, the next step is [Bot Configuration](configuration.md). + + +## Trouble shooting + +### MacOS installation error + +Newer versions of MacOS may have installation fail with errors like `error: command 'g++' failed with exit status 1`. + +This error will require an explicit installation of the SDK Headers, which are not installed by default in this version of MacOS. +For MacOS 10.14, this can be accomplished with the below command. + +``` bash +open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg +``` + +If this file is inexistant, then you're probably on a different version of MacOS, so you may need to consult the internet for specific resolution details. From 90744ff5abe262ef4d0731d966c5d8ad74ade076 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 19:36:31 +0100 Subject: [PATCH 27/31] show percent instead of ratio (!) --- freqtrade/plot/plotting.py | 4 ++-- tests/test_plotting.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index db4637ee5..e1989b249 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -120,8 +120,8 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots: ) ) # Create description for sell summarizing the trade - desc = trades.apply(lambda row: f"{round(row['profitperc'], 3)}%, {row['sell_reason']}, " - f"{row['duration']} min", + desc = trades.apply(lambda row: f"{round(row['profitperc'] * 100, 1)}%, " + f"{row['sell_reason']}, {row['duration']} min", axis=1) trade_sells = go.Scatter( x=trades["close_time"], diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 9934d2493..271246517 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -119,6 +119,7 @@ def test_plot_trades(testdatadir, caplog): assert trade_sell.yaxis == 'y' assert len(trades) == len(trade_sell.x) assert trade_sell.marker.color == 'red' + assert trade_sell.text[0] == "4.0%, roi, 15 min" def test_generate_candlestick_graph_no_signals_no_trades(default_conf, mocker, testdatadir, caplog): From 560aea876efa6aaaec65f7def6c58a9c4613d33a Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 2 Jan 2020 20:20:29 +0100 Subject: [PATCH 28/31] Remove fiat_currency temporary variable --- freqtrade/freqtradebot.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c18d9131f..373055165 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -964,9 +964,8 @@ class FreqtradeBot: } if 'fiat_display_currency' in self.config: - fiat_currency = self.config['fiat_display_currency'] msg.update({ - 'fiat_currency': fiat_currency, + 'fiat_currency': self.config['fiat_display_currency'], }) # Send the message From da1fea6582790e99b3cd69688dd6dfb7c3b57e00 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Jan 2020 06:37:36 +0100 Subject: [PATCH 29/31] Minor correction to wording of MacOS Specific install doc --- docs/installation.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index f7dce8787..267d91c8d 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -271,14 +271,13 @@ The easiest way is to download install Microsoft Visual Studio Community [here]( Now you have an environment ready, the next step is [Bot Configuration](configuration.md). - -## Trouble shooting +## Troubleshooting ### MacOS installation error -Newer versions of MacOS may have installation fail with errors like `error: command 'g++' failed with exit status 1`. +Newer versions of MacOS may have installation failed with errors like `error: command 'g++' failed with exit status 1`. -This error will require an explicit installation of the SDK Headers, which are not installed by default in this version of MacOS. +This error will require explicit installation of the SDK Headers, which are not installed by default in this version of MacOS. For MacOS 10.14, this can be accomplished with the below command. ``` bash From 55041878ae7da0758a8e58d2c1add3139e54fcbb Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Jan 2020 11:20:08 +0100 Subject: [PATCH 30/31] Update Backtesting fee documentation --- docs/backtesting.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index ac7c8e11a..c2359b370 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -79,12 +79,14 @@ Please also read about the [strategy startup period](strategy-customization.md#s Sometimes your account has certain fee rebates (fee reductions starting with a certain account size or monthly volume), which are not visible to ccxt. To account for this in backtesting, you can use `--fee 0.001` to supply this value to backtesting. -This fee must be a percentage, and will be applied twice (once for trade entry, and once for trade exit). +This fee must be a ratio, and will be applied twice (once for trade entry, and once for trade exit). ```bash freqtrade backtesting --fee 0.001 ``` +!!! Note + Only supply this parameter if you want to experiment with different fee values. By default, Backtesting fetches the exchange's default fee from the exchange. #### Running backtest with smaller testset by using timerange From e1f89e3ad34187caf0385a841f13ec1c710fbd17 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 3 Jan 2020 20:11:58 +0100 Subject: [PATCH 31/31] Reword Note in backtesting fee docs --- docs/backtesting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index c2359b370..45759e2aa 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -86,7 +86,7 @@ freqtrade backtesting --fee 0.001 ``` !!! Note - Only supply this parameter if you want to experiment with different fee values. By default, Backtesting fetches the exchange's default fee from the exchange. + Only supply this option (or the corresponding configuration parameter) if you want to experiment with different fee values. By default, Backtesting fetches the default fee from the exchange pair/market info. #### Running backtest with smaller testset by using timerange