Refactor filter_pairlist()

This commit is contained in:
hroff-1902 2020-05-20 13:27:07 +03:00
parent cd3900549c
commit 7e43574382
6 changed files with 44 additions and 64 deletions

View File

@ -3,6 +3,7 @@ PairList Handler base class
""" """
import logging import logging
from abc import ABC, abstractmethod, abstractproperty from abc import ABC, abstractmethod, abstractproperty
from copy import deepcopy
from typing import Any, Dict, List from typing import Any, Dict, List
from cachetools import TTLCache, cached from cachetools import TTLCache, cached
@ -75,16 +76,40 @@ class IPairList(ABC):
-> Please overwrite in subclasses -> Please overwrite in subclasses
""" """
@abstractmethod def _validate_pair(self, ticker) -> bool:
"""
Check one pair against Pairlist Handler's specific conditions.
Either implement it in the Pairlist Handler or override the generic
filter_pairlist() method.
:param ticker: ticker dict as returned from ccxt.load_markets()
:return: True if the pair can stay, false if it should be removed
"""
raise NotImplementedError()
def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]: def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
""" """
Filters and sorts pairlist and returns the whitelist again. Filters and sorts pairlist and returns the whitelist again.
Called on each bot iteration - please use internal caching if necessary Called on each bot iteration - please use internal caching if necessary
-> Please overwrite in subclasses This generic implementation calls self._validate_pair() for each pair
in the pairlist.
Some Pairlist Handlers override this generic implementation and employ
own filtration.
:param pairlist: pairlist to filter or sort :param pairlist: pairlist to filter or sort
:param tickers: Tickers (from exchange.get_tickers()). May be cached. :param tickers: Tickers (from exchange.get_tickers()). May be cached.
:return: new whitelist :return: new whitelist
""" """
# Copy list since we're modifying this list
for p in deepcopy(pairlist):
# Filter out assets
if not self._validate_pair(tickers[p]):
pairlist.remove(p)
return pairlist
def verify_blacklist(self, pairlist: List[str], logmethod) -> List[str]: def verify_blacklist(self, pairlist: List[str], logmethod) -> List[str]:
""" """

View File

@ -2,8 +2,7 @@
Precision pair list filter Precision pair list filter
""" """
import logging import logging
from copy import deepcopy from typing import Any, Dict
from typing import Any, Dict, List
from freqtrade.pairlist.IPairList import IPairList from freqtrade.pairlist.IPairList import IPairList
@ -36,16 +35,14 @@ class PrecisionFilter(IPairList):
""" """
return f"{self.name} - Filtering untradable pairs." return f"{self.name} - Filtering untradable pairs."
def _validate_precision_filter(self, ticker: dict, stoploss: float) -> bool: def _validate_pair(self, ticker: dict) -> bool:
""" """
Check if pair has enough room to add a stoploss to avoid "unsellable" buys of very Check if pair has enough room to add a stoploss to avoid "unsellable" buys of very
low value pairs. low value pairs.
:param ticker: ticker dict as returned from ccxt.load_markets() :param ticker: ticker dict as returned from ccxt.load_markets()
:param stoploss: stoploss value as set in the configuration
(already cleaned to be 1 - stoploss)
:return: True if the pair can stay, False if it should be removed :return: True if the pair can stay, False if it should be removed
""" """
stop_price = ticker['ask'] * stoploss stop_price = ticker['ask'] * self._stoploss
# Adjust stop-prices to precision # Adjust stop-prices to precision
sp = self._exchange.price_to_precision(ticker["symbol"], stop_price) sp = self._exchange.price_to_precision(ticker["symbol"], stop_price)
@ -60,15 +57,3 @@ class PrecisionFilter(IPairList):
return False return False
return True return True
def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
"""
Filters and sorts pairlists and assigns and returns them again.
"""
# Copy list since we're modifying this list
for p in deepcopy(pairlist):
# Filter out assets which would not allow setting a stoploss
if not self._validate_precision_filter(tickers[p], self._stoploss):
pairlist.remove(p)
return pairlist

View File

