• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

freqtrade / freqtrade / 9394559170

26 Apr 2024 06:36AM UTC coverage: 94.656% (-0.02%) from 94.674%
9394559170

push

github

xmatthias
Loader should be passed as kwarg for clarity

20280 of 21425 relevant lines covered (94.66%)

0.95 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.92
/freqtrade/strategy/informative_decorator.py
1
from dataclasses import dataclass
1✔
2
from typing import Any, Callable, Dict, Optional, Union
1✔
3

4
from pandas import DataFrame
1✔
5

6
from freqtrade.enums import CandleType
1✔
7
from freqtrade.exceptions import OperationalException
1✔
8
from freqtrade.strategy.strategy_helper import merge_informative_pair
1✔
9

10

11
PopulateIndicators = Callable[[Any, DataFrame, dict], DataFrame]
1✔
12

13

14
@dataclass
1✔
15
class InformativeData:
1✔
16
    asset: Optional[str]
1✔
17
    timeframe: str
1✔
18
    fmt: Union[str, Callable[[Any], str], None]
1✔
19
    ffill: bool
1✔
20
    candle_type: Optional[CandleType]
1✔
21

22

23
def informative(timeframe: str, asset: str = '',
1✔
24
                fmt: Optional[Union[str, Callable[[Any], str]]] = None,
25
                *,
26
                candle_type: Optional[Union[CandleType, str]] = None,
27
                ffill: bool = True) -> Callable[[PopulateIndicators], PopulateIndicators]:
28
    """
29
    A decorator for populate_indicators_Nn(self, dataframe, metadata), allowing these functions to
30
    define informative indicators.
31

32
    Example usage:
33

34
        @informative('1h')
35
        def populate_indicators_1h(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
36
            dataframe['rsi'] = ta.RSI(dataframe, timeperiod=14)
37
            return dataframe
38

39
    :param timeframe: Informative timeframe. Must always be equal or higher than strategy timeframe.
40
    :param asset: Informative asset, for example BTC, BTC/USDT, ETH/BTC. Do not specify to use
41
                  current pair. Also supports limited pair format strings (see below)
42
    :param fmt: Column format (str) or column formatter (callable(name, asset, timeframe)). When not
43
    specified, defaults to:
44
    * {base}_{quote}_{column}_{timeframe} if asset is specified.
45
    * {column}_{timeframe} if asset is not specified.
46
    Pair format supports these format variables:
47
    * {base} - base currency in lower case, for example 'eth'.
48
    * {BASE} - same as {base}, except in upper case.
49
    * {quote} - quote currency in lower case, for example 'usdt'.
50
    * {QUOTE} - same as {quote}, except in upper case.
51
    Format string additionally supports this variables.
52
    * {asset} - full name of the asset, for example 'BTC/USDT'.
53
    * {column} - name of dataframe column.
54
    * {timeframe} - timeframe of informative dataframe.
55
    :param ffill: ffill dataframe after merging informative pair.
56
    :param candle_type: '', mark, index, premiumIndex, or funding_rate
57
    """
58
    _asset = asset
1✔
59
    _timeframe = timeframe
1✔
60
    _fmt = fmt
1✔
61
    _ffill = ffill
1✔
62
    _candle_type = CandleType.from_string(candle_type) if candle_type else None
1✔
63

64
    def decorator(fn: PopulateIndicators):
1✔
65
        informative_pairs = getattr(fn, '_ft_informative', [])
1✔
66
        informative_pairs.append(InformativeData(_asset, _timeframe, _fmt, _ffill, _candle_type))
1✔
67
        setattr(fn, '_ft_informative', informative_pairs)  # noqa: B010
1✔
68
        return fn
1✔
69
    return decorator
1✔
70

71

72
def __get_pair_formats(market: Optional[Dict[str, Any]]) -> Dict[str, str]:
1✔
73
    if not market:
1✔
74
        return {}
1✔
75
    base = market['base']
1✔
76
    quote = market['quote']
1✔
77
    return {
1✔
78
        'base': base.lower(),
79
        'BASE': base.upper(),
80
        'quote': quote.lower(),
81
        'QUOTE': quote.upper(),
82
    }
83

84

85
def _format_pair_name(config, pair: str, market: Optional[Dict[str, Any]] = None) -> str:
1✔
86
    return pair.format(
1✔
87
        stake_currency=config['stake_currency'],
88
        stake=config['stake_currency'],
89
        **__get_pair_formats(market),
90
    ).upper()
91

92

93
def _create_and_merge_informative_pair(strategy, dataframe: DataFrame, metadata: dict,
1✔
94
                                       inf_data: InformativeData,
95
                                       populate_indicators: PopulateIndicators):
96
    asset = inf_data.asset or ''
1✔
97
    timeframe = inf_data.timeframe
1✔
98
    fmt = inf_data.fmt
1✔
99
    candle_type = inf_data.candle_type
1✔
100

101
    config = strategy.config
1✔
102

103
    if asset:
1✔
104
        # Insert stake currency if needed.
105
        market1 = strategy.dp.market(metadata['pair'])
1✔
106
        asset = _format_pair_name(config, asset, market1)
1✔
107
    else:
108
        # Not specifying an asset will define informative dataframe for current pair.
109
        asset = metadata['pair']
1✔
110

111
    market = strategy.dp.market(asset)
1✔
112
    if market is None:
1✔
113
        raise OperationalException(f'Market {asset} is not available.')
×
114

115
    # Default format. This optimizes for the common case: informative pairs using same stake
116
    # currency. When quote currency matches stake currency, column name will omit base currency.
117
    # This allows easily reconfiguring strategy to use different base currency. In a rare case
118
    # where it is desired to keep quote currency in column name at all times user should specify
119
    # fmt='{base}_{quote}_{column}_{timeframe}' format or similar.
120
    if not fmt:
1✔
121
        fmt = '{column}_{timeframe}'                # Informatives of current pair
1✔
122
        if inf_data.asset:
1✔
123
            fmt = '{base}_{quote}_' + fmt           # Informatives of other pairs
1✔
124

125
    inf_metadata = {'pair': asset, 'timeframe': timeframe}
1✔
126
    inf_dataframe = strategy.dp.get_pair_dataframe(asset, timeframe, candle_type)
1✔
127
    inf_dataframe = populate_indicators(strategy, inf_dataframe, inf_metadata)
1✔
128

129
    formatter: Any = None
1✔
130
    if callable(fmt):
1✔
131
        formatter = fmt             # A custom user-specified formatter function.
1✔
132
    else:
133
        formatter = fmt.format      # A default string formatter.
1✔
134

135
    fmt_args = {
1✔
136
        **__get_pair_formats(market),
137
        'asset': asset,
138
        'timeframe': timeframe,
139
    }
140
    inf_dataframe.rename(columns=lambda column: formatter(column=column, **fmt_args),
1✔
141
                         inplace=True)
142

143
    date_column = formatter(column='date', **fmt_args)
1✔
144
    if date_column in dataframe.columns:
1✔
145
        raise OperationalException(f'Duplicate column name {date_column} exists in '
×
146
                                   f'dataframe! Ensure column names are unique!')
147
    dataframe = merge_informative_pair(dataframe, inf_dataframe, strategy.timeframe, timeframe,
1✔
148
                                       ffill=inf_data.ffill, append_timeframe=False,
149
                                       date_column=date_column)
150
    return dataframe
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc