Merge pull request #3346 from hroff-1902/refactor_pairlists2

Refactor filter_pairlists()
This commit is contained in:
Matthias 2020-05-21 07:25:43 +02:00 committed by GitHub
commit d31a091d12
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 50 additions and 65 deletions

View File

@ -3,6 +3,7 @@ PairList Handler base class
"""
import logging
from abc import ABC, abstractmethod, abstractproperty
from copy import deepcopy
from typing import Any, Dict, List
from cachetools import TTLCache, cached
@ -25,6 +26,8 @@ class IPairList(ABC):
:param pairlistconfig: Configuration for this Pairlist Handler - can be empty.
:param pairlist_pos: Position of the Pairlist Handler in the chain
"""
self._enabled = True
self._exchange = exchange
self._pairlistmanager = pairlistmanager
self._config = config
@ -75,16 +78,41 @@ class IPairList(ABC):
-> 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]:
"""
Filters and sorts pairlist and returns the whitelist again.
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 tickers: Tickers (from exchange.get_tickers()). May be cached.
:return: new whitelist
"""
if self._enabled:
# 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]:
"""

View File

@ -2,8 +2,7 @@
Precision pair list filter
"""
import logging
from copy import deepcopy
from typing import Any, Dict, List
from typing import Any, Dict
from freqtrade.pairlist.IPairList import IPairList
@ -18,8 +17,11 @@ class PrecisionFilter(IPairList):
pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
self._stoploss = self._config['stoploss']
self._enabled = self._stoploss != 0
# Precalculate sanitized stoploss value to avoid recalculation for every pair
self._stoploss = 1 - abs(self._config['stoploss'])
self._stoploss = 1 - abs(self._stoploss)
@property
def needstickers(self) -> bool:
@ -36,16 +38,14 @@ class PrecisionFilter(IPairList):
"""
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
low value pairs.
: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
"""
stop_price = ticker['ask'] * stoploss
stop_price = ticker['ask'] * self._stoploss
# Adjust stop-prices to precision
sp = self._exchange.price_to_precision(ticker["symbol"], stop_price)
@ -60,15 +60,3 @@ class PrecisionFilter(IPairList):
return False
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
"""
import logging
from copy import deepcopy
from typing import Any, Dict, List
from typing import Any, Dict
from freqtrade.pairlist.IPairList import IPairList
@ -19,6 +18,7 @@ class PriceFilter(IPairList):
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
self._low_price_ratio = pairlistconfig.get('low_price_ratio', 0)
self._enabled = self._low_price_ratio != 0
@property
def needstickers(self) -> bool:
@ -35,7 +35,7 @@ class PriceFilter(IPairList):
"""
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.
:param ticker: ticker dict as returned from ccxt.load_markets()
@ -53,20 +53,3 @@ class PriceFilter(IPairList):
f"because 1 unit is {changeperc * 100:.3f}%")
return False
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 random
from typing import Dict, List
from typing import Any, Dict, List
from freqtrade.pairlist.IPairList import IPairList
@ -13,7 +13,8 @@ logger = logging.getLogger(__name__)
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:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)

View File

@ -2,8 +2,7 @@
Spread pair list filter
"""
import logging
from copy import deepcopy
from typing import Dict, List
from typing import Any, Dict
from freqtrade.pairlist.IPairList import IPairList
@ -13,11 +12,13 @@ logger = logging.getLogger(__name__)
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:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
self._max_spread_ratio = pairlistconfig.get('max_spread_ratio', 0.005)
self._enabled = self._max_spread_ratio != 0
@property
def needstickers(self) -> bool:
@ -35,7 +36,7 @@ class SpreadFilter(IPairList):
return (f"{self.name} - Filtering pairs with ask/bid diff above "
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
:param ticker: ticker dict as returned from ccxt.load_markets()
@ -51,20 +52,3 @@ class SpreadFilter(IPairList):
else:
return True
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):
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:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)