stable/freqtrade/pairlist/pairlistmanager.py

137 lines
4.9 KiB
Python
Raw Normal View History

2019-11-09 05:55:16 +00:00
"""
2020-05-17 11:10:11 +00:00
PairList manager class
"""
2019-11-09 05:55:16 +00:00
import logging
from copy import deepcopy
2020-05-18 18:59:50 +00:00
from typing import Dict, List
2019-11-09 05:55:16 +00:00
from cachetools import TTLCache, cached
from freqtrade.exceptions import OperationalException
2019-11-09 05:55:16 +00:00
from freqtrade.pairlist.IPairList import IPairList
from freqtrade.resolvers import PairListResolver
2020-05-22 18:56:34 +00:00
from freqtrade.constants import ListPairsWithTimeframes
2019-11-09 05:55:16 +00:00
2019-11-09 05:55:16 +00:00
logger = logging.getLogger(__name__)
class PairListManager():
def __init__(self, exchange, config: dict) -> None:
self._exchange = exchange
self._config = config
self._whitelist = self._config['exchange'].get('pair_whitelist')
self._blacklist = self._config['exchange'].get('pair_blacklist', [])
2020-05-19 00:35:01 +00:00
self._pairlist_handlers: List[IPairList] = []
self._tickers_needed = False
2020-05-19 00:35:01 +00:00
for pairlist_handler_config in self._config.get('pairlists', None):
if 'method' not in pairlist_handler_config:
logger.warning(f"No method found in {pairlist_handler_config}, ignoring.")
2019-11-09 13:49:25 +00:00
continue
2020-05-19 00:35:01 +00:00
pairlist_handler = PairListResolver.load_pairlist(
pairlist_handler_config['method'],
exchange=exchange,
pairlistmanager=self,
config=config,
pairlistconfig=pairlist_handler_config,
pairlist_pos=len(self._pairlist_handlers)
)
self._tickers_needed |= pairlist_handler.needstickers
self._pairlist_handlers.append(pairlist_handler)
if not self._pairlist_handlers:
raise OperationalException("No Pairlist Handlers defined")
2019-11-09 05:55:16 +00:00
@property
def whitelist(self) -> List[str]:
"""
Has the current whitelist
"""
return self._whitelist
@property
def blacklist(self) -> List[str]:
"""
Has the current blacklist
-> no need to overwrite in subclasses
"""
return self._blacklist
2019-11-09 08:07:46 +00:00
@property
2019-11-09 13:00:32 +00:00
def name_list(self) -> List[str]:
2019-11-09 08:07:46 +00:00
"""
2020-05-19 00:35:01 +00:00
Get list of loaded Pairlist Handler names
2019-11-09 08:07:46 +00:00
"""
2020-05-19 00:35:01 +00:00
return [p.name for p in self._pairlist_handlers]
2019-11-09 08:07:46 +00:00
def short_desc(self) -> List[Dict]:
"""
2020-05-19 00:35:01 +00:00
List of short_desc for each Pairlist Handler
2019-11-09 08:07:46 +00:00
"""
2020-05-19 00:35:01 +00:00
return [{p.name: p.short_desc()} for p in self._pairlist_handlers]
2019-11-09 08:07:46 +00:00
2019-11-09 18:45:09 +00:00
@cached(TTLCache(maxsize=1, ttl=1800))
def _get_cached_tickers(self):
return self._exchange.get_tickers()
2019-11-09 05:55:16 +00:00
def refresh_pairlist(self) -> None:
"""
2020-05-19 00:35:01 +00:00
Run pairlist through all configured Pairlist Handlers.
2019-11-09 05:55:16 +00:00
"""
2020-05-17 11:10:11 +00:00
# Tickers should be cached to avoid calling the exchange on each call.
2019-11-09 12:40:36 +00:00
tickers: Dict = {}
if self._tickers_needed:
2019-11-09 18:45:09 +00:00
tickers = self._get_cached_tickers()
# Adjust whitelist if filters are using tickers
pairlist = self._prepare_whitelist(self._whitelist.copy(), tickers)
# Generate the pairlist with first Pairlist Handler in the chain
pairlist = self._pairlist_handlers[0].gen_pairlist(self._whitelist, tickers)
2020-05-19 00:35:01 +00:00
# Process all Pairlist Handlers in the chain
for pairlist_handler in self._pairlist_handlers:
pairlist = pairlist_handler.filter_pairlist(pairlist, tickers)
2019-11-09 05:55:16 +00:00
2020-05-19 00:35:01 +00:00
# Validation against blacklist happens after the chain of Pairlist Handlers
# to ensure blacklist is respected.
pairlist = self.verify_blacklist(pairlist, logger.warning)
2019-11-09 06:23:34 +00:00
2019-11-09 05:55:16 +00:00
self._whitelist = pairlist
def _prepare_whitelist(self, pairlist: List[str], tickers) -> List[str]:
"""
2020-05-19 00:35:01 +00:00
Prepare sanitized pairlist for Pairlist Handlers that use tickers data - remove
pairs that do not have ticker available
"""
if self._tickers_needed:
# Copy list since we're modifying this list
for p in deepcopy(pairlist):
2020-05-17 11:10:11 +00:00
if p not in tickers:
pairlist.remove(p)
return pairlist
def verify_blacklist(self, pairlist: List[str], logmethod) -> List[str]:
2020-05-19 20:13:51 +00:00
"""
Verify and remove items from pairlist - returning a filtered pairlist.
Logs a warning or info depending on `aswarning`.
Pairlist Handlers explicitly using this method shall use
`logmethod=logger.info` to avoid spamming with warning messages
2020-05-19 20:13:51 +00:00
:param pairlist: Pairlist to validate
:param logmethod: Function that'll be called, `logger.info` or `logger.warning`.
2020-05-19 20:13:51 +00:00
:return: pairlist - blacklisted pairs
"""
for pair in deepcopy(pairlist):
if pair in self._blacklist:
logmethod(f"Pair {pair} in your blacklist. Removing it from whitelist...")
2020-05-19 20:13:51 +00:00
pairlist.remove(pair)
return pairlist
def create_pair_list(self, pairs: List[str], timeframe: str = None) -> ListPairsWithTimeframes:
"""
Create list of pair tuples with (pair, timeframe)
"""
return [(pair, timeframe or self._config['timeframe']) for pair in pairs]