From 9474cb17922ac4b6a0969bf1617a42c1caa210d0 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2020 07:48:46 +0000 Subject: [PATCH 01/10] Bump ccxt from 1.21.56 to 1.21.76 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.21.56 to 1.21.76. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md) - [Commits](https://github.com/ccxt/ccxt/compare/1.21.56...1.21.76) Signed-off-by: dependabot-preview[bot] --- requirements-common.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-common.txt b/requirements-common.txt index 15a9d687f..daf4984c0 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,6 +1,6 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs -ccxt==1.21.56 +ccxt==1.21.76 SQLAlchemy==1.3.12 python-telegram-bot==12.3.0 arrow==0.15.5 From 8d4515935ae9ac849798a7111b4aa3bff0a39802 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2020 07:49:18 +0000 Subject: [PATCH 02/10] Bump pytest from 5.3.2 to 5.3.3 Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.3.2 to 5.3.3. - [Release notes](https://github.com/pytest-dev/pytest/releases) - [Changelog](https://github.com/pytest-dev/pytest/blob/master/CHANGELOG.rst) - [Commits](https://github.com/pytest-dev/pytest/compare/5.3.2...5.3.3) Signed-off-by: dependabot-preview[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 16f9baf95..e602bf184 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ flake8==3.7.9 flake8-type-annotations==0.1.0 flake8-tidy-imports==4.0.0 mypy==0.761 -pytest==5.3.2 +pytest==5.3.3 pytest-asyncio==0.10.0 pytest-cov==2.8.1 pytest-mock==2.0.0 From 6e3336cb30aee9a279d9fda3659695e916d36560 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Jan 2020 20:10:06 +0100 Subject: [PATCH 03/10] Adapt test to verify behaviour of stoploss_on_exchange in dry-run --- tests/test_freqtradebot.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 5a4820f2f..6c6bd1753 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1318,6 +1318,14 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, rate=0.00002344 * 0.95 * 0.99, stop_price=0.00002344 * 0.95) + # price fell below stoploss, so dry-run sells trade. + mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ + 'bid': 0.00002144, + 'ask': 0.00002146, + 'last': 0.00002144 + })) + assert freqtrade.handle_trade(trade) is True + def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog, limit_buy_order, limit_sell_order) -> None: From 099bbc5c7f56df2ce193c42c60c99c7c45ea7b85 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Jan 2020 20:14:40 +0100 Subject: [PATCH 04/10] Fix bug when stoploss_on_exchange in combination with dry-run does not sell orders --- freqtrade/strategy/interface.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7bd6a9ac5..27bc8280e 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -389,9 +389,11 @@ class IStrategy(ABC): trade.adjust_stop_loss(high or current_rate, stop_loss_value) # evaluate if the stoploss was hit if stoploss is not on exchange + # in Dry-Run, this handles stoploss logic as well, as the logic will not be different to + # regular stoploss handling. if ((self.stoploss is not None) and (trade.stop_loss >= current_rate) and - (not self.order_types.get('stoploss_on_exchange'))): + (not self.order_types.get('stoploss_on_exchange') or self.config['dry_run'])): sell_type = SellType.STOP_LOSS From 1bf475fa1a1f5281f01b1b6a8ab3104f7553c1e8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 20 Jan 2020 20:24:40 +0100 Subject: [PATCH 05/10] Remove .get calls for dry_run - it's a mandatory property --- freqtrade/freqtradebot.py | 5 ++--- freqtrade/rpc/rpc.py | 6 +++--- freqtrade/rpc/rpc_manager.py | 2 +- tests/test_configuration.py | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e712892f1..acff2b2ad 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -63,8 +63,7 @@ class FreqtradeBot: self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) - persistence.init(self.config.get('db_url', None), - clean_open_orders=self.config.get('dry_run', False)) + persistence.init(self.config.get('db_url', None), clean_open_orders=self.config['dry_run']) self.wallets = Wallets(self.config, self.exchange) @@ -930,7 +929,7 @@ class FreqtradeBot: # if stoploss is on exchange and we are on dry_run mode, # we consider the sell price stop price - if self.config.get('dry_run', False) and sell_type == 'stoploss' \ + if self.config['dry_run'] and sell_type == 'stoploss' \ and self.strategy.order_types['stoploss_on_exchange']: limit = trade.stop_loss diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 0469d418d..d58b99f39 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -88,7 +88,7 @@ class RPC: """ config = self._freqtrade.config val = { - 'dry_run': config.get('dry_run', False), + 'dry_run': config['dry_run'], 'stake_currency': config['stake_currency'], 'stake_amount': config['stake_amount'], 'minimal_roi': config['minimal_roi'].copy(), @@ -337,7 +337,7 @@ class RPC: 'stake': stake_currency, }) if total == 0.0: - if self._freqtrade.config.get('dry_run', False): + if self._freqtrade.config['dry_run']: raise RPCException('Running in Dry Run, balances are not available.') else: raise RPCException('All balances are zero.') @@ -351,7 +351,7 @@ class RPC: 'symbol': symbol, 'value': value, 'stake': stake_currency, - 'note': 'Simulated balances' if self._freqtrade.config.get('dry_run', False) else '' + 'note': 'Simulated balances' if self._freqtrade.config['dry_run'] else '' } def _rpc_start(self) -> Dict[str, str]: diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index cb9e697e9..f687fe4d1 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -62,7 +62,7 @@ class RPCManager: logger.error(f"Message type {msg['type']} not implemented by handler {mod.name}.") def startup_messages(self, config, pairlist) -> None: - if config.get('dry_run', False): + if config['dry_run']: self.send_msg({ 'type': RPCMessageType.WARNING_NOTIFICATION, 'status': 'Dry run is enabled. All trades are simulated.' diff --git a/tests/test_configuration.py b/tests/test_configuration.py index a4d4c4abc..cbcd6416a 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -323,7 +323,7 @@ def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) -> configuration = Configuration(Arguments(arglist).get_parsed_arg()) validated_conf = configuration.load_config() - assert validated_conf.get('dry_run') is expected + assert validated_conf['dry_run'] is expected def test_load_custom_strategy(default_conf, mocker) -> None: From c1c2717bc9a9216c042a7bec2efe94c15bfc4f18 Mon Sep 17 00:00:00 2001 From: Daniel Goller Date: Tue, 21 Jan 2020 15:49:24 +0000 Subject: [PATCH 06/10] added missing word in hyperopt loss example --- freqtrade/templates/sample_hyperopt_loss.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/templates/sample_hyperopt_loss.py b/freqtrade/templates/sample_hyperopt_loss.py index 5a2fb72b6..ee2c28bc9 100644 --- a/freqtrade/templates/sample_hyperopt_loss.py +++ b/freqtrade/templates/sample_hyperopt_loss.py @@ -27,7 +27,7 @@ class SampleHyperOptLoss(IHyperOptLoss): Defines the default loss function for hyperopt This is intended to give you some inspiration for your own loss function. - The Function needs to return a number (float) - which becomes for better backtest results. + The Function needs to return a number (float) - which becomes smaller for better backtest results. """ @staticmethod From bff0a0953794c1de68c582c8ba50a6f25845086f Mon Sep 17 00:00:00 2001 From: Daniel Goller Date: Tue, 21 Jan 2020 16:14:19 +0000 Subject: [PATCH 07/10] line was too long --- freqtrade/templates/sample_hyperopt_loss.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/templates/sample_hyperopt_loss.py b/freqtrade/templates/sample_hyperopt_loss.py index ee2c28bc9..4173d97f5 100644 --- a/freqtrade/templates/sample_hyperopt_loss.py +++ b/freqtrade/templates/sample_hyperopt_loss.py @@ -27,7 +27,8 @@ class SampleHyperOptLoss(IHyperOptLoss): Defines the default loss function for hyperopt This is intended to give you some inspiration for your own loss function. - The Function needs to return a number (float) - which becomes smaller for better backtest results. + The Function needs to return a number (float) - which becomes smaller for better backtest + results. """ @staticmethod From 7d2d0235a0407b564038f02b4834ab365cc72f8a Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 22 Jan 2020 06:08:34 +0100 Subject: [PATCH 08/10] Fix typo in sell-reason table generation --- freqtrade/optimize/optimize_reports.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 3801546b1..67056eaa9 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -70,7 +70,7 @@ def generate_text_table_sell_reason(data: Dict[str, Dict], results: DataFrame) - for reason, count in results['sell_reason'].value_counts().iteritems(): result = results.loc[results['sell_reason'] == reason] profit = len(result[result['profit_abs'] >= 0]) - loss = len(result[results['profit_abs'] < 0]) + loss = len(result[result['profit_abs'] < 0]) profit_mean = round(result['profit_percent'].mean() * 100.0, 2) tabular_data.append([reason.value, count, profit, loss, profit_mean]) return tabulate(tabular_data, headers=headers, tablefmt="pipe") From e13045b599f921e749e494dd49b13cc00d1d9e57 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 22 Jan 2020 06:17:13 +0100 Subject: [PATCH 09/10] upgrade pip in windows environment --- build_helpers/install_windows.ps1 | 1 + 1 file changed, 1 insertion(+) diff --git a/build_helpers/install_windows.ps1 b/build_helpers/install_windows.ps1 index 30427c3cc..138fba208 100644 --- a/build_helpers/install_windows.ps1 +++ b/build_helpers/install_windows.ps1 @@ -2,6 +2,7 @@ # Downloaded from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib # Invoke-WebRequest -Uri "https://download.lfd.uci.edu/pythonlibs/xxxxxxx/TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl" -OutFile "TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl" +python -m pip install --upgrade pip pip install build_helpers\TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl pip install -r requirements-dev.txt From 8a940eb0c1d7cc15bb7cc559875259f819dc2ad2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 22 Jan 2020 14:46:28 +0100 Subject: [PATCH 10/10] Align price finding function name on buy side with get_sell_rate --- freqtrade/freqtradebot.py | 4 ++-- tests/test_freqtradebot.py | 24 ++++++++++++------------ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index acff2b2ad..387ddb063 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -218,7 +218,7 @@ class FreqtradeBot: return trades_created - def get_target_bid(self, pair: str, tick: Dict = None) -> float: + def get_buy_rate(self, pair: str, tick: Dict = None) -> float: """ Calculates bid target between current ask price and last price :return: float: Price @@ -435,7 +435,7 @@ class FreqtradeBot: buy_limit_requested = price else: # Calculate price - buy_limit_requested = self.get_target_bid(pair) + buy_limit_requested = self.get_buy_rate(pair) min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested) if min_stake_amount is not None and min_stake_amount > stake_amount: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 6c6bd1753..d00fab9c7 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -912,7 +912,7 @@ def test_balance_fully_ask_side(mocker, default_conf) -> None: mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={'ask': 20, 'last': 10})) - assert freqtrade.get_target_bid('ETH/BTC') == 20 + assert freqtrade.get_buy_rate('ETH/BTC') == 20 def test_balance_fully_last_side(mocker, default_conf) -> None: @@ -921,7 +921,7 @@ def test_balance_fully_last_side(mocker, default_conf) -> None: mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={'ask': 20, 'last': 10})) - assert freqtrade.get_target_bid('ETH/BTC') == 10 + assert freqtrade.get_buy_rate('ETH/BTC') == 10 def test_balance_bigger_last_ask(mocker, default_conf) -> None: @@ -929,7 +929,7 @@ def test_balance_bigger_last_ask(mocker, default_conf) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={'ask': 5, 'last': 10})) - assert freqtrade.get_target_bid('ETH/BTC') == 5 + assert freqtrade.get_buy_rate('ETH/BTC') == 5 def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: @@ -938,10 +938,10 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: freqtrade = FreqtradeBot(default_conf) stake_amount = 2 bid = 0.11 - get_bid = MagicMock(return_value=bid) + buy_rate_mock = MagicMock(return_value=bid) mocker.patch.multiple( 'freqtrade.freqtradebot.FreqtradeBot', - get_target_bid=get_bid, + get_buy_rate=buy_rate_mock, _get_min_pair_stake_amount=MagicMock(return_value=1) ) buy_mm = MagicMock(return_value={'id': limit_buy_order['id']}) @@ -958,7 +958,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: pair = 'ETH/BTC' assert freqtrade.execute_buy(pair, stake_amount) - assert get_bid.call_count == 1 + assert buy_rate_mock.call_count == 1 assert buy_mm.call_count == 1 call_args = buy_mm.call_args_list[0][1] assert call_args['pair'] == pair @@ -975,8 +975,8 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: # Test calling with price fix_price = 0.06 assert freqtrade.execute_buy(pair, stake_amount, fix_price) - # Make sure get_target_bid wasn't called again - assert get_bid.call_count == 1 + # Make sure get_buy_rate wasn't called again + assert buy_rate_mock.call_count == 1 assert buy_mm.call_count == 2 call_args = buy_mm.call_args_list[1][1] @@ -3500,7 +3500,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: """ - test if function get_target_bid will return the order book price + test if function get_buy_rate will return the order book price instead of the ask rate """ patch_exchange(mocker) @@ -3518,13 +3518,13 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: default_conf['telegram']['enabled'] = False freqtrade = FreqtradeBot(default_conf) - assert freqtrade.get_target_bid('ETH/BTC') == 0.043935 + assert freqtrade.get_buy_rate('ETH/BTC') == 0.043935 assert ticker_mock.call_count == 0 def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2) -> None: """ - test if function get_target_bid will return the ask rate (since its value is lower) + test if function get_buy_rate will return the ask rate (since its value is lower) instead of the order book rate (even if enabled) """ patch_exchange(mocker) @@ -3543,7 +3543,7 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2) -> None: freqtrade = FreqtradeBot(default_conf) # orderbook shall be used even if tickers would be lower. - assert freqtrade.get_target_bid('ETH/BTC') != 0.042 + assert freqtrade.get_buy_rate('ETH/BTC') != 0.042 assert ticker_mock.call_count == 0