Merge branch 'freqtrade:develop' into develop
This commit is contained in:
@@ -2371,12 +2371,12 @@ class Exchange:
|
|||||||
# Must fetch the leverage tiers for each market separately
|
# Must fetch the leverage tiers for each market separately
|
||||||
# * This is slow(~45s) on Okx, makes ~90 api calls to load all linear swap markets
|
# * This is slow(~45s) on Okx, makes ~90 api calls to load all linear swap markets
|
||||||
markets = self.markets
|
markets = self.markets
|
||||||
symbols = []
|
|
||||||
|
|
||||||
for symbol, market in markets.items():
|
symbols = [
|
||||||
|
symbol for symbol, market in markets.items()
|
||||||
if (self.market_is_future(market)
|
if (self.market_is_future(market)
|
||||||
and market['quote'] == self._config['stake_currency']):
|
and market['quote'] == self._config['stake_currency'])
|
||||||
symbols.append(symbol)
|
]
|
||||||
|
|
||||||
tiers: Dict[str, List[Dict]] = {}
|
tiers: Dict[str, List[Dict]] = {}
|
||||||
|
|
||||||
@@ -2396,25 +2396,26 @@ class Exchange:
|
|||||||
else:
|
else:
|
||||||
logger.info("Using cached leverage_tiers.")
|
logger.info("Using cached leverage_tiers.")
|
||||||
|
|
||||||
async def gather_results():
|
async def gather_results(input_coro):
|
||||||
return await asyncio.gather(*input_coro, return_exceptions=True)
|
return await asyncio.gather(*input_coro, return_exceptions=True)
|
||||||
|
|
||||||
for input_coro in chunks(coros, 100):
|
for input_coro in chunks(coros, 100):
|
||||||
|
|
||||||
with self._loop_lock:
|
with self._loop_lock:
|
||||||
results = self.loop.run_until_complete(gather_results())
|
results = self.loop.run_until_complete(gather_results(input_coro))
|
||||||
|
|
||||||
for symbol, res in results:
|
for res in results:
|
||||||
tiers[symbol] = res
|
if isinstance(res, Exception):
|
||||||
|
logger.warning(f"Leverage tier exception: {repr(res)}")
|
||||||
|
continue
|
||||||
|
symbol, tier = res
|
||||||
|
tiers[symbol] = tier
|
||||||
if len(coros) > 0:
|
if len(coros) > 0:
|
||||||
self.cache_leverage_tiers(tiers, self._config['stake_currency'])
|
self.cache_leverage_tiers(tiers, self._config['stake_currency'])
|
||||||
logger.info(f"Done initializing {len(symbols)} markets.")
|
logger.info(f"Done initializing {len(symbols)} markets.")
|
||||||
|
|
||||||
return tiers
|
return tiers
|
||||||
else:
|
return {}
|
||||||
return {}
|
|
||||||
else:
|
|
||||||
return {}
|
|
||||||
|
|
||||||
def cache_leverage_tiers(self, tiers: Dict[str, List[Dict]], stake_currency: str) -> None:
|
def cache_leverage_tiers(self, tiers: Dict[str, List[Dict]], stake_currency: str) -> None:
|
||||||
|
|
||||||
@@ -2430,14 +2431,17 @@ class Exchange:
|
|||||||
def load_cached_leverage_tiers(self, stake_currency: str) -> Optional[Dict[str, List[Dict]]]:
|
def load_cached_leverage_tiers(self, stake_currency: str) -> Optional[Dict[str, List[Dict]]]:
|
||||||
filename = self._config['datadir'] / "futures" / f"leverage_tiers_{stake_currency}.json"
|
filename = self._config['datadir'] / "futures" / f"leverage_tiers_{stake_currency}.json"
|
||||||
if filename.is_file():
|
if filename.is_file():
|
||||||
tiers = file_load_json(filename)
|
try:
|
||||||
updated = tiers.get('updated')
|
tiers = file_load_json(filename)
|
||||||
if updated:
|
updated = tiers.get('updated')
|
||||||
updated_dt = parser.parse(updated)
|
if updated:
|
||||||
if updated_dt < datetime.now(timezone.utc) - timedelta(weeks=4):
|
updated_dt = parser.parse(updated)
|
||||||
logger.info("Cached leverage tiers are outdated. Will update.")
|
if updated_dt < datetime.now(timezone.utc) - timedelta(weeks=4):
|
||||||
return None
|
logger.info("Cached leverage tiers are outdated. Will update.")
|
||||||
return tiers['data']
|
return None
|
||||||
|
return tiers['data']
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Error loading cached leverage tiers. Refreshing.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def fill_leverage_tiers(self) -> None:
|
def fill_leverage_tiers(self) -> None:
|
||||||
|
|||||||
@@ -273,7 +273,8 @@ def _get_resample_from_period(period: str) -> str:
|
|||||||
if period == 'day':
|
if period == 'day':
|
||||||
return '1d'
|
return '1d'
|
||||||
if period == 'week':
|
if period == 'week':
|
||||||
return '1w'
|
# Weekly defaulting to Monday.
|
||||||
|
return '1W-MON'
|
||||||
if period == 'month':
|
if period == 'month':
|
||||||
return '1M'
|
return '1M'
|
||||||
raise ValueError(f"Period {period} is not supported.")
|
raise ValueError(f"Period {period} is not supported.")
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ target-version = "py38"
|
|||||||
extend-select = [
|
extend-select = [
|
||||||
"C90", # mccabe
|
"C90", # mccabe
|
||||||
# "N", # pep8-naming
|
# "N", # pep8-naming
|
||||||
|
"F", # pyflakes
|
||||||
|
"E", # pycodestyle
|
||||||
|
"W", # pycodestyle
|
||||||
"UP", # pyupgrade
|
"UP", # pyupgrade
|
||||||
"TID", # flake8-tidy-imports
|
"TID", # flake8-tidy-imports
|
||||||
# "EXE", # flake8-executable
|
# "EXE", # flake8-executable
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@ numpy==1.24.2
|
|||||||
pandas==1.5.3
|
pandas==1.5.3
|
||||||
pandas-ta==0.3.14b
|
pandas-ta==0.3.14b
|
||||||
|
|
||||||
ccxt==3.0.59
|
ccxt==3.0.69
|
||||||
cryptography==40.0.2
|
cryptography==40.0.2
|
||||||
aiohttp==3.8.4
|
aiohttp==3.8.4
|
||||||
SQLAlchemy==2.0.9
|
SQLAlchemy==2.0.9
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import MagicMock, PropertyMock
|
from unittest.mock import AsyncMock, MagicMock, PropertyMock
|
||||||
|
|
||||||
import ccxt
|
import ccxt
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from freqtrade.enums import CandleType, MarginMode, TradingMode
|
from freqtrade.enums import CandleType, MarginMode, TradingMode
|
||||||
from freqtrade.exceptions import RetryableOrderError
|
from freqtrade.exceptions import RetryableOrderError, TemporaryError
|
||||||
from freqtrade.exchange.exchange import timeframe_to_minutes
|
from freqtrade.exchange.exchange import timeframe_to_minutes
|
||||||
from tests.conftest import EXMS, get_mock_coro, get_patched_exchange, log_has
|
from tests.conftest import EXMS, get_patched_exchange, log_has
|
||||||
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
||||||
|
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets, tmpdir, caplog,
|
|||||||
'fetchLeverageTiers': False,
|
'fetchLeverageTiers': False,
|
||||||
'fetchMarketLeverageTiers': True,
|
'fetchMarketLeverageTiers': True,
|
||||||
})
|
})
|
||||||
api_mock.fetch_market_leverage_tiers = get_mock_coro(side_effect=[
|
api_mock.fetch_market_leverage_tiers = AsyncMock(side_effect=[
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
'tier': 1,
|
'tier': 1,
|
||||||
@@ -341,6 +341,7 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets, tmpdir, caplog,
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
TemporaryError("this Failed"),
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
'tier': 1,
|
'tier': 1,
|
||||||
|
|||||||
@@ -465,7 +465,7 @@ def test_generate_periodic_breakdown_stats(testdatadir):
|
|||||||
def test__get_resample_from_period():
|
def test__get_resample_from_period():
|
||||||
|
|
||||||
assert _get_resample_from_period('day') == '1d'
|
assert _get_resample_from_period('day') == '1d'
|
||||||
assert _get_resample_from_period('week') == '1w'
|
assert _get_resample_from_period('week') == '1W-MON'
|
||||||
assert _get_resample_from_period('month') == '1M'
|
assert _get_resample_from_period('month') == '1M'
|
||||||
with pytest.raises(ValueError, match=r"Period noooo is not supported."):
|
with pytest.raises(ValueError, match=r"Period noooo is not supported."):
|
||||||
_get_resample_from_period('noooo')
|
_get_resample_from_period('noooo')
|
||||||
|
|||||||
Reference in New Issue
Block a user