Add --analyze-per-epoch - moving populate_analysis to the epoch process

This commit is contained in:
Matthias 2022-08-19 15:19:43 +02:00
parent 09f8904545
commit bc359675a2
6 changed files with 48 additions and 12 deletions

View File

@ -40,7 +40,8 @@ pip install -r requirements-hyperopt.txt
``` ```
usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
[--userdir PATH] [-s NAME] [--strategy-path PATH] [--userdir PATH] [-s NAME] [--strategy-path PATH]
[--recursive-strategy-search] [-i TIMEFRAME] [--recursive-strategy-search] [--freqaimodel NAME]
[--freqaimodel-path PATH] [-i TIMEFRAME]
[--timerange TIMERANGE] [--timerange TIMERANGE]
[--data-format-ohlcv {json,jsongz,hdf5}] [--data-format-ohlcv {json,jsongz,hdf5}]
[--max-open-trades INT] [--max-open-trades INT]
@ -53,7 +54,7 @@ usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH]
[--print-all] [--no-color] [--print-json] [-j JOBS] [--print-all] [--no-color] [--print-json] [-j JOBS]
[--random-state INT] [--min-trades INT] [--random-state INT] [--min-trades INT]
[--hyperopt-loss NAME] [--disable-param-export] [--hyperopt-loss NAME] [--disable-param-export]
[--ignore-missing-spaces] [--ignore-missing-spaces] [--analyze-per-epoch]
optional arguments: optional arguments:
-h, --help show this help message and exit -h, --help show this help message and exit
@ -129,6 +130,7 @@ optional arguments:
--ignore-missing-spaces, --ignore-unparameterized-spaces --ignore-missing-spaces, --ignore-unparameterized-spaces
Suppress errors for any requested Hyperopt spaces that Suppress errors for any requested Hyperopt spaces that
do not contain any parameters. do not contain any parameters.
--analyze-per-epoch Run populate_indicators once per epoch.
Common arguments: Common arguments:
-v, --verbose Verbose mode (-vv for more, -vvv to get all messages). -v, --verbose Verbose mode (-vv for more, -vvv to get all messages).
@ -154,6 +156,10 @@ Strategy arguments:
--recursive-strategy-search --recursive-strategy-search
Recursively search for a strategy in the strategies Recursively search for a strategy in the strategies
folder. folder.
--freqaimodel NAME Specify a custom freqaimodels.
--freqaimodel-path PATH
Specify additional lookup path for freqaimodels.
``` ```
### Hyperopt checklist ### Hyperopt checklist

View File

@ -34,7 +34,7 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + ["hyperopt", "hyperopt_path",
"print_colorized", "print_json", "hyperopt_jobs", "print_colorized", "print_json", "hyperopt_jobs",
"hyperopt_random_state", "hyperopt_min_trades", "hyperopt_random_state", "hyperopt_min_trades",
"hyperopt_loss", "disableparamexport", "hyperopt_loss", "disableparamexport",
"hyperopt_ignore_missing_space"] "hyperopt_ignore_missing_space", "analyze_per_epoch"]
ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"]

View File

