From c38f3a2b9a89fe2e6c76a8e098aa4d11b31c5614 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 28 Aug 2019 07:05:48 +0200 Subject: [PATCH 01/29] Apply dynamic versioning to develop --- freqtrade/__init__.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 5ccc2ff3c..175b689f9 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,15 @@ """ FreqTrade bot """ -__version__ = '2019.7-dev' +__version__ = 'develop' + +if __version__ == 'develop': + + try: + import subprocess + __version__ = str(subprocess.check_output( + ["git", "describe"], stderr=subprocess.DEVNULL).rstrip()) + except Exception: + # git not available, ignore + pass class DependencyException(Exception): From 85f1291597b507a997b62e750c66444afad2ece5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Sep 2019 20:18:47 +0200 Subject: [PATCH 02/29] use git log to print version --- freqtrade/__init__.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 175b689f9..83fee0b0d 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -5,8 +5,9 @@ if __version__ == 'develop': try: import subprocess - __version__ = str(subprocess.check_output( - ["git", "describe"], stderr=subprocess.DEVNULL).rstrip()) + __version__ = 'develop-' + subprocess.check_output( + ['git', 'log', '--format="%h"', '-n 1'], + stderr=subprocess.DEVNULL).decode("utf-8").rstrip().strip('"') except Exception: # git not available, ignore pass From 1cd8ed0c1afec3236e1f0e0bc0a198c33ca32357 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Sep 2019 20:02:07 +0200 Subject: [PATCH 03/29] Remove --refresh-pairs --- freqtrade/configuration/arguments.py | 2 +- freqtrade/configuration/cli_options.py | 7 ------- freqtrade/configuration/configuration.py | 4 ---- freqtrade/data/dataprovider.py | 1 - freqtrade/optimize/backtesting.py | 2 -- freqtrade/optimize/edge_cli.py | 2 +- freqtrade/optimize/hyperopt.py | 2 -- 7 files changed, 2 insertions(+), 18 deletions(-) diff --git a/freqtrade/configuration/arguments.py b/freqtrade/configuration/arguments.py index bb961173b..6e2ecea2e 100644 --- a/freqtrade/configuration/arguments.py +++ b/freqtrade/configuration/arguments.py @@ -15,7 +15,7 @@ ARGS_STRATEGY = ["strategy", "strategy_path"] ARGS_MAIN = ARGS_COMMON + ARGS_STRATEGY + ["db_url", "sd_notify"] ARGS_COMMON_OPTIMIZE = ["ticker_interval", "timerange", - "max_open_trades", "stake_amount", "refresh_pairs"] + "max_open_trades", "stake_amount"] ARGS_BACKTEST = ARGS_COMMON_OPTIMIZE + ["position_stacking", "use_max_market_positions", "strategy_list", "export", "exportfilename"] diff --git a/freqtrade/configuration/cli_options.py b/freqtrade/configuration/cli_options.py index bf1ec3620..cb07dbdba 100644 --- a/freqtrade/configuration/cli_options.py +++ b/freqtrade/configuration/cli_options.py @@ -107,13 +107,6 @@ AVAILABLE_CLI_OPTIONS = { help='Specify stake_amount.', type=float, ), - "refresh_pairs": Arg( - '-r', '--refresh-pairs-cached', - help='Refresh the pairs files in tests/testdata with the latest data from the ' - 'exchange. Use it if you want to run your optimization commands with ' - 'up-to-date data.', - action='store_true', - ), # Backtesting "position_stacking": Arg( '--eps', '--enable-position-stacking', diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 1aed32e50..af9e420f8 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -212,10 +212,6 @@ class Configuration: self._process_datadir_options(config) - self._args_to_config(config, argname='refresh_pairs', - logstring='Parameter -r/--refresh-pairs-cached detected ...', - deprecated_msg='-r/--refresh-pairs-cached will be removed soon.') - self._args_to_config(config, argname='strategy_list', logstring='Using strategy list of {} strategies', logfun=len) diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index eb6ec0f2a..7d5e4540b 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -65,7 +65,6 @@ class DataProvider: """ return load_pair_history(pair=pair, ticker_interval=ticker_interval or self._config['ticker_interval'], - refresh_pairs=False, datadir=Path(self._config['datadir']) ) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 4956907fb..6074b281b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -414,8 +414,6 @@ class Backtesting: datadir=Path(self.config['datadir']), pairs=pairs, ticker_interval=self.ticker_interval, - refresh_pairs=self.config.get('refresh_pairs', False), - exchange=self.exchange, timerange=timerange, ) diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index f22bcb642..6484a5328 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -39,7 +39,7 @@ class EdgeCli: self.strategy = StrategyResolver(self.config).strategy self.edge = Edge(config, self.exchange, self.strategy) - self.edge._refresh_pairs = self.config.get('refresh_pairs', False) + self.edge._refresh_pairs = False self.timerange = TimeRange.parse_timerange(None if self.config.get( 'timerange') is None else str(self.config.get('timerange'))) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index c511aa5ac..61623d3df 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -362,8 +362,6 @@ class Hyperopt: datadir=Path(self.config['datadir']), pairs=self.config['exchange']['pair_whitelist'], ticker_interval=self.backtesting.ticker_interval, - refresh_pairs=self.config.get('refresh_pairs', False), - exchange=self.backtesting.exchange, timerange=timerange ) From e66fa1cec69732670b93b1e8edbee4b34537cbe1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Sep 2019 20:12:35 +0200 Subject: [PATCH 04/29] Adjust tests to not use --refresh-pairs --- tests/data/test_dataprovider.py | 1 - tests/data/test_history.py | 7 ++----- tests/optimize/test_backtesting.py | 5 ----- tests/optimize/test_edge_cli.py | 9 --------- tests/optimize/test_hyperopt.py | 5 ----- tests/test_arguments.py | 2 -- tests/test_configuration.py | 7 ------- 7 files changed, 2 insertions(+), 34 deletions(-) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index ec176d889..39e2f7d2e 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -45,7 +45,6 @@ def test_historic_ohlcv(mocker, default_conf, ticker_history): data = dp.historic_ohlcv("UNITTEST/BTC", "5m") assert isinstance(data, DataFrame) assert historymock.call_count == 1 - assert historymock.call_args_list[0][1]["refresh_pairs"] is False assert historymock.call_args_list[0][1]["ticker_interval"] == "5m" diff --git a/tests/data/test_history.py b/tests/data/test_history.py index e747794e5..abe3f0886 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -74,8 +74,7 @@ def test_load_data_7min_ticker(mocker, caplog, default_conf, testdatadir) -> Non assert ld is None assert log_has( 'No history data for pair: "UNITTEST/BTC", interval: 7m. ' - 'Use --refresh-pairs-cached option or `freqtrade download-data` ' - 'script to download the data', caplog + 'Use `freqtrade download-data` to download the data', caplog ) @@ -105,13 +104,11 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, # do not download a new pair if refresh_pairs isn't set history.load_pair_history(datadir=testdatadir, ticker_interval='1m', - refresh_pairs=False, pair='MEME/BTC') assert os.path.isfile(file) is False assert log_has( 'No history data for pair: "MEME/BTC", interval: 1m. ' - 'Use --refresh-pairs-cached option or `freqtrade download-data` ' - 'script to download the data', caplog + 'Use `freqtrade download-data` to download the data', caplog ) # download a new pair if refresh_pairs is set diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 7b50f2b18..d06116755 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -197,7 +197,6 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert config['runmode'] == RunMode.BACKTEST -@pytest.mark.filterwarnings("ignore:DEPRECATED") def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) mocker.patch( @@ -213,7 +212,6 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> '--ticker-interval', '1m', '--enable-position-stacking', '--disable-max-market-positions', - '--refresh-pairs-cached', '--timerange', ':100', '--export', '/bar/foo', '--export-filename', 'foo_bar.json' @@ -240,9 +238,6 @@ def test_setup_bt_configuration_with_arguments(mocker, default_conf, caplog) -> assert log_has('Parameter --disable-max-market-positions detected ...', caplog) assert log_has('max_open_trades set to unlimited ...', caplog) - assert 'refresh_pairs' in config - assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) - assert 'timerange' in config assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) diff --git a/tests/optimize/test_edge_cli.py b/tests/optimize/test_edge_cli.py index f312bba2c..97103da55 100644 --- a/tests/optimize/test_edge_cli.py +++ b/tests/optimize/test_edge_cli.py @@ -3,8 +3,6 @@ from unittest.mock import MagicMock -import pytest - from freqtrade.edge import PairInfo from freqtrade.optimize import setup_configuration, start_edge from freqtrade.optimize.edge_cli import EdgeCli @@ -35,14 +33,10 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert 'ticker_interval' in config assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog) - assert 'refresh_pairs' not in config - assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) - assert 'timerange' not in config assert 'stoploss_range' not in config -@pytest.mark.filterwarnings("ignore:DEPRECATED") def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> None: patched_configuration_load_config_file(mocker, edge_conf) mocker.patch( @@ -56,7 +50,6 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N '--datadir', '/foo/bar', 'edge', '--ticker-interval', '1m', - '--refresh-pairs-cached', '--timerange', ':100', '--stoplosses=-0.01,-0.10,-0.001' ] @@ -74,8 +67,6 @@ def test_setup_edge_configuration_with_arguments(mocker, edge_conf, caplog) -> N assert log_has('Parameter -i/--ticker-interval detected ... Using ticker_interval: 1m ...', caplog) - assert 'refresh_pairs' in config - assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) assert 'timerange' in config assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 2e383c839..543acbde6 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -94,7 +94,6 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca assert config['runmode'] == RunMode.HYPEROPT -@pytest.mark.filterwarnings("ignore:DEPRECATED") def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) mocker.patch( @@ -108,7 +107,6 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo 'hyperopt', '--ticker-interval', '1m', '--timerange', ':100', - '--refresh-pairs-cached', '--enable-position-stacking', '--disable-max-market-positions', '--epochs', '1000', @@ -137,9 +135,6 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo assert log_has('Parameter --disable-max-market-positions detected ...', caplog) assert log_has('max_open_trades set to unlimited ...', caplog) - assert 'refresh_pairs' in config - assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) - assert 'timerange' in config assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 7b18aa356..cf0104c01 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -99,7 +99,6 @@ def test_parse_args_backtesting_custom() -> None: '-c', 'test_conf.json', 'backtesting', '--ticker-interval', '1m', - '--refresh-pairs-cached', '--strategy-list', 'DefaultStrategy', 'SampleStrategy' @@ -110,7 +109,6 @@ def test_parse_args_backtesting_custom() -> None: assert call_args["subparser"] == 'backtesting' assert call_args["func"] is not None assert call_args["ticker_interval"] == '1m' - assert call_args["refresh_pairs"] is True assert type(call_args["strategy_list"]) is list assert len(call_args["strategy_list"]) == 2 diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 53f46fe2e..6cd38bb6d 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -341,14 +341,10 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert 'position_stacking' not in config assert not log_has('Parameter --enable-position-stacking detected ...', caplog) - assert 'refresh_pairs' not in config - assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) - assert 'timerange' not in config assert 'export' not in config -@pytest.mark.filterwarnings("ignore:DEPRECATED") def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) mocker.patch( @@ -368,7 +364,6 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non '--ticker-interval', '1m', '--enable-position-stacking', '--disable-max-market-positions', - '--refresh-pairs-cached', '--timerange', ':100', '--export', '/bar/foo' ] @@ -398,8 +393,6 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non assert log_has('Parameter --disable-max-market-positions detected ...', caplog) assert log_has('max_open_trades set to unlimited ...', caplog) - assert 'refresh_pairs'in config - assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) assert 'timerange' in config assert log_has('Parameter --timerange detected: {} ...'.format(config['timerange']), caplog) From 9cedbc13455df7b8c763dd0357dd3155d04a2b12 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Sep 2019 20:16:49 +0200 Subject: [PATCH 05/29] Cleanup history.py and update documentation --- docs/bot-usage.md | 12 ------------ docs/deprecated.md | 6 ++---- freqtrade/data/history.py | 31 +++++++++---------------------- tests/data/test_history.py | 25 ------------------------- 4 files changed, 11 insertions(+), 63 deletions(-) diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 0b0561d3d..f44400e32 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -184,10 +184,6 @@ optional arguments: Specify max_open_trades to use. --stake_amount STAKE_AMOUNT Specify stake_amount. - -r, --refresh-pairs-cached - Refresh the pairs files in tests/testdata with the - latest data from the exchange. Use it if you want to - run your optimization commands with up-to-date data. --eps, --enable-position-stacking Allow buying the same pair multiple times (position stacking). @@ -245,10 +241,6 @@ optional arguments: Specify max_open_trades to use. --stake_amount STAKE_AMOUNT Specify stake_amount. - -r, --refresh-pairs-cached - Refresh the pairs files in tests/testdata with the - latest data from the exchange. Use it if you want to - run your optimization commands with up-to-date data. --customhyperopt NAME Specify hyperopt class name (default: `DefaultHyperOpts`). @@ -310,10 +302,6 @@ optional arguments: Specify max_open_trades to use. --stake_amount STAKE_AMOUNT Specify stake_amount. - -r, --refresh-pairs-cached - Refresh the pairs files in tests/testdata with the - latest data from the exchange. Use it if you want to - run your optimization commands with up-to-date data. --stoplosses STOPLOSS_RANGE Defines a range of stoploss against which edge will assess the strategy the format is "min,max,step" diff --git a/docs/deprecated.md b/docs/deprecated.md index ed70b1936..24c2bb1e3 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -4,7 +4,7 @@ This page contains description of the command line arguments, configuration para and the bot features that were declared as DEPRECATED by the bot development team and are no longer supported. Please avoid their usage in your configuration. -## Deprecated +## Removed features ### the `--refresh-pairs-cached` command line option @@ -12,9 +12,7 @@ and are no longer supported. Please avoid their usage in your configuration. Since this leads to much confusion, and slows down backtesting (while not being part of backtesting) this has been singled out as a seperate freqtrade subcommand `freqtrade download-data`. -This command line option was deprecated in `2019.7-dev` and will be removed after the next release. - -## Removed features +This command line option was deprecated in `2019.7-dev` and removed in `2019-9` ### The **--dynamic-whitelist** command line option diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index b4776fab0..981a398da 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -129,8 +129,7 @@ def load_pair_history(pair: str, else: logger.warning( f'No history data for pair: "{pair}", interval: {ticker_interval}. ' - 'Use --refresh-pairs-cached option or `freqtrade download-data` ' - 'script to download the data' + 'Use `freqtrade download-data` to download the data' ) return None @@ -142,33 +141,21 @@ def load_data(datadir: Path, exchange: Optional[Exchange] = None, timerange: TimeRange = TimeRange(None, None, 0, 0), fill_up_missing: bool = True, - live: bool = False ) -> Dict[str, DataFrame]: """ Loads ticker history data for a list of pairs the given parameters :return: dict(:) """ result: Dict[str, DataFrame] = {} - if live: - if exchange: - logger.info('Live: Downloading data for all defined pairs ...') - exchange.refresh_latest_ohlcv([(pair, ticker_interval) for pair in pairs]) - result = {key[0]: value for key, value in exchange._klines.items() if value is not None} - else: - raise OperationalException( - "Exchange needs to be initialized when using live data." - ) - else: - logger.info('Using local backtesting data ...') - for pair in pairs: - hist = load_pair_history(pair=pair, ticker_interval=ticker_interval, - datadir=datadir, timerange=timerange, - refresh_pairs=refresh_pairs, - exchange=exchange, - fill_up_missing=fill_up_missing) - if hist is not None: - result[pair] = hist + for pair in pairs: + hist = load_pair_history(pair=pair, ticker_interval=ticker_interval, + datadir=datadir, timerange=timerange, + refresh_pairs=refresh_pairs, + exchange=exchange, + fill_up_missing=fill_up_missing) + if hist is not None: + result[pair] = hist return result diff --git a/tests/data/test_history.py b/tests/data/test_history.py index abe3f0886..1983c2a9f 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -131,31 +131,6 @@ def test_load_data_with_new_pair_1min(ticker_history_list, mocker, caplog, _clean_test_file(file) -def test_load_data_live(default_conf, mocker, caplog, testdatadir) -> None: - refresh_mock = MagicMock() - mocker.patch("freqtrade.exchange.Exchange.refresh_latest_ohlcv", refresh_mock) - exchange = get_patched_exchange(mocker, default_conf) - - history.load_data(datadir=testdatadir, ticker_interval='5m', - pairs=['UNITTEST/BTC', 'UNITTEST2/BTC'], - live=True, - exchange=exchange) - assert refresh_mock.call_count == 1 - assert len(refresh_mock.call_args_list[0][0][0]) == 2 - assert log_has('Live: Downloading data for all defined pairs ...', caplog) - - -def test_load_data_live_noexchange(default_conf, mocker, caplog, testdatadir) -> None: - - with pytest.raises(OperationalException, - match=r'Exchange needs to be initialized when using live data.'): - history.load_data(datadir=testdatadir, ticker_interval='5m', - pairs=['UNITTEST/BTC', 'UNITTEST2/BTC'], - exchange=None, - live=True, - ) - - def test_testdata_path(testdatadir) -> None: assert str(Path('tests') / 'testdata') in str(testdatadir) From 508a35fc2067ef6123fe3aadfe58c5954ece79a4 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Sep 2019 20:20:16 +0200 Subject: [PATCH 06/29] Update comment as to why certain points have not been removed --- freqtrade/data/history.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 981a398da..865289f38 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -143,8 +143,12 @@ def load_data(datadir: Path, fill_up_missing: bool = True, ) -> Dict[str, DataFrame]: """ - Loads ticker history data for a list of pairs the given parameters + Loads ticker history data for a list of pairs :return: dict(:) + TODO: refresh_pairs is still used by edge to keep the data uptodate. + This should be replaced in the future. Instead, writing the current candles to disk + from dataprovider should be implemented, as this would avoid loading ohlcv data twice. + exchange and refresh_pairs are then not needed here nor in load_pair_history. """ result: Dict[str, DataFrame] = {} From 313091eb1caf6a2ce2d4838fcccd863cbbc9a582 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Sep 2019 20:22:51 +0200 Subject: [PATCH 07/29] some more refresh_pairs cleanups --- freqtrade/optimize/edge_cli.py | 1 + tests/data/test_converter.py | 1 - tests/data/test_history.py | 3 +-- tests/optimize/test_backtesting.py | 3 --- tests/optimize/test_hyperopt.py | 3 --- 5 files changed, 2 insertions(+), 9 deletions(-) diff --git a/freqtrade/optimize/edge_cli.py b/freqtrade/optimize/edge_cli.py index 6484a5328..0cf5a009b 100644 --- a/freqtrade/optimize/edge_cli.py +++ b/freqtrade/optimize/edge_cli.py @@ -39,6 +39,7 @@ class EdgeCli: self.strategy = StrategyResolver(self.config).strategy self.edge = Edge(config, self.exchange, self.strategy) + # Set refresh_pairs to false for edge-cli (it must be true for edge) self.edge._refresh_pairs = False self.timerange = TimeRange.parse_timerange(None if self.config.get( diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py index 72da47e76..e773a970e 100644 --- a/tests/data/test_converter.py +++ b/tests/data/test_converter.py @@ -24,7 +24,6 @@ def test_parse_ticker_dataframe(ticker_history_list, caplog): def test_ohlcv_fill_up_missing_data(testdatadir, caplog): data = load_pair_history(datadir=testdatadir, ticker_interval='1m', - refresh_pairs=False, pair='UNITTEST/BTC', fill_up_missing=False) caplog.set_level(logging.DEBUG) diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 1983c2a9f..e386c3506 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -321,7 +321,6 @@ def test_load_partial_missing(testdatadir, caplog) -> None: start = arrow.get('2018-01-01T00:00:00') end = arrow.get('2018-01-11T00:00:00') tickerdata = history.load_data(testdatadir, '5m', ['UNITTEST/BTC'], - refresh_pairs=False, timerange=TimeRange('date', 'date', start.timestamp, end.timestamp)) # timedifference in 5 minutes @@ -336,7 +335,7 @@ def test_load_partial_missing(testdatadir, caplog) -> None: start = arrow.get('2018-01-10T00:00:00') end = arrow.get('2018-02-20T00:00:00') tickerdata = history.load_data(datadir=testdatadir, ticker_interval='5m', - pairs=['UNITTEST/BTC'], refresh_pairs=False, + pairs=['UNITTEST/BTC'], timerange=TimeRange('date', 'date', start.timestamp, end.timestamp)) # timedifference in 5 minutes diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index d06116755..fa40809d8 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -188,9 +188,6 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) -> assert 'position_stacking' not in config assert not log_has('Parameter --enable-position-stacking detected ...', caplog) - assert 'refresh_pairs' not in config - assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) - assert 'timerange' not in config assert 'export' not in config assert 'runmode' in config diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 543acbde6..0888c79d4 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -86,9 +86,6 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca assert 'position_stacking' not in config assert not log_has('Parameter --enable-position-stacking detected ...', caplog) - assert 'refresh_pairs' not in config - assert not log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog) - assert 'timerange' not in config assert 'runmode' in config assert config['runmode'] == RunMode.HYPEROPT From 2fcddfc8666f982627b55188a6b502c98dcf1577 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 20 Sep 2019 20:29:26 +0200 Subject: [PATCH 08/29] Clarify updating existing data --- docs/backtesting.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 45d5f486f..879cf78a3 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -7,7 +7,7 @@ Backtesting. To download data (candles / OHLCV) needed for backtesting and hyperoptimization use the `freqtrade download-data` command. -If no additional parameter is specified, freqtrade will download data for `"1m"` and `"5m"` timeframes. +If no additional parameter is specified, freqtrade will download data for `"1m"` and `"5m"` timeframes for 30 days. Exchange and pairs will come from `config.json` (if specified using `-c/--config`). Otherwise `--exchange` becomes mandatory. Alternatively, a `pairs.json` file can be used. @@ -37,6 +37,10 @@ This will download ticker data for all the currency pairs you defined in `pairs. - Use `--timeframes` to specify which tickers to download. Default is `--timeframes 1m 5m` which will download 1-minute and 5-minute tickers. - To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options. +!!! Tip Updating existing data + If you already have backtesting data available in your data-directory and would like to refresh this data up to today, use `--days xx` with a number slightly higher than the missing number of days. Freqtrade will load the available data and only download the missing data. + Be carefull though: If the number is too small (which would result in a few missing days), the whole dataset will be removed and only xx days will be downloaded. + ## Test your strategy with Backtesting Now you have good Buy and Sell strategies and some historic data, you want to test it against From 3245ebccd473082de40de8901eb1705752b1889d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Sep 2019 11:24:51 +0200 Subject: [PATCH 09/29] Fix problme when no exchange is given to download-data --- freqtrade/configuration/check_exchange.py | 8 ++++++++ tests/test_configuration.py | 7 +++++++ tests/test_utils.py | 18 +++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index 61e862a9c..019081def 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -27,6 +27,14 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: logger.info("Checking exchange...") exchange = config.get('exchange', {}).get('name').lower() + if not exchange: + raise OperationalException( + f'This command requires a configured exchange. You can use either ' + f'`--exchange None: default_conf['runmode'] = RunMode.PLOT assert check_exchange(default_conf) + # Test no exchange... + default_conf.get('exchange').update({'name': ''}) + default_conf['runmode'] = RunMode.OTHER + with pytest.raises(OperationalException, + match=r'This command requires a configured exchange.*'): + check_exchange(default_conf) + def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) diff --git a/tests/test_utils.py b/tests/test_utils.py index 386efb5ec..8126e5055 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,10 +3,11 @@ from unittest.mock import MagicMock, PropertyMock import pytest +from freqtrade import OperationalException from freqtrade.state import RunMode -from tests.conftest import get_args, log_has, patch_exchange from freqtrade.utils import (setup_utils_configuration, start_create_userdir, start_download_data, start_list_exchanges) +from tests.conftest import get_args, log_has, log_has_re, patch_exchange def test_setup_utils_configuration(): @@ -103,3 +104,18 @@ def test_download_data_no_markets(mocker, caplog): start_download_data(get_args(args)) assert dl_mock.call_args[1]['timerange'].starttype == "date" assert log_has("Pairs [ETH/BTC,XRP/BTC] not available on exchange binance.", caplog) + + +def test_download_data_no_exchange(mocker, caplog): + mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data', + MagicMock(return_value=["ETH/BTC", "XRP/BTC"])) + patch_exchange(mocker) + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) + ) + args = [ + "download-data" + ] + with pytest.raises(OperationalException, + match=r"This command requires a configured exchange.*"): + start_download_data(get_args(args)) From 7aa42f8868a0afaa81b3d1a6d632c4c01a246bb6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Sep 2019 12:53:15 +0200 Subject: [PATCH 10/29] Fail download-data gracefully if no pairs-file exists --- freqtrade/utils.py | 6 ++++++ tests/test_utils.py | 24 ++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 5b2b08357..276c7267b 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -5,6 +5,7 @@ from typing import Any, Dict, List import arrow +from freqtrade import OperationalException from freqtrade.configuration import Configuration, TimeRange from freqtrade.configuration.directory_operations import create_userdata_dir from freqtrade.data.history import refresh_backtest_ohlcv_data @@ -70,6 +71,11 @@ def start_download_data(args: Dict[str, Any]) -> None: time_since = arrow.utcnow().shift(days=-config['days']).strftime("%Y%m%d") timerange = TimeRange.parse_timerange(f'{time_since}-') + if 'pairs' not in config: + raise OperationalException( + "Downloading data requires a list of pairs." + "Please check the documentation on how to configure this.") + dl_path = Path(config['datadir']) logger.info(f'About to download pairs: {config["pairs"]}, ' f'intervals: {config["timeframes"]} to {dl_path}') diff --git a/tests/test_utils.py b/tests/test_utils.py index 8126e5055..dc0badd01 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,4 +1,5 @@ import re +from pathlib import Path from unittest.mock import MagicMock, PropertyMock import pytest @@ -7,7 +8,7 @@ from freqtrade import OperationalException from freqtrade.state import RunMode from freqtrade.utils import (setup_utils_configuration, start_create_userdir, start_download_data, start_list_exchanges) -from tests.conftest import get_args, log_has, log_has_re, patch_exchange +from tests.conftest import get_args, log_has, patch_exchange def test_setup_utils_configuration(): @@ -114,8 +115,27 @@ def test_download_data_no_exchange(mocker, caplog): 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) ) args = [ - "download-data" + "download-data", ] with pytest.raises(OperationalException, match=r"This command requires a configured exchange.*"): start_download_data(get_args(args)) + + +def test_download_data_no_pairs(mocker, caplog): + mocker.patch.object(Path, "exists", MagicMock(return_value=False)) + + mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data', + MagicMock(return_value=["ETH/BTC", "XRP/BTC"])) + patch_exchange(mocker) + mocker.patch( + 'freqtrade.exchange.Exchange.markets', PropertyMock(return_value={}) + ) + args = [ + "download-data", + "--exchange", + "binance", + ] + with pytest.raises(OperationalException, + match=r"Downloading data requires a list of pairs\..*"): + start_download_data(get_args(args)) From ba4db0da493e501ea56cffe20767cf81c67fce15 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 21 Sep 2019 13:16:53 +0200 Subject: [PATCH 11/29] Improve configuration documentation --- docs/configuration.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index a1c0d1a75..08137fe79 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,14 +1,15 @@ # Configure the bot -This page explains how to configure the bot. +Freqtrade has many configurable options and possibilities. +By default, these options are configured via the configuration file (see below). ## The Freqtrade configuration file The bot uses a set of configuration parameters during its operation that all together conform the bot configuration. It normally reads its configuration from a file (Freqtrade configuration file). -Per default, the bot loads configuration from the `config.json` file located in the current working directory. +Per default, the bot loads the configuration from the file `config.json`, located in the current working directory. -You can change the name of the configuration file used by the bot with the `-c/--config` command line option. +You can use a different configuration file used by the bot with the `-c/--config` command line option. In some advanced use cases, multiple configuration files can be specified and used by the bot or the bot can read its configuration parameters from the process standard input stream. @@ -22,19 +23,26 @@ The Freqtrade configuration file is to be written in the JSON format. Additionally to the standard JSON syntax, you may use one-line `// ...` and multi-line `/* ... */` comments in your configuration files and trailing commas in the lists of parameters. -Do not worry if you are not familiar with JSON format -- simply open the configuration file with an editor of your choice, make some changes to the parameters you need, save your changes and, finally, restart the bot or, if it was previously stopped, run it again with the changes you made to the configuration. The bot validates syntax of the configuration file at startup and will warn you if you made any errors editing it. +Do not worry if you are not familiar with JSON format -- simply open the configuration file with an editor of your choice, make some changes to the parameters you need, save your changes and, finally, restart the bot or, if it was previously stopped, run it again with the changes you made to the configuration. The bot validates syntax of the configuration file at startup and will warn you if you made any errors editing it, pointing out problematic lines. ## Configuration parameters The table below will list all configuration parameters available. -Mandatory parameters are marked as **Required**. +Freqtrade can also load many options via command line arguments (check out the commands `--help` output for details). +The prevelance for all Options is as follows: + +- CLI arguments override any other option +- Configuration files are used in sequence (last file wins), and override Strategy configurations. +- Strategy configurations are only used if they are not set via configuration or via command line arguments. These options are market with [Strategy Override](#parameters-in-the-strategy) in the below table. + +Mandatory parameters are marked as **Required**, which means that they are required to be set in one of the possible ways. | Command | Default | Description | |----------|---------|-------------| | `max_open_trades` | 3 | **Required.** Number of trades open your bot will have. If -1 then it is ignored (i.e. potentially unlimited open trades) -| `stake_currency` | BTC | **Required.** Crypto-currency used for trading. [Strategy Override](#parameters-in-the-strategy). -| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance. [Strategy Override](#parameters-in-the-strategy). +| `stake_currency` | BTC | **Required.** Crypto-currency used for trading. +| `stake_amount` | 0.05 | **Required.** Amount of crypto-currency your bot will use for each trade. Per default, the bot will use (0.05 BTC x 3) = 0.15 BTC in total will be always engaged. Set it to `"unlimited"` to allow the bot to use all available balance. | `amount_reserve_percent` | 0.05 | Reserve some amount in min pair stake amount. Default is 5%. The bot will reserve `amount_reserve_percent` + stop-loss value when calculating min pair stake amount in order to avoid possible trade refusals. | `ticker_interval` | [1m, 5m, 15m, 30m, 1h, 1d, ...] | The ticker interval to use (1min, 5 min, 15 min, 30 min, 1 hour or 1 day). Default is 5 minutes. [Strategy Override](#parameters-in-the-strategy). | `fiat_display_currency` | USD | **Required.** Fiat currency used to show your profits. More information below. @@ -99,8 +107,6 @@ Mandatory parameters are marked as **Required**. The following parameters can be set in either configuration file or strategy. Values set in the configuration file always overwrite values set in the strategy. -* `stake_currency` -* `stake_amount` * `ticker_interval` * `minimal_roi` * `stoploss` From ab0adabd39ff057a85c97ecf3a2426b6ef68f9fc Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2019 08:54:11 +0000 Subject: [PATCH 12/29] Bump urllib3 from 1.25.3 to 1.25.5 Bumps [urllib3](https://github.com/urllib3/urllib3) from 1.25.3 to 1.25.5. - [Release notes](https://github.com/urllib3/urllib3/releases) - [Changelog](https://github.com/urllib3/urllib3/blob/master/CHANGES.rst) - [Commits](https://github.com/urllib3/urllib3/compare/1.25.3...1.25.5) 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 363890a4d..fe995836c 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -6,7 +6,7 @@ python-telegram-bot==12.1.0 arrow==0.15.2 cachetools==3.1.1 requests==2.22.0 -urllib3==1.25.3 +urllib3==1.25.5 wrapt==1.11.2 scikit-learn==0.21.3 joblib==0.13.2 From 242ff26e215c29933bd647d2c713775270d4988d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2019 08:54:57 +0000 Subject: [PATCH 13/29] Bump pytest from 5.1.2 to 5.1.3 Bumps [pytest](https://github.com/pytest-dev/pytest) from 5.1.2 to 5.1.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.1.2...5.1.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 1b9bf7570..2678130f3 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ flake8==3.7.8 flake8-type-annotations==0.1.0 flake8-tidy-imports==2.0.0 mypy==0.720 -pytest==5.1.2 +pytest==5.1.3 pytest-asyncio==0.10.0 pytest-cov==2.7.1 pytest-mock==1.10.4 From d8bc350445ba404bdbb0217184d7cf26aa2fd475 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2019 08:55:31 +0000 Subject: [PATCH 14/29] Bump python-telegram-bot from 12.1.0 to 12.1.1 Bumps [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) from 12.1.0 to 12.1.1. - [Release notes](https://github.com/python-telegram-bot/python-telegram-bot/releases) - [Changelog](https://github.com/python-telegram-bot/python-telegram-bot/blob/master/CHANGES.rst) - [Commits](https://github.com/python-telegram-bot/python-telegram-bot/compare/v12.1.0...v12.1.1) 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 363890a4d..c4b5383ae 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -2,7 +2,7 @@ # mainly used for Raspberry pi installs ccxt==1.18.1159 SQLAlchemy==1.3.8 -python-telegram-bot==12.1.0 +python-telegram-bot==12.1.1 arrow==0.15.2 cachetools==3.1.1 requests==2.22.0 From 0c6164df7eaa59c438279e44e03ba34614b1b36a Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 23 Sep 2019 11:59:34 +0300 Subject: [PATCH 15/29] Fix memory exhaustion in skopt models list --- freqtrade/optimize/hyperopt.py | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index c511aa5ac..04a05dc8f 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -36,6 +36,11 @@ logger = logging.getLogger(__name__) INITIAL_POINTS = 30 + +# Keep no more than 2*SKOPT_MODELS_MAX_NUM models +# in the skopt models list +SKOPT_MODELS_MAX_NUM = 10 + MAX_LOSS = 100000 # just a big enough number to be bad result in loss optimization @@ -255,12 +260,13 @@ class Hyperopt: spaces += self.custom_hyperopt.stoploss_space() return spaces - def generate_optimizer(self, _params: Dict) -> Dict: + def generate_optimizer(self, _params: Dict, iteration=None) -> Dict: """ Used Optimize function. Called once per epoch to optimize whatever is configured. Keep this function as optimized as possible! """ params = self.get_args(_params) + if self.has_space('roi'): self.backtesting.strategy.minimal_roi = \ self.custom_hyperopt.generate_roi_table(params) @@ -342,9 +348,25 @@ class Hyperopt: random_state=self.config.get('hyperopt_random_state', None) ) - def run_optimizer_parallel(self, parallel, asked) -> List: + def fix_optimizer_models_list(self): + """ + WORKAROUND: Since skopt is not actively supported, this resolves problems with skopt + memory usage, see also: https://github.com/scikit-optimize/scikit-optimize/pull/746 + + This may cease working when skopt updates if implementation of this intrinsic + part changes. + """ + n = len(self.opt.models) - SKOPT_MODELS_MAX_NUM + # Keep no more than 2*SKOPT_MODELS_MAX_NUM models in the skopt models list, + # remove the old ones. These are no really needed, the current model + # from the estimator is only used. + if n >= SKOPT_MODELS_MAX_NUM: + logger.debug(f"Fixing skopt models list, removing {n} old items...") + del self.opt.models[0:n] + + def run_optimizer_parallel(self, parallel, asked, i) -> List: return parallel(delayed( - wrap_non_picklable_objects(self.generate_optimizer))(v) for v in asked) + wrap_non_picklable_objects(self.generate_optimizer))(v, i) for v in asked) def load_previous_results(self): """ read trials file if we have one """ @@ -407,8 +429,9 @@ class Hyperopt: EVALS = max(self.total_epochs // jobs, 1) for i in range(EVALS): asked = self.opt.ask(n_points=jobs) - f_val = self.run_optimizer_parallel(parallel, asked) + f_val = self.run_optimizer_parallel(parallel, asked, i) self.opt.tell(asked, [v['loss'] for v in f_val]) + self.fix_optimizer_models_list() for j in range(jobs): current = i * jobs + j val = f_val[j] From 95e725c2b62c7752b92d61a270b85acfec87860a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 23 Sep 2019 10:15:28 +0000 Subject: [PATCH 16/29] Bump ccxt from 1.18.1159 to 1.18.1180 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.18.1159 to 1.18.1180. - [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.18.1159...1.18.1180) 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 c4b5383ae..cdaafd818 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.18.1159 +ccxt==1.18.1180 SQLAlchemy==1.3.8 python-telegram-bot==12.1.1 arrow==0.15.2 From 6ffb8b7a7017b834420a8de2f75a753a54a8f9c1 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Mon, 23 Sep 2019 13:25:31 +0300 Subject: [PATCH 17/29] Fix wordings in comment --- freqtrade/optimize/hyperopt.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 04a05dc8f..09d821962 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -358,8 +358,9 @@ class Hyperopt: """ n = len(self.opt.models) - SKOPT_MODELS_MAX_NUM # Keep no more than 2*SKOPT_MODELS_MAX_NUM models in the skopt models list, - # remove the old ones. These are no really needed, the current model - # from the estimator is only used. + # remove the old ones. These are actually of no use, the current model + # from the estimator is the only one used in the skopt optimizer. + # Freqtrade code also does not inspect details of the models. if n >= SKOPT_MODELS_MAX_NUM: logger.debug(f"Fixing skopt models list, removing {n} old items...") del self.opt.models[0:n] From 0f97a999fb7f49bb6de05fa4ba6c4a733cc45cc1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Sep 2019 06:35:41 +0200 Subject: [PATCH 18/29] Improve wording --- freqtrade/configuration/check_exchange.py | 4 ++-- freqtrade/utils.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/configuration/check_exchange.py b/freqtrade/configuration/check_exchange.py index 019081def..19c377732 100644 --- a/freqtrade/configuration/check_exchange.py +++ b/freqtrade/configuration/check_exchange.py @@ -29,8 +29,8 @@ def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool: exchange = config.get('exchange', {}).get('name').lower() if not exchange: raise OperationalException( - f'This command requires a configured exchange. You can use either ' - f'`--exchange ` or specify a configuration file via `--config`.\n' f'The following exchanges are supported by ccxt: ' f'{", ".join(available_exchanges())}' ) diff --git a/freqtrade/utils.py b/freqtrade/utils.py index 276c7267b..6ce5e888c 100644 --- a/freqtrade/utils.py +++ b/freqtrade/utils.py @@ -73,7 +73,7 @@ def start_download_data(args: Dict[str, Any]) -> None: if 'pairs' not in config: raise OperationalException( - "Downloading data requires a list of pairs." + "Downloading data requires a list of pairs. " "Please check the documentation on how to configure this.") dl_path = Path(config['datadir']) From 577b1fd965b7b8167b332ee3f30b5062eb556830 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Sep 2019 06:39:00 +0200 Subject: [PATCH 19/29] Improve documentation wording --- docs/backtesting.md | 2 +- docs/deprecated.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/backtesting.md b/docs/backtesting.md index 879cf78a3..004425323 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -38,7 +38,7 @@ This will download ticker data for all the currency pairs you defined in `pairs. - To use exchange, timeframe and list of pairs as defined in your configuration file, use the `-c/--config` option. With this, the script uses the whitelist defined in the config as the list of currency pairs to download data for and does not require the pairs.json file. You can combine `-c/--config` with most other options. !!! Tip Updating existing data - If you already have backtesting data available in your data-directory and would like to refresh this data up to today, use `--days xx` with a number slightly higher than the missing number of days. Freqtrade will load the available data and only download the missing data. + If you already have backtesting data available in your data-directory and would like to refresh this data up to today, use `--days xx` with a number slightly higher than the missing number of days. Freqtrade will keep the available data and only download the missing data. Be carefull though: If the number is too small (which would result in a few missing days), the whole dataset will be removed and only xx days will be downloaded. ## Test your strategy with Backtesting diff --git a/docs/deprecated.md b/docs/deprecated.md index 24c2bb1e3..349d41a09 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -12,7 +12,7 @@ and are no longer supported. Please avoid their usage in your configuration. Since this leads to much confusion, and slows down backtesting (while not being part of backtesting) this has been singled out as a seperate freqtrade subcommand `freqtrade download-data`. -This command line option was deprecated in `2019.7-dev` and removed in `2019-9` +This command line option was deprecated in 2019.7-dev (develop branch) and removed in 2019.9 (master branch). ### The **--dynamic-whitelist** command line option From fe40636ae10717a4e35ba8e104bd5f4f46333da5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Sep 2019 06:42:44 +0200 Subject: [PATCH 20/29] Improve wordings --- docs/configuration.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 08137fe79..0d902766a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -1,15 +1,15 @@ # Configure the bot -Freqtrade has many configurable options and possibilities. -By default, these options are configured via the configuration file (see below). +Freqtrade has many configurable features and possibilities. +By default, these settings are configured via the configuration file (see below). ## The Freqtrade configuration file The bot uses a set of configuration parameters during its operation that all together conform the bot configuration. It normally reads its configuration from a file (Freqtrade configuration file). -Per default, the bot loads the configuration from the file `config.json`, located in the current working directory. +Per default, the bot loads the configuration from the `config.json` file, located in the current working directory. -You can use a different configuration file used by the bot with the `-c/--config` command line option. +You can specify a different configuration file used by the bot with the `-c/--config` command line option. In some advanced use cases, multiple configuration files can be specified and used by the bot or the bot can read its configuration parameters from the process standard input stream. @@ -29,7 +29,7 @@ Do not worry if you are not familiar with JSON format -- simply open the configu The table below will list all configuration parameters available. -Freqtrade can also load many options via command line arguments (check out the commands `--help` output for details). +Freqtrade can also load many options via command line (CLI) arguments (check out the commands `--help` output for details). The prevelance for all Options is as follows: - CLI arguments override any other option From 6c0a1fc42cd279c0d7f4293b9e7fbe5d8919ae5b Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Sep 2019 11:07:12 +0200 Subject: [PATCH 21/29] Fix tests that fail when config.json is present --- tests/test_utils.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index dc0badd01..c99044610 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -117,12 +117,15 @@ def test_download_data_no_exchange(mocker, caplog): args = [ "download-data", ] + pargs = get_args(args) + pargs['config'] = None with pytest.raises(OperationalException, match=r"This command requires a configured exchange.*"): - start_download_data(get_args(args)) + start_download_data(pargs) def test_download_data_no_pairs(mocker, caplog): + mocker.patch.object(Path, "exists", MagicMock(return_value=False)) mocker.patch('freqtrade.utils.refresh_backtest_ohlcv_data', @@ -136,6 +139,8 @@ def test_download_data_no_pairs(mocker, caplog): "--exchange", "binance", ] + pargs = get_args(args) + pargs['config'] = None with pytest.raises(OperationalException, match=r"Downloading data requires a list of pairs\..*"): - start_download_data(get_args(args)) + start_download_data(pargs) From 665e0570ae9d2977e9cd67514213591cab3e50cc Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Wed, 25 Sep 2019 03:41:22 +0300 Subject: [PATCH 22/29] Fix hyperopt position stacking --- freqtrade/optimize/hyperopt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 61623d3df..f2984c111 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -90,7 +90,7 @@ class Hyperopt: else: logger.debug('Ignoring max_open_trades (--disable-max-market-positions was used) ...') self.max_open_trades = 0 - self.position_stacking = self.config.get('position_stacking', False), + self.position_stacking = self.config.get('position_stacking', False) if self.has_space('sell'): # Make sure experimental is enabled From 27cc73f47ed2936f3f3e2a98eee3095af302b3f2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Sep 2019 14:39:28 +0200 Subject: [PATCH 23/29] Dynamically import hyperopt modules --- freqtrade/optimize/__init__.py | 12 +++++++----- tests/optimize/test_hyperopt.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/__init__.py b/freqtrade/optimize/__init__.py index 7a3c290bf..3adf5eb43 100644 --- a/freqtrade/optimize/__init__.py +++ b/freqtrade/optimize/__init__.py @@ -1,9 +1,7 @@ import logging from typing import Any, Dict -from filelock import FileLock, Timeout - -from freqtrade import DependencyException, constants +from freqtrade import DependencyException, constants, OperationalException from freqtrade.state import RunMode from freqtrade.utils import setup_utils_configuration @@ -53,8 +51,12 @@ def start_hyperopt(args: Dict[str, Any]) -> None: :return: None """ # Import here to avoid loading hyperopt module when it's not used - from freqtrade.optimize.hyperopt import Hyperopt - + try: + from filelock import FileLock, Timeout + from freqtrade.optimize.hyperopt import Hyperopt + except ImportError as e: + raise OperationalException( + f"{e}. Please ensure that the hyperopt dependencies are installed.") from e # Initialize configuration config = setup_configuration(args, RunMode.HYPEROPT) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 0888c79d4..8ceea6caa 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -190,6 +190,37 @@ def test_hyperoptlossresolver_wrongname(mocker, default_conf, caplog) -> None: HyperOptLossResolver(default_conf, ).hyperopt +def test_start_not_installed(mocker, default_conf, caplog) -> None: + start_mock = MagicMock() + patched_configuration_load_config_file(mocker, default_conf) + # Source of this test-method: https://stackoverflow.com/questions/2481511/mocking-importerror-in-python + import builtins + realimport = builtins.__import__ + + def mockedimport(name, *args, **kwargs): + if name == "filelock": + raise ImportError("No module named 'filelock'") + return realimport(name, *args, **kwargs) + + builtins.__import__ = mockedimport + + mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) + patch_exchange(mocker) + + args = [ + '--config', 'config.json', + 'hyperopt', + '--epochs', '5' + ] + args = get_args(args) + + with pytest.raises(OperationalException, match=r"Please ensure that the hyperopt dependencies"): + start_hyperopt(args) + + # restore previous importfunction + builtins.__import__ = realimport + + def test_start(mocker, default_conf, caplog) -> None: start_mock = MagicMock() patched_configuration_load_config_file(mocker, default_conf) From 47b6b56566bd90752b44df044eaa66c5ca9c601f Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Sep 2019 14:53:36 +0200 Subject: [PATCH 24/29] Reorg dependencies to have hyperopt seperated --- environment.yml | 11 ++++++----- requirements-common.txt | 8 +------- requirements-dev.txt | 1 + requirements-hyperopt.txt | 9 +++++++++ requirements.txt | 1 - 5 files changed, 17 insertions(+), 13 deletions(-) create mode 100644 requirements-hyperopt.txt diff --git a/environment.yml b/environment.yml index cd3350fd5..4e8c1efcc 100644 --- a/environment.yml +++ b/environment.yml @@ -9,25 +9,26 @@ dependencies: - wheel - numpy - pandas - - scipy - SQLAlchemy - - scikit-learn - arrow - requests - urllib3 - wrapt - - joblib - jsonschema - tabulate - python-rapidjson - - filelock - flask - python-dotenv - cachetools - - scikit-optimize - python-telegram-bot # Optional for plotting - plotly + # Optional for hyperopt + - scipy + - scikit-optimize + - scikit-learn + - filelock + - joblib # Optional for development - flake8 - pytest diff --git a/requirements-common.txt b/requirements-common.txt index 70a37e695..f10134203 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -8,21 +8,15 @@ cachetools==3.1.1 requests==2.22.0 urllib3==1.25.5 wrapt==1.11.2 -scikit-learn==0.21.3 -joblib==0.13.2 jsonschema==3.0.2 TA-Lib==0.4.17 tabulate==0.8.3 coinmarketcap==5.0.3 -# Required for hyperopt -scikit-optimize==0.5.2 -filelock==3.0.12 - # find first, C search in arrays py_find_1st==1.1.4 -#Load ticker files 30% faster +# Load ticker files 30% faster python-rapidjson==0.8.0 # Notify systemd diff --git a/requirements-dev.txt b/requirements-dev.txt index 2678130f3..dcf2c7217 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,6 +1,7 @@ # Include all requirements to run the bot. -r requirements.txt -r requirements-plot.txt +-r requirements-hyperopt.txt coveralls==1.8.2 flake8==3.7.8 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt new file mode 100644 index 000000000..bb0ad60f0 --- /dev/null +++ b/requirements-hyperopt.txt @@ -0,0 +1,9 @@ +# Include all requirements to run the bot. +# -r requirements.txt + +# Required for hyperopt +scipy==1.3.1 +scikit-learn==0.21.3 +scikit-optimize==0.5.2 +filelock==3.0.12 +joblib==0.13.2 diff --git a/requirements.txt b/requirements.txt index 9a723fee4..2767180ac 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,3 @@ numpy==1.17.2 pandas==0.25.1 -scipy==1.3.1 From d2f247307092e5df9c67107f491655e991163d04 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 24 Sep 2019 15:03:40 +0200 Subject: [PATCH 25/29] install hyperopt seperately ([hyperopt]) --- setup.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index ca94dd338..2eb09e589 100644 --- a/setup.py +++ b/setup.py @@ -18,6 +18,13 @@ if readme_file.is_file(): # Requirements used for submodules api = ['flask'] plot = ['plotly>=4.0'] +hyperopt = [ + 'scipy', + 'scikit-learn', + 'scikit-optimize', + 'filelock', + 'joblib', + ] develop = [ 'coveralls', @@ -38,7 +45,7 @@ jupyter = [ 'ipykernel', ] -all_extra = api + plot + develop + jupyter +all_extra = api + plot + develop + jupyter + hyperopt setup(name='freqtrade', version=__version__, @@ -62,14 +69,10 @@ setup(name='freqtrade', 'requests', 'urllib3', 'wrapt', - 'scikit-learn', - 'joblib', 'jsonschema', 'TA-Lib', 'tabulate', 'coinmarketcap', - 'scikit-optimize', - 'filelock', 'py_find_1st', 'python-rapidjson', 'sdnotify', @@ -77,15 +80,14 @@ setup(name='freqtrade', # from requirements.txt 'numpy', 'pandas', - 'scipy', ], extras_require={ 'api': api, 'dev': all_extra, 'plot': plot, - 'all': all_extra, 'jupyter': jupyter, - + 'hyperopt': hyperopt, + 'all': all_extra, }, include_package_data=True, zip_safe=False, From d05db077e29666e4539bd929d67edb252be3b2dc Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Sep 2019 08:56:06 +0200 Subject: [PATCH 26/29] Update PI install documentation and dockerfile --- Dockerfile.pi | 4 ++-- docs/installation.md | 11 +++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/Dockerfile.pi b/Dockerfile.pi index 1b9c4c579..85ba5892f 100644 --- a/Dockerfile.pi +++ b/Dockerfile.pi @@ -22,13 +22,13 @@ RUN tar -xzf /freqtrade/ta-lib-0.4.0-src.tar.gz \ ENV LD_LIBRARY_PATH /usr/local/lib # Install berryconda -RUN wget https://github.com/jjhelmus/berryconda/releases/download/v2.0.0/Berryconda3-2.0.0-Linux-armv7l.sh \ +RUN wget -q https://github.com/jjhelmus/berryconda/releases/download/v2.0.0/Berryconda3-2.0.0-Linux-armv7l.sh \ && bash ./Berryconda3-2.0.0-Linux-armv7l.sh -b \ && rm Berryconda3-2.0.0-Linux-armv7l.sh # Install dependencies COPY requirements-common.txt /freqtrade/ -RUN ~/berryconda3/bin/conda install -y numpy pandas scipy \ +RUN ~/berryconda3/bin/conda install -y numpy pandas \ && ~/berryconda3/bin/pip install -r requirements-common.txt --no-cache-dir # Install and execute diff --git a/docs/installation.md b/docs/installation.md index f15cc356c..641879ff1 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -99,8 +99,8 @@ sudo apt-get install build-essential git Before installing FreqTrade on a Raspberry Pi running the official Raspbian Image, make sure you have at least Python 3.6 installed. The default image only provides Python 3.5. Probably the easiest way to get a recent version of python is [miniconda](https://repo.continuum.io/miniconda/). -The following assumes that miniconda3 is installed and available in your environment. Last miniconda3 installation file use python 3.4, we will update to python 3.6 on this installation. -It's recommended to use (mini)conda for this as installation/compilation of `numpy`, `scipy` and `pandas` takes a long time. +The following assumes that miniconda3 is installed and available in your environment. Since the last miniconda3 installation file uses python 3.4, we will update to python 3.6 on this installation. +It's recommended to use (mini)conda for this as installation/compilation of `numpy` and `pandas` takes a long time. Additional package to install on your Raspbian, `libffi-dev` required by cryptography (from python-telegram-bot). @@ -109,13 +109,17 @@ conda config --add channels rpi conda install python=3.6 conda create -n freqtrade python=3.6 conda activate freqtrade -conda install scipy pandas numpy +conda install pandas numpy sudo apt install libffi-dev python3 -m pip install -r requirements-common.txt python3 -m pip install -e . ``` +!!! Note + This does not install hyperopt dependencies. To install these, please use `python3 -m pip install -e .[hyperopt]`. + We do not advise to run hyperopt on a Raspberry Pi, since this is a very resource-heavy operation, which should be done on powerfull machine. + ### Common #### 1. Install TA-Lib @@ -175,7 +179,6 @@ cp config.json.example config.json ``` bash python3 -m pip install --upgrade pip -python3 -m pip install -r requirements.txt python3 -m pip install -e . ``` From e9de088209736e1f1f255c4339575ff7b34c38f2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 25 Sep 2019 11:54:57 +0200 Subject: [PATCH 27/29] Add import-fails code as a fixture --- tests/conftest.py | 21 +++++++++++++++++++++ tests/optimize/test_hyperopt.py | 15 +-------------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 8a5ba6683..6a0a74b5b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1054,3 +1054,24 @@ def rpc_balance(): def testdatadir() -> Path: """Return the path where testdata files are stored""" return (Path(__file__).parent / "testdata").resolve() + + +@pytest.fixture(scope="function") +def import_fails() -> None: + # Source of this test-method: + # https://stackoverflow.com/questions/2481511/mocking-importerror-in-python + import builtins + realimport = builtins.__import__ + + def mockedimport(name, *args, **kwargs): + if name in ["filelock"]: + raise ImportError(f"No module named '{name}'") + return realimport(name, *args, **kwargs) + + builtins.__import__ = mockedimport + + # Run test - then cleanup + yield + + # restore previous importfunction + builtins.__import__ = realimport diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 8ceea6caa..55f94f572 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -190,19 +190,9 @@ def test_hyperoptlossresolver_wrongname(mocker, default_conf, caplog) -> None: HyperOptLossResolver(default_conf, ).hyperopt -def test_start_not_installed(mocker, default_conf, caplog) -> None: +def test_start_not_installed(mocker, default_conf, caplog, import_fails) -> None: start_mock = MagicMock() patched_configuration_load_config_file(mocker, default_conf) - # Source of this test-method: https://stackoverflow.com/questions/2481511/mocking-importerror-in-python - import builtins - realimport = builtins.__import__ - - def mockedimport(name, *args, **kwargs): - if name == "filelock": - raise ImportError("No module named 'filelock'") - return realimport(name, *args, **kwargs) - - builtins.__import__ = mockedimport mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) patch_exchange(mocker) @@ -217,9 +207,6 @@ def test_start_not_installed(mocker, default_conf, caplog) -> None: with pytest.raises(OperationalException, match=r"Please ensure that the hyperopt dependencies"): start_hyperopt(args) - # restore previous importfunction - builtins.__import__ = realimport - def test_start(mocker, default_conf, caplog) -> None: start_mock = MagicMock() From 0268bfdbd4ac5aba98fde16a89256e11fdb558b7 Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Thu, 26 Sep 2019 02:04:48 +0300 Subject: [PATCH 28/29] Minor: fix typo in comment Minor cosmetics. typo caught. --- freqtrade/data/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/data/__init__.py b/freqtrade/data/__init__.py index 0a31d095c..0e7eea0d0 100644 --- a/freqtrade/data/__init__.py +++ b/freqtrade/data/__init__.py @@ -2,7 +2,7 @@ Module to handle data operations for freqtrade """ -# limit what's imported when using `from freqtrad.data import *`` +# limit what's imported when using `from freqtrade.data import *` __all__ = [ 'converter' ] From eb07f1fee9b5763a0e635c586b55e5d4d5243325 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 26 Sep 2019 09:31:31 +0200 Subject: [PATCH 29/29] Fix typo --- docs/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.md b/docs/installation.md index 641879ff1..081d7e0cf 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -118,7 +118,7 @@ python3 -m pip install -e . !!! Note This does not install hyperopt dependencies. To install these, please use `python3 -m pip install -e .[hyperopt]`. - We do not advise to run hyperopt on a Raspberry Pi, since this is a very resource-heavy operation, which should be done on powerfull machine. + We do not advise to run hyperopt on a Raspberry Pi, since this is a very resource-heavy operation, which should be done on powerful machine. ### Common