Merge branch 'develop' into feat/new_args_system

This commit is contained in:
Matthias
2019-10-20 19:32:34 +02:00
100 changed files with 2632 additions and 1112 deletions

View File

@@ -3,7 +3,7 @@ This module loads custom exchanges
"""
import logging
from freqtrade.exchange import Exchange
from freqtrade.exchange import Exchange, MAP_EXCHANGE_CHILDCLASS
import freqtrade.exchange as exchanges
from freqtrade.resolvers import IResolver
@@ -22,6 +22,8 @@ class ExchangeResolver(IResolver):
Load the custom class from config parameter
:param config: configuration dictionary
"""
# Map exchange name to avoid duplicate classes for identical exchanges
exchange_name = MAP_EXCHANGE_CHILDCLASS.get(exchange_name, exchange_name)
exchange_name = exchange_name.title()
try:
self.exchange = self._load_exchange(exchange_name, kwargs={'config': config})

View File

@@ -54,14 +54,8 @@ class HyperOptResolver(IResolver):
"""
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
abs_paths = [
config['user_data_dir'].joinpath('hyperopts'),
current_path,
]
if extra_dir:
# Add extra hyperopt directory on top of search paths
abs_paths.insert(0, Path(extra_dir).resolve())
abs_paths = self.build_search_paths(config, current_path=current_path,
user_subdir='hyperopts', extra_dir=extra_dir)
hyperopt = self._load_object(paths=abs_paths, object_type=IHyperOpt,
object_name=hyperopt_name, kwargs={'config': config})
@@ -112,14 +106,8 @@ class HyperOptLossResolver(IResolver):
"""
current_path = Path(__file__).parent.parent.joinpath('optimize').resolve()
abs_paths = [
config['user_data_dir'].joinpath('hyperopts'),
current_path,
]
if extra_dir:
# Add extra hyperopt directory on top of search paths
abs_paths.insert(0, Path(extra_dir).resolve())
abs_paths = self.build_search_paths(config, current_path=current_path,
user_subdir='hyperopts', extra_dir=extra_dir)
hyperoptloss = self._load_object(paths=abs_paths, object_type=IHyperOptLoss,
object_name=hyper_loss_name)

View File

@@ -7,7 +7,7 @@ import importlib.util
import inspect
import logging
from pathlib import Path
from typing import Any, List, Optional, Tuple, Type, Union
from typing import Any, List, Optional, Tuple, Union, Generator
logger = logging.getLogger(__name__)
@@ -17,15 +17,29 @@ class IResolver:
This class contains all the logic to load custom classes
"""
def build_search_paths(self, config, current_path: Path, user_subdir: str,
extra_dir: Optional[str] = None) -> List[Path]:
abs_paths = [
config['user_data_dir'].joinpath(user_subdir),
current_path,
]
if extra_dir:
# Add extra directory to the top of the search paths
abs_paths.insert(0, Path(extra_dir).resolve())
return abs_paths
@staticmethod
def _get_valid_object(object_type, module_path: Path,
object_name: str) -> Optional[Type[Any]]:
object_name: str) -> Generator[Any, None, None]:
"""
Returns the first object with matching object_type and object_name in the path given.
Generator returning objects with matching object_type and object_name in the path given.
:param object_type: object_type (class)
:param module_path: absolute path to the module
:param object_name: Class name of the object
:return: class or None
:return: generator containing matching objects
"""
# Generate spec based on absolute path
@@ -42,7 +56,7 @@ class IResolver:
obj for name, obj in inspect.getmembers(module, inspect.isclass)
if object_name == name and object_type in obj.__bases__
)
return next(valid_objects_gen, None)
return valid_objects_gen
@staticmethod
def _search_object(directory: Path, object_type, object_name: str,
@@ -59,9 +73,9 @@ class IResolver:
logger.debug('Ignoring %s', entry)
continue
module_path = entry.resolve()
obj = IResolver._get_valid_object(
object_type, module_path, object_name
)
obj = next(IResolver._get_valid_object(object_type, module_path, object_name), None)
if obj:
return (obj(**kwargs), module_path)
return (None, None)

View File

@@ -1,7 +1,7 @@
# pragma pylint: disable=attribute-defined-outside-init
"""
This module load custom hyperopts
This module load custom pairlists
"""
import logging
from pathlib import Path
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
class PairListResolver(IResolver):
"""
This class contains all the logic to load custom hyperopt class
This class contains all the logic to load custom PairList class
"""
__slots__ = ['pairlist']
@@ -39,10 +39,8 @@ class PairListResolver(IResolver):
"""
current_path = Path(__file__).parent.parent.joinpath('pairlist').resolve()
abs_paths = [
config['user_data_dir'].joinpath('pairlist'),
current_path,
]
abs_paths = self.build_search_paths(config, current_path=current_path,
user_subdir='pairlist', extra_dir=None)
pairlist = self._load_object(paths=abs_paths, object_type=IPairList,
object_name=pairlist_name, kwargs=kwargs)

