change from skopt.space.Real to optuna.distributions.FloatDistribution
This commit is contained in:
@@ -9,13 +9,17 @@ from abc import ABC
|
|||||||
from typing import TypeAlias
|
from typing import TypeAlias
|
||||||
|
|
||||||
from sklearn.base import RegressorMixin
|
from sklearn.base import RegressorMixin
|
||||||
from skopt.space import Categorical, Dimension, Integer
|
from skopt.space import Dimension # , Integer, Categorical,
|
||||||
|
|
||||||
from freqtrade.constants import Config
|
from freqtrade.constants import Config
|
||||||
from freqtrade.exchange import timeframe_to_minutes
|
from freqtrade.exchange import timeframe_to_minutes
|
||||||
from freqtrade.misc import round_dict
|
from freqtrade.misc import round_dict
|
||||||
from freqtrade.optimize.space import SKDecimal
|
from freqtrade.optimize.space import SKDecimal
|
||||||
from freqtrade.strategy import IStrategy
|
from freqtrade.strategy import IStrategy
|
||||||
|
from freqtrade.strategy.parameters import (
|
||||||
|
ft_CategoricalDistribution,
|
||||||
|
ft_IntDistribution,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -133,9 +137,12 @@ class IHyperOpt(ABC):
|
|||||||
logger.info(f"Max roi table: {round_dict(self.generate_roi_table(p), 3)}")
|
logger.info(f"Max roi table: {round_dict(self.generate_roi_table(p), 3)}")
|
||||||
|
|
||||||
return [
|
return [
|
||||||
Integer(roi_limits["roi_t1_min"], roi_limits["roi_t1_max"], name="roi_t1"),
|
# Integer(roi_limits["roi_t1_min"], roi_limits["roi_t1_max"], name="roi_t1"),
|
||||||
Integer(roi_limits["roi_t2_min"], roi_limits["roi_t2_max"], name="roi_t2"),
|
ft_IntDistribution("roi_t1", roi_limits["roi_t1_min"], roi_limits["roi_t1_max"]),
|
||||||
Integer(roi_limits["roi_t3_min"], roi_limits["roi_t3_max"], name="roi_t3"),
|
# Integer(roi_limits["roi_t2_min"], roi_limits["roi_t2_max"], name="roi_t2"),
|
||||||
|
ft_IntDistribution("roi_t2", roi_limits["roi_t2_min"], roi_limits["roi_t2_max"]),
|
||||||
|
# Integer(roi_limits["roi_t3_min"], roi_limits["roi_t3_max"], name="roi_t3"),
|
||||||
|
ft_IntDistribution("roi_t3", roi_limits["roi_t3_min"], roi_limits["roi_t3_max"]),
|
||||||
SKDecimal(
|
SKDecimal(
|
||||||
roi_limits["roi_p1_min"], roi_limits["roi_p1_max"], decimals=3, name="roi_p1"
|
roi_limits["roi_p1_min"], roi_limits["roi_p1_max"], decimals=3, name="roi_p1"
|
||||||
),
|
),
|
||||||
@@ -184,7 +191,8 @@ class IHyperOpt(ABC):
|
|||||||
# This parameter is included into the hyperspace dimensions rather than assigning
|
# This parameter is included into the hyperspace dimensions rather than assigning
|
||||||
# it explicitly in the code in order to have it printed in the results along with
|
# it explicitly in the code in order to have it printed in the results along with
|
||||||
# other 'trailing' hyperspace parameters.
|
# other 'trailing' hyperspace parameters.
|
||||||
Categorical([True], name="trailing_stop"),
|
# Categorical([True], name="trailing_stop"),
|
||||||
|
ft_CategoricalDistribution("trailing_stop", [True]),
|
||||||
SKDecimal(0.01, 0.35, decimals=3, name="trailing_stop_positive"),
|
SKDecimal(0.01, 0.35, decimals=3, name="trailing_stop_positive"),
|
||||||
# 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive',
|
# 'trailing_stop_positive_offset' should be greater than 'trailing_stop_positive',
|
||||||
# so this intermediate parameter is used as the value of the difference between
|
# so this intermediate parameter is used as the value of the difference between
|
||||||
@@ -192,7 +200,8 @@ class IHyperOpt(ABC):
|
|||||||
# generate_trailing_params() method.
|
# generate_trailing_params() method.
|
||||||
# This is similar to the hyperspace dimensions used for constructing the ROI tables.
|
# This is similar to the hyperspace dimensions used for constructing the ROI tables.
|
||||||
SKDecimal(0.001, 0.1, decimals=3, name="trailing_stop_positive_offset_p1"),
|
SKDecimal(0.001, 0.1, decimals=3, name="trailing_stop_positive_offset_p1"),
|
||||||
Categorical([True, False], name="trailing_only_offset_is_reached"),
|
# Categorical([True, False], name="trailing_only_offset_is_reached"),
|
||||||
|
ft_CategoricalDistribution("trailing_only_offset_is_reached", [True, False]),
|
||||||
]
|
]
|
||||||
|
|
||||||
def max_open_trades_space(self) -> list[Dimension]:
|
def max_open_trades_space(self) -> list[Dimension]:
|
||||||
@@ -201,9 +210,10 @@ class IHyperOpt(ABC):
|
|||||||
|
|
||||||
You may override it in your custom Hyperopt class.
|
You may override it in your custom Hyperopt class.
|
||||||
"""
|
"""
|
||||||
return [
|
# return [
|
||||||
Integer(-1, 10, name="max_open_trades"),
|
# Integer(-1, 10, name="max_open_trades"),
|
||||||
]
|
# ]
|
||||||
|
return [ft_IntDistribution("max_open_trades", -1, 10)]
|
||||||
|
|
||||||
# This is needed for proper unpickling the class attribute timeframe
|
# This is needed for proper unpickling the class attribute timeframe
|
||||||
# which is set to the actual value by the resolver.
|
# which is set to the actual value by the resolver.
|
||||||
|
|||||||
@@ -36,10 +36,14 @@ with warnings.catch_warnings():
|
|||||||
warnings.filterwarnings("ignore", category=FutureWarning)
|
warnings.filterwarnings("ignore", category=FutureWarning)
|
||||||
# from skopt import Optimizer
|
# from skopt import Optimizer
|
||||||
import optuna
|
import optuna
|
||||||
from skopt.space import Categorical, Dimension, Integer, Real
|
from skopt.space import Dimension
|
||||||
|
|
||||||
from freqtrade.optimize.space.decimalspace import SKDecimal
|
from freqtrade.optimize.space.decimalspace import SKDecimal
|
||||||
from freqtrade.strategy.parameters import ft_CategoricalDistribution, ft_IntDistribution
|
from freqtrade.strategy.parameters import (
|
||||||
|
ft_CategoricalDistribution,
|
||||||
|
ft_FloatDistribution,
|
||||||
|
ft_IntDistribution,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -407,27 +411,18 @@ class HyperOptimizer:
|
|||||||
log=False,
|
log=False,
|
||||||
step=1 / pow(10, original_dim.decimals),
|
step=1 / pow(10, original_dim.decimals),
|
||||||
)
|
)
|
||||||
elif isinstance(original_dim, Integer):
|
|
||||||
o_dimensions[original_dim.name] = optuna.distributions.IntDistribution(
|
|
||||||
original_dim.low, original_dim.high, log=False, step=1
|
|
||||||
)
|
|
||||||
elif isinstance(original_dim, Real):
|
|
||||||
o_dimensions[original_dim.name] = optuna.distributions.FloatDistribution(
|
|
||||||
original_dim.low,
|
|
||||||
original_dim.high,
|
|
||||||
log=False,
|
|
||||||
)
|
|
||||||
elif isinstance(original_dim, Categorical):
|
|
||||||
o_dimensions[original_dim.name] = optuna.distributions.CategoricalDistribution(
|
|
||||||
list(original_dim.bounds)
|
|
||||||
)
|
|
||||||
# for preparing to remove old skopt spaces
|
# for preparing to remove old skopt spaces
|
||||||
elif isinstance(original_dim, ft_CategoricalDistribution) or isinstance(
|
elif (
|
||||||
original_dim, ft_IntDistribution
|
isinstance(original_dim, ft_CategoricalDistribution)
|
||||||
|
or isinstance(original_dim, ft_IntDistribution)
|
||||||
|
or isinstance(original_dim, ft_FloatDistribution)
|
||||||
):
|
):
|
||||||
o_dimensions[original_dim.name] = original_dim
|
o_dimensions[original_dim.name] = original_dim
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Unknown search space {original_dim} / {type(original_dim)}")
|
raise Exception(
|
||||||
|
f"Unknown search space {original_dim.name} - {original_dim} / \
|
||||||
|
{type(original_dim)}"
|
||||||
|
)
|
||||||
# logger.info(f"convert_dimensions_to_optuna_space: {s_dimensions} - {o_dimensions}")
|
# logger.info(f"convert_dimensions_to_optuna_space: {s_dimensions} - {o_dimensions}")
|
||||||
return o_dimensions
|
return o_dimensions
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer
|
|||||||
|
|
||||||
|
|
||||||
with suppress(ImportError):
|
with suppress(ImportError):
|
||||||
from optuna.distributions import CategoricalDistribution, IntDistribution
|
from optuna.distributions import CategoricalDistribution, FloatDistribution, IntDistribution
|
||||||
from skopt.space import Integer, Real # Categorical
|
|
||||||
|
|
||||||
|
# from skopt.space import Integer, Real # Categorical
|
||||||
from freqtrade.optimize.space import SKDecimal
|
from freqtrade.optimize.space import SKDecimal
|
||||||
|
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
@@ -26,25 +26,37 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
class ft_CategoricalDistribution(CategoricalDistribution):
|
class ft_CategoricalDistribution(CategoricalDistribution):
|
||||||
name: str
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
name: str,
|
||||||
categories: Sequence[Any],
|
categories: Sequence[Any],
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
self.name = name
|
||||||
return super().__init__(categories)
|
return super().__init__(categories)
|
||||||
|
|
||||||
|
|
||||||
class ft_IntDistribution(IntDistribution):
|
class ft_IntDistribution(IntDistribution):
|
||||||
name: str
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
low: int,
|
name: str,
|
||||||
high: int,
|
low: int | float,
|
||||||
|
high: int | float,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
|
self.name = name
|
||||||
|
return super().__init__(int(low), int(high), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ft_FloatDistribution(FloatDistribution):
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
name: str,
|
||||||
|
low: float,
|
||||||
|
high: float,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
self.name = name
|
||||||
return super().__init__(low, high, **kwargs)
|
return super().__init__(low, high, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@@ -76,7 +88,7 @@ class BaseParameter(ABC):
|
|||||||
:param optimize: Include parameter in hyperopt optimizations.
|
:param optimize: Include parameter in hyperopt optimizations.
|
||||||
:param load: Load parameter value from {space}_params.
|
:param load: Load parameter value from {space}_params.
|
||||||
:param kwargs: Extra parameters to optuna.distributions.
|
:param kwargs: Extra parameters to optuna.distributions.
|
||||||
(IntDistribution|Real|CategoricalDistribution).
|
(IntDistribution|FloatDistribution|CategoricalDistribution).
|
||||||
"""
|
"""
|
||||||
if "name" in kwargs:
|
if "name" in kwargs:
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
@@ -94,7 +106,9 @@ class BaseParameter(ABC):
|
|||||||
@abstractmethod
|
@abstractmethod
|
||||||
def get_space(
|
def get_space(
|
||||||
self, name: str
|
self, name: str
|
||||||
) -> Union["ft_IntDistribution", "Real", "SKDecimal", "ft_CategoricalDistribution"]:
|
) -> Union[
|
||||||
|
"ft_IntDistribution", "ft_FloatDistribution", "SKDecimal", "ft_CategoricalDistribution"
|
||||||
|
]:
|
||||||
"""
|
"""
|
||||||
Get-space - will be used by Hyperopt to get the hyperopt Space
|
Get-space - will be used by Hyperopt to get the hyperopt Space
|
||||||
"""
|
"""
|
||||||
@@ -136,7 +150,7 @@ class NumericParameter(BaseParameter):
|
|||||||
parameter fieldname is prefixed with 'buy_' or 'sell_'.
|
parameter fieldname is prefixed with 'buy_' or 'sell_'.
|
||||||
:param optimize: Include parameter in hyperopt optimizations.
|
:param optimize: Include parameter in hyperopt optimizations.
|
||||||
:param load: Load parameter value from {space}_params.
|
:param load: Load parameter value from {space}_params.
|
||||||
:param kwargs: Extra parameters to skopt.space.*.
|
:param kwargs: Extra parameters to optuna.distributions.*.
|
||||||
"""
|
"""
|
||||||
if high is not None and isinstance(low, Sequence):
|
if high is not None and isinstance(low, Sequence):
|
||||||
raise OperationalException(f"{self.__class__.__name__} space invalid.")
|
raise OperationalException(f"{self.__class__.__name__} space invalid.")
|
||||||
@@ -185,15 +199,13 @@ class IntParameter(NumericParameter):
|
|||||||
low=low, high=high, default=default, space=space, optimize=optimize, load=load, **kwargs
|
low=low, high=high, default=default, space=space, optimize=optimize, load=load, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_space(self, name: str) -> "Integer":
|
def get_space(self, name: str) -> "ft_IntDistribution":
|
||||||
"""
|
"""
|
||||||
Create optuna distribution space.
|
Create optuna distribution space.
|
||||||
:param name: A name of parameter field.
|
:param name: A name of parameter field.
|
||||||
"""
|
"""
|
||||||
# return Integer(low=self.low, high=self.high, name=name, **self._space_params)
|
# return Integer(low=self.low, high=self.high, name=name, **self._space_params)
|
||||||
result = ft_IntDistribution(self.low, self.high, **self._space_params)
|
return ft_IntDistribution(name, self.low, self.high, **self._space_params)
|
||||||
result.name = name
|
|
||||||
return result
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def range(self):
|
def range(self):
|
||||||
@@ -235,18 +247,19 @@ class RealParameter(NumericParameter):
|
|||||||
parameter fieldname is prefixed with 'buy_' or 'sell_'.
|
parameter fieldname is prefixed with 'buy_' or 'sell_'.
|
||||||
:param optimize: Include parameter in hyperopt optimizations.
|
:param optimize: Include parameter in hyperopt optimizations.
|
||||||
:param load: Load parameter value from {space}_params.
|
:param load: Load parameter value from {space}_params.
|
||||||
:param kwargs: Extra parameters to skopt.space.Real.
|
:param kwargs: Extra parameters to optuna.distributions.FloatDistribution.
|
||||||
"""
|
"""
|
||||||
super().__init__(
|
super().__init__(
|
||||||
low=low, high=high, default=default, space=space, optimize=optimize, load=load, **kwargs
|
low=low, high=high, default=default, space=space, optimize=optimize, load=load, **kwargs
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_space(self, name: str) -> "Real":
|
def get_space(self, name: str) -> "ft_FloatDistribution":
|
||||||
"""
|
"""
|
||||||
Create skopt optimization space.
|
Create skopt optimization space.
|
||||||
:param name: A name of parameter field.
|
:param name: A name of parameter field.
|
||||||
"""
|
"""
|
||||||
return Real(low=self.low, high=self.high, name=name, **self._space_params)
|
return ft_FloatDistribution(name, self.low, self.high, **self._space_params)
|
||||||
|
# return Real(low=self.low, high=self.high, name=name, **self._space_params)
|
||||||
|
|
||||||
|
|
||||||
class DecimalParameter(NumericParameter):
|
class DecimalParameter(NumericParameter):
|
||||||
@@ -349,10 +362,8 @@ class CategoricalParameter(BaseParameter):
|
|||||||
Create optuna distribution space.
|
Create optuna distribution space.
|
||||||
:param name: A name of parameter field.
|
:param name: A name of parameter field.
|
||||||
"""
|
"""
|
||||||
# Categorical(self.opt_range, name=name, **self._space_params)
|
# return Categorical(self.opt_range, name=name, **self._space_params)
|
||||||
result = ft_CategoricalDistribution(self.opt_range)
|
return ft_CategoricalDistribution(name, self.opt_range)
|
||||||
result.name = name
|
|
||||||
return result
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def range(self):
|
def range(self):
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ from unittest.mock import ANY, MagicMock, PropertyMock
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
import pytest
|
import pytest
|
||||||
from filelock import Timeout
|
from filelock import Timeout
|
||||||
from skopt.space import Integer
|
|
||||||
|
|
||||||
from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_hyperopt
|
from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_hyperopt
|
||||||
from freqtrade.data.history import load_data
|
from freqtrade.data.history import load_data
|
||||||
@@ -19,6 +18,9 @@ from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
|||||||
from freqtrade.optimize.optimize_reports import generate_strategy_stats
|
from freqtrade.optimize.optimize_reports import generate_strategy_stats
|
||||||
from freqtrade.optimize.space import SKDecimal
|
from freqtrade.optimize.space import SKDecimal
|
||||||
from freqtrade.strategy import IntParameter
|
from freqtrade.strategy import IntParameter
|
||||||
|
|
||||||
|
# from skopt.space import Integer
|
||||||
|
from freqtrade.strategy.parameters import ft_IntDistribution
|
||||||
from freqtrade.util import dt_utc
|
from freqtrade.util import dt_utc
|
||||||
from tests.conftest import (
|
from tests.conftest import (
|
||||||
CURRENT_TEST_STRATEGY,
|
CURRENT_TEST_STRATEGY,
|
||||||
@@ -1304,7 +1306,8 @@ def test_max_open_trades_consistency(mocker, hyperopt_conf, tmp_path, fee) -> No
|
|||||||
assert isinstance(hyperopt.hyperopter.custom_hyperopt, HyperOptAuto)
|
assert isinstance(hyperopt.hyperopter.custom_hyperopt, HyperOptAuto)
|
||||||
|
|
||||||
hyperopt.hyperopter.custom_hyperopt.max_open_trades_space = lambda: [
|
hyperopt.hyperopter.custom_hyperopt.max_open_trades_space = lambda: [
|
||||||
Integer(1, 10, name="max_open_trades")
|
# Integer(1, 10, name="max_open_trades")
|
||||||
|
ft_IntDistribution("max_open_trades", 1, 10)
|
||||||
]
|
]
|
||||||
|
|
||||||
first_time_evaluated = False
|
first_time_evaluated = False
|
||||||
|
|||||||
@@ -895,8 +895,7 @@ def test_is_informative_pairs_callback(default_conf):
|
|||||||
|
|
||||||
def test_hyperopt_parameters():
|
def test_hyperopt_parameters():
|
||||||
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
|
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
|
||||||
from optuna.distributions import CategoricalDistribution, IntDistribution
|
from optuna.distributions import CategoricalDistribution, FloatDistribution, IntDistribution
|
||||||
from skopt.space import Real
|
|
||||||
|
|
||||||
with pytest.raises(OperationalException, match=r"Name is determined.*"):
|
with pytest.raises(OperationalException, match=r"Name is determined.*"):
|
||||||
IntParameter(low=0, high=5, default=1, name="hello")
|
IntParameter(low=0, high=5, default=1, name="hello")
|
||||||
@@ -939,7 +938,7 @@ def test_hyperopt_parameters():
|
|||||||
|
|
||||||
fltpar = RealParameter(low=0.0, high=5.5, default=1.0, space="buy")
|
fltpar = RealParameter(low=0.0, high=5.5, default=1.0, space="buy")
|
||||||
assert fltpar.value == 1
|
assert fltpar.value == 1
|
||||||
assert isinstance(fltpar.get_space(""), Real)
|
assert isinstance(fltpar.get_space(""), FloatDistribution)
|
||||||
|
|
||||||
fltpar = DecimalParameter(low=0.0, high=0.5, default=0.14, decimals=1, space="buy")
|
fltpar = DecimalParameter(low=0.0, high=0.5, default=0.14, decimals=1, space="buy")
|
||||||
assert fltpar.value == 0.1
|
assert fltpar.value == 0.1
|
||||||
|
|||||||
Reference in New Issue
Block a user