Merge pull request #2024 from freqtrade/custom_hyperopt_loss
Custom hyperopt loss function (and sharpe-ratio)
This commit is contained in:
@@ -8,8 +8,9 @@ from pathlib import Path
|
||||
from typing import Optional, Dict
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.constants import DEFAULT_HYPEROPT
|
||||
from freqtrade.constants import DEFAULT_HYPEROPT, DEFAULT_HYPEROPT_LOSS
|
||||
from freqtrade.optimize.hyperopt_interface import IHyperOpt
|
||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
||||
from freqtrade.resolvers import IResolver
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -54,7 +55,7 @@ class HyperOptResolver(IResolver):
|
||||
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
|
||||
|
||||
abs_paths = [
|
||||
current_path.parent.parent.joinpath('user_data/hyperopts'),
|
||||
Path.cwd().joinpath('user_data/hyperopts'),
|
||||
current_path,
|
||||
]
|
||||
|
||||
@@ -77,3 +78,66 @@ class HyperOptResolver(IResolver):
|
||||
f"Impossible to load Hyperopt '{hyperopt_name}'. This class does not exist "
|
||||
"or contains Python code errors."
|
||||
)
|
||||
|
||||
|
||||
class HyperOptLossResolver(IResolver):
|
||||
"""
|
||||
This class contains all the logic to load custom hyperopt loss class
|
||||
"""
|
||||
|
||||
__slots__ = ['hyperoptloss']
|
||||
|
||||
def __init__(self, config: Optional[Dict] = None) -> None:
|
||||
"""
|
||||
Load the custom class from config parameter
|
||||
:param config: configuration dictionary or None
|
||||
"""
|
||||
config = config or {}
|
||||
|
||||
# Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt
|
||||
hyperopt_name = config.get('hyperopt_loss') or DEFAULT_HYPEROPT_LOSS
|
||||
self.hyperoptloss = self._load_hyperoptloss(
|
||||
hyperopt_name, extra_dir=config.get('hyperopt_path'))
|
||||
|
||||
# Assign ticker_interval to be used in hyperopt
|
||||
self.hyperoptloss.__class__.ticker_interval = str(config['ticker_interval'])
|
||||
|
||||
if not hasattr(self.hyperoptloss, 'hyperopt_loss_function'):
|
||||
raise OperationalException(
|
||||
f"Found hyperopt {hyperopt_name} does not implement `hyperopt_loss_function`.")
|
||||
|
||||
def _load_hyperoptloss(
|
||||
self, hyper_loss_name: str, extra_dir: Optional[str] = None) -> IHyperOptLoss:
|
||||
"""
|
||||
Search and loads the specified hyperopt loss class.
|
||||
:param hyper_loss_name: name of the module to import
|
||||
:param extra_dir: additional directory to search for the given hyperopt
|
||||
:return: HyperOptLoss instance or None
|
||||
"""
|
||||
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
|
||||
|
||||
abs_paths = [
|
||||
Path.cwd().joinpath('user_data/hyperopts'),
|
||||
current_path,
|
||||
]
|
||||
|
||||
if extra_dir:
|
||||
# Add extra hyperopt directory on top of search paths
|
||||
abs_paths.insert(0, Path(extra_dir))
|
||||
|
||||
for _path in abs_paths:
|
||||
try:
|
||||
(hyperoptloss, module_path) = self._search_object(directory=_path,
|
||||
object_type=IHyperOptLoss,
|
||||
object_name=hyper_loss_name)
|
||||
if hyperoptloss:
|
||||
logger.info(
|
||||
f"Using resolved hyperopt {hyper_loss_name} from '{module_path}'...")
|
||||
return hyperoptloss
|
||||
except FileNotFoundError:
|
||||
logger.warning('Path "%s" does not exist.', _path.relative_to(Path.cwd()))
|
||||
|
||||
raise OperationalException(
|
||||
f"Impossible to load HyperoptLoss '{hyper_loss_name}'. This class does not exist "
|
||||
"or contains Python code errors."
|
||||
)
|
||||
|
Reference in New Issue
Block a user