Merge pull request #6723 from freqtrade/strategy_v1_remove
Strategy v1 remove
This commit is contained in:
commit
f45bafdb16
@ -217,15 +217,19 @@ class StrategyResolver(IResolver):
|
|||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
"`populate_exit_trend` or `populate_sell_trend` must be implemented.")
|
"`populate_exit_trend` or `populate_sell_trend` must be implemented.")
|
||||||
|
|
||||||
strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args)
|
_populate_fun_len = len(getfullargspec(strategy.populate_indicators).args)
|
||||||
strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)
|
_buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)
|
||||||
strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args)
|
_sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args)
|
||||||
if any(x == 2 for x in [
|
if any(x == 2 for x in [
|
||||||
strategy._populate_fun_len,
|
_populate_fun_len,
|
||||||
strategy._buy_fun_len,
|
_buy_fun_len,
|
||||||
strategy._sell_fun_len
|
_sell_fun_len
|
||||||
]):
|
]):
|
||||||
strategy.INTERFACE_VERSION = 1
|
raise OperationalException(
|
||||||
|
"Strategy Interface v1 is no longer supported. "
|
||||||
|
"Please update your strategy to implement "
|
||||||
|
"`populate_indicators`, `populate_entry_trend` and `populate_exit_trend` "
|
||||||
|
"with the metadata argument. ")
|
||||||
return strategy
|
return strategy
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -3,7 +3,6 @@ IStrategy interface
|
|||||||
This module defines the interface to apply for strategies
|
This module defines the interface to apply for strategies
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Dict, List, Optional, Tuple, Union
|
from typing import Dict, List, Optional, Tuple, Union
|
||||||
@ -44,14 +43,11 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
"""
|
"""
|
||||||
# Strategy interface version
|
# Strategy interface version
|
||||||
# Default to version 2
|
# Default to version 2
|
||||||
# Version 1 is the initial interface without metadata dict
|
# Version 1 is the initial interface without metadata dict - deprecated and no longer supported.
|
||||||
# Version 2 populate_* include metadata dict
|
# Version 2 populate_* include metadata dict
|
||||||
# Version 3 - First version with short and leverage support
|
# Version 3 - First version with short and leverage support
|
||||||
INTERFACE_VERSION: int = 3
|
INTERFACE_VERSION: int = 3
|
||||||
|
|
||||||
_populate_fun_len: int = 0
|
|
||||||
_buy_fun_len: int = 0
|
|
||||||
_sell_fun_len: int = 0
|
|
||||||
_ft_params_from_file: Dict
|
_ft_params_from_file: Dict
|
||||||
# associated minimal roi
|
# associated minimal roi
|
||||||
minimal_roi: Dict = {}
|
minimal_roi: Dict = {}
|
||||||
@ -1090,11 +1086,6 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
dataframe = _create_and_merge_informative_pair(
|
dataframe = _create_and_merge_informative_pair(
|
||||||
self, dataframe, metadata, inf_data, populate_fn)
|
self, dataframe, metadata, inf_data, populate_fn)
|
||||||
|
|
||||||
if self._populate_fun_len == 2:
|
|
||||||
warnings.warn("deprecated - check out the Sample strategy to see "
|
|
||||||
"the current function headers!", DeprecationWarning)
|
|
||||||
return self.populate_indicators(dataframe) # type: ignore
|
|
||||||
else:
|
|
||||||
return self.populate_indicators(dataframe, metadata)
|
return self.populate_indicators(dataframe, metadata)
|
||||||
|
|
||||||
def advise_entry(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def advise_entry(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
@ -1109,11 +1100,6 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
|
|
||||||
logger.debug(f"Populating enter signals for pair {metadata.get('pair')}.")
|
logger.debug(f"Populating enter signals for pair {metadata.get('pair')}.")
|
||||||
|
|
||||||
if self._buy_fun_len == 2:
|
|
||||||
warnings.warn("deprecated - check out the Sample strategy to see "
|
|
||||||
"the current function headers!", DeprecationWarning)
|
|
||||||
df = self.populate_buy_trend(dataframe) # type: ignore
|
|
||||||
else:
|
|
||||||
df = self.populate_entry_trend(dataframe, metadata)
|
df = self.populate_entry_trend(dataframe, metadata)
|
||||||
if 'enter_long' not in df.columns:
|
if 'enter_long' not in df.columns:
|
||||||
df = df.rename({'buy': 'enter_long', 'buy_tag': 'enter_tag'}, axis='columns')
|
df = df.rename({'buy': 'enter_long', 'buy_tag': 'enter_tag'}, axis='columns')
|
||||||
@ -1129,13 +1115,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
currently traded pair
|
currently traded pair
|
||||||
:return: DataFrame with exit column
|
:return: DataFrame with exit column
|
||||||
"""
|
"""
|
||||||
|
|
||||||
logger.debug(f"Populating exit signals for pair {metadata.get('pair')}.")
|
logger.debug(f"Populating exit signals for pair {metadata.get('pair')}.")
|
||||||
if self._sell_fun_len == 2:
|
|
||||||
warnings.warn("deprecated - check out the Sample strategy to see "
|
|
||||||
"the current function headers!", DeprecationWarning)
|
|
||||||
df = self.populate_sell_trend(dataframe) # type: ignore
|
|
||||||
else:
|
|
||||||
df = self.populate_exit_trend(dataframe, metadata)
|
df = self.populate_exit_trend(dataframe, metadata)
|
||||||
if 'exit_long' not in df.columns:
|
if 'exit_long' not in df.columns:
|
||||||
df = df.rename({'sell': 'exit_long'}, axis='columns')
|
df = df.rename({'sell': 'exit_long'}, axis='columns')
|
||||||
|
@ -859,8 +859,8 @@ def test_start_list_strategies(capsys):
|
|||||||
# pargs['config'] = None
|
# pargs['config'] = None
|
||||||
start_list_strategies(pargs)
|
start_list_strategies(pargs)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert "TestStrategyLegacyV1" in captured.out
|
assert "StrategyTestV2" in captured.out
|
||||||
assert "legacy_strategy_v1.py" not in captured.out
|
assert "strategy_test_v2.py" not in captured.out
|
||||||
assert CURRENT_TEST_STRATEGY in captured.out
|
assert CURRENT_TEST_STRATEGY in captured.out
|
||||||
|
|
||||||
# Test regular output
|
# Test regular output
|
||||||
@ -874,8 +874,8 @@ def test_start_list_strategies(capsys):
|
|||||||
# pargs['config'] = None
|
# pargs['config'] = None
|
||||||
start_list_strategies(pargs)
|
start_list_strategies(pargs)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert "TestStrategyLegacyV1" in captured.out
|
assert "StrategyTestV2" in captured.out
|
||||||
assert "legacy_strategy_v1.py" in captured.out
|
assert "strategy_test_v2.py" in captured.out
|
||||||
assert CURRENT_TEST_STRATEGY in captured.out
|
assert CURRENT_TEST_STRATEGY in captured.out
|
||||||
|
|
||||||
# Test color output
|
# Test color output
|
||||||
@ -888,8 +888,8 @@ def test_start_list_strategies(capsys):
|
|||||||
# pargs['config'] = None
|
# pargs['config'] = None
|
||||||
start_list_strategies(pargs)
|
start_list_strategies(pargs)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert "TestStrategyLegacyV1" in captured.out
|
assert "StrategyTestV2" in captured.out
|
||||||
assert "legacy_strategy_v1.py" in captured.out
|
assert "strategy_test_v2.py" in captured.out
|
||||||
assert CURRENT_TEST_STRATEGY in captured.out
|
assert CURRENT_TEST_STRATEGY in captured.out
|
||||||
assert "LOAD FAILED" in captured.out
|
assert "LOAD FAILED" in captured.out
|
||||||
# Recursive
|
# Recursive
|
||||||
@ -907,8 +907,8 @@ def test_start_list_strategies(capsys):
|
|||||||
# pargs['config'] = None
|
# pargs['config'] = None
|
||||||
start_list_strategies(pargs)
|
start_list_strategies(pargs)
|
||||||
captured = capsys.readouterr()
|
captured = capsys.readouterr()
|
||||||
assert "TestStrategyLegacyV1" in captured.out
|
assert "StrategyTestV2" in captured.out
|
||||||
assert "legacy_strategy_v1.py" in captured.out
|
assert "strategy_test_v2.py" in captured.out
|
||||||
assert "StrategyTestV2" in captured.out
|
assert "StrategyTestV2" in captured.out
|
||||||
assert "TestStrategyNoImplements" in captured.out
|
assert "TestStrategyNoImplements" in captured.out
|
||||||
assert str(Path("broken_strats/broken_futures_strategies.py")) in captured.out
|
assert str(Path("broken_strats/broken_futures_strategies.py")) in captured.out
|
||||||
|
@ -500,7 +500,7 @@ def test_backtesting_pairlist_list(default_conf, mocker, caplog, testdatadir, ti
|
|||||||
Backtesting(default_conf)
|
Backtesting(default_conf)
|
||||||
|
|
||||||
# Multiple strategies
|
# Multiple strategies
|
||||||
default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY, 'TestStrategyLegacyV1']
|
default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY, 'StrategyTestV2']
|
||||||
with pytest.raises(OperationalException,
|
with pytest.raises(OperationalException,
|
||||||
match='PrecisionFilter not allowed for backtesting multiple strategies.'):
|
match='PrecisionFilter not allowed for backtesting multiple strategies.'):
|
||||||
Backtesting(default_conf)
|
Backtesting(default_conf)
|
||||||
@ -1198,7 +1198,7 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
|||||||
'--disable-max-market-positions',
|
'--disable-max-market-positions',
|
||||||
'--strategy-list',
|
'--strategy-list',
|
||||||
CURRENT_TEST_STRATEGY,
|
CURRENT_TEST_STRATEGY,
|
||||||
'TestStrategyLegacyV1',
|
'StrategyTestV2',
|
||||||
]
|
]
|
||||||
args = get_args(args)
|
args = get_args(args)
|
||||||
start_backtesting(args)
|
start_backtesting(args)
|
||||||
@ -1221,14 +1221,13 @@ def test_backtest_start_multi_strat(default_conf, mocker, caplog, testdatadir):
|
|||||||
'up to 2017-11-14 22:58:00 (0 days).',
|
'up to 2017-11-14 22:58:00 (0 days).',
|
||||||
'Parameter --enable-position-stacking detected ...',
|
'Parameter --enable-position-stacking detected ...',
|
||||||
f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}',
|
f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}',
|
||||||
'Running backtesting for Strategy TestStrategyLegacyV1',
|
'Running backtesting for Strategy StrategyTestV2',
|
||||||
]
|
]
|
||||||
|
|
||||||
for line in exists:
|
for line in exists:
|
||||||
assert log_has(line, caplog)
|
assert log_has(line, caplog)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
|
||||||
def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdatadir, capsys):
|
def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdatadir, capsys):
|
||||||
default_conf.update({
|
default_conf.update({
|
||||||
"use_exit_signal": True,
|
"use_exit_signal": True,
|
||||||
@ -1310,7 +1309,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
|||||||
'--breakdown', 'day',
|
'--breakdown', 'day',
|
||||||
'--strategy-list',
|
'--strategy-list',
|
||||||
CURRENT_TEST_STRATEGY,
|
CURRENT_TEST_STRATEGY,
|
||||||
'TestStrategyLegacyV1',
|
'StrategyTestV2',
|
||||||
]
|
]
|
||||||
args = get_args(args)
|
args = get_args(args)
|
||||||
start_backtesting(args)
|
start_backtesting(args)
|
||||||
@ -1327,7 +1326,7 @@ def test_backtest_start_multi_strat_nomock(default_conf, mocker, caplog, testdat
|
|||||||
'up to 2017-11-14 22:58:00 (0 days).',
|
'up to 2017-11-14 22:58:00 (0 days).',
|
||||||
'Parameter --enable-position-stacking detected ...',
|
'Parameter --enable-position-stacking detected ...',
|
||||||
f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}',
|
f'Running backtesting for Strategy {CURRENT_TEST_STRATEGY}',
|
||||||
'Running backtesting for Strategy TestStrategyLegacyV1',
|
'Running backtesting for Strategy StrategyTestV2',
|
||||||
]
|
]
|
||||||
|
|
||||||
for line in exists:
|
for line in exists:
|
||||||
@ -1592,7 +1591,7 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda
|
|||||||
min_backtest_date = now - timedelta(weeks=4)
|
min_backtest_date = now - timedelta(weeks=4)
|
||||||
load_backtest_metadata = MagicMock(return_value={
|
load_backtest_metadata = MagicMock(return_value={
|
||||||
'StrategyTestV2': {'run_id': '1', 'backtest_start_time': now.timestamp()},
|
'StrategyTestV2': {'run_id': '1', 'backtest_start_time': now.timestamp()},
|
||||||
'TestStrategyLegacyV1': {'run_id': run_id, 'backtest_start_time': start_time.timestamp()}
|
'StrategyTestV3': {'run_id': run_id, 'backtest_start_time': start_time.timestamp()}
|
||||||
})
|
})
|
||||||
load_backtest_stats = MagicMock(side_effect=[
|
load_backtest_stats = MagicMock(side_effect=[
|
||||||
{
|
{
|
||||||
@ -1601,9 +1600,9 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda
|
|||||||
'strategy_comparison': [{'key': 'StrategyTestV2'}]
|
'strategy_comparison': [{'key': 'StrategyTestV2'}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
'metadata': {'TestStrategyLegacyV1': {'run_id': '2'}},
|
'metadata': {'StrategyTestV3': {'run_id': '2'}},
|
||||||
'strategy': {'TestStrategyLegacyV1': {}},
|
'strategy': {'StrategyTestV3': {}},
|
||||||
'strategy_comparison': [{'key': 'TestStrategyLegacyV1'}]
|
'strategy_comparison': [{'key': 'StrategyTestV3'}]
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
mocker.patch('pathlib.Path.glob', return_value=[
|
mocker.patch('pathlib.Path.glob', return_value=[
|
||||||
@ -1627,7 +1626,7 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda
|
|||||||
'--cache', cache,
|
'--cache', cache,
|
||||||
'--strategy-list',
|
'--strategy-list',
|
||||||
'StrategyTestV2',
|
'StrategyTestV2',
|
||||||
'TestStrategyLegacyV1',
|
'StrategyTestV3',
|
||||||
]
|
]
|
||||||
args = get_args(args)
|
args = get_args(args)
|
||||||
start_backtesting(args)
|
start_backtesting(args)
|
||||||
@ -1649,7 +1648,7 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda
|
|||||||
assert backtestmock.call_count == 2
|
assert backtestmock.call_count == 2
|
||||||
exists = [
|
exists = [
|
||||||
'Running backtesting for Strategy StrategyTestV2',
|
'Running backtesting for Strategy StrategyTestV2',
|
||||||
'Running backtesting for Strategy TestStrategyLegacyV1',
|
'Running backtesting for Strategy StrategyTestV3',
|
||||||
'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
|
'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
|
||||||
'Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:58:00 (0 days).',
|
'Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:58:00 (0 days).',
|
||||||
]
|
]
|
||||||
@ -1657,12 +1656,12 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda
|
|||||||
assert backtestmock.call_count == 0
|
assert backtestmock.call_count == 0
|
||||||
exists = [
|
exists = [
|
||||||
'Reusing result of previous backtest for StrategyTestV2',
|
'Reusing result of previous backtest for StrategyTestV2',
|
||||||
'Reusing result of previous backtest for TestStrategyLegacyV1',
|
'Reusing result of previous backtest for StrategyTestV3',
|
||||||
]
|
]
|
||||||
else:
|
else:
|
||||||
exists = [
|
exists = [
|
||||||
'Reusing result of previous backtest for StrategyTestV2',
|
'Reusing result of previous backtest for StrategyTestV2',
|
||||||
'Running backtesting for Strategy TestStrategyLegacyV1',
|
'Running backtesting for Strategy StrategyTestV3',
|
||||||
'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
|
'Ignoring max_open_trades (--disable-max-market-positions was used) ...',
|
||||||
'Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:58:00 (0 days).',
|
'Backtesting with data from 2017-11-14 21:17:00 up to 2017-11-14 22:58:00 (0 days).',
|
||||||
]
|
]
|
||||||
|
@ -1389,7 +1389,6 @@ def test_api_strategies(botclient):
|
|||||||
'StrategyTestV2',
|
'StrategyTestV2',
|
||||||
'StrategyTestV3',
|
'StrategyTestV3',
|
||||||
'StrategyTestV3Futures',
|
'StrategyTestV3Futures',
|
||||||
'TestStrategyLegacyV1',
|
|
||||||
]}
|
]}
|
||||||
|
|
||||||
|
|
||||||
|
30
tests/strategy/strats/broken_strats/legacy_strategy_v1.py
Normal file
30
tests/strategy/strats/broken_strats/legacy_strategy_v1.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# type: ignore
|
||||||
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
from freqtrade.strategy import IStrategy
|
||||||
|
|
||||||
|
|
||||||
|
# Dummy strategy - no longer loads but raises an exception.
|
||||||
|
class TestStrategyLegacyV1(IStrategy):
|
||||||
|
|
||||||
|
minimal_roi = {
|
||||||
|
"40": 0.0,
|
||||||
|
"30": 0.01,
|
||||||
|
"20": 0.02,
|
||||||
|
"0": 0.04
|
||||||
|
}
|
||||||
|
stoploss = -0.10
|
||||||
|
|
||||||
|
timeframe = '5m'
|
||||||
|
|
||||||
|
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
||||||
|
|
||||||
|
return dataframe
|
@ -1,85 +0,0 @@
|
|||||||
|
|
||||||
# --- Do not remove these libs ---
|
|
||||||
# Add your lib to import here
|
|
||||||
import talib.abstract as ta
|
|
||||||
from pandas import DataFrame
|
|
||||||
|
|
||||||
from freqtrade.strategy import IStrategy
|
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------
|
|
||||||
|
|
||||||
# This class is a sample. Feel free to customize it.
|
|
||||||
class TestStrategyLegacyV1(IStrategy):
|
|
||||||
"""
|
|
||||||
This is a test strategy using the legacy function headers, which will be
|
|
||||||
removed in a future update.
|
|
||||||
Please do not use this as a template, but refer to user_data/strategy/sample_strategy.py
|
|
||||||
for a uptodate version of this template.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Minimal ROI designed for the strategy.
|
|
||||||
# This attribute will be overridden if the config file contains "minimal_roi"
|
|
||||||
minimal_roi = {
|
|
||||||
"40": 0.0,
|
|
||||||
"30": 0.01,
|
|
||||||
"20": 0.02,
|
|
||||||
"0": 0.04
|
|
||||||
}
|
|
||||||
|
|
||||||
# Optimal stoploss designed for the strategy
|
|
||||||
# This attribute will be overridden if the config file contains "stoploss"
|
|
||||||
stoploss = -0.10
|
|
||||||
|
|
||||||
timeframe = '5m'
|
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Adds several different TA indicators to the given DataFrame
|
|
||||||
|
|
||||||
Performance Note: For the best performance be frugal on the number of indicators
|
|
||||||
you are using. Let uncomment only the indicator you are using in your strategies
|
|
||||||
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Momentum Indicator
|
|
||||||
# ------------------------------------
|
|
||||||
|
|
||||||
# ADX
|
|
||||||
dataframe['adx'] = ta.ADX(dataframe)
|
|
||||||
|
|
||||||
# TEMA - Triple Exponential Moving Average
|
|
||||||
dataframe['tema'] = ta.TEMA(dataframe, timeperiod=9)
|
|
||||||
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Based on TA indicators, populates the buy signal for the given dataframe
|
|
||||||
:param dataframe: DataFrame
|
|
||||||
:return: DataFrame with buy column
|
|
||||||
"""
|
|
||||||
dataframe.loc[
|
|
||||||
(
|
|
||||||
(dataframe['adx'] > 30) &
|
|
||||||
(dataframe['tema'] > dataframe['tema'].shift(1)) &
|
|
||||||
(dataframe['volume'] > 0)
|
|
||||||
),
|
|
||||||
'buy'] = 1
|
|
||||||
|
|
||||||
return dataframe
|
|
||||||
|
|
||||||
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
|
|
||||||
"""
|
|
||||||
Based on TA indicators, populates the sell signal for the given dataframe
|
|
||||||
:param dataframe: DataFrame
|
|
||||||
:return: DataFrame with buy column
|
|
||||||
"""
|
|
||||||
dataframe.loc[
|
|
||||||
(
|
|
||||||
(dataframe['adx'] > 70) &
|
|
||||||
(dataframe['tema'] < dataframe['tema'].shift(1)) &
|
|
||||||
(dataframe['volume'] > 0)
|
|
||||||
),
|
|
||||||
'sell'] = 1
|
|
||||||
return dataframe
|
|
@ -56,19 +56,6 @@ class StrategyTestV2(IStrategy):
|
|||||||
# By default this strategy does not use Position Adjustments
|
# By default this strategy does not use Position Adjustments
|
||||||
position_adjustment_enable = False
|
position_adjustment_enable = False
|
||||||
|
|
||||||
def informative_pairs(self):
|
|
||||||
"""
|
|
||||||
Define additional, informative pair/interval combinations to be cached from the exchange.
|
|
||||||
These pair/interval combinations are non-tradeable, unless they are part
|
|
||||||
of the whitelist as well.
|
|
||||||
For more information, please consult the documentation
|
|
||||||
:return: List of tuples in the format (pair, interval)
|
|
||||||
Sample: return [("ETH/USDT", "5m"),
|
|
||||||
("BTC/USDT", "15m"),
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
return []
|
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
Adds several different TA indicators to the given DataFrame
|
Adds several different TA indicators to the given DataFrame
|
||||||
|
@ -686,7 +686,7 @@ def test_is_pair_locked(default_conf):
|
|||||||
|
|
||||||
|
|
||||||
def test_is_informative_pairs_callback(default_conf):
|
def test_is_informative_pairs_callback(default_conf):
|
||||||
default_conf.update({'strategy': 'TestStrategyLegacyV1'})
|
default_conf.update({'strategy': 'StrategyTestV2'})
|
||||||
strategy = StrategyResolver.load_strategy(default_conf)
|
strategy = StrategyResolver.load_strategy(default_conf)
|
||||||
# Should return empty
|
# Should return empty
|
||||||
# Uses fallback to base implementation
|
# Uses fallback to base implementation
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
# pragma pylint: disable=missing-docstring, protected-access, C0103
|
||||||
import logging
|
import logging
|
||||||
import warnings
|
|
||||||
from base64 import urlsafe_b64encode
|
from base64 import urlsafe_b64encode
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
@ -35,7 +34,7 @@ def test_search_all_strategies_no_failed():
|
|||||||
directory = Path(__file__).parent / "strats"
|
directory = Path(__file__).parent / "strats"
|
||||||
strategies = StrategyResolver.search_all_objects(directory, enum_failed=False)
|
strategies = StrategyResolver.search_all_objects(directory, enum_failed=False)
|
||||||
assert isinstance(strategies, list)
|
assert isinstance(strategies, list)
|
||||||
assert len(strategies) == 6
|
assert len(strategies) == 5
|
||||||
assert isinstance(strategies[0], dict)
|
assert isinstance(strategies[0], dict)
|
||||||
|
|
||||||
|
|
||||||
@ -43,10 +42,10 @@ def test_search_all_strategies_with_failed():
|
|||||||
directory = Path(__file__).parent / "strats"
|
directory = Path(__file__).parent / "strats"
|
||||||
strategies = StrategyResolver.search_all_objects(directory, enum_failed=True)
|
strategies = StrategyResolver.search_all_objects(directory, enum_failed=True)
|
||||||
assert isinstance(strategies, list)
|
assert isinstance(strategies, list)
|
||||||
assert len(strategies) == 7
|
assert len(strategies) == 6
|
||||||
# with enum_failed=True search_all_objects() shall find 2 good strategies
|
# with enum_failed=True search_all_objects() shall find 2 good strategies
|
||||||
# and 1 which fails to load
|
# and 1 which fails to load
|
||||||
assert len([x for x in strategies if x['class'] is not None]) == 6
|
assert len([x for x in strategies if x['class'] is not None]) == 5
|
||||||
assert len([x for x in strategies if x['class'] is None]) == 1
|
assert len([x for x in strategies if x['class'] is None]) == 1
|
||||||
|
|
||||||
|
|
||||||
@ -100,7 +99,7 @@ def test_load_strategy_noname(default_conf):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
@pytest.mark.filterwarnings("ignore:deprecated")
|
||||||
@pytest.mark.parametrize('strategy_name', ['StrategyTestV2', 'TestStrategyLegacyV1'])
|
@pytest.mark.parametrize('strategy_name', ['StrategyTestV2'])
|
||||||
def test_strategy_pre_v3(result, default_conf, strategy_name):
|
def test_strategy_pre_v3(result, default_conf, strategy_name):
|
||||||
default_conf.update({'strategy': strategy_name})
|
default_conf.update({'strategy': strategy_name})
|
||||||
|
|
||||||
@ -346,40 +345,6 @@ def test_strategy_override_use_exit_profit_only(caplog, default_conf):
|
|||||||
assert log_has("Override strategy 'exit_profit_only' with value in config file: True.", caplog)
|
assert log_has("Override strategy 'exit_profit_only' with value in config file: True.", caplog)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
|
||||||
def test_deprecate_populate_indicators(result, default_conf):
|
|
||||||
default_location = Path(__file__).parent / "strats"
|
|
||||||
default_conf.update({'strategy': 'TestStrategyLegacyV1',
|
|
||||||
'strategy_path': default_location})
|
|
||||||
strategy = StrategyResolver.load_strategy(default_conf)
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
|
||||||
# Cause all warnings to always be triggered.
|
|
||||||
warnings.simplefilter("always")
|
|
||||||
indicators = strategy.advise_indicators(result, {'pair': 'ETH/BTC'})
|
|
||||||
assert len(w) == 1
|
|
||||||
assert issubclass(w[-1].category, DeprecationWarning)
|
|
||||||
assert "deprecated - check out the Sample strategy to see the current function headers!" \
|
|
||||||
in str(w[-1].message)
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
|
||||||
# Cause all warnings to always be triggered.
|
|
||||||
warnings.simplefilter("always")
|
|
||||||
strategy.advise_entry(indicators, {'pair': 'ETH/BTC'})
|
|
||||||
assert len(w) == 1
|
|
||||||
assert issubclass(w[-1].category, DeprecationWarning)
|
|
||||||
assert "deprecated - check out the Sample strategy to see the current function headers!" \
|
|
||||||
in str(w[-1].message)
|
|
||||||
|
|
||||||
with warnings.catch_warnings(record=True) as w:
|
|
||||||
# Cause all warnings to always be triggered.
|
|
||||||
warnings.simplefilter("always")
|
|
||||||
strategy.advise_exit(indicators, {'pair': 'ETH_BTC'})
|
|
||||||
assert len(w) == 1
|
|
||||||
assert issubclass(w[-1].category, DeprecationWarning)
|
|
||||||
assert "deprecated - check out the Sample strategy to see the current function headers!" \
|
|
||||||
in str(w[-1].message)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
@pytest.mark.filterwarnings("ignore:deprecated")
|
||||||
def test_missing_implements(default_conf, caplog):
|
def test_missing_implements(default_conf, caplog):
|
||||||
|
|
||||||
@ -438,33 +403,14 @@ def test_missing_implements(default_conf, caplog):
|
|||||||
StrategyResolver.load_strategy(default_conf)
|
StrategyResolver.load_strategy(default_conf)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.filterwarnings("ignore:deprecated")
|
def test_call_deprecated_function(default_conf):
|
||||||
def test_call_deprecated_function(result, default_conf, caplog):
|
default_location = Path(__file__).parent / "strats/broken_strats/"
|
||||||
default_location = Path(__file__).parent / "strats"
|
|
||||||
del default_conf['timeframe']
|
del default_conf['timeframe']
|
||||||
default_conf.update({'strategy': 'TestStrategyLegacyV1',
|
default_conf.update({'strategy': 'TestStrategyLegacyV1',
|
||||||
'strategy_path': default_location})
|
'strategy_path': default_location})
|
||||||
strategy = StrategyResolver.load_strategy(default_conf)
|
with pytest.raises(OperationalException,
|
||||||
metadata = {'pair': 'ETH/BTC'}
|
match=r"Strategy Interface v1 is no longer supported.*"):
|
||||||
|
StrategyResolver.load_strategy(default_conf)
|
||||||
# Make sure we are using a legacy function
|
|
||||||
assert strategy._populate_fun_len == 2
|
|
||||||
assert strategy._buy_fun_len == 2
|
|
||||||
assert strategy._sell_fun_len == 2
|
|
||||||
assert strategy.INTERFACE_VERSION == 1
|
|
||||||
assert strategy.timeframe == '5m'
|
|
||||||
|
|
||||||
indicator_df = strategy.advise_indicators(result, metadata=metadata)
|
|
||||||
assert isinstance(indicator_df, DataFrame)
|
|
||||||
assert 'adx' in indicator_df.columns
|
|
||||||
|
|
||||||
enterdf = strategy.advise_entry(result, metadata=metadata)
|
|
||||||
assert isinstance(enterdf, DataFrame)
|
|
||||||
assert 'enter_long' in enterdf.columns
|
|
||||||
|
|
||||||
exitdf = strategy.advise_exit(result, metadata=metadata)
|
|
||||||
assert isinstance(exitdf, DataFrame)
|
|
||||||
assert 'exit_long' in exitdf
|
|
||||||
|
|
||||||
|
|
||||||
def test_strategy_interface_versioning(result, default_conf):
|
def test_strategy_interface_versioning(result, default_conf):
|
||||||
@ -472,10 +418,6 @@ def test_strategy_interface_versioning(result, default_conf):
|
|||||||
strategy = StrategyResolver.load_strategy(default_conf)
|
strategy = StrategyResolver.load_strategy(default_conf)
|
||||||
metadata = {'pair': 'ETH/BTC'}
|
metadata = {'pair': 'ETH/BTC'}
|
||||||
|
|
||||||
# Make sure we are using a legacy function
|
|
||||||
assert strategy._populate_fun_len == 3
|
|
||||||
assert strategy._buy_fun_len == 3
|
|
||||||
assert strategy._sell_fun_len == 3
|
|
||||||
assert strategy.INTERFACE_VERSION == 2
|
assert strategy.INTERFACE_VERSION == 2
|
||||||
|
|
||||||
indicator_df = strategy.advise_indicators(result, metadata=metadata)
|
indicator_df = strategy.advise_indicators(result, metadata=metadata)
|
||||||
|
Loading…
Reference in New Issue
Block a user