Merge pull request #578 from gcarq/feature/enhance-strategy-resolving

enhance strategy resolving
This commit is contained in:
Janne Sinivirta 2018-03-27 12:44:33 +03:00 committed by GitHub
commit 1cec06f808
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 134 additions and 161 deletions

View File

@ -14,12 +14,12 @@ Since the version `0.16.0` the bot allows using custom strategy file.
This is very simple. Copy paste your strategy file into the folder This is very simple. Copy paste your strategy file into the folder
`user_data/strategies`. `user_data/strategies`.
Let assume you have a strategy file `awesome-strategy.py`: Let assume you have a class called `AwesomeStrategy` in the file `awesome-strategy.py`:
1. Move your file into `user_data/strategies` (you should have `user_data/strategies/awesome-strategy.py` 1. Move your file into `user_data/strategies` (you should have `user_data/strategies/awesome-strategy.py`
2. Start the bot with the param `--strategy awesome-strategy` (the parameter is the name of the file without '.py') 2. Start the bot with the param `--strategy AwesomeStrategy` (the parameter is the class name)
```bash ```bash
python3 ./freqtrade/main.py --strategy awesome_strategy python3 ./freqtrade/main.py --strategy AwesomeStrategy
``` ```
## Change your strategy ## Change your strategy
@ -35,11 +35,11 @@ A strategy file contains all the information needed to build a good strategy:
- Stoploss recommended - Stoploss recommended
- Hyperopt parameter - Hyperopt parameter
The bot also include a sample strategy you can update: `user_data/strategies/test_strategy.py`. The bot also include a sample strategy called `TestStrategy` you can update: `user_data/strategies/test_strategy.py`.
You can test it with the parameter: `--strategy test_strategy` You can test it with the parameter: `--strategy TestStrategy`
```bash ```bash
python3 ./freqtrade/main.py --strategy awesome_strategy python3 ./freqtrade/main.py --strategy AwesomeStrategy
``` ```
**For the following section we will use the [user_data/strategies/test_strategy.py](https://github.com/gcarq/freqtrade/blob/develop/user_data/strategies/test_strategy.py) **For the following section we will use the [user_data/strategies/test_strategy.py](https://github.com/gcarq/freqtrade/blob/develop/user_data/strategies/test_strategy.py)

View File

@ -26,9 +26,8 @@ optional arguments:
--version show program's version number and exit --version show program's version number and exit
-c PATH, --config PATH -c PATH, --config PATH
specify configuration file (default: config.json) specify configuration file (default: config.json)
-s PATH, --strategy PATH -s NAME, --strategy NAME
specify strategy file (default: specify strategy class name (default: DefaultStrategy)
freqtrade/strategy/default_strategy.py)
--dry-run-db Force dry run to use a local DB --dry-run-db Force dry run to use a local DB
"tradesv3.dry_run.sqlite" instead of memory DB. Work "tradesv3.dry_run.sqlite" instead of memory DB. Work
only if dry_run is enabled. only if dry_run is enabled.
@ -48,21 +47,19 @@ python3 ./freqtrade/main.py -c path/far/far/away/config.json
``` ```
### How to use --strategy? ### How to use --strategy?
This parameter will allow you to load your custom strategy file. Per This parameter will allow you to load your custom strategy class.
default without `--strategy` or `-s` the bot will load the Per default without `--strategy` or `-s` the bot will load the
`default_strategy` included with the bot (`freqtrade/strategy/default_strategy.py`). `DefaultStrategy` included with the bot (`freqtrade/strategy/default_strategy.py`).
The bot will search your strategy file into `user_data/strategies` and The bot will search your strategy file within `user_data/strategies` and `freqtrade/strategy`.
`freqtrade/strategy`.
To load a strategy, simply pass the file name (without .py) in this To load a strategy, simply pass the class name (e.g.: `CustomStrategy`) in this parameter.
parameters.
**Example:** **Example:**
In `user_data/strategies` you have a file `my_awesome_strategy.py` to In `user_data/strategies` you have a file `my_awesome_strategy.py` which has
load it: a strategy class called `AwesomeStrategy` to load it:
```bash ```bash
python3 ./freqtrade/main.py --strategy my_awesome_strategy python3 ./freqtrade/main.py --strategy AwesomeStrategy
``` ```
If the bot does not find your strategy file, it will display in an error If the bot does not find your strategy file, it will display in an error

View File

@ -11,7 +11,7 @@ from pandas import DataFrame, to_datetime
from freqtrade.exchange import get_ticker_history from freqtrade.exchange import get_ticker_history
from freqtrade.persistence import Trade from freqtrade.persistence import Trade
from freqtrade.strategy.strategy import Strategy from freqtrade.strategy.resolver import StrategyResolver
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -36,7 +36,7 @@ class Analyze(object):
:param config: Bot configuration (use the one from Configuration()) :param config: Bot configuration (use the one from Configuration())
""" """
self.config = config self.config = config
self.strategy = Strategy(self.config) self.strategy = StrategyResolver(self.config)
@staticmethod @staticmethod
def parse_ticker_dataframe(ticker: list) -> DataFrame: def parse_ticker_dataframe(ticker: list) -> DataFrame:

View File

@ -80,11 +80,11 @@ class Arguments(object):
) )
self.parser.add_argument( self.parser.add_argument(
'-s', '--strategy', '-s', '--strategy',
help='specify strategy file (default: %(default)s)', help='specify strategy class name (default: %(default)s)',
dest='strategy', dest='strategy',
default='default_strategy', default='DefaultStrategy',
type=str, type=str,
metavar='PATH', metavar='NAME',
) )
self.parser.add_argument( self.parser.add_argument(
'--dynamic-whitelist', '--dynamic-whitelist',

View File

@ -14,7 +14,7 @@ class Constants(object):
TICKER_INTERVAL = 5 # min TICKER_INTERVAL = 5 # min
HYPEROPT_EPOCH = 100 # epochs HYPEROPT_EPOCH = 100 # epochs
RETRY_TIMEOUT = 30 # sec RETRY_TIMEOUT = 30 # sec
DEFAULT_STRATEGY = 'default_strategy' DEFAULT_STRATEGY = 'DefaultStrategy'
# Required json-schema for user specified config # Required json-schema for user specified config
CONF_SCHEMA = { CONF_SCHEMA = {

View File

@ -7,8 +7,6 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
from freqtrade.indicator_helpers import fishers_inverse from freqtrade.indicator_helpers import fishers_inverse
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
class_name = 'DefaultStrategy'
class DefaultStrategy(IStrategy): class DefaultStrategy(IStrategy):
""" """

View File

@ -3,40 +3,41 @@
""" """
This module load custom strategies This module load custom strategies
""" """
import importlib import importlib.util
import inspect
import logging import logging
import os import os
import sys
from collections import OrderedDict from collections import OrderedDict
from typing import Optional, Dict, Type
from pandas import DataFrame from pandas import DataFrame
from freqtrade.constants import Constants from freqtrade.constants import Constants
from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.interface import IStrategy
sys.path.insert(0, r'../../user_data/strategies')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Strategy(object): class StrategyResolver(object):
""" """
This class contains all the logic to load custom strategy class This class contains all the logic to load custom strategy class
""" """
def __init__(self, config: dict = {}) -> None: def __init__(self, config: Optional[Dict] = None) -> None:
""" """
Load the custom class from config parameter Load the custom class from config parameter
:param config: :param config:
:return: :return:
""" """
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
if 'strategy' in config: if 'strategy' in config:
strategy = config['strategy'] strategy = config['strategy']
else: else:
strategy = Constants.DEFAULT_STRATEGY strategy = Constants.DEFAULT_STRATEGY
# Load the strategy # Try to load the strategy
self._load_strategy(strategy) self._load_strategy(strategy)
# Set attributes # Set attributes
@ -70,26 +71,27 @@ class Strategy(object):
def _load_strategy(self, strategy_name: str) -> None: def _load_strategy(self, strategy_name: str) -> None:
""" """
Search and load the custom strategy. If no strategy found, fallback on the default strategy Search and loads the specified strategy.
Set the object into self.custom_strategy
:param strategy_name: name of the module to import :param strategy_name: name of the module to import
:return: None :return: None
""" """
try: try:
# Start by sanitizing the file name (remove any extensions) current_path = os.path.dirname(os.path.realpath(__file__))
strategy_name = self._sanitize_module_name(filename=strategy_name) abs_paths = [
os.path.join(current_path, '..', '..', 'user_data', 'strategies'),
# Search where can be the strategy file current_path,
path = self._search_strategy(filename=strategy_name) ]
for path in abs_paths:
# Load the strategy self.custom_strategy = self._search_strategy(path, strategy_name)
self.custom_strategy = self._load_class(path + strategy_name) if self.custom_strategy:
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
return None
raise ImportError('not found')
# Fallback to the default strategy # Fallback to the default strategy
except (ImportError, TypeError) as error: except (ImportError, TypeError) as error:
logger.error( logger.error(
"Impossible to load Strategy 'user_data/strategies/%s.py'. This file does not exist" "Impossible to load Strategy '%s'. This class does not exist"
" or contains Python code errors", " or contains Python code errors",
strategy_name strategy_name
) )
@ -98,50 +100,45 @@ class Strategy(object):
error error
) )
def _load_class(self, filename: str) -> IStrategy: @staticmethod
def _get_valid_strategies(module_path: str, strategy_name: str) -> Optional[Type[IStrategy]]:
""" """
Import a strategy as a module Returns a list of all possible strategies for the given module_path
:param filename: path to the strategy (path from freqtrade/strategy/) :param module_path: absolute path to the module
:return: return the strategy class :param strategy_name: Class name of the strategy
:return: Tuple with (name, class) or None
""" """
module = importlib.import_module(filename, __package__)
custom_strategy = getattr(module, module.class_name)
logger.info("Load strategy class: %s (%s.py)", module.class_name, filename) # Generate spec based on absolute path
return custom_strategy() spec = importlib.util.spec_from_file_location('user_data.strategies', module_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
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 @staticmethod
def _sanitize_module_name(filename: str) -> str: def _search_strategy(directory: str, strategy_name: str) -> Optional[IStrategy]:
""" """
Remove any extension from filename Search for the strategy_name in the given directory
:param filename: filename to sanatize :param directory: relative or absolute directory path
:return: return the filename without extensions :return: name of the strategy class
""" """
filename = os.path.basename(filename) logger.debug('Searching for strategy %s in \'%s\'', strategy_name, directory)
filename = os.path.splitext(filename)[0] for entry in os.listdir(directory):
return filename # Only consider python files
if not entry.endswith('.py'):
@staticmethod logger.debug('Ignoring %s', entry)
def _search_strategy(filename: str) -> str: continue
""" strategy = StrategyResolver._get_valid_strategies(
Search for the Strategy file in different folder os.path.abspath(os.path.join(directory, entry)), strategy_name
1. search into the user_data/strategies folder )
2. search into the freqtrade/strategy folder if strategy:
3. if nothing found, return None return strategy()
:param strategy_name: module name to search return None
:return: module path where is the strategy
"""
pwd = os.path.dirname(os.path.realpath(__file__)) + '/'
user_data = os.path.join(pwd, '..', '..', 'user_data', 'strategies', filename + '.py')
strategy_folder = os.path.join(pwd, filename + '.py')
path = None
if os.path.isfile(user_data):
path = 'user_data.strategies.'
elif os.path.isfile(strategy_folder):
path = '.'
return path
def populate_indicators(self, dataframe: DataFrame) -> DataFrame: def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
""" """

View File

@ -174,7 +174,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
args = [ args = [
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'default_strategy', '--strategy', 'DefaultStrategy',
'backtesting' 'backtesting'
] ]
@ -215,7 +215,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
args = [ args = [
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'default_strategy', '--strategy', 'DefaultStrategy',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'backtesting', 'backtesting',
'--ticker-interval', '1', '--ticker-interval', '1',
@ -277,7 +277,7 @@ def test_start(mocker, default_conf, caplog) -> None:
)) ))
args = [ args = [
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'default_strategy', '--strategy', 'DefaultStrategy',
'backtesting' 'backtesting'
] ]
args = get_args(args) args = get_args(args)
@ -498,7 +498,7 @@ def test_backtest_ticks(default_conf):
def test_backtest_clash_buy_sell(default_conf): def test_backtest_clash_buy_sell(default_conf):
# Override the default buy trend function in our default_strategy # Override the default buy trend function in our DefaultStrategy
def fun(dataframe=None): def fun(dataframe=None):
buy_value = 1 buy_value = 1
sell_value = 1 sell_value = 1
@ -510,7 +510,7 @@ def test_backtest_clash_buy_sell(default_conf):
def test_backtest_only_sell(default_conf): def test_backtest_only_sell(default_conf):
# Override the default buy trend function in our default_strategy # Override the default buy trend function in our DefaultStrategy
def fun(dataframe=None): def fun(dataframe=None):
buy_value = 0 buy_value = 0
sell_value = 1 sell_value = 1
@ -578,12 +578,12 @@ def test_backtest_start_live(default_conf, mocker, caplog):
args.live = True args.live = True
args.datadir = None args.datadir = None
args.export = None args.export = None
args.strategy = 'default_strategy' args.strategy = 'DefaultStrategy'
args.timerange = '-100' # needed due to MagicMock malleability args.timerange = '-100' # needed due to MagicMock malleability
args = [ args = [
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'default_strategy', '--strategy', 'DefaultStrategy',
'backtesting', 'backtesting',
'--ticker-interval', '1', '--ticker-interval', '1',
'--live', '--live',

View File

@ -8,7 +8,7 @@ import pandas as pd
from freqtrade.optimize.__init__ import load_tickerdata_file from freqtrade.optimize.__init__ import load_tickerdata_file
from freqtrade.optimize.hyperopt import Hyperopt, start from freqtrade.optimize.hyperopt import Hyperopt, start
from freqtrade.strategy.strategy import Strategy from freqtrade.strategy.resolver import StrategyResolver
from freqtrade.tests.conftest import default_conf, log_has from freqtrade.tests.conftest import default_conf, log_has
from freqtrade.tests.optimize.test_backtesting import get_args from freqtrade.tests.optimize.test_backtesting import get_args
@ -56,12 +56,12 @@ def test_start(mocker, default_conf, caplog) -> None:
)) ))
args = [ args = [
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'default_strategy', '--strategy', 'DefaultStrategy',
'hyperopt', 'hyperopt',
'--epochs', '5' '--epochs', '5'
] ]
args = get_args(args) args = get_args(args)
Strategy({'strategy': 'default_strategy'}) StrategyResolver({'strategy': 'DefaultStrategy'})
start(args) start(args)
import pprint import pprint
@ -79,7 +79,7 @@ def test_loss_calculation_prefer_correct_trade_count() -> None:
Test Hyperopt.calculate_loss() Test Hyperopt.calculate_loss()
""" """
hyperopt = _HYPEROPT hyperopt = _HYPEROPT
Strategy({'strategy': 'default_strategy'}) StrategyResolver({'strategy': 'DefaultStrategy'})
correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20) correct = hyperopt.calculate_loss(1, hyperopt.target_trades, 20)
over = hyperopt.calculate_loss(1, hyperopt.target_trades + 100, 20) over = hyperopt.calculate_loss(1, hyperopt.target_trades + 100, 20)
@ -170,7 +170,7 @@ def test_fmin_best_results(mocker, default_conf, caplog) -> None:
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value=fmin_result) mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value=fmin_result)
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
Strategy({'strategy': 'default_strategy'}) StrategyResolver({'strategy': 'DefaultStrategy'})
hyperopt = Hyperopt(conf) hyperopt = Hyperopt(conf)
hyperopt.trials = create_trials(mocker) hyperopt.trials = create_trials(mocker)
hyperopt.tickerdata_to_dataframe = MagicMock() hyperopt.tickerdata_to_dataframe = MagicMock()
@ -213,7 +213,7 @@ def test_fmin_throw_value_error(mocker, default_conf, caplog) -> None:
conf.update({'timerange': None}) conf.update({'timerange': None})
conf.update({'spaces': 'all'}) conf.update({'spaces': 'all'})
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
Strategy({'strategy': 'default_strategy'}) StrategyResolver({'strategy': 'DefaultStrategy'})
hyperopt = Hyperopt(conf) hyperopt = Hyperopt(conf)
hyperopt.trials = create_trials(mocker) hyperopt.trials = create_trials(mocker)
hyperopt.tickerdata_to_dataframe = MagicMock() hyperopt.tickerdata_to_dataframe = MagicMock()
@ -255,7 +255,7 @@ def test_resuming_previous_hyperopt_results_succeeds(mocker, default_conf) -> No
mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={}) mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
Strategy({'strategy': 'default_strategy'}) StrategyResolver({'strategy': 'DefaultStrategy'})
hyperopt = Hyperopt(conf) hyperopt = Hyperopt(conf)
hyperopt.trials = trials hyperopt.trials = trials
hyperopt.tickerdata_to_dataframe = MagicMock() hyperopt.tickerdata_to_dataframe = MagicMock()

