implement '--strategy-path' argument
This commit is contained in:
parent
5fb6fa38aa
commit
a356edb117
@ -86,6 +86,13 @@ class Arguments(object):
|
|||||||
type=str,
|
type=str,
|
||||||
metavar='NAME',
|
metavar='NAME',
|
||||||
)
|
)
|
||||||
|
self.parser.add_argument(
|
||||||
|
'--strategy-path',
|
||||||
|
help='specify additional strategy lookup path',
|
||||||
|
dest='strategy_path',
|
||||||
|
type=str,
|
||||||
|
metavar='PATH',
|
||||||
|
)
|
||||||
self.parser.add_argument(
|
self.parser.add_argument(
|
||||||
'--dynamic-whitelist',
|
'--dynamic-whitelist',
|
||||||
help='dynamically generate and update whitelist \
|
help='dynamically generate and update whitelist \
|
||||||
|
@ -36,6 +36,9 @@ class Configuration(object):
|
|||||||
# Add the strategy file to use
|
# Add the strategy file to use
|
||||||
config.update({'strategy': self.args.strategy})
|
config.update({'strategy': self.args.strategy})
|
||||||
|
|
||||||
|
if self.args.strategy_path:
|
||||||
|
config.update({'strategy_path': self.args.strategy_path})
|
||||||
|
|
||||||
# Load Common configuration
|
# Load Common configuration
|
||||||
config = self._load_common_config(config)
|
config = self._load_common_config(config)
|
||||||
|
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
Main Freqtrade bot script.
|
Main Freqtrade bot script.
|
||||||
Read the documentation to know what cli arguments you need.
|
Read the documentation to know what cli arguments you need.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
import sys
|
import sys
|
||||||
from typing import List
|
from typing import List
|
||||||
|
@ -33,7 +33,8 @@ class StrategyResolver(object):
|
|||||||
config = config or {}
|
config = config or {}
|
||||||
|
|
||||||
# Verify the strategy is in the configuration, otherwise fallback to the default strategy
|
# Verify the strategy is in the configuration, otherwise fallback to the default strategy
|
||||||
self.strategy = self._load_strategy(config.get('strategy') or Constants.DEFAULT_STRATEGY)
|
strategy_name = config.get('strategy') or Constants.DEFAULT_STRATEGY
|
||||||
|
self.strategy = self._load_strategy(strategy_name, extra_dir=config.get('strategy_path'))
|
||||||
|
|
||||||
# Set attributes
|
# Set attributes
|
||||||
# Check if we need to override configuration
|
# Check if we need to override configuration
|
||||||
@ -61,33 +62,34 @@ class StrategyResolver(object):
|
|||||||
self.strategy.stoploss = float(self.strategy.stoploss)
|
self.strategy.stoploss = float(self.strategy.stoploss)
|
||||||
self.strategy.ticker_interval = int(self.strategy.ticker_interval)
|
self.strategy.ticker_interval = int(self.strategy.ticker_interval)
|
||||||
|
|
||||||
def _load_strategy(self, strategy_name: str) -> Optional[IStrategy]:
|
def _load_strategy(
|
||||||
|
self, strategy_name: str, extra_dir: Optional[str] = None) -> Optional[IStrategy]:
|
||||||
"""
|
"""
|
||||||
Search and loads the specified strategy.
|
Search and loads the specified strategy.
|
||||||
:param strategy_name: name of the module to import
|
:param strategy_name: name of the module to import
|
||||||
|
:param extra_dir: additional directory to search for the given strategy
|
||||||
:return: Strategy instance or None
|
:return: Strategy instance or None
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
current_path = os.path.dirname(os.path.realpath(__file__))
|
current_path = os.path.dirname(os.path.realpath(__file__))
|
||||||
abs_paths = [
|
abs_paths = [
|
||||||
os.path.join(current_path, '..', '..', 'user_data', 'strategies'),
|
os.path.join(current_path, '..', '..', 'user_data', 'strategies'),
|
||||||
current_path,
|
current_path,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
if extra_dir:
|
||||||
|
# Add extra strategy directory on top of search paths
|
||||||
|
abs_paths.insert(0, extra_dir)
|
||||||
|
|
||||||
for path in abs_paths:
|
for path in abs_paths:
|
||||||
strategy = self._search_strategy(path, strategy_name)
|
strategy = self._search_strategy(path, strategy_name)
|
||||||
if strategy:
|
if strategy:
|
||||||
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
|
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
|
||||||
return strategy
|
return strategy
|
||||||
|
|
||||||
raise ImportError('not found')
|
raise ImportError(
|
||||||
# Fallback to the default strategy
|
"Impossible to load Strategy '{}'. This class does not exist"
|
||||||
except (ImportError, TypeError):
|
" or contains Python code errors".format(strategy_name)
|
||||||
logger.exception(
|
|
||||||
"Impossible to load Strategy '%s'. This class does not exist"
|
|
||||||
" or contains Python code errors",
|
|
||||||
strategy_name
|
|
||||||
)
|
)
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_valid_strategies(module_path: str, strategy_name: str) -> Optional[Type[IStrategy]]:
|
def _get_valid_strategies(module_path: str, strategy_name: str) -> Optional[Type[IStrategy]]:
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy.interface import IStrategy
|
||||||
from freqtrade.strategy.resolver import StrategyResolver
|
from freqtrade.strategy.resolver import StrategyResolver
|
||||||
|
|
||||||
@ -30,15 +31,29 @@ def test_load_strategy(result):
|
|||||||
assert 'adx' in resolver.strategy.populate_indicators(result)
|
assert 'adx' in resolver.strategy.populate_indicators(result)
|
||||||
|
|
||||||
|
|
||||||
def test_load_not_found_strategy(caplog):
|
def test_load_strategy_custom_directory(result):
|
||||||
strategy = StrategyResolver()
|
resolver = StrategyResolver()
|
||||||
|
|
||||||
assert not hasattr(StrategyResolver, 'custom_strategy')
|
assert not hasattr(StrategyResolver, 'custom_strategy')
|
||||||
strategy._load_strategy('NotFoundStrategy')
|
|
||||||
|
|
||||||
error_msg = "Impossible to load Strategy '{}'. This class does not " \
|
extra_dir = os.path.join('some', 'path')
|
||||||
"exist or contains Python code errors".format('NotFoundStrategy')
|
with pytest.raises(
|
||||||
assert ('freqtrade.strategy.resolver', logging.ERROR, error_msg) in caplog.record_tuples
|
FileNotFoundError,
|
||||||
|
match=r".*No such file or directory: '{}'".format(extra_dir)):
|
||||||
|
resolver._load_strategy('TestStrategy', extra_dir)
|
||||||
|
|
||||||
|
assert not hasattr(StrategyResolver, 'custom_strategy')
|
||||||
|
|
||||||
|
assert hasattr(resolver.strategy, 'populate_indicators')
|
||||||
|
assert 'adx' in resolver.strategy.populate_indicators(result)
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_not_found_strategy():
|
||||||
|
strategy = StrategyResolver()
|
||||||
|
with pytest.raises(ImportError,
|
||||||
|
match=r'Impossible to load Strategy \'NotFoundStrategy\'.'
|
||||||
|
r' This class does not exist or contains Python code errors'):
|
||||||
|
strategy._load_strategy('NotFoundStrategy')
|
||||||
|
|
||||||
|
|
||||||
def test_strategy(result):
|
def test_strategy(result):
|
||||||
@ -111,11 +126,3 @@ def test_strategy_override_ticker_interval(caplog):
|
|||||||
logging.INFO,
|
logging.INFO,
|
||||||
'Override strategy \'ticker_interval\' with value in config file: 60.'
|
'Override strategy \'ticker_interval\' with value in config file: 60.'
|
||||||
) in caplog.record_tuples
|
) in caplog.record_tuples
|
||||||
|
|
||||||
|
|
||||||
def test_strategy_fallback_default_strategy():
|
|
||||||
strategy = StrategyResolver()
|
|
||||||
|
|
||||||
assert not hasattr(StrategyResolver, 'custom_strategy')
|
|
||||||
strategy._load_strategy('../../super_duper')
|
|
||||||
assert not hasattr(StrategyResolver, 'custom_strategy')
|
|
||||||
|
@ -71,6 +71,26 @@ def test_parse_args_invalid() -> None:
|
|||||||
Arguments(['-c'], '').get_parsed_arg()
|
Arguments(['-c'], '').get_parsed_arg()
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_strategy() -> None:
|
||||||
|
args = Arguments(['--strategy', 'SomeStrategy'], '').get_parsed_arg()
|
||||||
|
assert args.strategy == 'SomeStrategy'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_strategy_invalid() -> None:
|
||||||
|
with pytest.raises(SystemExit, match=r'2'):
|
||||||
|
Arguments(['--strategy'], '').get_parsed_arg()
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_strategy_path() -> None:
|
||||||
|
args = Arguments(['--strategy-path', '/some/path'], '').get_parsed_arg()
|
||||||
|
assert args.strategy_path == '/some/path'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_args_strategy_path_invalid() -> None:
|
||||||
|
with pytest.raises(SystemExit, match=r'2'):
|
||||||
|
Arguments(['--strategy-path'], '').get_parsed_arg()
|
||||||
|
|
||||||
|
|
||||||
def test_parse_args_dynamic_whitelist() -> None:
|
def test_parse_args_dynamic_whitelist() -> None:
|
||||||
args = Arguments(['--dynamic-whitelist'], '').get_parsed_arg()
|
args = Arguments(['--dynamic-whitelist'], '').get_parsed_arg()
|
||||||
assert args.dynamic_whitelist == 20
|
assert args.dynamic_whitelist == 20
|
||||||
|
Loading…
Reference in New Issue
Block a user