@ -255,6 +255,13 @@ AVAILABLE_CLI_OPTIONS = {
nargs='+', nargs='+',
default='default', default='default',
), ),
"analyze_per_epoch": Arg(
'--analyze-per-epoch',
help='Run populate_indicators once per epoch.',
action='store_true',
default=False,
),
"print_all": Arg( "print_all": Arg(
'--print-all', '--print-all',
help='Print all results, not only the best ones.', help='Print all results, not only the best ones.',

View File

@ -302,6 +302,9 @@ class Configuration:
self._args_to_config(config, argname='spaces', self._args_to_config(config, argname='spaces',
logstring='Parameter -s/--spaces detected: {}') logstring='Parameter -s/--spaces detected: {}')
self._args_to_config(config, argname='analyze_per_epoch',
logstring='Parameter --analyze-per-epoch detected.')
self._args_to_config(config, argname='print_all', self._args_to_config(config, argname='print_all',
logstring='Parameter --print-all detected ...') logstring='Parameter --print-all detected ...')

View File

@ -24,13 +24,15 @@ from pandas import DataFrame
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN
from freqtrade.data.converter import trim_dataframes from freqtrade.data.converter import trim_dataframes
from freqtrade.data.history import get_timerange from freqtrade.data.history import get_timerange
from freqtrade.enums import HyperoptState
from freqtrade.exceptions import OperationalException from freqtrade.exceptions import OperationalException
from freqtrade.misc import deep_merge_dicts, file_dump_json, plural from freqtrade.misc import deep_merge_dicts, file_dump_json, plural
from freqtrade.optimize.backtesting import Backtesting from freqtrade.optimize.backtesting import Backtesting
# Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules # Import IHyperOpt and IHyperOptLoss to allow unpickling classes from these modules
from freqtrade.optimize.hyperopt_auto import HyperOptAuto from freqtrade.optimize.hyperopt_auto import HyperOptAuto
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer from freqtrade.optimize.hyperopt_tools import (HyperoptStateContainer, HyperoptTools,
hyperopt_serializer)
from freqtrade.optimize.optimize_reports import generate_strategy_stats from freqtrade.optimize.optimize_reports import generate_strategy_stats
from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver
@ -74,10 +76,14 @@ class Hyperopt:
self.dimensions: List[Dimension] = [] self.dimensions: List[Dimension] = []
self.config = config self.config = config
self.min_date: datetime
self.max_date: datetime
self.backtesting = Backtesting(self.config) self.backtesting = Backtesting(self.config)
self.pairlist = self.backtesting.pairlists.whitelist self.pairlist = self.backtesting.pairlists.whitelist
self.custom_hyperopt: HyperOptAuto self.custom_hyperopt: HyperOptAuto
self.analyze_per_epoch = self.config.get('analyze_per_epoch', False)
HyperoptStateContainer.set_state(HyperoptState.STARTUP)
if not self.config.get('hyperopt'): if not self.config.get('hyperopt'):
self.custom_hyperopt = HyperOptAuto(self.config) self.custom_hyperopt = HyperOptAuto(self.config)
@ -290,6 +296,7 @@ class Hyperopt:
Called once per epoch to optimize whatever is configured. Called once per epoch to optimize whatever is configured.
Keep this function as optimized as possible! Keep this function as optimized as possible!
""" """
HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE)
backtest_start_time = datetime.now(timezone.utc) backtest_start_time = datetime.now(timezone.utc)
params_dict = self._get_params_dict(self.dimensions, raw_params) params_dict = self._get_params_dict(self.dimensions, raw_params)
@ -321,6 +328,10 @@ class Hyperopt:
with self.data_pickle_file.open('rb') as f: with self.data_pickle_file.open('rb') as f:
processed = load(f, mmap_mode='r') processed = load(f, mmap_mode='r')
if self.analyze_per_epoch:
# Data is not yet analyzed, rerun populate_indicators.
processed = self.advise_and_trim(processed)
bt_results = self.backtesting.backtest( bt_results = self.backtesting.backtest(
processed=processed, processed=processed,
start_date=self.min_date, start_date=self.min_date,
@ -415,19 +426,24 @@ class Hyperopt:
return processed return processed
def prepare_hyperopt_data(self) -> None: def prepare_hyperopt_data(self) -> None:
data, timerange = self.backtesting.load_bt_data() HyperoptStateContainer.set_state(HyperoptState.DATALOAD)
data, self.timerange = self.backtesting.load_bt_data()
self.backtesting.load_bt_data_detail() self.backtesting.load_bt_data_detail()
logger.info("Dataload complete. Calculating indicators") logger.info("Dataload complete. Calculating indicators")
preprocessed = self.backtesting.strategy.advise_all_indicators(data) if not self.analyze_per_epoch:
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
preprocessed = self.advise_and_trim(data) preprocessed = self.advise_and_trim(data)
logger.info(f'Hyperopting with data from {self.min_date.strftime(DATETIME_PRINT_FORMAT)} ' logger.info(f'Hyperopting with data from '
f'{self.min_date.strftime(DATETIME_PRINT_FORMAT)} '
f'up to {self.max_date.strftime(DATETIME_PRINT_FORMAT)} ' f'up to {self.max_date.strftime(DATETIME_PRINT_FORMAT)} '
f'({(self.max_date - self.min_date).days} days)..') f'({(self.max_date - self.min_date).days} days)..')
# Store non-trimmed data - will be trimmed after signal generation. # Store non-trimmed data - will be trimmed after signal generation.
dump(preprocessed, self.data_pickle_file) dump(preprocessed, self.data_pickle_file)
else:
dump(data, self.data_pickle_file)
def get_asked_points(self, n_points: int) -> Tuple[List[List[Any]], List[bool]]: def get_asked_points(self, n_points: int) -> Tuple[List[List[Any]], List[bool]]:
""" """

View File

@ -7,6 +7,9 @@ from abc import ABC, abstractmethod
from contextlib import suppress from contextlib import suppress
from typing import Any, Optional, Sequence, Union from typing import Any, Optional, Sequence, Union
from freqtrade.enums.hyperoptstate import HyperoptState
from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer
with suppress(ImportError): with suppress(ImportError):
from skopt.space import Integer, Real, Categorical from skopt.space import Integer, Real, Categorical
@ -61,6 +64,7 @@ class BaseParameter(ABC):
return ( return (
self.in_space self.in_space
and self.optimize and self.optimize
and HyperoptStateContainer.state != HyperoptState.OPTIMIZE
) )