2019-10-30 14:59:52 +00:00
|
|
|
import logging
|
|
|
|
from copy import deepcopy
|
|
|
|
from typing import Dict, List
|
|
|
|
|
2019-11-09 05:55:16 +00:00
|
|
|
from freqtrade.pairlist.IPairList import IPairList
|
2019-10-30 14:59:52 +00:00
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
2019-11-09 05:55:16 +00:00
|
|
|
class PrecisionFilter(IPairList):
|
2019-10-30 14:59:52 +00:00
|
|
|
|
2019-11-09 06:05:17 +00:00
|
|
|
@property
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
return True
|
|
|
|
|
2019-11-09 05:55:16 +00:00
|
|
|
def short_desc(self) -> str:
|
|
|
|
"""
|
|
|
|
Short whitelist method description - used for startup-messages
|
|
|
|
"""
|
|
|
|
return f"{self.name} - Filtering untradable pairs."
|
2019-10-30 14:59:52 +00:00
|
|
|
|
|
|
|
def _validate_precision_filter(self, ticker: dict, stoploss: float) -> 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
|
|
|
|
"""
|
2019-11-09 08:07:46 +00:00
|
|
|
stop_price = ticker['ask'] * stoploss
|
2019-10-30 14:59:52 +00:00
|
|
|
# Adjust stop-prices to precision
|
2019-11-09 08:07:46 +00:00
|
|
|
sp = self._exchange.symbol_price_prec(ticker["symbol"], stop_price)
|
|
|
|
stop_gap_price = self._exchange.symbol_price_prec(ticker["symbol"], stop_price * 0.99)
|
2019-10-30 14:59:52 +00:00
|
|
|
logger.debug(f"{ticker['symbol']} - {sp} : {stop_gap_price}")
|
|
|
|
if sp <= stop_gap_price:
|
|
|
|
logger.info(f"Removed {ticker['symbol']} from whitelist, "
|
|
|
|
f"because stop price {sp} would be <= stop limit {stop_gap_price}")
|
|
|
|
return False
|
|
|
|
return True
|
|
|
|
|
|
|
|
def filter_pairlist(self, pairlist: List[str], tickers: List[Dict]) -> List[str]:
|
|
|
|
"""
|
2019-11-09 05:55:16 +00:00
|
|
|
Filters and sorts pairlists and assigns and returns them again.
|
2019-10-30 14:59:52 +00:00
|
|
|
"""
|
2019-11-09 08:07:46 +00:00
|
|
|
if self._config.get('stoploss') is not None:
|
2019-10-30 14:59:52 +00:00
|
|
|
# Precalculate sanitized stoploss value to avoid recalculation for every pair
|
2019-11-09 08:07:46 +00:00
|
|
|
stoploss = 1 - abs(self._config.get('stoploss'))
|
2019-10-30 14:59:52 +00:00
|
|
|
# Copy list since we're modifying this list
|
|
|
|
for p in deepcopy(pairlist):
|
|
|
|
ticker = [t for t in tickers if t['symbol'] == p][0]
|
|
|
|
# Filter out assets which would not allow setting a stoploss
|
|
|
|
if (stoploss and not self._validate_precision_filter(ticker, stoploss)):
|
|
|
|
pairlist.remove(p)
|
|
|
|
continue
|
|
|
|
|
|
|
|
return pairlist
|