Add valuefilter to Pricefilters
This commit is contained in:
parent
3006396398
commit
369f19df6b
@ -112,6 +112,7 @@ The `PriceFilter` allows filtering of pairs by price. Currently the following pr
|
|||||||
|
|
||||||
* `min_price`
|
* `min_price`
|
||||||
* `max_price`
|
* `max_price`
|
||||||
|
* `max_value`
|
||||||
* `low_price_ratio`
|
* `low_price_ratio`
|
||||||
|
|
||||||
The `min_price` setting removes pairs where the price is below the specified price. This is useful if you wish to avoid trading very low-priced pairs.
|
The `min_price` setting removes pairs where the price is below the specified price. This is useful if you wish to avoid trading very low-priced pairs.
|
||||||
@ -120,6 +121,11 @@ This option is disabled by default, and will only apply if set to > 0.
|
|||||||
The `max_price` setting removes pairs where the price is above the specified price. This is useful if you wish to trade only low-priced pairs.
|
The `max_price` setting removes pairs where the price is above the specified price. This is useful if you wish to trade only low-priced pairs.
|
||||||
This option is disabled by default, and will only apply if set to > 0.
|
This option is disabled by default, and will only apply if set to > 0.
|
||||||
|
|
||||||
|
The `max_value` setting removes pairs where the minimum value change is above a specified value.
|
||||||
|
This is useful when an exchange has unbalanced limits. For example, if step-size = 1 (so you can only buy 1, or 2, or 3, but not 1.1 Coins) - and the price is pretty high (like 20$) as the coin has risen sharply since the last limit adaption.
|
||||||
|
As a result of the above, you can only buy for 20$, or 40$ - but not for 25$.
|
||||||
|
On exchanges that deduct fees from the receiving currency (e.g. FTX) - this can result in high value coins / amounts that are unsellable as the amount is slightly below the limit.
|
||||||
|
|
||||||
The `low_price_ratio` setting removes pairs where a raise of 1 price unit (pip) is above the `low_price_ratio` ratio.
|
The `low_price_ratio` setting removes pairs where a raise of 1 price unit (pip) is above the `low_price_ratio` ratio.
|
||||||
This option is disabled by default, and will only apply if set to > 0.
|
This option is disabled by default, and will only apply if set to > 0.
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ from copy import deepcopy
|
|||||||
from typing import Any, Dict, List
|
from typing import Any, Dict, List
|
||||||
|
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import market_is_active
|
from freqtrade.exchange import Exchange, market_is_active
|
||||||
from freqtrade.mixins import LoggingMixin
|
from freqtrade.mixins import LoggingMixin
|
||||||
|
|
||||||
|
|
||||||
@ -16,7 +16,7 @@ logger = logging.getLogger(__name__)
|
|||||||
|
|
||||||
class IPairList(LoggingMixin, ABC):
|
class IPairList(LoggingMixin, ABC):
|
||||||
|
|
||||||
def __init__(self, exchange, pairlistmanager,
|
def __init__(self, exchange: Exchange, pairlistmanager,
|
||||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||||
pairlist_pos: int) -> None:
|
pairlist_pos: int) -> None:
|
||||||
"""
|
"""
|
||||||
@ -28,7 +28,7 @@ class IPairList(LoggingMixin, ABC):
|
|||||||
"""
|
"""
|
||||||
self._enabled = True
|
self._enabled = True
|
||||||
|
|
||||||
self._exchange = exchange
|
self._exchange: Exchange = exchange
|
||||||
self._pairlistmanager = pairlistmanager
|
self._pairlistmanager = pairlistmanager
|
||||||
self._config = config
|
self._config = config
|
||||||
self._pairlistconfig = pairlistconfig
|
self._pairlistconfig = pairlistconfig
|
||||||
|
@ -27,9 +27,13 @@ class PriceFilter(IPairList):
|
|||||||
self._max_price = pairlistconfig.get('max_price', 0)
|
self._max_price = pairlistconfig.get('max_price', 0)
|
||||||
if self._max_price < 0:
|
if self._max_price < 0:
|
||||||
raise OperationalException("PriceFilter requires max_price to be >= 0")
|
raise OperationalException("PriceFilter requires max_price to be >= 0")
|
||||||
|
self._max_value = pairlistconfig.get('max_value', 0)
|
||||||
|
if self._max_value < 0:
|
||||||
|
raise OperationalException("PriceFilter requires max_value to be >= 0")
|
||||||
self._enabled = ((self._low_price_ratio > 0) or
|
self._enabled = ((self._low_price_ratio > 0) or
|
||||||
(self._min_price > 0) or
|
(self._min_price > 0) or
|
||||||
(self._max_price > 0))
|
(self._max_price > 0) or
|
||||||
|
(self._max_value))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def needstickers(self) -> bool:
|
def needstickers(self) -> bool:
|
||||||
@ -51,6 +55,8 @@ class PriceFilter(IPairList):
|
|||||||
active_price_filters.append(f"below {self._min_price:.8f}")
|
active_price_filters.append(f"below {self._min_price:.8f}")
|
||||||
if self._max_price != 0:
|
if self._max_price != 0:
|
||||||
active_price_filters.append(f"above {self._max_price:.8f}")
|
active_price_filters.append(f"above {self._max_price:.8f}")
|
||||||
|
if self._max_value != 0:
|
||||||
|
active_price_filters.append(f"Value above {self._max_value:.8f}")
|
||||||
|
|
||||||
if len(active_price_filters):
|
if len(active_price_filters):
|
||||||
return f"{self.name} - Filtering pairs priced {' or '.join(active_price_filters)}."
|
return f"{self.name} - Filtering pairs priced {' or '.join(active_price_filters)}."
|
||||||
@ -79,6 +85,32 @@ class PriceFilter(IPairList):
|
|||||||
f"because 1 unit is {changeperc * 100:.3f}%", logger.info)
|
f"because 1 unit is {changeperc * 100:.3f}%", logger.info)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# Perform low_amount check
|
||||||
|
if self._max_value != 0:
|
||||||
|
price = ticker['last']
|
||||||
|
market = self._exchange.markets[pair]
|
||||||
|
limits = market['limits']
|
||||||
|
if ('amount' in limits and 'min' in limits['amount']
|
||||||
|
and limits['amount']['min'] is not None):
|
||||||
|
min_amount = limits['amount']['min']
|
||||||
|
min_precision = market['precision']['amount']
|
||||||
|
|
||||||
|
min_value = min_amount * price
|
||||||
|
if self._exchange.precisionMode == 4:
|
||||||
|
# tick size
|
||||||
|
next_value = (min_amount + min_precision) * price
|
||||||
|
else:
|
||||||
|
# Decimal places
|
||||||
|
min_precision = pow(0.1, min_precision)
|
||||||
|
next_value = (min_amount + min_precision) * price
|
||||||
|
diff = next_value - min_value
|
||||||
|
|
||||||
|
if diff > self._max_value:
|
||||||
|
self.log_once(f"Removed {pair} from whitelist, "
|
||||||
|
f"because min value change of {diff} > {self._max_value}) ",
|
||||||
|
logger.info)
|
||||||
|
return False
|
||||||
|
|
||||||
# Perform min_price check.
|
# Perform min_price check.
|
||||||
if self._min_price != 0:
|
if self._min_price != 0:
|
||||||
if ticker['last'] < self._min_price:
|
if ticker['last'] < self._min_price:
|
||||||
@ -89,7 +121,7 @@ class PriceFilter(IPairList):
|
|||||||
# Perform max_price check.
|
# Perform max_price check.
|
||||||
if self._max_price != 0:
|
if self._max_price != 0:
|
||||||
if ticker['last'] > self._max_price:
|
if ticker['last'] > self._max_price:
|
||||||
self.log_once(f"Removed {ticker['symbol']} from whitelist, "
|
self.log_once(f"Removed {pair} from whitelist, "
|
||||||
f"because last price > {self._max_price:.8f}", logger.info)
|
f"because last price > {self._max_price:.8f}", logger.info)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -800,6 +800,10 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo
|
|||||||
"[{'PriceFilter': 'PriceFilter - Filtering pairs priced below 0.00002000.'}]",
|
"[{'PriceFilter': 'PriceFilter - Filtering pairs priced below 0.00002000.'}]",
|
||||||
None
|
None
|
||||||
),
|
),
|
||||||
|
({"method": "PriceFilter", "max_value": 0.00002000},
|
||||||
|
"[{'PriceFilter': 'PriceFilter - Filtering pairs priced Value above 0.00002000.'}]",
|
||||||
|
None
|
||||||
|
),
|
||||||
({"method": "PriceFilter"},
|
({"method": "PriceFilter"},
|
||||||
"[{'PriceFilter': 'PriceFilter - No price filters configured.'}]",
|
"[{'PriceFilter': 'PriceFilter - No price filters configured.'}]",
|
||||||
None
|
None
|
||||||
@ -816,6 +820,10 @@ def test_spreadfilter_invalid_data(mocker, default_conf, markets, tickers, caplo
|
|||||||
None,
|
None,
|
||||||
"PriceFilter requires max_price to be >= 0"
|
"PriceFilter requires max_price to be >= 0"
|
||||||
), # OperationalException expected
|
), # OperationalException expected
|
||||||
|
({"method": "PriceFilter", "max_value": -1.00010000},
|
||||||
|
None,
|
||||||
|
"PriceFilter requires max_value to be >= 0"
|
||||||
|
), # OperationalException expected
|
||||||
({"method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01},
|
({"method": "RangeStabilityFilter", "lookback_days": 10, "min_rate_of_change": 0.01},
|
||||||
"[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below "
|
"[{'RangeStabilityFilter': 'RangeStabilityFilter - Filtering pairs with rate of change below "
|
||||||
"0.01 over the last days.'}]",
|
"0.01 over the last days.'}]",
|
||||||
|
Loading…
Reference in New Issue
Block a user