@ -2,8 +2,7 @@
Price pair list filter Price pair list filter
""" """
import logging import logging
from copy import deepcopy from typing import Any, Dict
from typing import Any, Dict, List
from freqtrade.pairlist.IPairList import IPairList from freqtrade.pairlist.IPairList import IPairList
@ -35,12 +34,15 @@ class PriceFilter(IPairList):
""" """
return f"{self.name} - Filtering pairs priced below {self._low_price_ratio * 100}%." return f"{self.name} - Filtering pairs priced below {self._low_price_ratio * 100}%."
def _validate_ticker_lowprice(self, ticker) -> bool: def _validate_pair(self, ticker) -> bool:
""" """
Check if if one price-step (pip) is > than a certain barrier. Check if if one price-step (pip) is > than a certain barrier.
:param ticker: ticker dict as returned from ccxt.load_markets() :param ticker: ticker dict as returned from ccxt.load_markets()
:return: True if the pair can stay, false if it should be removed :return: True if the pair can stay, false if it should be removed
""" """
if not self._low_price_ratio:
return True
if ticker['last'] is None: if ticker['last'] is None:
self.log_on_refresh(logger.info, self.log_on_refresh(logger.info,
f"Removed {ticker['symbol']} from whitelist, because " f"Removed {ticker['symbol']} from whitelist, because "
@ -53,20 +55,3 @@ class PriceFilter(IPairList):
f"because 1 unit is {changeperc * 100:.3f}%") f"because 1 unit is {changeperc * 100:.3f}%")
return False return False
return True return True
def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
"""
Filters and sorts pairlist and returns the whitelist again.
Called on each bot iteration - please use internal caching if necessary
:param pairlist: pairlist to filter or sort
:param tickers: Tickers (from exchange.get_tickers()). May be cached.
:return: new whitelist
"""
if self._low_price_ratio:
# Copy list since we're modifying this list
for p in deepcopy(pairlist):
# Filter out assets which would not allow setting a stoploss
if not self._validate_ticker_lowprice(tickers[p]):
pairlist.remove(p)
return pairlist

View File

@ -3,7 +3,7 @@ Shuffle pair list filter
""" """
import logging import logging
import random import random
from typing import Dict, List from typing import Any, Dict, List
from freqtrade.pairlist.IPairList import IPairList from freqtrade.pairlist.IPairList import IPairList
@ -13,7 +13,8 @@ logger = logging.getLogger(__name__)
class ShuffleFilter(IPairList): class ShuffleFilter(IPairList):
def __init__(self, exchange, pairlistmanager, config, pairlistconfig: dict, def __init__(self, exchange, pairlistmanager,
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
pairlist_pos: int) -> None: pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)

View File

@ -2,8 +2,7 @@
Spread pair list filter Spread pair list filter
""" """
import logging import logging
from copy import deepcopy from typing import Any, Dict
from typing import Dict, List
from freqtrade.pairlist.IPairList import IPairList from freqtrade.pairlist.IPairList import IPairList
@ -13,7 +12,8 @@ logger = logging.getLogger(__name__)
class SpreadFilter(IPairList): class SpreadFilter(IPairList):
def __init__(self, exchange, pairlistmanager, config, pairlistconfig: dict, def __init__(self, exchange, pairlistmanager,
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
pairlist_pos: int) -> None: pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
@ -35,7 +35,7 @@ class SpreadFilter(IPairList):
return (f"{self.name} - Filtering pairs with ask/bid diff above " return (f"{self.name} - Filtering pairs with ask/bid diff above "
f"{self._max_spread_ratio * 100}%.") f"{self._max_spread_ratio * 100}%.")
def _validate_spread(self, ticker: dict) -> bool: def _validate_pair(self, ticker: dict) -> bool:
""" """
Validate spread for the ticker Validate spread for the ticker
:param ticker: ticker dict as returned from ccxt.load_markets() :param ticker: ticker dict as returned from ccxt.load_markets()
@ -51,20 +51,3 @@ class SpreadFilter(IPairList):
else: else:
return True return True
return False return False
def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
"""
Filters and sorts pairlist and returns the whitelist again.
Called on each bot iteration - please use internal caching if necessary
:param pairlist: pairlist to filter or sort
:param tickers: Tickers (from exchange.get_tickers()). May be cached.
:return: new whitelist
"""
# Copy list since we're modifying this list
for p in deepcopy(pairlist):
ticker = tickers[p]
# Filter out assets
if not self._validate_spread(ticker):
pairlist.remove(p)
return pairlist

View File

@ -19,7 +19,8 @@ SORT_VALUES = ['askVolume', 'bidVolume', 'quoteVolume']
class VolumePairList(IPairList): class VolumePairList(IPairList):
def __init__(self, exchange, pairlistmanager, config: Dict[str, Any], pairlistconfig: dict, def __init__(self, exchange, pairlistmanager,
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
pairlist_pos: int) -> None: pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos) super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)