diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 37aa96b68..4e6ef154b 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -153,6 +153,10 @@ class StrategyResolver(IResolver): strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args) strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args) strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args) + if any([x == 2 for x in [strategy._populate_fun_len, + strategy._buy_fun_len, + strategy._sell_fun_len]]): + strategy.strategy_version = 1 try: return import_strategy(strategy, config=config) diff --git a/freqtrade/strategy/default_strategy.py b/freqtrade/strategy/default_strategy.py index 5c7d50a65..51ef49193 100644 --- a/freqtrade/strategy/default_strategy.py +++ b/freqtrade/strategy/default_strategy.py @@ -13,6 +13,7 @@ class DefaultStrategy(IStrategy): Default Strategy provided by freqtrade bot. You can override it with your own strategy """ + strategy_version: int = 2 # Minimal ROI designed for the strategy minimal_roi = { diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 99f5f26de..4133d31a9 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -60,6 +60,11 @@ class IStrategy(ABC): stoploss -> float: optimal stoploss designed for the strategy ticker_interval -> str: value of the ticker interval to use for the strategy """ + # Strategy interface version + # Default to version 2 + # version 1 is the initial interface without metadata dict + # Version 2 populate_* include metadata dict + strategy_version: int = 2 _populate_fun_len: int = 0 _buy_fun_len: int = 0 diff --git a/freqtrade/tests/strategy/test_strategy.py b/freqtrade/tests/strategy/test_strategy.py index 240b83b8b..28acdea61 100644 --- a/freqtrade/tests/strategy/test_strategy.py +++ b/freqtrade/tests/strategy/test_strategy.py @@ -380,6 +380,31 @@ def test_call_deprecated_function(result, monkeypatch, default_conf): assert resolver.strategy._populate_fun_len == 2 assert resolver.strategy._buy_fun_len == 2 assert resolver.strategy._sell_fun_len == 2 + assert resolver.strategy.strategy_version == 1 + + indicator_df = resolver.strategy.advise_indicators(result, metadata=metadata) + assert isinstance(indicator_df, DataFrame) + assert 'adx' in indicator_df.columns + + buydf = resolver.strategy.advise_buy(result, metadata=metadata) + assert isinstance(buydf, DataFrame) + assert 'buy' in buydf.columns + + selldf = resolver.strategy.advise_sell(result, metadata=metadata) + assert isinstance(selldf, DataFrame) + assert 'sell' in selldf + + +def test_strategy_versioning(result, monkeypatch, default_conf): + default_conf.update({'strategy': 'DefaultStrategy'}) + resolver = StrategyResolver(default_conf) + metadata = {'pair': 'ETH/BTC'} + + # Make sure we are using a legacy function + assert resolver.strategy._populate_fun_len == 3 + assert resolver.strategy._buy_fun_len == 3 + assert resolver.strategy._sell_fun_len == 3 + assert resolver.strategy.strategy_version == 2 indicator_df = resolver.strategy.advise_indicators(result, metadata=metadata) assert isinstance(indicator_df, DataFrame) diff --git a/user_data/strategies/test_strategy.py b/user_data/strategies/test_strategy.py index d8ff790b2..876a2845a 100644 --- a/user_data/strategies/test_strategy.py +++ b/user_data/strategies/test_strategy.py @@ -28,6 +28,9 @@ class TestStrategy(IStrategy): - the prototype for the methods: minimal_roi, stoploss, populate_indicators, populate_buy_trend, populate_sell_trend, hyperopt_space, buy_strategy_generator """ + # Strategy intervace version - allow new iterations of the strategy interface. + # Check the documentation or the Sample strategy to get the latest version. + strategy_version: int = 2 # Minimal ROI designed for the strategy. # This attribute will be overridden if the config file contains "minimal_roi" @@ -256,14 +259,14 @@ class TestStrategy(IStrategy): # Retrieve best bid and best ask # ------------------------------------ """ - # first check if dataprovider is available + # first check if dataprovider is available if self.dp: if self.dp.runmode in ('live', 'dry_run'): ob = self.dp.orderbook(metadata['pair'], 1) dataframe['best_bid'] = ob['bids'][0][0] dataframe['best_ask'] = ob['asks'][0][0] """ - + return dataframe def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: