diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 671768bfa..2b22dd274 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -698,10 +698,7 @@ def informative(timeframe: str, asset: str = '', current pair. :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not specified, defaults to: - * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake - currency. - * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match - stake currency. + * {base}_{quote}_{column}_{timeframe} if asset is specified. * {column}_{timeframe} if asset is not specified. Format string supports these format variables: * {asset} - full name of the asset, for example 'BTC/USDT'. @@ -746,7 +743,7 @@ for more information. # Define BTC/STAKE informative pair. Available in populate_indicators and other methods as # 'btc_rsi_1h'. Current stake currency should be specified as {stake} format variable # instead of hardcoding actual stake currency. Available in populate_indicators and other - # methods as 'btc_rsi_1h'. + # methods as 'btc_usdt_rsi_1h' (when stake currency is USDT). @informative('1h', 'BTC/{stake}') def populate_indicators_btc_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame: dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14) @@ -780,6 +777,25 @@ for more information. Do not use `@informative` decorator if you need to use data of one informative pair when generating another informative pair. Instead, define informative pairs manually as described [in the DataProvider section](#complete-data-provider-sample). +!!! Note + Use string formatting when accessing informative dataframes of other pairs. This will allow easily changing stake currency in config without having to adjust strategy code. + + ``` python + def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: + stake = self.config['stake_currency'] + dataframe.loc[ + ( + (dataframe[f'btc_{stake}_rsi_1h'] < 35) + & + (dataframe['volume'] > 0) + ), + ['buy', 'buy_tag']] = (1, 'buy_signal_rsi') + + return dataframe + ``` + + Alternatively column renaming may be used to remove stake currency from column names: `@informative('1h', 'BTC/{stake}', fmt='{base}_{column}_{timeframe}')`. + !!! Warning "Duplicate method names" Methods tagged with `@informative()` decorator must always have unique names! Re-using same name (for example when copy-pasting already defined informative method) will overwrite previously defined method and not produce any errors due to limitations of Python programming language. In such cases you will find that indicators diff --git a/freqtrade/strategy/informative_decorator.py b/freqtrade/strategy/informative_decorator.py index f09e634b0..c8ebf5989 100644 --- a/freqtrade/strategy/informative_decorator.py +++ b/freqtrade/strategy/informative_decorator.py @@ -36,10 +36,7 @@ def informative(timeframe: str, asset: str = '', current pair. :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not specified, defaults to: - * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake - currency. - * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match - stake currency. + * {base}_{quote}_{column}_{timeframe} if asset is specified. * {column}_{timeframe} if asset is not specified. Format string supports these format variables: * {asset} - full name of the asset, for example 'BTC/USDT'. @@ -88,7 +85,7 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: base, quote = asset.split('/') else: # When futures are supported this may need reevaluation. - # base, quote = asset, None + # base, quote = asset, '' raise OperationalException('Not implemented.') # Default format. This optimizes for the common case: informative pairs using same stake @@ -98,10 +95,8 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: # fmt='{base}_{quote}_{column}_{timeframe}' format or similar. if not fmt: fmt = '{column}_{timeframe}' # Informatives of current pair - if quote != config['stake_currency']: - fmt = '{quote}_' + fmt # Informatives of different quote currency if inf_data.asset: - fmt = '{base}_' + fmt # Informatives of other pair + fmt = '{base}_{quote}_' + fmt # Informatives of other pairs inf_metadata = {'pair': asset, 'timeframe': timeframe} inf_dataframe = strategy.dp.get_pair_dataframe(asset, timeframe) diff --git a/tests/strategy/test_strategy_helpers.py b/tests/strategy/test_strategy_helpers.py index 95ca0416f..d4206ba8c 100644 --- a/tests/strategy/test_strategy_helpers.py +++ b/tests/strategy/test_strategy_helpers.py @@ -177,7 +177,7 @@ def test_informative_decorator(mocker, default_conf): {p: data[(p, strategy.timeframe)] for p in ('XRP/USDT', 'LTC/USDT')}) expected_columns = [ 'rsi_1h', 'rsi_30m', # Stacked informative decorators - 'btc_rsi_1h', # BTC 1h informative + 'btc_usdt_rsi_1h', # BTC 1h informative 'rsi_BTC_USDT_btc_usdt_BTC/USDT_30m', # Column formatting 'rsi_from_callable', # Custom column formatter 'eth_btc_rsi_1h', # Quote currency not matching stake currency