[SQUASH] Fix informatives for each pair not being created because dataprovider was not available.

Fix not being able to have informative dataframe of a pair in whitelist.
This commit is contained in:
Rokas Kupstys 2021-09-07 15:40:53 +03:00
parent f2a1d9d2fc
commit dfa61b7ad2
7 changed files with 47 additions and 33 deletions

View File

@ -702,9 +702,9 @@ def informative(timeframe: str, asset: str = '',
:param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not
specified, defaults to: specified, defaults to:
* {base}_{column}_{timeframe} if asset is specified and quote currency does match stake * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake
curerncy. currency.
* {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match
stake curerncy. stake currency.
* {column}_{timeframe} if asset is not specified. * {column}_{timeframe} if asset is not specified.
Format string supports these format variables: Format string supports these format variables:
* {asset} - full name of the asset, for example 'BTC/USDT'. * {asset} - full name of the asset, for example 'BTC/USDT'.

View File

@ -83,10 +83,12 @@ class FreqtradeBot(LoggingMixin):
self.dataprovider = DataProvider(self.config, self.exchange, self.pairlists) self.dataprovider = DataProvider(self.config, self.exchange, self.pairlists)
# Attach Dataprovider to Strategy baseclass # Attach Dataprovider to strategy instance
IStrategy.dp = self.dataprovider self.strategy.dp = self.dataprovider
# Attach Wallets to Strategy baseclass # Attach Wallets to strategy instance
IStrategy.wallets = self.wallets self.strategy.wallets = self.wallets
# Late initialization (may depend on dp/wallets)
self.strategy._initialize()
# Initializing Edge only if enabled # Initializing Edge only if enabled
self.edge = Edge(self.config, self.exchange, self.strategy) if \ self.edge = Edge(self.config, self.exchange, self.strategy) if \

View File

@ -154,11 +154,12 @@ class Backtesting:
self.strategy: IStrategy = strategy self.strategy: IStrategy = strategy
strategy.dp = self.dataprovider strategy.dp = self.dataprovider
# Attach Wallets to Strategy baseclass # Attach Wallets to Strategy baseclass
IStrategy.wallets = self.wallets strategy.wallets = self.wallets
# Set stoploss_on_exchange to false for backtesting, # Set stoploss_on_exchange to false for backtesting,
# since a "perfect" stoploss-sell is assumed anyway # since a "perfect" stoploss-sell is assumed anyway
# And the regular "stoploss" function would not apply to that case # And the regular "stoploss" function would not apply to that case
self.strategy.order_types['stoploss_on_exchange'] = False self.strategy.order_types['stoploss_on_exchange'] = False
strategy._initialize()
def _load_protections(self, strategy: IStrategy): def _load_protections(self, strategy: IStrategy):
if self.config.get('enable_protections', False): if self.config.get('enable_protections', False):

View File

@ -8,6 +8,7 @@ from typing import Any, Dict
from freqtrade import constants from freqtrade import constants
from freqtrade.configuration import TimeRange, validate_config_consistency from freqtrade.configuration import TimeRange, validate_config_consistency
from freqtrade.data.dataprovider import DataProvider
from freqtrade.edge import Edge from freqtrade.edge import Edge
from freqtrade.optimize.optimize_reports import generate_edge_table from freqtrade.optimize.optimize_reports import generate_edge_table
from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.resolvers import ExchangeResolver, StrategyResolver
@ -33,6 +34,8 @@ class EdgeCli:
self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config)
self.strategy = StrategyResolver.load_strategy(self.config) self.strategy = StrategyResolver.load_strategy(self.config)
self.strategy.dp = DataProvider(config, None)
self.strategy._initialize()
validate_config_consistency(self.config) validate_config_consistency(self.config)

View File

@ -137,9 +137,13 @@ class IStrategy(ABC, HyperStrategyMixin):
self._last_candle_seen_per_pair: Dict[str, datetime] = {} self._last_candle_seen_per_pair: Dict[str, datetime] = {}
super().__init__(config) super().__init__(config)
def _initialize(self):
"""
Late initialization tasks, which may depend on availability of dataprovider/wallets/etc.
"""
# Gather informative pairs from @informative-decorated methods. # Gather informative pairs from @informative-decorated methods.
self._ft_informative: Dict[ self._ft_informative: Dict[
Tuple[str, str], Tuple[InformativeData, PopulateIndicators]] = {} Tuple[str, str], List[Tuple[InformativeData, PopulateIndicators]]] = {}
for attr_name in dir(self.__class__): for attr_name in dir(self.__class__):
cls_method = getattr(self.__class__, attr_name) cls_method = getattr(self.__class__, attr_name)
if not callable(cls_method): if not callable(cls_method):
@ -158,16 +162,19 @@ class IStrategy(ABC, HyperStrategyMixin):
'strategy timeframe!') 'strategy timeframe!')
if asset: if asset:
pair = _format_pair_name(self.config, asset) pair = _format_pair_name(self.config, asset)
if (pair, timeframe) in self._ft_informative: try:
raise OperationalException(f'Informative pair {pair} {timeframe} can not ' self._ft_informative[(pair, timeframe)].append(
f'be defined more than once!') (informative_data, cls_method))
self._ft_informative[(pair, timeframe)] = (informative_data, cls_method) except KeyError:
elif self.dp is not None: self._ft_informative[(pair, timeframe)] = [(informative_data, cls_method)]
else:
for pair in self.dp.current_whitelist(): for pair in self.dp.current_whitelist():
if (pair, timeframe) in self._ft_informative: try:
raise OperationalException(f'Informative pair {pair} {timeframe} can ' self._ft_informative[(pair, timeframe)].append(
f'not be defined more than once!') (informative_data, cls_method))
self._ft_informative[(pair, timeframe)] = (informative_data, cls_method) except KeyError:
self._ft_informative[(pair, timeframe)] = \
[(informative_data, cls_method)]
@abstractmethod @abstractmethod
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
@ -838,11 +845,12 @@ class IStrategy(ABC, HyperStrategyMixin):
logger.debug(f"Populating indicators for pair {metadata.get('pair')}.") logger.debug(f"Populating indicators for pair {metadata.get('pair')}.")
# call populate_indicators_Nm() which were tagged with @informative decorator. # call populate_indicators_Nm() which were tagged with @informative decorator.
for (pair, timeframe), (informative_data, populate_fn) in self._ft_informative.items(): for (pair, timeframe), informatives in self._ft_informative.items():
if not informative_data.asset and pair != metadata['pair']: for (informative_data, populate_fn) in informatives:
continue if not informative_data.asset and pair != metadata['pair']:
dataframe = _create_and_merge_informative_pair( continue
self, dataframe, metadata, informative_data, populate_fn) dataframe = _create_and_merge_informative_pair(
self, dataframe, metadata, informative_data, populate_fn)
if self._populate_fun_len == 2: if self._populate_fun_len == 2:
warnings.warn("deprecated - check out the Sample strategy to see " warnings.warn("deprecated - check out the Sample strategy to see "

View File

@ -139,9 +139,9 @@ def informative(timeframe: str, asset: str = '',
:param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not
specified, defaults to: specified, defaults to:
* {base}_{column}_{timeframe} if asset is specified and quote currency does match stake * {base}_{column}_{timeframe} if asset is specified and quote currency does match stake
curerncy. currency.
* {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match * {base}_{quote}_{column}_{timeframe} if asset is specified and quote currency does not match
stake curerncy. stake currency.
* {column}_{timeframe} if asset is not specified. * {column}_{timeframe} if asset is not specified.
Format string supports these format variables: Format string supports these format variables:
* {asset} - full name of the asset, for example 'BTC/USDT'. * {asset} - full name of the asset, for example 'BTC/USDT'.
@ -203,11 +203,10 @@ def _create_and_merge_informative_pair(strategy, dataframe: DataFrame,
# fmt='{base}_{quote}_{column}_{timeframe}' format or similar. # fmt='{base}_{quote}_{column}_{timeframe}' format or similar.
if not fmt: if not fmt:
fmt = '{column}_{timeframe}' # Informatives of current pair fmt = '{column}_{timeframe}' # Informatives of current pair
if asset != metadata['pair']: if quote != config['stake_currency']:
if quote == config['stake_currency']: fmt = '{quote}_' + fmt # Informatives of different quote currency
fmt = '{base}_' + fmt # Informatives of other pair if informative_data.asset:
else: fmt = '{base}_' + fmt # Informatives of other pair
fmt = '{base}_{quote}_' + fmt # Informatives of different quote currency
inf_metadata = {'pair': asset, 'timeframe': timeframe} inf_metadata = {'pair': asset, 'timeframe': timeframe}
inf_dataframe = dp.get_pair_dataframe(asset, timeframe) inf_dataframe = dp.get_pair_dataframe(asset, timeframe)

View File

@ -155,11 +155,12 @@ def test_informative_decorator(mocker, default_conf):
} }
from .strats.informative_decorator_strategy import InformativeDecoratorTest from .strats.informative_decorator_strategy import InformativeDecoratorTest
default_conf['stake_currency'] = 'USDT' default_conf['stake_currency'] = 'USDT'
InformativeDecoratorTest.dp = DataProvider({}, None, None)
mocker.patch.object(InformativeDecoratorTest.dp, 'current_whitelist', return_value=[
'XRP/USDT', 'LTC/USDT'
])
strategy = InformativeDecoratorTest(config=default_conf) strategy = InformativeDecoratorTest(config=default_conf)
strategy.dp = DataProvider({}, None, None)
mocker.patch.object(strategy.dp, 'current_whitelist', return_value=[
'XRP/USDT', 'LTC/USDT', 'BTC/USDT'
])
strategy._initialize()
assert len(strategy._ft_informative) == 8 assert len(strategy._ft_informative) == 8
informative_pairs = [('XRP/USDT', '1h'), ('LTC/USDT', '1h'), ('XRP/USDT', '30m'), informative_pairs = [('XRP/USDT', '1h'), ('LTC/USDT', '1h'), ('XRP/USDT', '30m'),