From 7606d814fa3158f7383ecca47e60ebb9c8be9595 Mon Sep 17 00:00:00 2001 From: Fredrik81 Date: Thu, 5 Mar 2020 01:58:33 +0100 Subject: [PATCH 01/29] Initial work on csv-file export. Missing docs and tests --- freqtrade/commands/arguments.py | 3 +- freqtrade/commands/cli_options.py | 6 +++ freqtrade/commands/hyperopt_commands.py | 12 +++++ freqtrade/configuration/configuration.py | 3 ++ freqtrade/optimize/hyperopt.py | 58 ++++++++++++++++++++++++ 5 files changed, 81 insertions(+), 1 deletion(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 73e77d69d..8a8b06782 100644 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -69,7 +69,8 @@ ARGS_HYPEROPT_LIST = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_list_min_avg_time", "hyperopt_list_max_avg_time", "hyperopt_list_min_avg_profit", "hyperopt_list_max_avg_profit", "hyperopt_list_min_total_profit", "hyperopt_list_max_total_profit", - "print_colorized", "print_json", "hyperopt_list_no_details"] + "print_colorized", "print_json", "hyperopt_list_no_details", + "export_csv"] ARGS_HYPEROPT_SHOW = ["hyperopt_list_best", "hyperopt_list_profitable", "hyperopt_show_index", "print_json", "hyperopt_show_no_header"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index ef674c5c2..77fba8eef 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -221,6 +221,12 @@ AVAILABLE_CLI_OPTIONS = { action='store_true', default=False, ), + "export_csv": Arg( + '--export-csv', + help='Export to CSV-File. Put + in front of filename to overwrite.' + 'Example: --export-csv +hyperopt.csv', + metavar='FILE', + ), "hyperopt_jobs": Arg( '-j', '--job-workers', help='The number of concurrently running jobs for hyperoptimization ' diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index 4803f6885..f4f119351 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -21,6 +21,7 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: print_colorized = config.get('print_colorized', False) print_json = config.get('print_json', False) + export_csv = config.get('export_csv', None) no_details = config.get('hyperopt_list_no_details', False) no_header = False @@ -59,6 +60,17 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: sorted_trials = sorted(trials, key=itemgetter('loss')) results = sorted_trials[0] Hyperopt.print_epoch_details(results, total_epochs, print_json, no_header) + print(export_csv) + if trials and export_csv: + overwrite_csv = False + if export_csv[0] == '+': + overwrite_csv = True + export_csv = export_csv[1:] + + Hyperopt.export_csv_file( + config, trials, total_epochs, + not filteroptions['only_best'], export_csv, overwrite_csv + ) def start_hyperopt_show(args: Dict[str, Any]) -> None: diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 6a0441957..0645d72be 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -282,6 +282,9 @@ class Configuration: self._args_to_config(config, argname='print_json', logstring='Parameter --print-json detected ...') + self._args_to_config(config, argname='export_csv', + logstring='Parameter --export-csv detected: {}') + self._args_to_config(config, argname='hyperopt_jobs', logstring='Parameter -j/--job-workers detected: {}') diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index e9ab469f4..d397c720c 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -23,6 +23,8 @@ from joblib import (Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects) from pandas import DataFrame, json_normalize, isna import tabulate +from os import path +import io from freqtrade.data.converter import trim_dataframe from freqtrade.data.history import get_timerange @@ -381,6 +383,62 @@ class Hyperopt: ) print(table) + @staticmethod + def export_csv_file(config: dict, results: list, total_epochs: int, highlight_best: bool, + csv_file: str, overwrite: bool) -> None: + """ + Log result to csv-file + """ + if not results: + return + + # Verification for owerwrite + if not overwrite and path.isfile(csv_file): + logging.error("CSV-File already exists and no overwrite specified!") + return + + try: + io.open(csv_file, 'w+').close() + except IOError: + logging.error("Filed to create/overwrite CSV-File!") + return + + trials = json_normalize(results, max_level=1) + trials['Best'] = '' + trials['Stake currency'] = config['stake_currency'] + trials = trials[['Best', 'current_epoch', 'results_metrics.trade_count', + 'results_metrics.avg_profit', 'results_metrics.total_profit', + 'Stake currency', 'results_metrics.profit', 'results_metrics.duration', + 'loss', 'is_initial_point', 'is_best']] + trials.columns = ['Best', 'Epoch', 'Trades', 'Avg profit', 'Total profit', 'Stake currency', + 'Profit', 'Avg duration', 'Objective', 'is_initial_point', 'is_best'] + trials['is_profit'] = False + trials.loc[trials['is_initial_point'], 'Best'] = '*' + trials.loc[trials['is_best'], 'Best'] = 'Best' + trials.loc[trials['Total profit'] > 0, 'is_profit'] = True + trials['Epoch'] = trials['Epoch'].astype(str) + trials['Trades'] = trials['Trades'].astype(str) + + trials['Total profit'] = trials['Total profit'].apply( + lambda x: '{:,.8f}'.format(x) if x != 0.0 else "--" + ) + trials['Profit'] = trials['Profit'].apply( + lambda x: '{:,.2f}'.format(x) if not isna(x) else "--" + ) + trials['Avg profit'] = trials['Avg profit'].apply( + lambda x: ('{:,.2f}%'.format(x)) if not isna(x) else "--" + ) + trials['Avg duration'] = trials['Avg duration'].apply( + lambda x: ('{:,.1f} m'.format(x)) if not isna(x) else "--" + ) + trials['Objective'] = trials['Objective'].apply( + lambda x: '{:,.5f}'.format(x) if x != 100000 else "N/A" + ) + + trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit']) + trials.to_csv(csv_file, index=False, header=True, mode='w', encoding='UTF-8') + print("CSV-File created!") + def has_space(self, space: str) -> bool: """ Tell if the space value is contained in the configuration From 91db75a7073763d8c00c1057b5485d50e5103e46 Mon Sep 17 00:00:00 2001 From: Fredrik81 Date: Thu, 5 Mar 2020 19:43:43 +0100 Subject: [PATCH 02/29] Added tests and updated doc --- docs/utils.md | 3 +++ freqtrade/commands/cli_options.py | 3 ++- freqtrade/commands/hyperopt_commands.py | 13 +++++++------ tests/commands/test_commands.py | 16 +++++++++++++++- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index cdf0c31af..dd7a9dfe3 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -450,6 +450,9 @@ optional arguments: useful if you are redirecting output to a file. --print-json Print best result detailization in JSON format. --no-details Do not print best epoch details. + --export-csv FILE Export to CSV-File. Put + in front of filename to + overwrite. This will disable table print. Example: + --export-csv +hyperopt.csv Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 77fba8eef..b782c2fb9 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -224,7 +224,8 @@ AVAILABLE_CLI_OPTIONS = { "export_csv": Arg( '--export-csv', help='Export to CSV-File. Put + in front of filename to overwrite.' - 'Example: --export-csv +hyperopt.csv', + ' This will disable table print.' + ' Example: --export-csv +hyperopt.csv', metavar='FILE', ), "hyperopt_jobs": Arg( diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index f4f119351..efc9aba88 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -50,17 +50,18 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: if print_colorized: colorama_init(autoreset=True) - try: - Hyperopt.print_result_table(config, trials, total_epochs, - not filteroptions['only_best'], print_colorized, 0) - except KeyboardInterrupt: - print('User interrupted..') + if not export_csv: + try: + Hyperopt.print_result_table(config, trials, total_epochs, + not filteroptions['only_best'], print_colorized, 0) + except KeyboardInterrupt: + print('User interrupted..') if trials and not no_details: sorted_trials = sorted(trials, key=itemgetter('loss')) results = sorted_trials[0] Hyperopt.print_epoch_details(results, total_epochs, print_json, no_header) - print(export_csv) + if trials and export_csv: overwrite_csv = False if export_csv[0] == '+': diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 3e1c0a581..58e97fd49 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -902,7 +902,21 @@ def test_hyperopt_list(mocker, capsys, hyperopt_results): assert all(x not in captured.out for x in [" 1/12", " 3/12", " 4/12", " 5/12", " 7/12", " 8/12" " 9/12", " 10/12", " 11/12", " 12/12"]) - + args = [ + "hyperopt-list", + "--no-details", + "--export-csv", "+test_file.csv" + ] + pargs = get_args(args) + pargs['config'] = None + start_hyperopt_list(pargs) + captured = capsys.readouterr() + assert all(x in captured.out + for x in ["CSV-File created!"]) + f = Path("test_file.csv") + assert 'Best,1,2,-1.25%,-0.00125625,,-2.51,"3,930.0 m",0.43662' in f.read_text() + assert f.is_file() + f.unlink() def test_hyperopt_show(mocker, capsys, hyperopt_results): mocker.patch( From f0d56e23a340e77dac0c6cd64ea3de2eeaedc1c4 Mon Sep 17 00:00:00 2001 From: Fredrik81 Date: Thu, 5 Mar 2020 19:58:01 +0100 Subject: [PATCH 03/29] PEP8 fix --- tests/commands/test_commands.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 58e97fd49..f404a4671 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -918,6 +918,7 @@ def test_hyperopt_list(mocker, capsys, hyperopt_results): assert f.is_file() f.unlink() + def test_hyperopt_show(mocker, capsys, hyperopt_results): mocker.patch( 'freqtrade.optimize.hyperopt.Hyperopt.load_previous_results', From 4ad93ed6bbe2f9f5b653552d05f7d9b73fb9a30c Mon Sep 17 00:00:00 2001 From: Fredrik81 Date: Sun, 8 Mar 2020 22:41:05 +0100 Subject: [PATCH 04/29] Changed output for null columns --- freqtrade/optimize/hyperopt.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index d397c720c..f0c992760 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -420,19 +420,19 @@ class Hyperopt: trials['Trades'] = trials['Trades'].astype(str) trials['Total profit'] = trials['Total profit'].apply( - lambda x: '{:,.8f}'.format(x) if x != 0.0 else "--" + lambda x: '{:,.8f}'.format(x) if x != 0.0 else "" ) trials['Profit'] = trials['Profit'].apply( - lambda x: '{:,.2f}'.format(x) if not isna(x) else "--" + lambda x: '{:,.2f}'.format(x) if not isna(x) else "" ) trials['Avg profit'] = trials['Avg profit'].apply( - lambda x: ('{:,.2f}%'.format(x)) if not isna(x) else "--" + lambda x: ('{:,.2f}%'.format(x)) if not isna(x) else "" ) trials['Avg duration'] = trials['Avg duration'].apply( - lambda x: ('{:,.1f} m'.format(x)) if not isna(x) else "--" + lambda x: ('{:,.1f} m'.format(x)) if not isna(x) else "" ) trials['Objective'] = trials['Objective'].apply( - lambda x: '{:,.5f}'.format(x) if x != 100000 else "N/A" + lambda x: '{:,.5f}'.format(x) if x != 100000 else "" ) trials = trials.drop(columns=['is_initial_point', 'is_best', 'is_profit']) From cb419614cd10ae7e4fb063e4bc2b28c795a0ce31 Mon Sep 17 00:00:00 2001 From: Fredrik81 Date: Sun, 8 Mar 2020 23:00:21 +0100 Subject: [PATCH 05/29] Spelling miss --- 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 f0c992760..d12e9cd45 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -392,7 +392,7 @@ class Hyperopt: if not results: return - # Verification for owerwrite + # Verification for overwrite if not overwrite and path.isfile(csv_file): logging.error("CSV-File already exists and no overwrite specified!") return From c7b2f173ebcade7092be49f4d2aa87ec19745768 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2020 08:32:42 +0000 Subject: [PATCH 06/29] Bump wrapt from 1.12.0 to 1.12.1 Bumps [wrapt](https://github.com/GrahamDumpleton/wrapt) from 1.12.0 to 1.12.1. - [Release notes](https://github.com/GrahamDumpleton/wrapt/releases) - [Changelog](https://github.com/GrahamDumpleton/wrapt/blob/develop/docs/changes.rst) - [Commits](https://github.com/GrahamDumpleton/wrapt/compare/1.12.0...1.12.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 a844e81bc..e44881041 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -7,7 +7,7 @@ arrow==0.15.5 cachetools==4.0.0 requests==2.23.0 urllib3==1.25.8 -wrapt==1.12.0 +wrapt==1.12.1 jsonschema==3.2.0 TA-Lib==0.4.17 tabulate==0.8.6 From 09c25faa51a68ab7c0e42795bc809b0da135782a Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2020 08:33:37 +0000 Subject: [PATCH 07/29] Bump ccxt from 1.23.30 to 1.23.81 Bumps [ccxt](https://github.com/ccxt/ccxt) from 1.23.30 to 1.23.81. - [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.23.30...1.23.81) 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 a844e81bc..f64644db9 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.23.30 +ccxt==1.23.81 SQLAlchemy==1.3.13 python-telegram-bot==12.4.2 arrow==0.15.5 From 46763e148b6ccec222d849467f0cabb60f900f7e Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2020 08:33:59 +0000 Subject: [PATCH 08/29] Bump prompt-toolkit from 3.0.3 to 3.0.4 Bumps [prompt-toolkit](https://github.com/prompt-toolkit/python-prompt-toolkit) from 3.0.3 to 3.0.4. - [Release notes](https://github.com/prompt-toolkit/python-prompt-toolkit/releases) - [Changelog](https://github.com/prompt-toolkit/python-prompt-toolkit/blob/master/CHANGELOG) - [Commits](https://github.com/prompt-toolkit/python-prompt-toolkit/compare/3.0.3...3.0.4) 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 a844e81bc..ec12f1c65 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -30,4 +30,4 @@ flask==1.1.1 colorama==0.4.3 # Building config files interactively questionary==1.5.1 -prompt-toolkit==3.0.3 +prompt-toolkit==3.0.4 From 23127b8da08ab45641a955dc94b58bfec582d38d Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2020 08:34:33 +0000 Subject: [PATCH 09/29] Bump scikit-learn from 0.22.2 to 0.22.2.post1 Bumps [scikit-learn](https://github.com/scikit-learn/scikit-learn) from 0.22.2 to 0.22.2.post1. - [Release notes](https://github.com/scikit-learn/scikit-learn/releases) - [Commits](https://github.com/scikit-learn/scikit-learn/compare/0.22.2...0.22.2.post1) Signed-off-by: dependabot-preview[bot] --- requirements-hyperopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index c713317ec..c7e586a33 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -3,7 +3,7 @@ # Required for hyperopt scipy==1.4.1 -scikit-learn==0.22.2 +scikit-learn==0.22.2.post1 scikit-optimize==0.7.4 filelock==3.0.12 joblib==0.14.1 From 4cc0d3dbc42600136a7539e7f523a31f375683c9 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2020 08:35:16 +0000 Subject: [PATCH 10/29] Bump plotly from 4.5.2 to 4.5.3 Bumps [plotly](https://github.com/plotly/plotly.py) from 4.5.2 to 4.5.3. - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v4.5.2...v4.5.3) Signed-off-by: dependabot-preview[bot] --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index a70c3e0cf..381334a66 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,5 +1,5 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==4.5.2 +plotly==4.5.3 From 5cbf325fda4f0a3d9bb6032122aa56d085118d83 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Mar 2020 11:30:13 +0100 Subject: [PATCH 11/29] Allow different loglevels for message --- freqtrade/pairlist/IPairList.py | 24 ++++++++++++++++++++---- freqtrade/pairlist/VolumePairList.py | 2 +- freqtrade/pairlist/pairlistmanager.py | 2 +- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/freqtrade/pairlist/IPairList.py b/freqtrade/pairlist/IPairList.py index d45a329dd..f591fa8da 100644 --- a/freqtrade/pairlist/IPairList.py +++ b/freqtrade/pairlist/IPairList.py @@ -67,21 +67,37 @@ class IPairList(ABC): """ @staticmethod - def verify_blacklist(pairlist: List[str], blacklist: List[str]) -> List[str]: + def verify_blacklist(pairlist: List[str], blacklist: List[str], + aswarning: bool) -> List[str]: """ Verify and remove items from pairlist - returning a filtered pairlist. + Logs a warning or info depending on `aswarning`. + Pairlists explicitly using this method shall use `aswarning=False`! + :param pairlist: Pairlist to validate + :param blacklist: Blacklist to validate pairlist against + :param aswarning: Log message as Warning or info + :return: pairlist - blacklisted pairs """ for pair in deepcopy(pairlist): if pair in blacklist: - logger.warning(f"Pair {pair} in your blacklist. Removing it from whitelist...") + if aswarning: + logger.warning(f"Pair {pair} in your blacklist. Removing it from whitelist...") + else: + logger.info(f"Pair {pair} in your blacklist. Removing it from whitelist...") pairlist.remove(pair) return pairlist - def _verify_blacklist(self, pairlist: List[str]) -> List[str]: + def _verify_blacklist(self, pairlist: List[str], aswarning: bool = True) -> List[str]: """ Proxy method to verify_blacklist for easy access for child classes. + Logs a warning or info depending on `aswarning`. + Pairlists explicitly using this method shall use aswarning=False! + :param pairlist: Pairlist to validate + :param aswarning: Log message as Warning or info. + :return: pairlist - blacklisted pairs """ - return IPairList.verify_blacklist(pairlist, self._pairlistmanager.blacklist) + return IPairList.verify_blacklist(pairlist, self._pairlistmanager.blacklist, + aswarning=aswarning) def _whitelist_for_active_markets(self, pairlist: List[str]) -> List[str]: """ diff --git a/freqtrade/pairlist/VolumePairList.py b/freqtrade/pairlist/VolumePairList.py index d067d5e8a..9ce2adc9e 100644 --- a/freqtrade/pairlist/VolumePairList.py +++ b/freqtrade/pairlist/VolumePairList.py @@ -106,7 +106,7 @@ class VolumePairList(IPairList): # Validate whitelist to only have active market pairs pairs = self._whitelist_for_active_markets([s['symbol'] for s in sorted_tickers]) - pairs = self._verify_blacklist(pairs) + pairs = self._verify_blacklist(pairs, aswarning=False) # Limit to X number of pairs pairs = pairs[:self._number_pairs] logger.info(f"Searching {self._number_pairs} pairs: {pairs}") diff --git a/freqtrade/pairlist/pairlistmanager.py b/freqtrade/pairlist/pairlistmanager.py index 55828c6ef..5b4c5b602 100644 --- a/freqtrade/pairlist/pairlistmanager.py +++ b/freqtrade/pairlist/pairlistmanager.py @@ -91,6 +91,6 @@ class PairListManager(): pairlist = pl.filter_pairlist(pairlist, tickers) # Validation against blacklist happens after the pairlists to ensure blacklist is respected. - pairlist = IPairList.verify_blacklist(pairlist, self.blacklist) + pairlist = IPairList.verify_blacklist(pairlist, self.blacklist, True) self._whitelist = pairlist From c049651784491f82807a07cbc2b3d3d6a8f57e22 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Mar 2020 11:30:28 +0100 Subject: [PATCH 12/29] whitelist_for_active_markets should not remove blacklisted items --- freqtrade/pairlist/IPairList.py | 1 - tests/pairlist/test_pairlist.py | 2 -- 2 files changed, 3 deletions(-) diff --git a/freqtrade/pairlist/IPairList.py b/freqtrade/pairlist/IPairList.py index f591fa8da..35844a99e 100644 --- a/freqtrade/pairlist/IPairList.py +++ b/freqtrade/pairlist/IPairList.py @@ -129,6 +129,5 @@ class IPairList(ABC): if pair not in sanitized_whitelist: sanitized_whitelist.append(pair) - sanitized_whitelist = self._verify_blacklist(sanitized_whitelist) # We need to remove pairs that are unknown return sanitized_whitelist diff --git a/tests/pairlist/test_pairlist.py b/tests/pairlist/test_pairlist.py index b8a4be037..1ce1151b7 100644 --- a/tests/pairlist/test_pairlist.py +++ b/tests/pairlist/test_pairlist.py @@ -240,8 +240,6 @@ def test_pairlist_class(mocker, whitelist_conf, markets, pairlist): (['ETH/BTC', 'TKN/BTC', 'ETH/USDT'], "is not compatible with your stake currency"), # BCH/BTC not available (['ETH/BTC', 'TKN/BTC', 'BCH/BTC'], "is not compatible with exchange"), - # BLK/BTC in blacklist - (['ETH/BTC', 'TKN/BTC', 'BLK/BTC'], "in your blacklist. Removing "), # BTT/BTC is inactive (['ETH/BTC', 'TKN/BTC', 'BTT/BTC'], "Market is not active") ]) From 856ba203d959716d40a5f0bab3569e63b807d76d Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Mar 2020 15:04:28 +0100 Subject: [PATCH 13/29] Update hyperopt samples docstring --- freqtrade/templates/base_hyperopt.py.j2 | 15 +++++++++------ freqtrade/templates/sample_hyperopt.py | 19 ++++++++++++------- .../templates/sample_hyperopt_advanced.py | 7 ++++--- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/freqtrade/templates/base_hyperopt.py.j2 b/freqtrade/templates/base_hyperopt.py.j2 index 05ba08b81..08178da4b 100644 --- a/freqtrade/templates/base_hyperopt.py.j2 +++ b/freqtrade/templates/base_hyperopt.py.j2 @@ -21,7 +21,7 @@ class {{ hyperopt }}(IHyperOpt): """ This is a Hyperopt template to get you started. - More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md + More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/ You should: - Add any lib you need to build your hyperopt. @@ -29,11 +29,14 @@ class {{ hyperopt }}(IHyperOpt): You must keep: - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - The roi_space, generate_roi_table, stoploss_space methods are no longer required to be - copied in every custom hyperopt. However, you may override them if you need the - 'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade. - Sample implementation of these methods can be found in - https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py + The methods roi_space, generate_roi_table and stoploss_space are not required + and are provided by default. + However, you may override them if you need 'roi' and 'stoploss' spaces that + differ from the defaults offered by Freqtrade. + Sample implementation of these methods will be copied to `user_data/hyperopts` when + creating the user-data directory using `freqtrade create-userdir --userdir user_data`, + or is available online under the following URL: + https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py. """ @staticmethod diff --git a/freqtrade/templates/sample_hyperopt.py b/freqtrade/templates/sample_hyperopt.py index f1dcb404a..0baa00442 100644 --- a/freqtrade/templates/sample_hyperopt.py +++ b/freqtrade/templates/sample_hyperopt.py @@ -20,23 +20,28 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib class SampleHyperOpt(IHyperOpt): """ This is a sample Hyperopt to inspire you. - Feel free to customize it. - More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md + More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/ You should: - Rename the class name to some unique name. - Add any methods you want to build your hyperopt. - Add any lib you need to build your hyperopt. + An easier way to get a new hyperopt file is by using + `freqtrade new-hyperopt --hyperopt MyCoolHyperopt`. + You must keep: - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - The roi_space, generate_roi_table, stoploss_space methods are no longer required to be - copied in every custom hyperopt. However, you may override them if you need the - 'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade. - Sample implementation of these methods can be found in - https://github.com/freqtrade/freqtrade/blob/develop/user_data/hyperopts/sample_hyperopt_advanced.py + The methods roi_space, generate_roi_table and stoploss_space are not required + and are provided by default. + However, you may override them if you need 'roi' and 'stoploss' spaces that + differ from the defaults offered by Freqtrade. + Sample implementation of these methods will be copied to `user_data/hyperopts` when + creating the user-data directory using `freqtrade create-userdir --userdir user_data`, + or is available online under the following URL: + https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_hyperopt_advanced.py. """ @staticmethod diff --git a/freqtrade/templates/sample_hyperopt_advanced.py b/freqtrade/templates/sample_hyperopt_advanced.py index e66ef948b..c8067ad28 100644 --- a/freqtrade/templates/sample_hyperopt_advanced.py +++ b/freqtrade/templates/sample_hyperopt_advanced.py @@ -22,7 +22,7 @@ class AdvancedSampleHyperOpt(IHyperOpt): This is a sample hyperopt to inspire you. Feel free to customize it. - More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md + More information in the documentation: https://www.freqtrade.io/en/latest/hyperopt/ You should: - Rename the class name to some unique name. @@ -32,8 +32,9 @@ class AdvancedSampleHyperOpt(IHyperOpt): You must keep: - The prototypes for the methods: populate_indicators, indicator_space, buy_strategy_generator. - The roi_space, generate_roi_table, stoploss_space methods are no longer required to be - copied in every custom hyperopt. However, you may override them if you need the + The methods roi_space, generate_roi_table and stoploss_space are not required + and are provided by default. + However, you may override them if you need the 'roi' and the 'stoploss' spaces that differ from the defaults offered by Freqtrade. This sample illustrates how to override these methods. From 5da63d399be30908273ce7b2db7178e380a54dad Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Mar 2020 17:38:25 +0100 Subject: [PATCH 14/29] Reduce default order_book_max to 1 --- config.json.example | 2 +- config_binance.json.example | 2 +- config_full.json.example | 2 +- config_kraken.json.example | 2 +- freqtrade/templates/base_config.json.j2 | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config.json.example b/config.json.example index 46441e72d..8ebb092e1 100644 --- a/config.json.example +++ b/config.json.example @@ -23,7 +23,7 @@ "ask_strategy":{ "use_order_book": false, "order_book_min": 1, - "order_book_max": 9, + "order_book_max": 1, "use_sell_signal": true, "sell_profit_only": false, "ignore_roi_if_buy_signal": false diff --git a/config_binance.json.example b/config_binance.json.example index e2c9879b0..d324ce883 100644 --- a/config_binance.json.example +++ b/config_binance.json.example @@ -23,7 +23,7 @@ "ask_strategy":{ "use_order_book": false, "order_book_min": 1, - "order_book_max": 9, + "order_book_max": 1, "use_sell_signal": true, "sell_profit_only": false, "ignore_roi_if_buy_signal": false diff --git a/config_full.json.example b/config_full.json.example index f0414bd0d..181740b9a 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -38,7 +38,7 @@ "price_side": "ask", "use_order_book": false, "order_book_min": 1, - "order_book_max": 9, + "order_book_max": 1, "use_sell_signal": true, "sell_profit_only": false, "ignore_roi_if_buy_signal": false diff --git a/config_kraken.json.example b/config_kraken.json.example index 4f74d0b7d..dcf4c552a 100644 --- a/config_kraken.json.example +++ b/config_kraken.json.example @@ -23,7 +23,7 @@ "ask_strategy":{ "use_order_book": false, "order_book_min": 1, - "order_book_max": 9, + "order_book_max": 1, "use_sell_signal": true, "sell_profit_only": false, "ignore_roi_if_buy_signal": false diff --git a/freqtrade/templates/base_config.json.j2 b/freqtrade/templates/base_config.json.j2 index 0049d59a0..134719273 100644 --- a/freqtrade/templates/base_config.json.j2 +++ b/freqtrade/templates/base_config.json.j2 @@ -24,7 +24,7 @@ "price_side": "ask", "use_order_book": false, "order_book_min": 1, - "order_book_max": 9, + "order_book_max": 1, "use_sell_signal": true, "sell_profit_only": false, "ignore_roi_if_buy_signal": false From 74a17c7b7b45cac99238bcb684e7089c5b92c735 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Mar 2020 17:38:35 +0100 Subject: [PATCH 15/29] Clarify warning in the documentation --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index 5580b9c68..aedd3e043 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -537,7 +537,7 @@ The idea here is to place the sell order early, to be ahead in the queue. A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting `ask_strategy.order_book_min` and `ask_strategy.order_book_max` to the same number. !!! Warning "Orderbook and stoploss_on_exchange" - Using `ask_strategy.order_book_max` higher than 1 may increase the risk, since an eventual [stoploss on exchange](#understand-order_types) will be needed to be cancelled as soon as the order is placed. + Using `ask_strategy.order_book_max` higher than 1 will increase the risk, since an eventual [stoploss on exchange](#understand-order_types) will be needed to be cancelled as soon as the order is placed. Also, the sell order will remain on the exchange for `unfilledtimeout.sell` (or until it's filled) - which can lead to missed stoplosses (even without stoploss on exchange). #### Sell price without Orderbook enabled From e0afbcd4af8e2b1e51a5eaca2b7e5ecd457f9f53 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 9 Mar 2020 17:41:44 +0100 Subject: [PATCH 16/29] Additional warning about order_book-max --- docs/configuration.md | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index aedd3e043..a9487a0ed 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -536,8 +536,14 @@ The idea here is to place the sell order early, to be ahead in the queue. A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting `ask_strategy.order_book_min` and `ask_strategy.order_book_max` to the same number. -!!! Warning "Orderbook and stoploss_on_exchange" - Using `ask_strategy.order_book_max` higher than 1 will increase the risk, since an eventual [stoploss on exchange](#understand-order_types) will be needed to be cancelled as soon as the order is placed. Also, the sell order will remain on the exchange for `unfilledtimeout.sell` (or until it's filled) - which can lead to missed stoplosses (even without stoploss on exchange). +!!! Warning "Order_book_max > 1 - increased Risk!" + Using `ask_strategy.order_book_max` higher than 1 will increase the risk, since an eventual [stoploss on exchange](#understand-order_types) will be needed to be cancelled as soon as the order is placed. + Also, the sell order will remain on the exchange for `unfilledtimeout.sell` (or until it's filled) - which can lead to missed stoplosses (with or without using stoploss_on_exchange). + +!!! Warning "Order_book_max > 1 in dry-run" + Using `ask_strategy.order_book_max` higher than 1 will result in improved dry-run results, since dry-run assumes orders to be filled almost instantly. + It is therefore advised to not use this setting for dry-runs. + #### Sell price without Orderbook enabled From 3eaae4661d3256ca27c92329b60661734c3ed716 Mon Sep 17 00:00:00 2001 From: orehunt Date: Mon, 9 Mar 2020 07:39:23 +0100 Subject: [PATCH 17/29] check again for emptiness after trimming dataframe --- freqtrade/data/history/idatahandler.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/freqtrade/data/history/idatahandler.py b/freqtrade/data/history/idatahandler.py index df03e7713..87810c95f 100644 --- a/freqtrade/data/history/idatahandler.py +++ b/freqtrade/data/history/idatahandler.py @@ -160,6 +160,13 @@ class IDataHandler(ABC): if timerange_startup: self._validate_pairdata(pair, pairdf, timerange_startup) pairdf = trim_dataframe(pairdf, timerange_startup) + if pairdf.empty: + if warn_no_data: + logger.warning( + f'No history data for pair: "{pair}", timeframe: {timeframe}. ' + 'Use `freqtrade download-data` to download the data' + ) + return pairdf # incomplete candles should only be dropped if we didn't trim the end beforehand. return clean_ohlcv_dataframe(pairdf, timeframe, From 2f5fc731bba51239a3195cbd0fb79e9e58a20d33 Mon Sep 17 00:00:00 2001 From: Fredrik81 Date: Mon, 9 Mar 2020 18:53:30 +0100 Subject: [PATCH 18/29] Removed overwrite option --- docs/utils.md | 13 +++++++------ freqtrade/commands/cli_options.py | 4 ++-- freqtrade/commands/hyperopt_commands.py | 8 +------- freqtrade/optimize/hyperopt.py | 6 +++--- tests/commands/test_commands.py | 2 +- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/docs/utils.md b/docs/utils.md index dd7a9dfe3..eb71c509c 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -429,6 +429,7 @@ usage: freqtrade hyperopt-list [-h] [-v] [--logfile FILE] [-V] [-c PATH] [--min-total-profit FLOAT] [--max-total-profit FLOAT] [--no-color] [--print-json] [--no-details] + [--export-csv FILE] optional arguments: -h, --help show this help message and exit @@ -450,9 +451,8 @@ optional arguments: useful if you are redirecting output to a file. --print-json Print best result detailization in JSON format. --no-details Do not print best epoch details. - --export-csv FILE Export to CSV-File. Put + in front of filename to - overwrite. This will disable table print. Example: - --export-csv +hyperopt.csv + --export-csv FILE Export to CSV-File. This will disable table print. + Example: --export-csv hyperopt.csv Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). @@ -461,9 +461,10 @@ Common arguments: details. -V, --version show program's version number and exit -c PATH, --config PATH - Specify configuration file (default: `config.json`). - Multiple --config options may be used. Can be set to - `-` to read config from stdin. + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. -d PATH, --datadir PATH Path to directory with historical backtesting data. --userdir PATH, --user-data-dir PATH diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index b782c2fb9..8548bd887 100644 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -223,9 +223,9 @@ AVAILABLE_CLI_OPTIONS = { ), "export_csv": Arg( '--export-csv', - help='Export to CSV-File. Put + in front of filename to overwrite.' + help='Export to CSV-File.' ' This will disable table print.' - ' Example: --export-csv +hyperopt.csv', + ' Example: --export-csv hyperopt.csv', metavar='FILE', ), "hyperopt_jobs": Arg( diff --git a/freqtrade/commands/hyperopt_commands.py b/freqtrade/commands/hyperopt_commands.py index efc9aba88..5b2388252 100755 --- a/freqtrade/commands/hyperopt_commands.py +++ b/freqtrade/commands/hyperopt_commands.py @@ -63,14 +63,8 @@ def start_hyperopt_list(args: Dict[str, Any]) -> None: Hyperopt.print_epoch_details(results, total_epochs, print_json, no_header) if trials and export_csv: - overwrite_csv = False - if export_csv[0] == '+': - overwrite_csv = True - export_csv = export_csv[1:] - Hyperopt.export_csv_file( - config, trials, total_epochs, - not filteroptions['only_best'], export_csv, overwrite_csv + config, trials, total_epochs, not filteroptions['only_best'], export_csv ) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index d12e9cd45..8e0eba4d7 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -385,7 +385,7 @@ class Hyperopt: @staticmethod def export_csv_file(config: dict, results: list, total_epochs: int, highlight_best: bool, - csv_file: str, overwrite: bool) -> None: + csv_file: str) -> None: """ Log result to csv-file """ @@ -393,8 +393,8 @@ class Hyperopt: return # Verification for overwrite - if not overwrite and path.isfile(csv_file): - logging.error("CSV-File already exists and no overwrite specified!") + if path.isfile(csv_file): + logging.error("CSV-File already exists!") return try: diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index f404a4671..4530cd03d 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -905,7 +905,7 @@ def test_hyperopt_list(mocker, capsys, hyperopt_results): args = [ "hyperopt-list", "--no-details", - "--export-csv", "+test_file.csv" + "--export-csv", "test_file.csv" ] pargs = get_args(args) pargs['config'] = None From bd158eefd29a3cdec29887e7f54895935a379179 Mon Sep 17 00:00:00 2001 From: Fredrik81 Date: Tue, 10 Mar 2020 03:02:52 +0100 Subject: [PATCH 19/29] Fixed loggin --- freqtrade/optimize/hyperopt.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 8e0eba4d7..1b6343208 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -394,13 +394,13 @@ class Hyperopt: # Verification for overwrite if path.isfile(csv_file): - logging.error("CSV-File already exists!") + logger.error("CSV-File already exists!") return try: io.open(csv_file, 'w+').close() except IOError: - logging.error("Filed to create/overwrite CSV-File!") + logger.error("Filed to create CSV-File!") return trials = json_normalize(results, max_level=1) From 42038da7f1f47d5e34cc10af04f9b42c5f6e4533 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 10 Mar 2020 07:57:25 +0100 Subject: [PATCH 20/29] Update docs/configuration.md Co-Authored-By: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> --- docs/configuration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/configuration.md b/docs/configuration.md index a9487a0ed..1fca61fdb 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -541,7 +541,7 @@ A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting Also, the sell order will remain on the exchange for `unfilledtimeout.sell` (or until it's filled) - which can lead to missed stoplosses (with or without using stoploss_on_exchange). !!! Warning "Order_book_max > 1 in dry-run" - Using `ask_strategy.order_book_max` higher than 1 will result in improved dry-run results, since dry-run assumes orders to be filled almost instantly. + Using `ask_strategy.order_book_max` higher than 1 will result in improper dry-run results (significantly better than real orders executed on exchange), since dry-run assumes orders to be filled almost instantly. It is therefore advised to not use this setting for dry-runs. From f148b5f73450861d8f416f0e3b03d355c568ec6e Mon Sep 17 00:00:00 2001 From: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> Date: Tue, 10 Mar 2020 10:38:37 +0300 Subject: [PATCH 21/29] cosmetics in lambdas --- freqtrade/optimize/hyperopt.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 1b6343208..4c32a0543 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -332,10 +332,10 @@ class Hyperopt: lambda x: '{}/{}'.format(str(x).rjust(len(str(total_epochs)), ' '), total_epochs) ) trials['Avg profit'] = trials['Avg profit'].apply( - lambda x: ('{:,.2f}%'.format(x)).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ') + lambda x: '{:,.2f}%'.format(x).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ') ) trials['Avg duration'] = trials['Avg duration'].apply( - lambda x: ('{:,.1f} m'.format(x)).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ') + lambda x: '{:,.1f} m'.format(x).rjust(7, ' ') if not isna(x) else "--".rjust(7, ' ') ) trials['Objective'] = trials['Objective'].apply( lambda x: '{:,.5f}'.format(x).rjust(8, ' ') if x != 100000 else "N/A".rjust(8, ' ') @@ -426,10 +426,10 @@ class Hyperopt: lambda x: '{:,.2f}'.format(x) if not isna(x) else "" ) trials['Avg profit'] = trials['Avg profit'].apply( - lambda x: ('{:,.2f}%'.format(x)) if not isna(x) else "" + lambda x: '{:,.2f}%'.format(x) if not isna(x) else "" ) trials['Avg duration'] = trials['Avg duration'].apply( - lambda x: ('{:,.1f} m'.format(x)) if not isna(x) else "" + lambda x: '{:,.1f} m'.format(x) if not isna(x) else "" ) trials['Objective'] = trials['Objective'].apply( lambda x: '{:,.5f}'.format(x) if x != 100000 else "" From a046c4829c01ad2934337c59a304b181cb4f2a23 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 10 Mar 2020 09:03:44 +0100 Subject: [PATCH 22/29] Apply suggestions from code review Co-Authored-By: hroff-1902 <47309513+hroff-1902@users.noreply.github.com> --- docs/configuration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/configuration.md b/docs/configuration.md index 1fca61fdb..76df5bd08 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -536,9 +536,9 @@ The idea here is to place the sell order early, to be ahead in the queue. A fixed slot (mirroring `bid_strategy.order_book_top`) can be defined by setting `ask_strategy.order_book_min` and `ask_strategy.order_book_max` to the same number. -!!! Warning "Order_book_max > 1 - increased Risk!" - Using `ask_strategy.order_book_max` higher than 1 will increase the risk, since an eventual [stoploss on exchange](#understand-order_types) will be needed to be cancelled as soon as the order is placed. - Also, the sell order will remain on the exchange for `unfilledtimeout.sell` (or until it's filled) - which can lead to missed stoplosses (with or without using stoploss_on_exchange). +!!! Warning "Order_book_max > 1 - increased risks for stoplosses!" + Using `ask_strategy.order_book_max` higher than 1 will increase the risk the stoploss on exchange is cancelled too early, since an eventual [stoploss on exchange](#understand-order_types) will be cancelled as soon as the order is placed. + Also, the sell order will remain on the exchange for `unfilledtimeout.sell` (or until it's filled) - which can lead to missed stoplosses (with or without using stoploss on exchange). !!! Warning "Order_book_max > 1 in dry-run" Using `ask_strategy.order_book_max` higher than 1 will result in improper dry-run results (significantly better than real orders executed on exchange), since dry-run assumes orders to be filled almost instantly. From f7ad6c20c7276ca6f76721d8195034ec2993bb87 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 10 Mar 2020 12:02:23 +0300 Subject: [PATCH 23/29] Do not allow unlimited stake_amount for hyperopt --- freqtrade/commands/optimize_commands.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/freqtrade/commands/optimize_commands.py b/freqtrade/commands/optimize_commands.py index a2d1b4601..2fc605926 100644 --- a/freqtrade/commands/optimize_commands.py +++ b/freqtrade/commands/optimize_commands.py @@ -17,10 +17,15 @@ def setup_optimize_configuration(args: Dict[str, Any], method: RunMode) -> Dict[ """ config = setup_utils_configuration(args, method) - if method == RunMode.BACKTEST: - if config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT: - raise DependencyException('stake amount could not be "%s" for backtesting' % - constants.UNLIMITED_STAKE_AMOUNT) + no_unlimited_runmodes = { + RunMode.BACKTEST: 'backtesting', + RunMode.HYPEROPT: 'hyperoptimization', + } + if (method in no_unlimited_runmodes.keys() and + config['stake_amount'] == constants.UNLIMITED_STAKE_AMOUNT): + raise DependencyException( + f'The value of `stake_amount` cannot be set as "{constants.UNLIMITED_STAKE_AMOUNT}" ' + f'for {no_unlimited_runmodes[method]}') return config From 81b6a950ac58e231e7e29bd1429b34b40b40c1ff Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 10 Mar 2020 12:42:11 +0300 Subject: [PATCH 24/29] Adjust test for backtesting --- tests/optimize/test_backtesting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 96855dc9d..702337463 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -241,7 +241,7 @@ def test_setup_optimize_configuration_unlimited_stake_amount(mocker, default_con '--strategy', 'DefaultStrategy', ] - with pytest.raises(DependencyException, match=r'.*stake amount.*'): + with pytest.raises(DependencyException, match=r'.`stake_amount`.*'): setup_optimize_configuration(get_args(args), RunMode.BACKTEST) From 1b6e77649a437a75f4eae10d78d3fc0bc7dd7ba7 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 10 Mar 2020 12:42:31 +0300 Subject: [PATCH 25/29] Add test for hyperopt --- tests/optimize/test_hyperopt.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/tests/optimize/test_hyperopt.py b/tests/optimize/test_hyperopt.py index 0406157f6..c0bddd085 100644 --- a/tests/optimize/test_hyperopt.py +++ b/tests/optimize/test_hyperopt.py @@ -10,10 +10,11 @@ import pytest from arrow import Arrow from filelock import Timeout +from freqtrade import constants from freqtrade.commands.optimize_commands import (setup_optimize_configuration, start_hyperopt) from freqtrade.data.history import load_data -from freqtrade.exceptions import OperationalException +from freqtrade.exceptions import DependencyException, OperationalException from freqtrade.optimize.default_hyperopt import DefaultHyperOpt from freqtrade.optimize.default_hyperopt_loss import DefaultHyperOptLoss from freqtrade.optimize.hyperopt import Hyperopt @@ -158,6 +159,21 @@ def test_setup_hyperopt_configuration_with_arguments(mocker, default_conf, caplo assert log_has('Parameter --print-all detected ...', caplog) +def test_setup_hyperopt_configuration_unlimited_stake_amount(mocker, default_conf, caplog) -> None: + default_conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT + + patched_configuration_load_config_file(mocker, default_conf) + + args = [ + 'hyperopt', + '--config', 'config.json', + '--hyperopt', 'DefaultHyperOpt', + ] + + with pytest.raises(DependencyException, match=r'.`stake_amount`.*'): + setup_optimize_configuration(get_args(args), RunMode.HYPEROPT) + + def test_hyperoptresolver(mocker, default_conf, caplog) -> None: patched_configuration_load_config_file(mocker, default_conf) From 73c19da4b988e13db70ec6f434baf773aa71faba Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 10 Mar 2020 13:44:16 +0300 Subject: [PATCH 26/29] Adjust handling of zero stdev in loss functions --- freqtrade/optimize/hyperopt_loss_sharpe.py | 2 +- freqtrade/optimize/hyperopt_loss_sharpe_daily.py | 2 +- freqtrade/optimize/hyperopt_loss_sortino.py | 2 +- freqtrade/optimize/hyperopt_loss_sortino_daily.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/optimize/hyperopt_loss_sharpe.py b/freqtrade/optimize/hyperopt_loss_sharpe.py index a4ec6f90a..29377bdd5 100644 --- a/freqtrade/optimize/hyperopt_loss_sharpe.py +++ b/freqtrade/optimize/hyperopt_loss_sharpe.py @@ -36,7 +36,7 @@ class SharpeHyperOptLoss(IHyperOptLoss): expected_returns_mean = total_profit.sum() / days_period up_stdev = np.std(total_profit) - if (np.std(total_profit) != 0.): + if up_stdev != 0: sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365) else: # Define high (negative) sharpe ratio to be clear that this is NOT optimal. diff --git a/freqtrade/optimize/hyperopt_loss_sharpe_daily.py b/freqtrade/optimize/hyperopt_loss_sharpe_daily.py index 5a8ebaa11..e4cd1d749 100644 --- a/freqtrade/optimize/hyperopt_loss_sharpe_daily.py +++ b/freqtrade/optimize/hyperopt_loss_sharpe_daily.py @@ -51,7 +51,7 @@ class SharpeHyperOptLossDaily(IHyperOptLoss): expected_returns_mean = total_profit.mean() up_stdev = total_profit.std() - if (up_stdev != 0.): + if up_stdev != 0: sharp_ratio = expected_returns_mean / up_stdev * math.sqrt(days_in_year) else: # Define high (negative) sharpe ratio to be clear that this is NOT optimal. diff --git a/freqtrade/optimize/hyperopt_loss_sortino.py b/freqtrade/optimize/hyperopt_loss_sortino.py index 83f644a43..d470a9977 100644 --- a/freqtrade/optimize/hyperopt_loss_sortino.py +++ b/freqtrade/optimize/hyperopt_loss_sortino.py @@ -39,7 +39,7 @@ class SortinoHyperOptLoss(IHyperOptLoss): results.loc[total_profit < 0, 'downside_returns'] = results['profit_percent'] down_stdev = np.std(results['downside_returns']) - if np.std(total_profit) != 0.0: + if down_stdev != 0: sortino_ratio = expected_returns_mean / down_stdev * np.sqrt(365) else: # Define high (negative) sortino ratio to be clear that this is NOT optimal. diff --git a/freqtrade/optimize/hyperopt_loss_sortino_daily.py b/freqtrade/optimize/hyperopt_loss_sortino_daily.py index 16dc26142..cd6a8bcc2 100644 --- a/freqtrade/optimize/hyperopt_loss_sortino_daily.py +++ b/freqtrade/optimize/hyperopt_loss_sortino_daily.py @@ -59,7 +59,7 @@ class SortinoHyperOptLossDaily(IHyperOptLoss): # where P = sum_daily["profit_percent_after_slippage"] down_stdev = math.sqrt((total_downside**2).sum() / len(total_downside)) - if (down_stdev != 0.): + if down_stdev != 0: sortino_ratio = expected_returns_mean / down_stdev * math.sqrt(days_in_year) else: # Define high (negative) sortino ratio to be clear that this is NOT optimal. From 2b1c146940633b50dc0d9a9d225f44cc893bb088 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 10 Mar 2020 16:05:33 +0100 Subject: [PATCH 27/29] Add default volume > 0 filter --- docs/hyperopt.md | 3 +++ freqtrade/templates/base_hyperopt.py.j2 | 6 ++++++ freqtrade/templates/sample_hyperopt.py | 6 ++++++ freqtrade/templates/sample_hyperopt_advanced.py | 6 ++++++ 4 files changed, 21 insertions(+) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 9bc5888ce..1293c7ab4 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -159,6 +159,9 @@ So let's write the buy strategy using these values: dataframe['macd'], dataframe['macdsignal'] )) + # Check that volume is not 0 + conditions.append(dataframe['volume'] > 0) + if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), diff --git a/freqtrade/templates/base_hyperopt.py.j2 b/freqtrade/templates/base_hyperopt.py.j2 index 08178da4b..ec787cbb6 100644 --- a/freqtrade/templates/base_hyperopt.py.j2 +++ b/freqtrade/templates/base_hyperopt.py.j2 @@ -66,6 +66,9 @@ class {{ hyperopt }}(IHyperOpt): dataframe['close'], dataframe['sar'] )) + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), @@ -111,6 +114,9 @@ class {{ hyperopt }}(IHyperOpt): dataframe['sar'], dataframe['close'] )) + # Check that the candle had volume + conditions.append(dataframe['volume'] > 0) + if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), diff --git a/freqtrade/templates/sample_hyperopt.py b/freqtrade/templates/sample_hyperopt.py index 0baa00442..0b6d030db 100644 --- a/freqtrade/templates/sample_hyperopt.py +++ b/freqtrade/templates/sample_hyperopt.py @@ -78,6 +78,9 @@ class SampleHyperOpt(IHyperOpt): dataframe['close'], dataframe['sar'] )) + # Check that volume is not 0 + conditions.append(dataframe['volume'] > 0) + if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), @@ -138,6 +141,9 @@ class SampleHyperOpt(IHyperOpt): dataframe['sar'], dataframe['close'] )) + # Check that volume is not 0 + conditions.append(dataframe['volume'] > 0) + if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), diff --git a/freqtrade/templates/sample_hyperopt_advanced.py b/freqtrade/templates/sample_hyperopt_advanced.py index c8067ad28..7f05c4430 100644 --- a/freqtrade/templates/sample_hyperopt_advanced.py +++ b/freqtrade/templates/sample_hyperopt_advanced.py @@ -93,6 +93,9 @@ class AdvancedSampleHyperOpt(IHyperOpt): dataframe['close'], dataframe['sar'] )) + # Check that volume is not 0 + conditions.append(dataframe['volume'] > 0) + if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), @@ -153,6 +156,9 @@ class AdvancedSampleHyperOpt(IHyperOpt): dataframe['sar'], dataframe['close'] )) + # Check that volume is not 0 + conditions.append(dataframe['volume'] > 0) + if conditions: dataframe.loc[ reduce(lambda x, y: x & y, conditions), From 129a88d5da1879e1cae93e1771a995da6d5f0bfc Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 11 Mar 2020 19:53:28 +0100 Subject: [PATCH 28/29] Extract emptyness check to it's own method --- freqtrade/data/history/idatahandler.py | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/freqtrade/data/history/idatahandler.py b/freqtrade/data/history/idatahandler.py index 87810c95f..bde9612f2 100644 --- a/freqtrade/data/history/idatahandler.py +++ b/freqtrade/data/history/idatahandler.py @@ -147,12 +147,7 @@ class IDataHandler(ABC): pairdf = self._ohlcv_load(pair, timeframe, timerange=timerange_startup) - if pairdf.empty: - if warn_no_data: - logger.warning( - f'No history data for pair: "{pair}", timeframe: {timeframe}. ' - 'Use `freqtrade download-data` to download the data' - ) + if self._check_empty_df(pairdf, pair, timeframe, warn_no_data): return pairdf else: enddate = pairdf.iloc[-1]['date'] @@ -160,12 +155,7 @@ class IDataHandler(ABC): if timerange_startup: self._validate_pairdata(pair, pairdf, timerange_startup) pairdf = trim_dataframe(pairdf, timerange_startup) - if pairdf.empty: - if warn_no_data: - logger.warning( - f'No history data for pair: "{pair}", timeframe: {timeframe}. ' - 'Use `freqtrade download-data` to download the data' - ) + if self._check_empty_df(pairdf, pair, timeframe, warn_no_data): return pairdf # incomplete candles should only be dropped if we didn't trim the end beforehand. @@ -175,6 +165,19 @@ class IDataHandler(ABC): drop_incomplete=(drop_incomplete and enddate == pairdf.iloc[-1]['date'])) + def _check_empty_df(self, pairdf: DataFrame, pair: str, timeframe: str, warn_no_data: bool): + """ + Warn on empty dataframe + """ + if pairdf.empty: + if warn_no_data: + logger.warning( + f'No history data for pair: "{pair}", timeframe: {timeframe}. ' + 'Use `freqtrade download-data` to download the data' + ) + return True + return False + def _validate_pairdata(self, pair, pairdata: DataFrame, timerange: TimeRange): """ Validates pairdata for missing data at start end end and logs warnings. From 6f67b8d9b900961afb93508214d981925261d3ec Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 12 Mar 2020 19:50:46 +0100 Subject: [PATCH 29/29] iCheck after clean_dataframe, too --- freqtrade/data/history/idatahandler.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/freqtrade/data/history/idatahandler.py b/freqtrade/data/history/idatahandler.py index bde9612f2..b0a6a97dc 100644 --- a/freqtrade/data/history/idatahandler.py +++ b/freqtrade/data/history/idatahandler.py @@ -159,11 +159,13 @@ class IDataHandler(ABC): return pairdf # incomplete candles should only be dropped if we didn't trim the end beforehand. - return clean_ohlcv_dataframe(pairdf, timeframe, - pair=pair, - fill_missing=fill_missing, - drop_incomplete=(drop_incomplete and - enddate == pairdf.iloc[-1]['date'])) + pairdf = clean_ohlcv_dataframe(pairdf, timeframe, + pair=pair, + fill_missing=fill_missing, + drop_incomplete=(drop_incomplete and + enddate == pairdf.iloc[-1]['date'])) + self._check_empty_df(pairdf, pair, timeframe, warn_no_data) + return pairdf def _check_empty_df(self, pairdf: DataFrame, pair: str, timeframe: str, warn_no_data: bool): """