View File

@ -4,7 +4,7 @@ import pytest
from pandas import DataFrame from pandas import DataFrame
from freqtrade.analyze import Analyze from freqtrade.analyze import Analyze
from freqtrade.strategy.default_strategy import DefaultStrategy, class_name from freqtrade.strategy.default_strategy import DefaultStrategy
@pytest.fixture @pytest.fixture
@ -13,10 +13,6 @@ def result():
return Analyze.parse_ticker_dataframe(json.load(data_file)) return Analyze.parse_ticker_dataframe(json.load(data_file))
def test_default_strategy_class_name():
assert class_name == DefaultStrategy.__name__
def test_default_strategy_structure(): def test_default_strategy_structure():
assert hasattr(DefaultStrategy, 'minimal_roi') assert hasattr(DefaultStrategy, 'minimal_roi')
assert hasattr(DefaultStrategy, 'stoploss') assert hasattr(DefaultStrategy, 'stoploss')

View File

@ -2,56 +2,47 @@
import logging import logging
from freqtrade.strategy.strategy import Strategy import os
from freqtrade.strategy.interface import IStrategy
def test_sanitize_module_name(): from freqtrade.strategy.resolver import StrategyResolver
assert Strategy._sanitize_module_name('default_strategy') == 'default_strategy'
assert Strategy._sanitize_module_name('default_strategy.py') == 'default_strategy'
assert Strategy._sanitize_module_name('../default_strategy.py') == 'default_strategy'
assert Strategy._sanitize_module_name('../default_strategy') == 'default_strategy'
assert Strategy._sanitize_module_name('.default_strategy') == '.default_strategy'
assert Strategy._sanitize_module_name('foo-bar') == 'foo-bar'
assert Strategy._sanitize_module_name('foo/bar') == 'bar'
def test_search_strategy(): def test_search_strategy():
assert Strategy._search_strategy('default_strategy') == '.' default_location = os.path.join(os.path.dirname(
assert Strategy._search_strategy('test_strategy') == 'user_data.strategies.' os.path.realpath(__file__)), '..', '..', 'strategy'
assert Strategy._search_strategy('super_duper') is None )
assert isinstance(
StrategyResolver._search_strategy(default_location, 'DefaultStrategy'), IStrategy
def test_strategy_structure(): )
assert hasattr(Strategy, 'populate_indicators') assert StrategyResolver._search_strategy(default_location, 'NotFoundStrategy') is None
assert hasattr(Strategy, 'populate_buy_trend')
assert hasattr(Strategy, 'populate_sell_trend')
def test_load_strategy(result): def test_load_strategy(result):
strategy = Strategy() strategy = StrategyResolver()
assert not hasattr(Strategy, 'custom_strategy') assert not hasattr(StrategyResolver, 'custom_strategy')
strategy._load_strategy('test_strategy') strategy._load_strategy('TestStrategy')
assert not hasattr(Strategy, 'custom_strategy') assert not hasattr(StrategyResolver, 'custom_strategy')
assert hasattr(strategy.custom_strategy, 'populate_indicators') assert hasattr(strategy.custom_strategy, 'populate_indicators')
assert 'adx' in strategy.populate_indicators(result) assert 'adx' in strategy.populate_indicators(result)
def test_load_not_found_strategy(caplog): def test_load_not_found_strategy(caplog):
strategy = Strategy() strategy = StrategyResolver()
assert not hasattr(Strategy, 'custom_strategy') assert not hasattr(StrategyResolver, 'custom_strategy')
strategy._load_strategy('NotFoundStrategy') strategy._load_strategy('NotFoundStrategy')
error_msg = "Impossible to load Strategy 'user_data/strategies/{}.py'. This file does not " \ error_msg = "Impossible to load Strategy '{}'. This class does not " \
"exist or contains Python code errors".format('NotFoundStrategy') "exist or contains Python code errors".format('NotFoundStrategy')
assert ('freqtrade.strategy.strategy', logging.ERROR, error_msg) in caplog.record_tuples assert ('freqtrade.strategy.resolver', logging.ERROR, error_msg) in caplog.record_tuples
def test_strategy(result): def test_strategy(result):
strategy = Strategy({'strategy': 'default_strategy'}) strategy = StrategyResolver({'strategy': 'DefaultStrategy'})
assert hasattr(strategy.custom_strategy, 'minimal_roi') assert hasattr(strategy.custom_strategy, 'minimal_roi')
assert strategy.minimal_roi[0] == 0.04 assert strategy.minimal_roi[0] == 0.04
@ -74,16 +65,16 @@ def test_strategy(result):
def test_strategy_override_minimal_roi(caplog): def test_strategy_override_minimal_roi(caplog):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
config = { config = {
'strategy': 'default_strategy', 'strategy': 'DefaultStrategy',
'minimal_roi': { 'minimal_roi': {
"0": 0.5 "0": 0.5
} }
} }
strategy = Strategy(config) strategy = StrategyResolver(config)
assert hasattr(strategy.custom_strategy, 'minimal_roi') assert hasattr(strategy.custom_strategy, 'minimal_roi')
assert strategy.minimal_roi[0] == 0.5 assert strategy.minimal_roi[0] == 0.5
assert ('freqtrade.strategy.strategy', assert ('freqtrade.strategy.resolver',
logging.INFO, logging.INFO,
'Override strategy \'minimal_roi\' with value in config file.' 'Override strategy \'minimal_roi\' with value in config file.'
) in caplog.record_tuples ) in caplog.record_tuples
@ -92,14 +83,14 @@ def test_strategy_override_minimal_roi(caplog):
def test_strategy_override_stoploss(caplog): def test_strategy_override_stoploss(caplog):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
config = { config = {
'strategy': 'default_strategy', 'strategy': 'DefaultStrategy',
'stoploss': -0.5 'stoploss': -0.5
} }
strategy = Strategy(config) strategy = StrategyResolver(config)
assert hasattr(strategy.custom_strategy, 'stoploss') assert hasattr(strategy.custom_strategy, 'stoploss')
assert strategy.stoploss == -0.5 assert strategy.stoploss == -0.5
assert ('freqtrade.strategy.strategy', assert ('freqtrade.strategy.resolver',
logging.INFO, logging.INFO,
'Override strategy \'stoploss\' with value in config file: -0.5.' 'Override strategy \'stoploss\' with value in config file: -0.5.'
) in caplog.record_tuples ) in caplog.record_tuples
@ -109,34 +100,34 @@ def test_strategy_override_ticker_interval(caplog):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
config = { config = {
'strategy': 'default_strategy', 'strategy': 'DefaultStrategy',
'ticker_interval': 60 'ticker_interval': 60
} }
strategy = Strategy(config) strategy = StrategyResolver(config)
assert hasattr(strategy.custom_strategy, 'ticker_interval') assert hasattr(strategy.custom_strategy, 'ticker_interval')
assert strategy.ticker_interval == 60 assert strategy.ticker_interval == 60
assert ('freqtrade.strategy.strategy', assert ('freqtrade.strategy.resolver',
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(): def test_strategy_fallback_default_strategy():
strategy = Strategy() strategy = StrategyResolver()
strategy.logger = logging.getLogger(__name__) strategy.logger = logging.getLogger(__name__)
assert not hasattr(Strategy, 'custom_strategy') assert not hasattr(StrategyResolver, 'custom_strategy')
strategy._load_strategy('../../super_duper') strategy._load_strategy('../../super_duper')
assert not hasattr(Strategy, 'custom_strategy') assert not hasattr(StrategyResolver, 'custom_strategy')
def test_strategy_singleton(): def test_strategy_singleton():
strategy1 = Strategy({'strategy': 'default_strategy'}) strategy1 = StrategyResolver({'strategy': 'DefaultStrategy'})
assert hasattr(strategy1.custom_strategy, 'minimal_roi') assert hasattr(strategy1.custom_strategy, 'minimal_roi')
assert strategy1.minimal_roi[0] == 0.04 assert strategy1.minimal_roi[0] == 0.04
strategy2 = Strategy() strategy2 = StrategyResolver()
assert hasattr(strategy2.custom_strategy, 'minimal_roi') assert hasattr(strategy2.custom_strategy, 'minimal_roi')
assert strategy2.minimal_roi[0] == 0.04 assert strategy2.minimal_roi[0] == 0.04

View File

@ -16,7 +16,7 @@ from freqtrade.optimize.__init__ import load_tickerdata_file
from freqtrade.tests.conftest import log_has from freqtrade.tests.conftest import log_has
# Avoid to reinit the same object again and again # Avoid to reinit the same object again and again
_ANALYZE = Analyze({'strategy': 'default_strategy'}) _ANALYZE = Analyze({'strategy': 'DefaultStrategy'})
def test_signaltype_object() -> None: def test_signaltype_object() -> None:

View File

@ -99,7 +99,7 @@ def test_load_config(default_conf, mocker) -> None:
validated_conf = configuration.load_config() validated_conf = configuration.load_config()
assert 'strategy' in validated_conf assert 'strategy' in validated_conf
assert validated_conf['strategy'] == 'default_strategy' assert validated_conf['strategy'] == 'DefaultStrategy'
assert 'dynamic_whitelist' not in validated_conf assert 'dynamic_whitelist' not in validated_conf
assert 'dry_run_db' not in validated_conf assert 'dry_run_db' not in validated_conf
@ -114,7 +114,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
args = [ args = [
'--dynamic-whitelist', '10', '--dynamic-whitelist', '10',
'--strategy', 'test_strategy', '--strategy', 'TestStrategy',
'--dry-run-db' '--dry-run-db'
] ]
args = Arguments(args, '').get_parsed_arg() args = Arguments(args, '').get_parsed_arg()
@ -125,7 +125,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
assert 'dynamic_whitelist' in validated_conf assert 'dynamic_whitelist' in validated_conf
assert validated_conf['dynamic_whitelist'] == 10 assert validated_conf['dynamic_whitelist'] == 10
assert 'strategy' in validated_conf assert 'strategy' in validated_conf
assert validated_conf['strategy'] == 'test_strategy' assert validated_conf['strategy'] == 'TestStrategy'
assert 'dry_run_db' in validated_conf assert 'dry_run_db' in validated_conf
assert validated_conf['dry_run_db'] is True assert validated_conf['dry_run_db'] is True
@ -140,7 +140,7 @@ def test_show_info(default_conf, mocker, caplog) -> None:
args = [ args = [
'--dynamic-whitelist', '10', '--dynamic-whitelist', '10',
'--strategy', 'test_strategy', '--strategy', 'TestStrategy',
'--dry-run-db' '--dry-run-db'
] ]
args = Arguments(args, '').get_parsed_arg() args = Arguments(args, '').get_parsed_arg()
@ -184,7 +184,7 @@ def test_setup_configuration_without_arguments(mocker, default_conf, caplog) ->
args = [ args = [
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'default_strategy', '--strategy', 'DefaultStrategy',
'backtesting' 'backtesting'
] ]
@ -228,7 +228,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
args = [ args = [
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'default_strategy', '--strategy', 'DefaultStrategy',
'--datadir', '/foo/bar', '--datadir', '/foo/bar',
'backtesting', 'backtesting',
'--ticker-interval', '1', '--ticker-interval', '1',

View File

@ -4,7 +4,7 @@ import pandas
from freqtrade.analyze import Analyze from freqtrade.analyze import Analyze
from freqtrade.optimize import load_data from freqtrade.optimize import load_data
from freqtrade.strategy.strategy import Strategy from freqtrade.strategy.resolver import StrategyResolver
_pairs = ['BTC_ETH'] _pairs = ['BTC_ETH']
@ -15,19 +15,19 @@ def load_dataframe_pair(pairs):
assert isinstance(pairs[0], str) assert isinstance(pairs[0], str)
dataframe = ld[pairs[0]] dataframe = ld[pairs[0]]
analyze = Analyze({'strategy': 'default_strategy'}) analyze = Analyze({'strategy': 'DefaultStrategy'})
dataframe = analyze.analyze_ticker(dataframe) dataframe = analyze.analyze_ticker(dataframe)
return dataframe return dataframe
def test_dataframe_load(): def test_dataframe_load():
Strategy({'strategy': 'default_strategy'}) StrategyResolver({'strategy': 'DefaultStrategy'})
dataframe = load_dataframe_pair(_pairs) dataframe = load_dataframe_pair(_pairs)
assert isinstance(dataframe, pandas.core.frame.DataFrame) assert isinstance(dataframe, pandas.core.frame.DataFrame)
def test_dataframe_columns_exists(): def test_dataframe_columns_exists():
Strategy({'strategy': 'default_strategy'}) StrategyResolver({'strategy': 'DefaultStrategy'})
dataframe = load_dataframe_pair(_pairs) dataframe = load_dataframe_pair(_pairs)
assert 'high' in dataframe.columns assert 'high' in dataframe.columns
assert 'low' in dataframe.columns assert 'low' in dataframe.columns

View File

@ -47,8 +47,6 @@ def test_common_datearray(default_conf, mocker) -> None:
Test common_datearray() Test common_datearray()
:return: None :return: None
""" """
mocker.patch('freqtrade.strategy.strategy.Strategy', MagicMock())
analyze = Analyze(default_conf) analyze = Analyze(default_conf)
tick = load_tickerdata_file(None, 'BTC_UNITEST', 1) tick = load_tickerdata_file(None, 'BTC_UNITEST', 1)
tickerlist = {'BTC_UNITEST': tick} tickerlist = {'BTC_UNITEST': tick}

View File

@ -10,10 +10,6 @@ import freqtrade.vendor.qtpylib.indicators as qtpylib
import numpy # noqa import numpy # noqa
# Update this variable if you change the class name
class_name = 'TestStrategy'
# This class is a sample. Feel free to customize it. # This class is a sample. Feel free to customize it.
class TestStrategy(IStrategy): class TestStrategy(IStrategy):
""" """