View File

@@ -41,13 +41,13 @@ class StrategyResolver(IResolver):
config=config,
extra_dir=config.get('strategy_path'))
# make sure experimental dict is available
if 'experimental' not in config:
config['experimental'] = {}
# make sure ask_strategy dict is available
if 'ask_strategy' not in config:
config['ask_strategy'] = {}
# Set attributes
# Check if we need to override configuration
# (Attribute name, default, experimental)
# (Attribute name, default, ask_strategy)
attributes = [("minimal_roi", {"0": 10.0}, False),
("ticker_interval", None, False),
("stoploss", None, False),
@@ -60,20 +60,20 @@ class StrategyResolver(IResolver):
("order_time_in_force", None, False),
("stake_currency", None, False),
("stake_amount", None, False),
("use_sell_signal", False, True),
("use_sell_signal", True, True),
("sell_profit_only", False, True),
("ignore_roi_if_buy_signal", False, True),
]
for attribute, default, experimental in attributes:
if experimental:
self._override_attribute_helper(config['experimental'], attribute, default)
for attribute, default, ask_strategy in attributes:
if ask_strategy:
self._override_attribute_helper(config['ask_strategy'], attribute, default)
else:
self._override_attribute_helper(config, attribute, default)
# Loop this list again to have output combined
for attribute, _, exp in attributes:
if exp and attribute in config['experimental']:
logger.info("Strategy using %s: %s", attribute, config['experimental'][attribute])
if exp and attribute in config['ask_strategy']:
logger.info("Strategy using %s: %s", attribute, config['ask_strategy'][attribute])
elif attribute in config:
logger.info("Strategy using %s: %s", attribute, config[attribute])
@@ -98,7 +98,10 @@ class StrategyResolver(IResolver):
logger.info("Override strategy '%s' with value in config file: %s.",
attribute, config[attribute])
elif hasattr(self.strategy, attribute):
config[attribute] = getattr(self.strategy, attribute)
val = getattr(self.strategy, attribute)
# None's cannot exist in the config, so do not copy them
if val is not None:
config[attribute] = val
# Explicitly check for None here as other "falsy" values are possible
elif default is not None:
setattr(self.strategy, attribute, default)
@@ -124,14 +127,8 @@ class StrategyResolver(IResolver):
"""
current_path = Path(__file__).parent.parent.joinpath('strategy').resolve()
abs_paths = [
config['user_data_dir'].joinpath('strategies'),
current_path,
]
if extra_dir:
# Add extra strategy directory on top of search paths
abs_paths.insert(0, Path(extra_dir).resolve())
abs_paths = self.build_search_paths(config, current_path=current_path,
user_subdir='strategies', extra_dir=extra_dir)
if ":" in strategy_name:
logger.info("loading base64 encoded strategy")