From f14d6249e0860f5ed5b53fcc3bfc9e40613fff0b Mon Sep 17 00:00:00 2001 From: Janne Sinivirta Date: Fri, 9 Feb 2018 20:59:06 +0200 Subject: [PATCH 1/7] allow selecting hyperopt searchspace --- freqtrade/misc.py | 8 +++++ freqtrade/optimize/hyperopt.py | 36 ++++++++++++++++++----- freqtrade/tests/optimize/test_hyperopt.py | 20 +++++++++---- 3 files changed, 50 insertions(+), 14 deletions(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 4fa70b678..9c7e2dd9f 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -280,6 +280,14 @@ def hyperopt_options(parser: argparse.ArgumentParser) -> None: type=str, dest='timerange', ) + parser.add_argument( + '-s', '--spaces', + help='Specify which parameters to hyperopt. Comma separate list. \ + Allowed values: all, buy, sell, roi, stoploss. Default: $(default)s', + default='all', + type=str, + dest='spaces', + ) def parse_timerange(text): diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 12c061b4f..45e3fc20c 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -312,8 +312,21 @@ def indicator_space() -> Dict[str, Any]: } -def hyperopt_space() -> Dict[str, Any]: - return {**indicator_space(), **roi_space(), **stoploss_space()} +def has_space(spaces, space): + if space in spaces or 'all' in spaces: + return True + return False + + +def hyperopt_space(selected_spaces: str) -> Dict[str, Any]: + spaces = {} + if has_space(selected_spaces, 'buy'): + spaces = {**spaces, **indicator_space()} + if has_space(selected_spaces, 'roi'): + spaces = {**spaces, **roi_space()} + if has_space(selected_spaces, 'stoploss'): + spaces = {**spaces, **stoploss_space()} + return spaces def buy_strategy_generator(params: Dict[str, Any]) -> Callable: @@ -393,15 +406,21 @@ def buy_strategy_generator(params: Dict[str, Any]) -> Callable: def optimizer(params): global _CURRENT_TRIES + strategy = Strategy() if 'roi_t1' in params: - strategy = Strategy() strategy.minimal_roi = generate_roi_table(params) - backtesting.populate_buy_trend = buy_strategy_generator(params) + if 'trigger' in params: + backtesting.populate_buy_trend = buy_strategy_generator(params) + + if 'stoploss' in params: + stoploss = params['stoploss'] + else: + stoploss = strategy.stoploss results = backtest({'stake_amount': OPTIMIZE_CONFIG['stake_amount'], 'processed': PROCESSED, - 'stoploss': params['stoploss']}) + 'stoploss': stoploss}) result_explanation = format_results(results) total_profit = results.profit_percent.sum() @@ -475,7 +494,8 @@ def start(args): data = optimize.load_data(args.datadir, pairs=pairs, ticker_interval=strategy.ticker_interval, timerange=timerange) - optimize.populate_indicators = populate_indicators + if has_space(args.spaces, 'buy') or has_space(args.spaces, 'sell'): + optimize.populate_indicators = populate_indicators PROCESSED = optimize.tickerdata_to_dataframe(data) if args.mongodb: @@ -500,7 +520,7 @@ def start(args): try: best_parameters = fmin( fn=optimizer, - space=hyperopt_space(), + space=hyperopt_space(args.spaces), algo=tpe.suggest, max_evals=TOTAL_TRIES, trials=TRIALS @@ -517,7 +537,7 @@ def start(args): # Improve best parameter logging display if best_parameters: best_parameters = space_eval( - hyperopt_space(), + hyperopt_space(args.spaces), best_parameters ) diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 93cb6ba8b..073af5bde 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -6,7 +6,7 @@ from unittest.mock import MagicMock import pandas as pd from freqtrade.optimize.hyperopt import calculate_loss, TARGET_TRADES, EXPECTED_MAX_PROFIT, start, \ - log_results, save_trials, read_trials, generate_roi_table + log_results, save_trials, read_trials, generate_roi_table, has_space import freqtrade.optimize.hyperopt as hyperopt @@ -71,7 +71,7 @@ def test_start_calls_fmin(mocker): mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={}) args = mocker.Mock(epochs=1, config='config.json.example', mongodb=False, - timerange=None) + timerange=None, spaces='all') start(args) mock_fmin.assert_called_once() @@ -85,7 +85,7 @@ def test_start_uses_mongotrials(mocker): mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={}) args = mocker.Mock(epochs=1, config='config.json.example', mongodb=True, - timerange=None) + timerange=None, spaces='all') start(args) mock_mongotrials.assert_called_once() @@ -148,7 +148,7 @@ def test_fmin_best_results(mocker, caplog): mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value=fmin_result) args = mocker.Mock(epochs=1, config='config.json.example', - timerange=None) + timerange=None, spaces='all') start(args) exists = [ @@ -172,7 +172,7 @@ def test_fmin_throw_value_error(mocker, caplog): mocker.patch('freqtrade.optimize.hyperopt.fmin', side_effect=ValueError()) args = mocker.Mock(epochs=1, config='config.json.example', - timerange=None) + timerange=None, spaces='all') start(args) exists = [ @@ -207,7 +207,8 @@ def test_resuming_previous_hyperopt_results_succeeds(mocker): args = mocker.Mock(epochs=1, config='config.json.example', mongodb=False, - timerange=None) + timerange=None, + spaces='all') start(args) @@ -279,3 +280,10 @@ def test_signal_handler(mocker): mocker.patch('freqtrade.optimize.hyperopt.log_trials_result', m) hyperopt.signal_handler(9, None) assert m.call_count == 3 + + +def test_has_space(): + assert has_space('buy,roi', 'roi') + assert has_space('buy,roi', 'buy') + assert not has_space('buy,roi', 'stoploss') + assert has_space('all', 'buy') From 55a1f604d6c1e64c57c94a3a09cacd3c91184129 Mon Sep 17 00:00:00 2001 From: Janne Sinivirta Date: Sat, 10 Feb 2018 10:59:18 +0200 Subject: [PATCH 2/7] small corrections and typo fixes to hyperopt documentation --- docs/hyperopt.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 3c3cb7d25..e24fdd621 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -51,12 +51,12 @@ def populate_buy_trend(dataframe: DataFrame) -> DataFrame: return dataframe ``` -Your hyperopt file must contains `guards` to find the right value for +Your hyperopt file must contain `guards` to find the right value for `(dataframe['adx'] > 65)` & and `(dataframe['plus_di'] > 0.5)`. That means you will need to enable/disable triggers. In our case the `SPACE` and `populate_buy_trend` in your strategy file -will be look like: +will look like: ```python space = { 'rsi': hp.choice('rsi', [ @@ -105,7 +105,7 @@ def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: ### 2. Update the hyperopt config file -Hyperopt is using a dedicated config file. At this moment hyperopt +Hyperopt is using a dedicated config file. Currently hyperopt cannot use your config file. It is also made on purpose to allow you testing your strategy with different configurations. @@ -127,19 +127,21 @@ If it's a guard, you will add a line like this: {'enabled': True, 'value': hp.quniform('rsi-value', 20, 40, 1)} ]), ``` -This says, "*one of guards is RSI, it can have two values, enabled or +This says, "*one of the guards is RSI, it can have two values, enabled or disabled. If it is enabled, try different values for it between 20 and 40*". So, the part of the strategy builder using the above setting looks like this: + ``` if params['rsi']['enabled']: conditions.append(dataframe['rsi'] < params['rsi']['value']) ``` + It checks if Hyperopt wants the RSI guard to be enabled for this round `params['rsi']['enabled']` and if it is, then it will add a -condition that says RSI must be < than the value hyperopt picked -for this evaluation, that is given in the `params['rsi']['value']`. +condition that says RSI must be smaller than the value hyperopt picked +for this evaluation, which is given in the `params['rsi']['value']`. That's it. Now you can add new parts of strategies to Hyperopt and it will try all the combinations with all different values in the search @@ -148,8 +150,7 @@ for best working algo. ### Add a new Indicators If you want to test an indicator that isn't used by the bot currently, -you need to add it to your strategy file (example: [user_data/strategies/test_strategy.py](https://github.com/gcarq/freqtrade/blob/develop/user_data/strategies/test_strategy.py)) -inside the `populate_indicators()` method. +you need to add it to the `populate_indicators()` method in `hyperopt.py`. ## Execute Hyperopt Once you have updated your hyperopt configuration you can run it. @@ -158,17 +159,19 @@ it will take time you will have the result (more than 30 mins). We strongly recommend to use `screen` to prevent any connection loss. ```bash -python3 ./freqtrade/main.py -c config.json hyperopt +python3 ./freqtrade/main.py -c config.json hyperopt -e 5000 ``` +The `-e` flag will set how many evaluations hyperopt will do. We recommend +running at least several thousand evaluations. + ### Execute hyperopt with different ticker-data source -If you would like to learn parameters using an alternate ticke-data that +If you would like to hyperopt parameters using an alternate ticker data that you have on-disk, use the `--datadir PATH` option. Default hyperopt will use data from directory `user_data/data`. ### Running hyperopt with smaller testset - -Use the --timeperiod argument to change how much of the testset +Use the `--timeperiod` argument to change how much of the testset you want to use. The last N ticks/timeframes will be used. Example: @@ -267,7 +270,6 @@ customizable value. - You should **ignore** the guard "mfi" (`"mfi"` is `"enabled": false`) - and so on... - You have to look inside your strategy file into `buy_strategy_generator()` method, what those values match to. @@ -277,7 +279,7 @@ at `adx`-block, that translates to the following code block: (dataframe['adx'] > 15.0) ``` -So translating your whole hyperopt result to as the new buy-signal +Translating your whole hyperopt result to as the new buy-signal would be the following: ``` def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame: From d74543ac329fd74eb2b5d2273b2db42e6033c789 Mon Sep 17 00:00:00 2001 From: Janne Sinivirta Date: Sat, 10 Feb 2018 11:04:16 +0200 Subject: [PATCH 3/7] document the new --spaces flag for hyperopt --- docs/hyperopt.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index e24fdd621..05ed13734 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -179,6 +179,22 @@ Example: python3 ./freqtrade/main.py hyperopt --timeperiod -200 ``` +### Running hyperopt with smaller search space +Use the `--spaces` argument to limit the search space used by hyperopt. +Letting Hyperopt optimize everything is a huuuuge search space. Often it +might make more sense to start by just searching for initial buy algorithm. +Or maybe you just want to optimize your stoploss or roi table for that awesome +new buy strategy you have. + +Legal values are: + +- `all`: optimize everything +- `buy`: just search for a new buy strategy +- `sell`: not supported yet +- `roi`: just optimize the minimal profit table for your strategy +- `stoploss`: search for the best stoploss value +- comma-separated list of any of the above values for example `--spaces roi,stoploss` + ### Hyperopt with MongoDB Hyperopt with MongoDB, is like Hyperopt under steroids. As you saw by executing the previous command is the execution takes a long time. From fe28addb51142acb9a9a42f67299ca7ab203382f Mon Sep 17 00:00:00 2001 From: Janne Sinivirta Date: Sun, 11 Feb 2018 19:16:21 +0200 Subject: [PATCH 4/7] specify allowed values for --spaces flag --- freqtrade/misc.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 9c7e2dd9f..b24f961e0 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -282,10 +282,11 @@ def hyperopt_options(parser: argparse.ArgumentParser) -> None: ) parser.add_argument( '-s', '--spaces', - help='Specify which parameters to hyperopt. Comma separate list. \ - Allowed values: all, buy, sell, roi, stoploss. Default: $(default)s', + help='Specify which parameters to hyperopt. Space separate list. \ + Default: %(default)s', + choices=['all', 'buy', 'roi', 'stoploss'], default='all', - type=str, + nargs='+', dest='spaces', ) From 1eecf28a8b11bc16e78f8fc8f4c354f41639cf55 Mon Sep 17 00:00:00 2001 From: Janne Sinivirta Date: Sun, 11 Feb 2018 19:18:11 +0200 Subject: [PATCH 5/7] adjust documentation to match changes to --spaces flag --- docs/hyperopt.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 05ed13734..0bfa9ae2d 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -190,10 +190,9 @@ Legal values are: - `all`: optimize everything - `buy`: just search for a new buy strategy -- `sell`: not supported yet - `roi`: just optimize the minimal profit table for your strategy - `stoploss`: search for the best stoploss value -- comma-separated list of any of the above values for example `--spaces roi,stoploss` +- space-separated list of any of the above values for example `--spaces roi stoploss` ### Hyperopt with MongoDB Hyperopt with MongoDB, is like Hyperopt under steroids. As you saw by From b1230b27b7fa5ffedaca19e1a040f791614367c4 Mon Sep 17 00:00:00 2001 From: Janne Sinivirta Date: Sun, 11 Feb 2018 19:22:13 +0200 Subject: [PATCH 6/7] adjust unit test to match new --spaces format --- freqtrade/tests/optimize/test_hyperopt.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 073af5bde..67413e3fa 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -283,7 +283,7 @@ def test_signal_handler(mocker): def test_has_space(): - assert has_space('buy,roi', 'roi') - assert has_space('buy,roi', 'buy') - assert not has_space('buy,roi', 'stoploss') - assert has_space('all', 'buy') + assert has_space(['buy', 'roi'], 'roi') + assert has_space(['buy', 'roi'], 'buy') + assert not has_space(['buy', 'roi'], 'stoploss') + assert has_space(['all'], 'buy') From 3e07d41fa94209a32dd6f80a92ec7a6b97711662 Mon Sep 17 00:00:00 2001 From: Janne Sinivirta Date: Mon, 12 Feb 2018 07:01:51 +0200 Subject: [PATCH 7/7] remove mention of sell space --- 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 45e3fc20c..0cabf83b0 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -494,7 +494,7 @@ def start(args): data = optimize.load_data(args.datadir, pairs=pairs, ticker_interval=strategy.ticker_interval, timerange=timerange) - if has_space(args.spaces, 'buy') or has_space(args.spaces, 'sell'): + if has_space(args.spaces, 'buy'): optimize.populate_indicators = populate_indicators PROCESSED = optimize.tickerdata_to_dataframe(data)