Merge branch 'develop' into pr/yazeed/3055
This commit is contained in:
@@ -29,7 +29,7 @@ class DefaultStrategy(IStrategy):
|
||||
stoploss = -0.10
|
||||
|
||||
# Optimal ticker interval for the strategy
|
||||
ticker_interval = '5m'
|
||||
timeframe = '5m'
|
||||
|
||||
# Optional order type mapping
|
||||
order_types = {
|
||||
|
@@ -31,6 +31,7 @@ class TestStrategyLegacy(IStrategy):
|
||||
stoploss = -0.10
|
||||
|
||||
# Optimal ticker interval for the strategy
|
||||
# Keep the legacy value here to test compatibility
|
||||
ticker_interval = '5m'
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||
|
@@ -6,7 +6,7 @@ from .strats.default_strategy import DefaultStrategy
|
||||
def test_default_strategy_structure():
|
||||
assert hasattr(DefaultStrategy, 'minimal_roi')
|
||||
assert hasattr(DefaultStrategy, 'stoploss')
|
||||
assert hasattr(DefaultStrategy, 'ticker_interval')
|
||||
assert hasattr(DefaultStrategy, 'timeframe')
|
||||
assert hasattr(DefaultStrategy, 'populate_indicators')
|
||||
assert hasattr(DefaultStrategy, 'populate_buy_trend')
|
||||
assert hasattr(DefaultStrategy, 'populate_sell_trend')
|
||||
@@ -18,7 +18,7 @@ def test_default_strategy(result):
|
||||
metadata = {'pair': 'ETH/BTC'}
|
||||
assert type(strategy.minimal_roi) is dict
|
||||
assert type(strategy.stoploss) is float
|
||||
assert type(strategy.ticker_interval) is str
|
||||
assert type(strategy.timeframe) is str
|
||||
indicators = strategy.populate_indicators(result, metadata)
|
||||
assert type(indicators) is DataFrame
|
||||
assert type(strategy.populate_buy_trend(indicators, metadata)) is DataFrame
|
||||
|
@@ -13,12 +13,14 @@ from freqtrade.exceptions import StrategyError
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
|
||||
from tests.conftest import get_patched_exchange, log_has, log_has_re
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from tests.conftest import log_has, log_has_re
|
||||
|
||||
from .strats.default_strategy import DefaultStrategy
|
||||
|
||||
# Avoid to reinit the same object again and again
|
||||
_STRATEGY = DefaultStrategy(config={})
|
||||
_STRATEGY.dp = DataProvider({}, None, None)
|
||||
|
||||
|
||||
def test_returns_latest_signal(mocker, default_conf, ohlcv_history):
|
||||
@@ -29,63 +31,60 @@ def test_returns_latest_signal(mocker, default_conf, ohlcv_history):
|
||||
mocked_history['buy'] = 0
|
||||
mocked_history.loc[1, 'sell'] = 1
|
||||
|
||||
mocker.patch.object(
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
return_value=mocked_history
|
||||
)
|
||||
|
||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', ohlcv_history) == (False, True)
|
||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, True)
|
||||
mocked_history.loc[1, 'sell'] = 0
|
||||
mocked_history.loc[1, 'buy'] = 1
|
||||
|
||||
mocker.patch.object(
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
return_value=mocked_history
|
||||
)
|
||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', ohlcv_history) == (True, False)
|
||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (True, False)
|
||||
mocked_history.loc[1, 'sell'] = 0
|
||||
mocked_history.loc[1, 'buy'] = 0
|
||||
|
||||
mocker.patch.object(
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
return_value=mocked_history
|
||||
)
|
||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', ohlcv_history) == (False, False)
|
||||
assert _STRATEGY.get_signal('ETH/BTC', '5m', mocked_history) == (False, False)
|
||||
|
||||
|
||||
def test_get_signal_empty(default_conf, mocker, caplog):
|
||||
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'],
|
||||
DataFrame())
|
||||
assert log_has('Empty candle (OHLCV) data for pair foo', caplog)
|
||||
caplog.clear()
|
||||
|
||||
assert (False, False) == _STRATEGY.get_signal('bar', default_conf['ticker_interval'],
|
||||
[])
|
||||
assert log_has('Empty candle (OHLCV) data for pair bar', caplog)
|
||||
|
||||
|
||||
def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ohlcv_history):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch.object(
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
side_effect=ValueError('xyz')
|
||||
)
|
||||
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['ticker_interval'],
|
||||
ohlcv_history)
|
||||
assert log_has_re(r'Strategy caused the following exception: xyz.*', caplog)
|
||||
|
||||
|
||||
def test_get_signal_empty_dataframe(default_conf, mocker, caplog, ohlcv_history):
|
||||
caplog.set_level(logging.INFO)
|
||||
def test_analyze_pair_empty(default_conf, mocker, caplog, ohlcv_history):
|
||||
mocker.patch.object(_STRATEGY.dp, 'ohlcv', return_value=ohlcv_history)
|
||||
mocker.patch.object(
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
return_value=DataFrame([])
|
||||
)
|
||||
mocker.patch.object(_STRATEGY, 'assert_df')
|
||||
|
||||
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'],
|
||||
ohlcv_history)
|
||||
assert log_has('Empty dataframe for pair xyz', caplog)
|
||||
_STRATEGY.analyze_pair('ETH/BTC')
|
||||
|
||||
assert log_has('Empty dataframe for pair ETH/BTC', caplog)
|
||||
|
||||
|
||||
def test_get_signal_empty(default_conf, mocker, caplog):
|
||||
assert (False, False) == _STRATEGY.get_signal('foo', default_conf['timeframe'], DataFrame())
|
||||
assert log_has('Empty candle (OHLCV) data for pair foo', caplog)
|
||||
caplog.clear()
|
||||
|
||||
assert (False, False) == _STRATEGY.get_signal('bar', default_conf['timeframe'], None)
|
||||
assert log_has('Empty candle (OHLCV) data for pair bar', caplog)
|
||||
caplog.clear()
|
||||
|
||||
assert (False, False) == _STRATEGY.get_signal('baz', default_conf['timeframe'], DataFrame([]))
|
||||
assert log_has('Empty candle (OHLCV) data for pair baz', caplog)
|
||||
|
||||
|
||||
def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ohlcv_history):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch.object(_STRATEGY.dp, 'ohlcv', return_value=ohlcv_history)
|
||||
mocker.patch.object(
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
side_effect=ValueError('xyz')
|
||||
)
|
||||
_STRATEGY.analyze_pair('foo')
|
||||
assert log_has_re(r'Strategy caused the following exception: xyz.*', caplog)
|
||||
caplog.clear()
|
||||
|
||||
mocker.patch.object(
|
||||
_STRATEGY, 'analyze_ticker',
|
||||
side_effect=Exception('invalid ticker history ')
|
||||
)
|
||||
_STRATEGY.analyze_pair('foo')
|
||||
assert log_has_re(r'Strategy caused the following exception: xyz.*', caplog)
|
||||
|
||||
|
||||
def test_get_signal_old_candle(default_conf, mocker, caplog, ohlcv_history):
|
||||
@@ -98,7 +97,7 @@ def test_get_signal_old_candle(default_conf, mocker, caplog, ohlcv_history):
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
return_value=DataFrame(ticks)
|
||||
)
|
||||
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'],
|
||||
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'],
|
||||
ohlcv_history)
|
||||
assert log_has('Old candle for pair xyz. Last candle is 10 minutes old', caplog)
|
||||
|
||||
@@ -114,13 +113,9 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history):
|
||||
mocked_history.loc[1, 'buy'] = 1
|
||||
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch.object(
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
return_value=mocked_history
|
||||
)
|
||||
mocker.patch.object(_STRATEGY, 'assert_df')
|
||||
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'],
|
||||
ohlcv_history)
|
||||
|
||||
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'], mocked_history)
|
||||
assert log_has('Outdated history for pair xyz. Last tick is 16 minutes old', caplog)
|
||||
|
||||
|
||||
@@ -135,17 +130,18 @@ def test_assert_df_raise(default_conf, mocker, caplog, ohlcv_history):
|
||||
mocked_history.loc[1, 'buy'] = 1
|
||||
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch.object(_STRATEGY.dp, 'ohlcv', return_value=ohlcv_history)
|
||||
mocker.patch.object(_STRATEGY.dp, 'get_analyzed_dataframe', return_value=(mocked_history, 0))
|
||||
mocker.patch.object(
|
||||
_STRATEGY, 'assert_df',
|
||||
side_effect=StrategyError('Dataframe returned...')
|
||||
)
|
||||
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['ticker_interval'],
|
||||
ohlcv_history)
|
||||
_STRATEGY.analyze_pair('xyz')
|
||||
assert log_has('Unable to analyze candle (OHLCV) data for pair xyz: Dataframe returned...',
|
||||
caplog)
|
||||
|
||||
|
||||
def test_assert_df(default_conf, mocker, ohlcv_history):
|
||||
def test_assert_df(default_conf, mocker, ohlcv_history, caplog):
|
||||
# Ensure it's running when passed correctly
|
||||
_STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
|
||||
ohlcv_history.loc[1, 'close'], ohlcv_history.loc[1, 'date'])
|
||||
@@ -163,14 +159,13 @@ def test_assert_df(default_conf, mocker, ohlcv_history):
|
||||
_STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
|
||||
ohlcv_history.loc[1, 'close'], ohlcv_history.loc[0, 'date'])
|
||||
|
||||
|
||||
def test_get_signal_handles_exceptions(mocker, default_conf):
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.object(
|
||||
_STRATEGY, 'analyze_ticker',
|
||||
side_effect=Exception('invalid ticker history ')
|
||||
)
|
||||
assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
|
||||
_STRATEGY.disable_dataframe_checks = True
|
||||
caplog.clear()
|
||||
_STRATEGY.assert_df(ohlcv_history, len(ohlcv_history),
|
||||
ohlcv_history.loc[1, 'close'], ohlcv_history.loc[0, 'date'])
|
||||
assert log_has_re(r"Dataframe returned from strategy.*last date\.", caplog)
|
||||
# reset to avoid problems in other tests due to test leakage
|
||||
_STRATEGY.disable_dataframe_checks = False
|
||||
|
||||
|
||||
def test_ohlcvdata_to_dataframe(default_conf, testdatadir) -> None:
|
||||
@@ -349,6 +344,7 @@ def test__analyze_ticker_internal_skip_analyze(ohlcv_history, mocker, caplog) ->
|
||||
|
||||
)
|
||||
strategy = DefaultStrategy({})
|
||||
strategy.dp = DataProvider({}, None, None)
|
||||
strategy.process_only_new_candles = True
|
||||
|
||||
ret = strategy._analyze_ticker_internal(ohlcv_history, {'pair': 'ETH/BTC'})
|
||||
@@ -407,6 +403,14 @@ def test_is_pair_locked(default_conf):
|
||||
assert not strategy.is_pair_locked(pair)
|
||||
|
||||
|
||||
def test_is_informative_pairs_callback(default_conf):
|
||||
default_conf.update({'strategy': 'TestStrategyLegacy'})
|
||||
strategy = StrategyResolver.load_strategy(default_conf)
|
||||
# Should return empty
|
||||
# Uses fallback to base implementation
|
||||
assert [] == strategy.informative_pairs()
|
||||
|
||||
|
||||
@pytest.mark.parametrize('error', [
|
||||
ValueError, KeyError, Exception,
|
||||
])
|
||||
@@ -426,6 +430,11 @@ def test_strategy_safe_wrapper_error(caplog, error):
|
||||
assert isinstance(ret, bool)
|
||||
assert ret
|
||||
|
||||
caplog.clear()
|
||||
# Test supressing error
|
||||
ret = strategy_safe_wrapper(failing_method, message='DeadBeef', supress_error=True)()
|
||||
assert log_has_re(r'DeadBeef.*', caplog)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('value', [
|
||||
1, 22, 55, True, False, {'a': 1, 'b': '112'},
|
||||
|
@@ -105,8 +105,9 @@ def test_strategy(result, default_conf):
|
||||
assert strategy.stoploss == -0.10
|
||||
assert default_conf['stoploss'] == -0.10
|
||||
|
||||
assert strategy.timeframe == '5m'
|
||||
assert strategy.ticker_interval == '5m'
|
||||
assert default_conf['ticker_interval'] == '5m'
|
||||
assert default_conf['timeframe'] == '5m'
|
||||
|
||||
df_indicators = strategy.advise_indicators(result, metadata=metadata)
|
||||
assert 'adx' in df_indicators
|
||||
@@ -176,19 +177,19 @@ def test_strategy_override_trailing_stop_positive(caplog, default_conf):
|
||||
caplog)
|
||||
|
||||
|
||||
def test_strategy_override_ticker_interval(caplog, default_conf):
|
||||
def test_strategy_override_timeframe(caplog, default_conf):
|
||||
caplog.set_level(logging.INFO)
|
||||
|
||||
default_conf.update({
|
||||
'strategy': 'DefaultStrategy',
|
||||
'ticker_interval': 60,
|
||||
'timeframe': 60,
|
||||
'stake_currency': 'ETH'
|
||||
})
|
||||
strategy = StrategyResolver.load_strategy(default_conf)
|
||||
|
||||
assert strategy.ticker_interval == 60
|
||||
assert strategy.timeframe == 60
|
||||
assert strategy.stake_currency == 'ETH'
|
||||
assert log_has("Override strategy 'ticker_interval' with value in config file: 60.",
|
||||
assert log_has("Override strategy 'timeframe' with value in config file: 60.",
|
||||
caplog)
|
||||
|
||||
|
||||
@@ -357,8 +358,9 @@ def test_deprecate_populate_indicators(result, default_conf):
|
||||
|
||||
|
||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
||||
def test_call_deprecated_function(result, monkeypatch, default_conf):
|
||||
def test_call_deprecated_function(result, monkeypatch, default_conf, caplog):
|
||||
default_location = Path(__file__).parent / "strats"
|
||||
del default_conf['timeframe']
|
||||
default_conf.update({'strategy': 'TestStrategyLegacy',
|
||||
'strategy_path': default_location})
|
||||
strategy = StrategyResolver.load_strategy(default_conf)
|
||||
@@ -369,6 +371,8 @@ def test_call_deprecated_function(result, monkeypatch, default_conf):
|
||||
assert strategy._buy_fun_len == 2
|
||||
assert strategy._sell_fun_len == 2
|
||||
assert strategy.INTERFACE_VERSION == 1
|
||||
assert strategy.timeframe == '5m'
|
||||
assert strategy.ticker_interval == '5m'
|
||||
|
||||
indicator_df = strategy.advise_indicators(result, metadata=metadata)
|
||||
assert isinstance(indicator_df, DataFrame)
|
||||
@@ -382,6 +386,9 @@ def test_call_deprecated_function(result, monkeypatch, default_conf):
|
||||
assert isinstance(selldf, DataFrame)
|
||||
assert 'sell' in selldf
|
||||
|
||||
assert log_has("DEPRECATED: Please migrate to using 'timeframe' instead of 'ticker_interval'.",
|
||||
caplog)
|
||||
|
||||
|
||||
def test_strategy_interface_versioning(result, monkeypatch, default_conf):
|
||||
default_conf.update({'strategy': 'DefaultStrategy'})
|
||||
|
Reference in New Issue
Block a user