Now using resolver for custom hyperopts
This commit is contained in:
parent
e0f420983e
commit
477515c4b5
@ -105,12 +105,12 @@ class Arguments(object):
|
|||||||
metavar='PATH',
|
metavar='PATH',
|
||||||
)
|
)
|
||||||
self.parser.add_argument(
|
self.parser.add_argument(
|
||||||
'--hyperopt',
|
'--customhyperopt',
|
||||||
help='specify hyperopt file (default: %(default)s)',
|
help='specify hyperopt class name (default: %(default)s)',
|
||||||
dest='hyperopt',
|
dest='hyperopt',
|
||||||
default=Constants.DEFAULT_HYPEROPT,
|
default=Constants.DEFAULT_HYPEROPT,
|
||||||
type=str,
|
type=str,
|
||||||
metavar='PATH',
|
metavar='NAME',
|
||||||
)
|
)
|
||||||
self.parser.add_argument(
|
self.parser.add_argument(
|
||||||
'--dynamic-whitelist',
|
'--dynamic-whitelist',
|
||||||
|
@ -9,7 +9,7 @@ TICKER_INTERVAL = 5 # min
|
|||||||
HYPEROPT_EPOCH = 100 # epochs
|
HYPEROPT_EPOCH = 100 # epochs
|
||||||
RETRY_TIMEOUT = 30 # sec
|
RETRY_TIMEOUT = 30 # sec
|
||||||
DEFAULT_STRATEGY = 'DefaultStrategy'
|
DEFAULT_STRATEGY = 'DefaultStrategy'
|
||||||
DEFAULT_HYPEROPT = 'default_hyperopt'
|
DEFAULT_HYPEROPT = 'DefaultHyperOpts'
|
||||||
DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite'
|
DEFAULT_DB_PROD_URL = 'sqlite:///tradesv3.sqlite'
|
||||||
DEFAULT_DB_DRYRUN_URL = 'sqlite://'
|
DEFAULT_DB_DRYRUN_URL = 'sqlite://'
|
||||||
UNLIMITED_STAKE_AMOUNT = 'unlimited'
|
UNLIMITED_STAKE_AMOUNT = 'unlimited'
|
||||||
|
@ -1,153 +0,0 @@
|
|||||||
# pragma pylint: disable=attribute-defined-outside-init
|
|
||||||
|
|
||||||
"""
|
|
||||||
This module load custom hyperopts
|
|
||||||
"""
|
|
||||||
import importlib
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from typing import Dict, Any, Callable
|
|
||||||
|
|
||||||
from pandas import DataFrame
|
|
||||||
|
|
||||||
from freqtrade.constants import Constants
|
|
||||||
from freqtrade.optimize.interface import IHyperOpt
|
|
||||||
|
|
||||||
sys.path.insert(0, r'../../user_data/hyperopts')
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class CustomHyperOpt(object):
|
|
||||||
"""
|
|
||||||
This class contains all the logic to load custom hyperopt class
|
|
||||||
"""
|
|
||||||
def __init__(self, config: dict = {}) -> None:
|
|
||||||
"""
|
|
||||||
Load the custom class from config parameter
|
|
||||||
:param config:
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Verify the hyperopt is in the configuration, otherwise fallback to the default hyperopt
|
|
||||||
if 'hyperopt' in config:
|
|
||||||
hyperopt = config['hyperopt']
|
|
||||||
else:
|
|
||||||
hyperopt = Constants.DEFAULT_HYPEROPT
|
|
||||||
|
|
||||||
# Load the hyperopt
|
|
||||||
self._load_hyperopt(hyperopt)
|
|
||||||
|
|
||||||
def _load_hyperopt(self, hyperopt_name: str) -> None:
|
|
||||||
"""
|
|
||||||
Search and load the custom hyperopt. If no hyperopt found, fallback on the default hyperopt
|
|
||||||
Set the object into self.custom_hyperopt
|
|
||||||
:param hyperopt_name: name of the module to import
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Start by sanitizing the file name (remove any extensions)
|
|
||||||
hyperopt_name = self._sanitize_module_name(filename=hyperopt_name)
|
|
||||||
|
|
||||||
# Search where can be the hyperopt file
|
|
||||||
path = self._search_hyperopt(filename=hyperopt_name)
|
|
||||||
|
|
||||||
# Load the hyperopt
|
|
||||||
self.custom_hyperopt = self._load_class(path + hyperopt_name)
|
|
||||||
|
|
||||||
# Fallback to the default hyperopt
|
|
||||||
except (ImportError, TypeError) as error:
|
|
||||||
logger.error(
|
|
||||||
"Impossible to load Hyperopt 'user_data/hyperopts/%s.py'. This file does not exist"
|
|
||||||
" or contains Python code errors",
|
|
||||||
hyperopt_name
|
|
||||||
)
|
|
||||||
logger.error(
|
|
||||||
"The error is:\n%s.",
|
|
||||||
error
|
|
||||||
)
|
|
||||||
|
|
||||||
def _load_class(self, filename: str) -> IHyperOpt:
|
|
||||||
"""
|
|
||||||
Import a hyperopt as a module
|
|
||||||
:param filename: path to the hyperopt (path from freqtrade/optimize/)
|
|
||||||
:return: return the hyperopt class
|
|
||||||
"""
|
|
||||||
module = importlib.import_module(filename, __package__)
|
|
||||||
custom_hyperopt = getattr(module, module.class_name)
|
|
||||||
|
|
||||||
logger.info("Load hyperopt class: %s (%s.py)", module.class_name, filename)
|
|
||||||
return custom_hyperopt()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _sanitize_module_name(filename: str) -> str:
|
|
||||||
"""
|
|
||||||
Remove any extension from filename
|
|
||||||
:param filename: filename to sanatize
|
|
||||||
:return: return the filename without extensions
|
|
||||||
"""
|
|
||||||
filename = os.path.basename(filename)
|
|
||||||
filename = os.path.splitext(filename)[0]
|
|
||||||
return filename
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _search_hyperopt(filename: str) -> str:
|
|
||||||
"""
|
|
||||||
Search for the hyperopt file in different folder
|
|
||||||
1. search into the user_data/hyperopts folder
|
|
||||||
2. search into the freqtrade/optimize folder
|
|
||||||
3. if nothing found, return None
|
|
||||||
:param hyperopt_name: module name to search
|
|
||||||
:return: module path where is the hyperopt
|
|
||||||
"""
|
|
||||||
pwd = os.path.dirname(os.path.realpath(__file__)) + '/'
|
|
||||||
user_data = os.path.join(pwd, '..', '..', 'user_data', 'hyperopts', filename + '.py')
|
|
||||||
hyperopt_folder = os.path.join(pwd, filename + '.py')
|
|
||||||
|
|
||||||
path = None
|
|
||||||
if os.path.isfile(user_data):
|
|
||||||
path = 'user_data.hyperopts.'
|
|
||||||
elif os.path.isfile(hyperopt_folder):
|
|
||||||
path = '.'
|
|
||||||
|
|
||||||
return path
|
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Populate indicators that will be used in the Buy and Sell hyperopt
|
|
||||||
:param dataframe: Raw data from the exchange and parsed by parse_ticker_dataframe()
|
|
||||||
:return: a Dataframe with all mandatory indicators for the strategies
|
|
||||||
"""
|
|
||||||
return self.custom_hyperopt.populate_indicators(dataframe)
|
|
||||||
|
|
||||||
def buy_strategy_generator(self, params: Dict[str, Any]) -> Callable:
|
|
||||||
"""
|
|
||||||
Create a buy strategy generator
|
|
||||||
"""
|
|
||||||
return self.custom_hyperopt.buy_strategy_generator(params)
|
|
||||||
|
|
||||||
def indicator_space(self) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Create an indicator space
|
|
||||||
"""
|
|
||||||
return self.custom_hyperopt.indicator_space()
|
|
||||||
|
|
||||||
def generate_roi_table(self, params: Dict) -> Dict[int, float]:
|
|
||||||
"""
|
|
||||||
Create an roi table
|
|
||||||
"""
|
|
||||||
return self.custom_hyperopt.generate_roi_table(params)
|
|
||||||
|
|
||||||
def stoploss_space(self) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Create a stoploss space
|
|
||||||
"""
|
|
||||||
return self.custom_hyperopt.stoploss_space()
|
|
||||||
|
|
||||||
def roi_space(self) -> Dict[str, Any]:
|
|
||||||
"""
|
|
||||||
Create a roi space
|
|
||||||
"""
|
|
||||||
return self.custom_hyperopt.roi_space()
|
|
@ -22,7 +22,8 @@ from freqtrade.arguments import Arguments
|
|||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
from freqtrade.optimize import load_data
|
from freqtrade.optimize import load_data
|
||||||
from freqtrade.optimize.backtesting import Backtesting
|
from freqtrade.optimize.backtesting import Backtesting
|
||||||
from freqtrade.optimize.custom_hyperopt import CustomHyperOpt
|
from freqtrade.optimize.resolver import HyperOptResolver
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -40,8 +41,8 @@ class Hyperopt(Backtesting):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Dict[str, Any]) -> None:
|
||||||
super().__init__(config)
|
super().__init__(config)
|
||||||
|
self.config = config
|
||||||
self.custom_hyperopt = CustomHyperOpt(self.config)
|
self.custom_hyperopt = HyperOptResolver(self.config).hyperopt
|
||||||
|
|
||||||
# set TARGET_TRADES to suit your number concurrent trades so its realistic
|
# set TARGET_TRADES to suit your number concurrent trades so its realistic
|
||||||
# to the number of days
|
# to the number of days
|
||||||
|
104
freqtrade/optimize/resolver.py
Normal file
104
freqtrade/optimize/resolver.py
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
# pragma pylint: disable=attribute-defined-outside-init
|
||||||
|
|
||||||
|
"""
|
||||||
|
This module load custom hyperopts
|
||||||
|
"""
|
||||||
|
import importlib.util
|
||||||
|
import inspect
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from typing import Optional, Dict, Type
|
||||||
|
|
||||||
|
from freqtrade.constants import Constants
|
||||||
|
from freqtrade.optimize.interface import IHyperOpt
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class HyperOptResolver(object):
|
||||||
|
"""
|
||||||
|
This class contains all the logic to load custom hyperopt class
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ['hyperopt']
|
||||||
|
|
||||||
|
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') or Constants.DEFAULT_HYPEROPT
|
||||||
|
self.hyperopt = self._load_hyperopt(hyperopt_name, extra_dir=config.get('hyperopt_path'))
|
||||||
|
|
||||||
|
def _load_hyperopt(
|
||||||
|
self, hyperopt_name: str, extra_dir: Optional[str] = None) -> Optional[IHyperOpt]:
|
||||||
|
"""
|
||||||
|
Search and loads the specified hyperopt.
|
||||||
|
:param hyperopt_name: name of the module to import
|
||||||
|
:param extra_dir: additional directory to search for the given hyperopt
|
||||||
|
:return: HyperOpt instance or None
|
||||||
|
"""
|
||||||
|
current_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
abs_paths = [
|
||||||
|
os.path.join(current_path, '..', '..', 'user_data', 'hyperopts'),
|
||||||
|
current_path,
|
||||||
|
]
|
||||||
|
|
||||||
|
if extra_dir:
|
||||||
|
# Add extra hyperopt directory on top of search paths
|
||||||
|
abs_paths.insert(0, extra_dir)
|
||||||
|
|
||||||
|
for path in abs_paths:
|
||||||
|
hyperopt = self._search_hyperopt(path, hyperopt_name)
|
||||||
|
if hyperopt:
|
||||||
|
logger.info('Using resolved hyperopt %s from \'%s\'', hyperopt_name, path)
|
||||||
|
return hyperopt
|
||||||
|
|
||||||
|
raise ImportError(
|
||||||
|
"Impossible to load Hyperopt '{}'. This class does not exist"
|
||||||
|
" or contains Python code errors".format(hyperopt_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _get_valid_hyperopts(module_path: str, hyperopt_name: str) -> Optional[Type[IHyperOpt]]:
|
||||||
|
"""
|
||||||
|
Returns a list of all possible hyperopts for the given module_path
|
||||||
|
:param module_path: absolute path to the module
|
||||||
|
:param hyperopt_name: Class name of the hyperopt
|
||||||
|
:return: Tuple with (name, class) or None
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Generate spec based on absolute path
|
||||||
|
spec = importlib.util.spec_from_file_location('user_data.hyperopts', module_path)
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
|
||||||
|
valid_hyperopts_gen = (
|
||||||
|
obj for name, obj in inspect.getmembers(module, inspect.isclass)
|
||||||
|
if hyperopt_name == name and IHyperOpt in obj.__bases__
|
||||||
|
)
|
||||||
|
return next(valid_hyperopts_gen, None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _search_hyperopt(directory: str, hyperopt_name: str) -> Optional[IHyperOpt]:
|
||||||
|
"""
|
||||||
|
Search for the hyperopt_name in the given directory
|
||||||
|
:param directory: relative or absolute directory path
|
||||||
|
:return: name of the hyperopt class
|
||||||
|
"""
|
||||||
|
logger.debug('Searching for hyperopt %s in \'%s\'', hyperopt_name, directory)
|
||||||
|
for entry in os.listdir(directory):
|
||||||
|
# Only consider python files
|
||||||
|
if not entry.endswith('.py'):
|
||||||
|
logger.debug('Ignoring %s', entry)
|
||||||
|
continue
|
||||||
|
hyperopt = HyperOptResolver._get_valid_hyperopts(
|
||||||
|
os.path.abspath(os.path.join(directory, entry)), hyperopt_name
|
||||||
|
)
|
||||||
|
if hyperopt:
|
||||||
|
return hyperopt()
|
||||||
|
return None
|
@ -15,10 +15,6 @@ from freqtrade.indicator_helpers import fishers_inverse
|
|||||||
from freqtrade.optimize.interface import IHyperOpt
|
from freqtrade.optimize.interface import IHyperOpt
|
||||||
|
|
||||||
|
|
||||||
# Update this variable if you change the class name
|
|
||||||
class_name = 'TestHyperOpt'
|
|
||||||
|
|
||||||
|
|
||||||
# This class is a sample. Feel free to customize it.
|
# This class is a sample. Feel free to customize it.
|
||||||
class TestHyperOpt(IHyperOpt):
|
class TestHyperOpt(IHyperOpt):
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user