stable/freqtrade/plugins/pairlist/RemotePairList.py

171 lines
6.0 KiB
Python
Raw Normal View History

2022-12-07 16:01:45 +00:00
"""
Remote PairList provider
Provides pair list fetched from a remote source
"""
import json
import logging
2022-12-07 23:52:54 +00:00
from pathlib import Path
2022-12-07 16:01:45 +00:00
from typing import Any, Dict, List
import requests
from cachetools import TTLCache
from freqtrade.constants import Config
from freqtrade.exceptions import OperationalException
from freqtrade.exchange.types import Tickers
from freqtrade.plugins.pairlist.IPairList import IPairList
logger = logging.getLogger(__name__)
class RemotePairList(IPairList):
def __init__(self, exchange, pairlistmanager,
config: Config, pairlistconfig: Dict[str, Any],
pairlist_pos: int) -> None:
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
if 'number_assets' not in self._pairlistconfig:
raise OperationalException(
'`number_assets` not specified. Please check your configuration '
'for "pairlist.config.number_assets"')
if 'pairlist_url' not in self._pairlistconfig:
raise OperationalException(
'`pairlist_url` not specified. Please check your configuration '
'for "pairlist.config.pairlist_url"')
self._number_pairs = self._pairlistconfig['number_assets']
self._refresh_period = self._pairlistconfig.get('refresh_period', 1800)
self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True)
self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period)
self._pairlist_url = self._pairlistconfig.get('pairlist_url', '')
self._read_timeout = self._pairlistconfig.get('read_timeout', 60)
self._last_pairlist: List[Any] = list()
@property
def needstickers(self) -> bool:
"""
Boolean property defining if tickers are necessary.
If no Pairlist requires tickers, an empty Dict is passed
as tickers argument to filter_pairlist
"""
return False
def short_desc(self) -> str:
"""
Short whitelist method description - used for startup-messages
"""
return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist."
def fetch_pairlist(self):
headers = {
'User-Agent': 'Freqtrade - Remotepairlist',
}
2022-12-07 16:49:21 +00:00
info = "Pairlist"
try:
response = requests.get(self._pairlist_url, headers=headers,
timeout=self._read_timeout)
content_type = response.headers.get('content-type')
time_elapsed = response.elapsed.total_seconds()
rsplit = response.text.split("#")
if "text/html" in str(content_type):
if len(rsplit) > 1:
plist = rsplit[0].strip()
plist = json.loads(plist)
info = rsplit[1].strip()
else:
plist = json.loads(rsplit[0])
elif "application/json" in str(content_type):
jsonr = response.json()
plist = jsonr['pairs']
if 'info' in jsonr:
info = jsonr['info']
if 'refresh_period' in jsonr:
self._refresh_period = jsonr['refresh_period']
except requests.exceptions.RequestException:
self.log_once(f'Was not able to fetch pairlist from:'
f' {self._pairlist_url}', logger.info)
if self._keep_pairlist_on_failure:
plist = str(self._last_pairlist)
self.log_once('Keeping last fetched pairlist', logger.info)
else:
plist = ""
time_elapsed = 0
return plist, time_elapsed, info
2022-12-07 16:01:45 +00:00
def gen_pairlist(self, tickers: Tickers) -> List[str]:
"""
Generate the pairlist
:param tickers: Tickers (from exchange.get_tickers). May be cached.
:return: List of pairs
"""
2022-12-07 23:52:54 +00:00
time_elapsed = 0
2022-12-07 16:01:45 +00:00
pairlist = self._pair_cache.get('pairlist')
if pairlist:
# Item found - no refresh necessary
return pairlist.copy()
else:
2022-12-07 23:52:54 +00:00
if self._pairlist_url.startswith("file:///"):
filename = self._pairlist_url.split("file:///", 1)[1]
file_path = Path(filename)
if file_path.exists():
with open(filename) as json_file:
# Load the JSON data into a dictionary
jsonp = json.load(json_file)
plist = jsonp['pairs']
2022-12-07 16:01:45 +00:00
else:
2022-12-07 23:52:54 +00:00
raise ValueError(f"{self._pairlist_url} does not exist.")
else:
# Fetch Pairlist from Remote URL
plist, time_elapsed, info = self.fetch_pairlist()
pairlist = []
for i in plist:
if i not in pairlist:
pairlist.append(i)
else:
continue
2022-12-07 16:01:45 +00:00
pairlist = self.filter_pairlist(pairlist, tickers)
self._pair_cache['pairlist'] = pairlist.copy()
if(time_elapsed):
self.log_once(f'{info} Fetched in {time_elapsed} seconds.', logger.info)
2022-12-07 16:01:45 +00:00
self._last_pairlist = list(pairlist)
return pairlist
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
"""
# Validate whitelist to only have active market pairs
pairlist = self._whitelist_for_active_markets(pairlist)
pairlist = self.verify_blacklist(pairlist, logger.info)
# Limit pairlist to the requested number of pairs
pairlist = pairlist[:self._number_pairs]
self.log_once(f"Searching {self._number_pairs} pairs: {pairlist}", logger.info)
return pairlist