parse exception handling, remove info, cache change
This commit is contained in:
parent
6380c3d462
commit
43f5a16006
@ -202,11 +202,10 @@ The user is responsible for providing a server or local file that returns a JSON
|
|||||||
{
|
{
|
||||||
"pairs": ["XRP/USDT", "ETH/USDT", "LTC/USDT"],
|
"pairs": ["XRP/USDT", "ETH/USDT", "LTC/USDT"],
|
||||||
"refresh_period": 1800,
|
"refresh_period": 1800,
|
||||||
"info": "Pairlist updated on 2022-12-12 at 12:12" // Maximum Length: 256 Characters, Charset: Alphanumeric + "+-.,%:"
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The `pairs` property should contain a list of strings with the trading pairs to be used by the bot. The `refresh_period` property is optional and specifies the number of seconds that the pairlist should be cached before being refreshed. The `info` property is also optional and can be used to provide any additional information about the pairlist.
|
The `pairs` property should contain a list of strings with the trading pairs to be used by the bot. The `refresh_period` property is optional and specifies the number of seconds that the pairlist should be cached before being refreshed.
|
||||||
|
|
||||||
The optional `keep_pairlist_on_failure` specifies whether the previous received pairlist should be used if the remote server is not reachable or returns an error. The default value is true.
|
The optional `keep_pairlist_on_failure` specifies whether the previous received pairlist should be used if the remote server is not reachable or returns an error. The default value is true.
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ Provides pair list fetched from a remote source
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Tuple
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from cachetools import TTLCache
|
from cachetools import TTLCache
|
||||||
@ -41,7 +41,7 @@ class RemotePairList(IPairList):
|
|||||||
self._number_pairs = self._pairlistconfig['number_assets']
|
self._number_pairs = self._pairlistconfig['number_assets']
|
||||||
self._refresh_period: int = self._pairlistconfig.get('refresh_period', 1800)
|
self._refresh_period: int = self._pairlistconfig.get('refresh_period', 1800)
|
||||||
self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True)
|
self._keep_pairlist_on_failure = self._pairlistconfig.get('keep_pairlist_on_failure', True)
|
||||||
self._pair_cache: Optional[TTLCache] = None
|
self._pair_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period)
|
||||||
self._pairlist_url = self._pairlistconfig.get('pairlist_url', '')
|
self._pairlist_url = self._pairlistconfig.get('pairlist_url', '')
|
||||||
self._read_timeout = self._pairlistconfig.get('read_timeout', 60)
|
self._read_timeout = self._pairlistconfig.get('read_timeout', 60)
|
||||||
self._bearer_token = self._pairlistconfig.get('bearer_token', '')
|
self._bearer_token = self._pairlistconfig.get('bearer_token', '')
|
||||||
@ -63,28 +63,20 @@ class RemotePairList(IPairList):
|
|||||||
"""
|
"""
|
||||||
return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist."
|
return f"{self.name} - {self._pairlistconfig['number_assets']} pairs from RemotePairlist."
|
||||||
|
|
||||||
def process_json(self, jsonparse) -> Tuple[List[str], str]:
|
def process_json(self, jsonparse) -> List[str]:
|
||||||
|
|
||||||
pairlist = jsonparse.get('pairs', [])
|
pairlist = jsonparse.get('pairs', [])
|
||||||
remote_info = jsonparse.get('info', '')[:256].strip()
|
remote_refresh_period = int(jsonparse.get('refresh_period', self._refresh_period))
|
||||||
remote_refresh_period = jsonparse.get('refresh_period', self._refresh_period)
|
|
||||||
|
|
||||||
info = "".join(char if char.isalnum() or
|
|
||||||
char in " +-.,%:" else "-" for char in remote_info)
|
|
||||||
|
|
||||||
if not self._init_done:
|
|
||||||
if self._refresh_period < remote_refresh_period:
|
if self._refresh_period < remote_refresh_period:
|
||||||
self.log_once(f'Refresh Period has been increased from {self._refresh_period}'
|
self.log_once(f'Refresh Period has been increased from {self._refresh_period}'
|
||||||
f' to {remote_refresh_period} from Remote.', logger.info)
|
f' to minimum allowed: {remote_refresh_period} from Remote.', logger.info)
|
||||||
|
|
||||||
self._refresh_period = remote_refresh_period
|
self._refresh_period = remote_refresh_period
|
||||||
self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period)
|
self._pair_cache = TTLCache(maxsize=1, ttl=remote_refresh_period)
|
||||||
else:
|
|
||||||
self._pair_cache = TTLCache(maxsize=1, ttl=self._refresh_period)
|
|
||||||
|
|
||||||
self._init_done = True
|
self._init_done = True
|
||||||
|
|
||||||
return pairlist, info
|
return pairlist
|
||||||
|
|
||||||
def return_last_pairlist(self) -> List[str]:
|
def return_last_pairlist(self) -> List[str]:
|
||||||
if self._keep_pairlist_on_failure:
|
if self._keep_pairlist_on_failure:
|
||||||
@ -95,7 +87,7 @@ class RemotePairList(IPairList):
|
|||||||
|
|
||||||
return pairlist
|
return pairlist
|
||||||
|
|
||||||
def fetch_pairlist(self) -> Tuple[List[str], float, str]:
|
def fetch_pairlist(self) -> Tuple[List[str], float]:
|
||||||
|
|
||||||
headers = {
|
headers = {
|
||||||
'User-Agent': 'Freqtrade/' + __version__ + ' Remotepairlist'
|
'User-Agent': 'Freqtrade/' + __version__ + ' Remotepairlist'
|
||||||
@ -104,8 +96,6 @@ class RemotePairList(IPairList):
|
|||||||
if self._bearer_token:
|
if self._bearer_token:
|
||||||
headers['Authorization'] = f'Bearer {self._bearer_token}'
|
headers['Authorization'] = f'Bearer {self._bearer_token}'
|
||||||
|
|
||||||
info = "Pairlist"
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = requests.get(self._pairlist_url, headers=headers,
|
response = requests.get(self._pairlist_url, headers=headers,
|
||||||
timeout=self._read_timeout)
|
timeout=self._read_timeout)
|
||||||
@ -114,7 +104,17 @@ class RemotePairList(IPairList):
|
|||||||
|
|
||||||
if "application/json" in str(content_type):
|
if "application/json" in str(content_type):
|
||||||
jsonparse = response.json()
|
jsonparse = response.json()
|
||||||
pairlist, info = self.process_json(jsonparse)
|
|
||||||
|
try:
|
||||||
|
pairlist = self.process_json(jsonparse)
|
||||||
|
except Exception as e:
|
||||||
|
|
||||||
|
if self._init_done:
|
||||||
|
pairlist = self.return_last_pairlist()
|
||||||
|
logger.warning(f'Error while processing JSON data: {type(e)}')
|
||||||
|
else:
|
||||||
|
raise OperationalException(f'Error while processing JSON data: {type(e)}')
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if self._init_done:
|
if self._init_done:
|
||||||
self.log_once(f'Error: RemotePairList is not of type JSON: '
|
self.log_once(f'Error: RemotePairList is not of type JSON: '
|
||||||
@ -131,7 +131,7 @@ class RemotePairList(IPairList):
|
|||||||
|
|
||||||
time_elapsed = 0
|
time_elapsed = 0
|
||||||
|
|
||||||
return pairlist, time_elapsed, info
|
return pairlist, time_elapsed
|
||||||
|
|
||||||
def gen_pairlist(self, tickers: Tickers) -> List[str]:
|
def gen_pairlist(self, tickers: Tickers) -> List[str]:
|
||||||
"""
|
"""
|
||||||
@ -140,7 +140,7 @@ class RemotePairList(IPairList):
|
|||||||
:return: List of pairs
|
:return: List of pairs
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if self._init_done and self._pair_cache is not None:
|
if self._init_done:
|
||||||
pairlist = self._pair_cache.get('pairlist')
|
pairlist = self._pair_cache.get('pairlist')
|
||||||
else:
|
else:
|
||||||
pairlist = []
|
pairlist = []
|
||||||
@ -159,25 +159,33 @@ class RemotePairList(IPairList):
|
|||||||
with open(filename) as json_file:
|
with open(filename) as json_file:
|
||||||
# Load the JSON data into a dictionary
|
# Load the JSON data into a dictionary
|
||||||
jsonparse = json.load(json_file)
|
jsonparse = json.load(json_file)
|
||||||
pairlist, info = self.process_json(jsonparse)
|
|
||||||
|
try:
|
||||||
|
pairlist = self.process_json(jsonparse)
|
||||||
|
except Exception as e:
|
||||||
|
if self._init_done:
|
||||||
|
pairlist = self.return_last_pairlist()
|
||||||
|
logger.warning(f'Error while processing JSON data: {type(e)}')
|
||||||
|
else:
|
||||||
|
raise OperationalException('Error while processing'
|
||||||
|
f'JSON data: {type(e)}')
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"{self._pairlist_url} does not exist.")
|
raise ValueError(f"{self._pairlist_url} does not exist.")
|
||||||
else:
|
else:
|
||||||
# Fetch Pairlist from Remote URL
|
# Fetch Pairlist from Remote URL
|
||||||
pairlist, time_elapsed, info = self.fetch_pairlist()
|
pairlist, time_elapsed = self.fetch_pairlist()
|
||||||
|
|
||||||
self.log_once(f"Fetched pairs: {pairlist}", logger.debug)
|
self.log_once(f"Fetched pairs: {pairlist}", logger.debug)
|
||||||
|
|
||||||
pairlist = self._whitelist_for_active_markets(pairlist)
|
pairlist = self._whitelist_for_active_markets(pairlist)
|
||||||
pairlist = pairlist[:self._number_pairs]
|
pairlist = pairlist[:self._number_pairs]
|
||||||
|
|
||||||
if self._pair_cache is not None:
|
|
||||||
self._pair_cache['pairlist'] = pairlist.copy()
|
self._pair_cache['pairlist'] = pairlist.copy()
|
||||||
|
|
||||||
if time_elapsed != 0.0:
|
if time_elapsed != 0.0:
|
||||||
self.log_once(f'{info} Fetched in {time_elapsed} seconds.', logger.info)
|
self.log_once(f'Pairlist Fetched in {time_elapsed} seconds.', logger.info)
|
||||||
else:
|
else:
|
||||||
self.log_once(f'{info} Fetched Pairlist.', logger.info)
|
self.log_once('Fetched Pairlist.', logger.info)
|
||||||
|
|
||||||
self._last_pairlist = list(pairlist)
|
self._last_pairlist = list(pairlist)
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ def test_fetch_pairlist_timeout_keep_last_pairlist(mocker, rpl_config, caplog):
|
|||||||
|
|
||||||
remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
|
remote_pairlist._last_pairlist = ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
|
||||||
|
|
||||||
pairs, time_elapsed, info = remote_pairlist.fetch_pairlist()
|
pairs, time_elapsed = remote_pairlist.fetch_pairlist()
|
||||||
assert log_has(f"Was not able to fetch pairlist from: {remote_pairlist._pairlist_url}", caplog)
|
assert log_has(f"Was not able to fetch pairlist from: {remote_pairlist._pairlist_url}", caplog)
|
||||||
assert log_has("Keeping last fetched pairlist", caplog)
|
assert log_has("Keeping last fetched pairlist", caplog)
|
||||||
assert pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
|
assert pairs == ["BTC/USDT", "ETH/USDT", "LTC/USDT"]
|
||||||
@ -163,7 +163,6 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config):
|
|||||||
|
|
||||||
mock_response.json.return_value = {
|
mock_response.json.return_value = {
|
||||||
"pairs": ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"],
|
"pairs": ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"],
|
||||||
"info": "Mock pairlist response",
|
|
||||||
"refresh_period": 60
|
"refresh_period": 60
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,9 +178,8 @@ def test_fetch_pairlist_mock_response_valid(mocker, rpl_config):
|
|||||||
pairlistmanager = PairListManager(exchange, rpl_config)
|
pairlistmanager = PairListManager(exchange, rpl_config)
|
||||||
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
|
remote_pairlist = RemotePairList(exchange, pairlistmanager, rpl_config,
|
||||||
rpl_config['pairlists'][0], 0)
|
rpl_config['pairlists'][0], 0)
|
||||||
pairs, time_elapsed, info = remote_pairlist.fetch_pairlist()
|
pairs, time_elapsed = remote_pairlist.fetch_pairlist()
|
||||||
|
|
||||||
assert pairs == ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"]
|
assert pairs == ["ETH/USDT", "XRP/USDT", "LTC/USDT", "EOS/USDT"]
|
||||||
assert time_elapsed == 0.4
|
assert time_elapsed == 0.4
|
||||||
assert info == "Mock pairlist response"
|
|
||||||
assert remote_pairlist._refresh_period == 60
|
assert remote_pairlist._refresh_period == 60
|
||||||
|
Loading…
Reference in New Issue
Block a user