stable/freqtrade/pairlist/rangestabilityfilter.py

89 lines
3.4 KiB
Python
Raw Normal View History

2020-11-21 14:29:11 +00:00
"""
2020-11-22 18:59:18 +00:00
Rate of change pairlist filter
2020-11-21 14:29:11 +00:00
"""
import logging
from typing import Any, Dict
import arrow
from cachetools.ttl import TTLCache
from freqtrade.exceptions import OperationalException
from freqtrade.misc import plural
from freqtrade.pairlist.IPairList import IPairList
logger = logging.getLogger(__name__)
class RangeStabilityFilter(IPairList):
2020-11-21 14:29:11 +00:00
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._days = pairlistconfig.get('lookback_days', 10)
self._min_rate_of_change = pairlistconfig.get('min_rate_of_change', 0.01)
2020-11-21 14:29:11 +00:00
self._refresh_period = pairlistconfig.get('refresh_period', 1440)
self._pair_cache: TTLCache = TTLCache(maxsize=100, ttl=self._refresh_period)
if self._days < 1:
raise OperationalException("RangeStabilityFilter requires lookback_days to be >= 1")
2020-11-21 14:29:11 +00:00
if self._days > exchange.ohlcv_candle_limit:
raise OperationalException("RangeStabilityFilter requires lookback_days to not "
2020-11-21 15:01:52 +00:00
"exceed exchange max request size "
2020-11-21 14:29:11 +00:00
f"({exchange.ohlcv_candle_limit})")
@property
def needstickers(self) -> bool:
"""
Boolean property defining if tickers are necessary.
If no Pairlist requires tickers, an empty List is passed
as tickers argument to filter_pairlist
"""
return True
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
"""
return (f"{self.name} - Filtering pairs with rate of change below "
f"{self._min_rate_of_change} over the last {plural(self._days, 'day')}.")
2020-11-21 14:29:11 +00:00
def _validate_pair(self, ticker: Dict) -> bool:
"""
Validate trading range
2020-11-21 14:29:11 +00:00
:param ticker: ticker dict as returned from ccxt.load_markets()
:return: True if the pair can stay, False if it should be removed
"""
pair = ticker['symbol']
# Check symbol in cache
if pair in self._pair_cache:
return self._pair_cache[pair]
since_ms = int(arrow.utcnow()
.floor('day')
.shift(days=-self._days)
.float_timestamp) * 1000
daily_candles = self._exchange.get_historic_ohlcv_as_df(pair=pair,
timeframe='1d',
since_ms=since_ms)
result = False
if daily_candles is not None and not daily_candles.empty:
2020-11-21 14:29:11 +00:00
highest_high = daily_candles['high'].max()
lowest_low = daily_candles['low'].min()
2020-11-22 14:50:44 +00:00
pct_change = ((highest_high - lowest_low) / lowest_low) if lowest_low > 0 else 0
if pct_change >= self._min_rate_of_change:
2020-11-21 14:29:11 +00:00
result = True
else:
2020-11-22 10:49:41 +00:00
self.log_once(f"Removed {pair} from whitelist, because rate of change "
2020-11-19 18:45:22 +00:00
f"over {plural(self._days, 'day')} is {pct_change:.3f}, "
2020-11-22 10:49:41 +00:00
f"which is below the threshold of {self._min_rate_of_change}.",
logger.info)
2020-11-21 14:29:11 +00:00
result = False
self._pair_cache[pair] = result
return result