Skip to content

Commit

Permalink
extract resolvers to IResolvers and it's own package
Browse files Browse the repository at this point in the history
  • Loading branch information
xmatthias committed Nov 24, 2018
1 parent e442390 commit 21a093b
Show file tree
Hide file tree
Showing 11 changed files with 107 additions and 69 deletions.
4 changes: 2 additions & 2 deletions freqtrade/freqtradebot.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
from freqtrade.edge import Edge
from freqtrade.persistence import Trade
from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.resolvers import StrategyResolver
from freqtrade.state import State
from freqtrade.strategy.interface import SellType
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
from freqtrade.strategy.interface import SellType, IStrategy
from freqtrade.exchange.exchange_helpers import order_book_to_dataframe


Expand Down
4 changes: 2 additions & 2 deletions freqtrade/optimize/backtesting.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
from freqtrade.exchange import Exchange
from freqtrade.misc import file_dump_json
from freqtrade.persistence import Trade
from freqtrade.strategy.interface import SellType
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
from freqtrade.resolvers import StrategyResolver
from freqtrade.strategy.interface import SellType, IStrategy

logger = logging.getLogger(__name__)

Expand Down
2 changes: 1 addition & 1 deletion freqtrade/optimize/edge_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from freqtrade.configuration import Configuration
from freqtrade.arguments import Arguments
from freqtrade.exchange import Exchange
from freqtrade.strategy.resolver import StrategyResolver
from freqtrade.resolvers import StrategyResolver

logger = logging.getLogger(__name__)

Expand Down
2 changes: 2 additions & 0 deletions freqtrade/resolvers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from freqtrade.resolvers.iresolver import IResolver # noqa: F401
from freqtrade.resolvers.strategyresolver import StrategyResolver # noqa: F401
72 changes: 72 additions & 0 deletions freqtrade/resolvers/iresolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# 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, Any

from freqtrade.constants import DEFAULT_HYPEROPT
from freqtrade.optimize.hyperopt_interface import IHyperOpt


logger = logging.getLogger(__name__)


class IResolver(object):
"""
This class contains all the logic to load custom hyperopt class
"""

def __init__(self, object_type, config: Optional[Dict] = None) -> None:
"""
Load the custom class from config parameter
:param config: configuration dictionary or None
"""
config = config or {}

@staticmethod
def _get_valid_objects(object_type, module_path: str,
object_name: str) -> Optional[Type[Any]]:
"""
Returns a list of all possible objects for the given module_path of type oject_type
:param object_type: object_type (class)
:param module_path: absolute path to the module
:param object_name: Class name of the object
:return: Tuple with (name, class) or None
"""

