• 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

98.75
/freqtrade/plugins/pairlist/PriceFilter.py
1
"""
2
Price pair list filter
3
"""
4
import logging
1✔
5
from typing import Any, Dict, Optional
1✔
6

7
from freqtrade.constants import Config
1✔
8
from freqtrade.exceptions import OperationalException
1✔
9
from freqtrade.exchange.types import Ticker
1✔
10
from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter
1✔
11

12

13
logger = logging.getLogger(__name__)
1✔
14

15

16
class PriceFilter(IPairList):
1✔
17

18
    def __init__(self, exchange, pairlistmanager,
1✔
19
                 config: Config, pairlistconfig: Dict[str, Any],
20
                 pairlist_pos: int) -> None:
21
        super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
1✔
22

23
        self._low_price_ratio = pairlistconfig.get('low_price_ratio', 0)
1✔
24
        if self._low_price_ratio < 0:
1✔
25
            raise OperationalException("PriceFilter requires low_price_ratio to be >= 0")
1✔
26
        self._min_price = pairlistconfig.get('min_price', 0)
1✔
27
        if self._min_price < 0:
1✔
28
            raise OperationalException("PriceFilter requires min_price to be >= 0")
1✔
29
        self._max_price = pairlistconfig.get('max_price', 0)
1✔
30
        if self._max_price < 0:
1✔
31
            raise OperationalException("PriceFilter requires max_price to be >= 0")
1✔
32
        self._max_value = pairlistconfig.get('max_value', 0)
1✔
33
        if self._max_value < 0:
1✔
34
            raise OperationalException("PriceFilter requires max_value to be >= 0")
1✔
35
        self._enabled = ((self._low_price_ratio > 0) or
1✔
36
                         (self._min_price > 0) or
37
                         (self._max_price > 0) or
38
                         (self._max_value > 0))
39

40
    @property
1✔
41
    def needstickers(self) -> bool:
1✔
42
        """
43
        Boolean property defining if tickers are necessary.
44
        If no Pairlist requires tickers, an empty Dict is passed
45
        as tickers argument to filter_pairlist
46
        """
47
        return True
1✔
48

49
    def short_desc(self) -> str:
1✔
50
        """
51
        Short whitelist method description - used for startup-messages
52
        """
53
        active_price_filters = []
1✔
54
        if self._low_price_ratio != 0:
1✔
55
            active_price_filters.append(f"below {self._low_price_ratio:.1%}")
1✔
56
        if self._min_price != 0:
1✔
57
            active_price_filters.append(f"below {self._min_price:.8f}")
1✔
58
        if self._max_price != 0:
1✔
59
            active_price_filters.append(f"above {self._max_price:.8f}")
1✔
60
        if self._max_value != 0:
1✔
61
            active_price_filters.append(f"Value above {self._max_value:.8f}")
1✔
62

63
        if len(active_price_filters):
1✔
64
            return f"{self.name} - Filtering pairs priced {' or '.join(active_price_filters)}."
1✔
65

66
        return f"{self.name} - No price filters configured."
1✔
67

68
    @staticmethod
1✔
69
    def description() -> str:
1✔
70
        return "Filter pairs by price."
1✔
71

72
    @staticmethod
1✔
73
    def available_parameters() -> Dict[str, PairlistParameter]:
1✔
74
        return {
1✔
75
            "low_price_ratio": {
76
                "type": "number",
77
                "default": 0,
78
                "description": "Low price ratio",
79
                "help": ("Remove pairs where a price move of 1 price unit (pip) "
80
                         "is above this ratio."),
81
            },
82
            "min_price": {
83
                "type": "number",
84
                "default": 0,
85
                "description": "Minimum price",
86
                "help": "Remove pairs with a price below this value.",
87
            },
88
            "max_price": {
89
                "type": "number",
90
                "default": 0,
91
                "description": "Maximum price",
92
                "help": "Remove pairs with a price above this value.",
93
            },
94
            "max_value": {
95
                "type": "number",
96
                "default": 0,
97
                "description": "Maximum value",
98
                "help": "Remove pairs with a value (price * amount) above this value.",
99
            },
100
        }
101

102
    def _validate_pair(self, pair: str, ticker: Optional[Ticker]) -> bool:
1✔
103
        """
104
        Check if one price-step (pip) is > than a certain barrier.
105
        :param pair: Pair that's currently validated
106
        :param ticker: ticker dict as returned from ccxt.fetch_ticker
107
        :return: True if the pair can stay, false if it should be removed
108
        """
109
        if ticker and 'last' in ticker and ticker['last'] is not None and ticker.get('last') != 0:
1✔
110
            price: float = ticker['last']
1✔
111
        else:
112
            self.log_once(f"Removed {pair} from whitelist, because "
1✔
113
                          "ticker['last'] is empty (Usually no trade in the last 24h).",
114
                          logger.info)
115
            return False
1✔
116

117
        # Perform low_price_ratio check.
118
        if self._low_price_ratio != 0:
1✔
119
            compare = self._exchange.price_get_one_pip(pair, price)
1✔
120
            changeperc = compare / price
1✔
121
            if changeperc > self._low_price_ratio:
1✔
122
                self.log_once(f"Removed {pair} from whitelist, "
1✔
123
                              f"because 1 unit is {changeperc:.3%}", logger.info)
124
                return False
1✔
125

126
        # Perform low_amount check
127
        if self._max_value != 0:
1✔
128
            market = self._exchange.markets[pair]
1✔
129
            limits = market['limits']
1✔
130
            if (limits['amount']['min'] is not None):
1✔
131
                min_amount = limits['amount']['min']
1✔
132
                min_precision = market['precision']['amount']
1✔
133

134
                min_value = min_amount * price
1✔
135
                if self._exchange.precisionMode == 4:
1✔
136
                    # tick size
137
                    next_value = (min_amount + min_precision) * price
×
138
                else:
139
                    # Decimal places
140
                    min_precision = pow(0.1, min_precision)
1✔
141
                    next_value = (min_amount + min_precision) * price
1✔
142
                diff = next_value - min_value
1✔
143

144
                if diff > self._max_value:
1✔
145
                    self.log_once(f"Removed {pair} from whitelist, "
1✔
146
                                  f"because min value change of {diff} > {self._max_value}.",
147
                                  logger.info)
148
                    return False
1✔
149

150
        # Perform min_price check.
151
        if self._min_price != 0:
1✔
152
            if price < self._min_price:
1✔
153
                self.log_once(f"Removed {pair} from whitelist, "
1✔
154
                              f"because last price < {self._min_price:.8f}", logger.info)
155
                return False
1✔
156

157
        # Perform max_price check.
158
        if self._max_price != 0:
1✔
159
            if price > self._max_price:
1✔
160
                self.log_once(f"Removed {pair} from whitelist, "
1✔
161
                              f"because last price > {self._max_price:.8f}", logger.info)
162
                return False
1✔
163

164
        return True
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