freqtrade_origin/freqtrade/pairlist/IPairList.py

182 lines
6.9 KiB
Python
Raw Normal View History

2018-12-05 19:44:56 +00:00
"""
2020-05-19 00:35:01 +00:00
PairList Handler base class
2020-05-17 11:13:26 +00:00
"""
2018-12-05 19:44:56 +00:00
import logging
from abc import ABC, abstractmethod, abstractproperty
2020-05-20 10:27:07 +00:00
from copy import deepcopy
2020-02-02 04:00:40 +00:00
from typing import Any, Dict, List
2018-12-05 19:44:56 +00:00
from cachetools import TTLCache, cached
from freqtrade.exceptions import OperationalException
from freqtrade.exchange import market_is_active
2020-05-17 11:13:26 +00:00
2018-12-05 19:44:56 +00:00
logger = logging.getLogger(__name__)
class IPairList(ABC):
2020-02-02 04:00:40 +00:00
def __init__(self, exchange, pairlistmanager,
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
pairlist_pos: int) -> None:
"""
:param exchange: Exchange instance
2020-05-19 00:35:01 +00:00
:param pairlistmanager: Instantiated Pairlist manager
:param config: Global bot configuration
2020-05-19 00:35:01 +00:00
:param pairlistconfig: Configuration for this Pairlist Handler - can be empty.
:param pairlist_pos: Position of the Pairlist Handler in the chain
"""
self._enabled = True
2019-11-09 05:55:16 +00:00
self._exchange = exchange
self._pairlistmanager = pairlistmanager
2018-12-05 19:44:56 +00:00
self._config = config
2019-11-09 05:55:16 +00:00
self._pairlistconfig = pairlistconfig
self._pairlist_pos = pairlist_pos
self.refresh_period = self._pairlistconfig.get('refresh_period', 1800)
self._last_refresh = 0
self._log_cache = TTLCache(maxsize=1024, ttl=self.refresh_period)
2018-12-05 19:44:56 +00:00
@property
def name(self) -> str:
"""
Gets name of the class
-> no need to overwrite in subclasses
"""
return self.__class__.__name__
def log_on_refresh(self, logmethod, message: str) -> None:
"""
Logs message - not more often than "refresh_period" to avoid log spamming
Logs the log-message as debug as well to simplify debugging.
:param logmethod: Function that'll be called. Most likely `logger.info`.
:param message: String containing the message to be sent to the function.
:return: None.
"""
@cached(cache=self._log_cache)
def _log_on_refresh(message: str):
logmethod(message)
# Log as debug first
logger.debug(message)
# Call hidden function.
_log_on_refresh(message)
@abstractproperty
def needstickers(self) -> bool:
"""
Boolean property defining if tickers are necessary.
If no Pairlist requries tickers, an empty List is passed
as tickers argument to filter_pairlist
"""
2018-12-05 19:44:56 +00:00
@abstractmethod
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
-> Please overwrite in subclasses
"""
2020-05-20 10:27:07 +00:00
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 gen_pairlist(self, cached_pairlist: List[str], tickers: Dict) -> List[str]:
"""
Generate the pairlist.
This method is called once by the pairlistmanager in the refresh_pairlist()
method to supply the starting pairlist for the chain of the Pairlist Handlers.
Pairlist Filters (those Pairlist Handlers that cannot be used at the first
position in the chain) shall not override this base implementation --
it will raise the exception if a Pairlist Handler is used at the first
position in the chain.
:param cached_pairlist: Previously generated pairlist (cached)
:param tickers: Tickers (from exchange.get_tickers()).
:return: List of pairs
"""
raise OperationalException("This Pairlist Handler should not be used "
"at the first position in the list of Pairlist Handlers.")
2019-11-09 12:40:36 +00:00
def filter_pairlist(self, pairlist: List[str], tickers: Dict) -> List[str]:
2018-12-05 19:44:56 +00:00
"""
2019-11-09 05:55:16 +00:00
Filters and sorts pairlist and returns the whitelist again.
2020-05-20 10:27:07 +00:00
2019-11-09 05:55:16 +00:00
Called on each bot iteration - please use internal caching if necessary
2020-05-20 10:27:07 +00:00
This generic implementation calls self._validate_pair() for each pair
in the pairlist.
Some Pairlist Handlers override this generic implementation and employ
own filtration.
2019-11-09 05:55:16 +00:00
:param pairlist: pairlist to filter or sort
:param tickers: Tickers (from exchange.get_tickers()). May be cached.
:return: new whitelist
2018-12-05 19:44:56 +00:00
"""
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)
2020-05-20 10:27:07 +00:00
return pairlist
2018-12-05 19:44:56 +00:00
def verify_blacklist(self, pairlist: List[str], logmethod) -> List[str]:
2019-11-09 06:23:34 +00:00
"""
Proxy method to verify_blacklist for easy access for child classes.
2020-03-09 10:30:13 +00:00
:param pairlist: Pairlist to validate
:param logmethod: Function that'll be called, `logger.info` or `logger.warning`.
2020-03-09 10:30:13 +00:00
:return: pairlist - blacklisted pairs
"""
return self._pairlistmanager.verify_blacklist(pairlist, logmethod)
2019-11-09 06:23:34 +00:00
2019-11-09 13:44:39 +00:00
def _whitelist_for_active_markets(self, pairlist: List[str]) -> List[str]:
2018-12-05 19:44:56 +00:00
"""
Check available markets and remove pair from whitelist if necessary
2019-03-11 20:10:22 +00:00
:param whitelist: the sorted list of pairs the user might want to trade
:return: the list of pairs the user wants to trade without those unavailable or
2018-12-05 19:44:56 +00:00
black_listed
"""
2019-11-09 05:55:16 +00:00
markets = self._exchange.markets
2018-12-05 19:44:56 +00:00
2019-10-30 14:59:52 +00:00
sanitized_whitelist: List[str] = []
2019-11-09 13:44:39 +00:00
for pair in pairlist:
2019-11-09 05:55:16 +00:00
# pair is not in the generated dynamic market or has the wrong stake currency
if pair not in markets:
2019-03-17 17:18:35 +00:00
logger.warning(f"Pair {pair} is not compatible with exchange "
2019-11-09 05:55:16 +00:00
f"{self._exchange.name}. Removing it from whitelist..")
2018-12-05 19:44:56 +00:00
continue
2020-02-24 19:41:45 +00:00
if not self._exchange.market_is_tradable(markets[pair]):
logger.warning(f"Pair {pair} is not tradable with Freqtrade."
"Removing it from whitelist..")
continue
2020-02-26 06:08:09 +00:00
if self._exchange.get_pair_quote_currency(pair) != self._config['stake_currency']:
logger.warning(f"Pair {pair} is not compatible with your stake currency "
f"{self._config['stake_currency']}. Removing it from whitelist..")
continue
# Check if market is active
market = markets[pair]
if not market_is_active(market):
2019-03-21 05:14:43 +00:00
logger.info(f"Ignoring {pair} from whitelist. Market is not active.")
continue
2019-10-30 14:59:52 +00:00
if pair not in sanitized_whitelist:
sanitized_whitelist.append(pair)
2018-12-05 19:44:56 +00:00
# We need to remove pairs that are unknown
2019-10-30 14:59:52 +00:00
return sanitized_whitelist