# Generate spec based on absolute path
spec = importlib.util.spec_from_file_location('unknown', module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints

valid_objects_gen = (
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)

@staticmethod
def _search_object(directory: str, object_type, object_name: str,
kwargs: dict) -> Optional[Any]:
"""
Search for the objectname in the given directory
:param directory: relative or absolute directory path
:return: object instance
"""
logger.debug('Searching for %s %s in \'%s\'', object_type.__name__, object_name, directory)
for entry in os.listdir(directory):
# Only consider python files
if not entry.endswith('.py'):
logger.debug('Ignoring %s', entry)
continue
obj = IResolver._get_valid_objects(
object_type, os.path.abspath(os.path.join(directory, entry)), object_name
)
if obj:
return obj(**kwargs)
return None
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
"""
This module load custom strategies
"""
import importlib.util
import inspect
import logging
import os
import tempfile
from base64 import urlsafe_b64decode
from collections import OrderedDict
from pathlib import Path
from typing import Dict, Optional, Type
from typing import Dict, Optional

from freqtrade import constants
from freqtrade.resolvers import IResolver
from freqtrade.strategy import import_strategy
from freqtrade.strategy.interface import IStrategy

logger = logging.getLogger(__name__)


class StrategyResolver(object):
class StrategyResolver(IResolver):
"""
This class contains all the logic to load custom strategy class
"""
Expand Down Expand Up @@ -103,7 +103,8 @@ def _load_strategy(
:param extra_dir: additional directory to search for the given strategy
:return: Strategy instance or None
"""
current_path = os.path.dirname(os.path.realpath(__file__))
current_path = os.path.join(os.path.dirname(os.path.dirname(os.path.realpath(__file__))), 'strategy')

abs_paths = [
os.path.join(os.getcwd(), 'user_data', 'strategies'),
current_path,
Expand Down Expand Up @@ -131,7 +132,8 @@ def _load_strategy(

for path in abs_paths:
try:
strategy = self._search_strategy(path, strategy_name=strategy_name, config=config)
strategy = self._search_object(directory=path, object_type=IStrategy,
object_name=strategy_name, kwargs={'config': config})
if strategy:
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
strategy._populate_fun_len = len(
Expand All @@ -149,43 +151,3 @@ def _load_strategy(
"Impossible to load Strategy '{}'. This class does not exist"
" or contains Python code errors".format(strategy_name)
)

@staticmethod
def _get_valid_strategies(module_path: str, strategy_name: str) -> Optional[Type[IStrategy]]:
"""
Returns a list of all possible strategies for the given module_path
:param module_path: absolute path to the module
:param strategy_name: Class name of the strategy
:return: Tuple with (name, class) or None
"""

# Generate spec based on absolute path
spec = importlib.util.spec_from_file_location('unknown', module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module) # type: ignore # importlib does not use typehints

valid_strategies_gen = (
obj for name, obj in inspect.getmembers(module, inspect.isclass)
if strategy_name == name and IStrategy in obj.__bases__
)
return next(valid_strategies_gen, None)

@staticmethod
def _search_strategy(directory: str, strategy_name: str, config: dict) -> Optional[IStrategy]:
"""
Search for the strategy_name in the given directory
:param directory: relative or absolute directory path
:return: name of the strategy class
"""
logger.debug('Searching for strategy %s in \'%s\'', strategy_name, directory)
for entry in os.listdir(directory):
# Only consider python files
if not entry.endswith('.py'):
logger.debug('Ignoring %s', entry)
continue
strategy = StrategyResolver._get_valid_strategies(
os.path.abspath(os.path.join(directory, entry)), strategy_name
)
if strategy:
return strategy(config)
return None
2 changes: 1 addition & 1 deletion freqtrade/tests/optimize/test_hyperopt.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from freqtrade.optimize.__init__ import load_tickerdata_file
from freqtrade.optimize.hyperopt import Hyperopt, start
from freqtrade.strategy.resolver import StrategyResolver
from freqtrade.resolvers import StrategyResolver
from freqtrade.tests.conftest import log_has, patch_exchange
from freqtrade.tests.optimize.test_backtesting import get_args

Expand Down
32 changes: 17 additions & 15 deletions freqtrade/tests/strategy/test_strategy.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from freqtrade.strategy import import_strategy
from freqtrade.strategy.default_strategy import DefaultStrategy
from freqtrade.strategy.interface import IStrategy
from freqtrade.strategy.resolver import StrategyResolver
from freqtrade.resolvers import StrategyResolver


def test_import_strategy(caplog):
Expand Down Expand Up @@ -44,17 +44,19 @@ def test_search_strategy():
path.realpath(__file__)), '..', '..', 'strategy'
)
assert isinstance(
StrategyResolver._search_strategy(
default_location,
config=default_config,
strategy_name='DefaultStrategy'
StrategyResolver._search_object(
directory=default_location,
object_type=IStrategy,
kwargs={'config': default_config},
object_name='DefaultStrategy'
),
IStrategy
)
assert StrategyResolver._search_strategy(
default_location,
config=default_config,
strategy_name='NotFoundStrategy'
assert StrategyResolver._search_object(
directory=default_location,
object_type=IStrategy,
kwargs={'config': default_config},
object_name='NotFoundStrategy'
) is None


Expand All @@ -77,7 +79,7 @@ def test_load_strategy_invalid_directory(result, caplog):
resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir)

assert (
'freqtrade.strategy.resolver',
'freqtrade.resolvers.strategyresolver',
logging.WARNING,
'Path "{}" does not exist'.format(extra_dir),
) in caplog.record_tuples
Expand Down Expand Up @@ -128,7 +130,7 @@ def test_strategy_override_minimal_roi(caplog):
resolver = StrategyResolver(config)

assert resolver.strategy.minimal_roi[0] == 0.5
assert ('freqtrade.strategy.resolver',
assert ('freqtrade.resolvers.strategyresolver',
logging.INFO,
"Override strategy 'minimal_roi' with value in config file: {'0': 0.5}."
) in caplog.record_tuples
Expand All @@ -143,7 +145,7 @@ def test_strategy_override_stoploss(caplog):
resolver = StrategyResolver(config)

assert resolver.strategy.stoploss == -0.5
assert ('freqtrade.strategy.resolver',
assert ('freqtrade.resolvers.strategyresolver',
logging.INFO,
"Override strategy 'stoploss' with value in config file: -0.5."
) in caplog.record_tuples
Expand All @@ -159,7 +161,7 @@ def test_strategy_override_ticker_interval(caplog):
resolver = StrategyResolver(config)

assert resolver.strategy.ticker_interval == 60
assert ('freqtrade.strategy.resolver',
assert ('freqtrade.resolvers.strategyresolver',
logging.INFO,
"Override strategy 'ticker_interval' with value in config file: 60."
) in caplog.record_tuples
Expand All @@ -175,7 +177,7 @@ def test_strategy_override_process_only_new_candles(caplog):
resolver = StrategyResolver(config)

assert resolver.strategy.process_only_new_candles
assert ('freqtrade.strategy.resolver',
assert ('freqtrade.resolvers.strategyresolver',
logging.INFO,
"Override process_only_new_candles 'process_only_new_candles' "
"with value in config file: True."
Expand All @@ -201,7 +203,7 @@ def test_strategy_override_order_types(caplog):
for method in ['buy', 'sell', 'stoploss']:
assert resolver.strategy.order_types[method] == order_types[method]

assert ('freqtrade.strategy.resolver',
assert ('freqtrade.resolvers.strategyresolver',
logging.INFO,
"Override strategy 'order_types' with value in config file:"
" {'buy': 'market', 'sell': 'limit', 'stoploss': 'limit'}."
Expand Down
2 changes: 1 addition & 1 deletion freqtrade/tests/test_dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pandas

from freqtrade.optimize import load_data
from freqtrade.strategy.resolver import StrategyResolver
from freqtrade.resolvers import StrategyResolver

_pairs = ['ETH/BTC']

Expand Down
2 changes: 1 addition & 1 deletion scripts/plot_dataframe.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
from freqtrade.exchange import Exchange
from freqtrade.optimize.backtesting import setup_configuration
from freqtrade.persistence import Trade
from freqtrade.strategy.resolver import StrategyResolver
from freqtrade.resolvers import StrategyResolver

logger = logging.getLogger(__name__)
_CONF: Dict[str, Any] = {}
Expand Down
2 changes: 1 addition & 1 deletion scripts/plot_profit.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade import constants
from freqtrade.strategy.resolver import StrategyResolver
from freqtrade.resolvers import StrategyResolver
import freqtrade.optimize as optimize
import freqtrade.misc as misc

Expand Down

0 comments on commit 21a093b

Please sign in to comment.