Use startup_candle-count to determine call count
This commit is contained in:
parent
9fa64c2647
commit
a08dd17bc1
@ -155,8 +155,8 @@ class Exchange:
|
|||||||
self.validate_pairs(config['exchange']['pair_whitelist'])
|
self.validate_pairs(config['exchange']['pair_whitelist'])
|
||||||
self.validate_ordertypes(config.get('order_types', {}))
|
self.validate_ordertypes(config.get('order_types', {}))
|
||||||
self.validate_order_time_in_force(config.get('order_time_in_force', {}))
|
self.validate_order_time_in_force(config.get('order_time_in_force', {}))
|
||||||
self.validate_required_startup_candles(config.get('startup_candle_count', 0),
|
self.required_candle_call_count = self.validate_required_startup_candles(
|
||||||
config.get('timeframe', ''))
|
config.get('startup_candle_count', 0), config.get('timeframe', ''))
|
||||||
|
|
||||||
# Converts the interval provided in minutes in config to seconds
|
# Converts the interval provided in minutes in config to seconds
|
||||||
self.markets_refresh_interval: int = exchange_config.get(
|
self.markets_refresh_interval: int = exchange_config.get(
|
||||||
@ -477,10 +477,23 @@ class Exchange:
|
|||||||
Requires a grace-period of 5 candles - so a startup-period up to 494 is allowed by default.
|
Requires a grace-period of 5 candles - so a startup-period up to 494 is allowed by default.
|
||||||
"""
|
"""
|
||||||
candle_limit = self.ohlcv_candle_limit(timeframe)
|
candle_limit = self.ohlcv_candle_limit(timeframe)
|
||||||
if startup_candles + 5 > candle_limit:
|
# Require one more candle - to account for the still open candle.
|
||||||
|
candle_count = startup_candles + 1
|
||||||
|
# Allow 5 calls to the exchange per pair
|
||||||
|
required_candle_call_count = int(
|
||||||
|
(candle_count / candle_limit) + (0 if candle_count % candle_limit == 0 else 1))
|
||||||
|
|
||||||
|
if required_candle_call_count > 5:
|
||||||
|
# Only allow 5 calls per pair to somewhat limit the impact
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f"This strategy requires {startup_candles} candles to start. "
|
f"This strategy requires {startup_candles} candles to start, which is more than 5x "
|
||||||
f"{self.name} only provides {candle_limit - 5} for {timeframe}.")
|
f"the amount of candles {self.name} provides for {timeframe}.")
|
||||||
|
|
||||||
|
if required_candle_call_count > 1:
|
||||||
|
logger.warning(f"Using {required_candle_call_count} calls to get OHLCV. "
|
||||||
|
f"This can result in slower operations for the bot. Please check "
|
||||||
|
f"if you really need {startup_candles} candles for your strategy")
|
||||||
|
return required_candle_call_count
|
||||||
|
|
||||||
def exchange_has(self, endpoint: str) -> bool:
|
def exchange_has(self, endpoint: str) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -1283,11 +1296,10 @@ class Exchange:
|
|||||||
for pair, timeframe in set(pair_list):
|
for pair, timeframe in set(pair_list):
|
||||||
if ((pair, timeframe) not in self._klines
|
if ((pair, timeframe) not in self._klines
|
||||||
or self._now_is_time_to_refresh(pair, timeframe)):
|
or self._now_is_time_to_refresh(pair, timeframe)):
|
||||||
call_count = self._ft_has.get('ohlcv_candle_call_count', 1)
|
if not since_ms and self.required_candle_call_count > 1:
|
||||||
if not since_ms and call_count > 1:
|
|
||||||
# Multiple calls for one pair - to get more history
|
# Multiple calls for one pair - to get more history
|
||||||
one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe)
|
one_call = timeframe_to_msecs(timeframe) * self.ohlcv_candle_limit(timeframe)
|
||||||
move_to = one_call * call_count
|
move_to = one_call * self.required_candle_call_count
|
||||||
now = timeframe_to_next_date(timeframe)
|
now = timeframe_to_next_date(timeframe)
|
||||||
since_ms = int((now - timedelta(seconds=move_to // 1000)).timestamp() * 1000)
|
since_ms = int((now - timedelta(seconds=move_to // 1000)).timestamp() * 1000)
|
||||||
|
|
||||||
|
@ -941,12 +941,26 @@ def test_validate_required_startup_candles(default_conf, mocker, caplog):
|
|||||||
default_conf['startup_candle_count'] = 20
|
default_conf['startup_candle_count'] = 20
|
||||||
ex = Exchange(default_conf)
|
ex = Exchange(default_conf)
|
||||||
assert ex
|
assert ex
|
||||||
default_conf['startup_candle_count'] = 600
|
# assumption is that the exchange provides 500 candles per call.s
|
||||||
|
assert ex.validate_required_startup_candles(200, '5m') == 1
|
||||||
|
assert ex.validate_required_startup_candles(499, '5m') == 1
|
||||||
|
assert ex.validate_required_startup_candles(600, '5m') == 2
|
||||||
|
assert ex.validate_required_startup_candles(501, '5m') == 2
|
||||||
|
assert ex.validate_required_startup_candles(499, '5m') == 1
|
||||||
|
assert ex.validate_required_startup_candles(1000, '5m') == 3
|
||||||
|
assert ex.validate_required_startup_candles(2499, '5m') == 5
|
||||||
|
assert log_has_re(r'Using 5 calls to get OHLCV. This.*', caplog)
|
||||||
|
|
||||||
with pytest.raises(OperationalException, match=r'This strategy requires 600.*'):
|
with pytest.raises(OperationalException, match=r'This strategy requires 2500.*'):
|
||||||
|
ex.validate_required_startup_candles(2500, '5m')
|
||||||
|
|
||||||
|
# Ensure the same also happens on init
|
||||||
|
default_conf['startup_candle_count'] = 6000
|
||||||
|
with pytest.raises(OperationalException, match=r'This strategy requires 6000.*'):
|
||||||
Exchange(default_conf)
|
Exchange(default_conf)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def test_exchange_has(default_conf, mocker):
|
def test_exchange_has(default_conf, mocker):
|
||||||
exchange = get_patched_exchange(mocker, default_conf)
|
exchange = get_patched_exchange(mocker, default_conf)
|
||||||
assert not exchange.exchange_has('ASDFASDF')
|
assert not exchange.exchange_has('ASDFASDF')
|
||||||
@ -1632,12 +1646,14 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
|
|||||||
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
||||||
exchange._api_async.fetch_ohlcv.reset_mock()
|
exchange._api_async.fetch_ohlcv.reset_mock()
|
||||||
|
|
||||||
|
exchange.required_candle_call_count = 2
|
||||||
res = exchange.refresh_latest_ohlcv(pairs)
|
res = exchange.refresh_latest_ohlcv(pairs)
|
||||||
assert len(res) == len(pairs)
|
assert len(res) == len(pairs)
|
||||||
|
|
||||||
assert log_has(f'Refreshing candle (OHLCV) data for {len(pairs)} pairs', caplog)
|
assert log_has(f'Refreshing candle (OHLCV) data for {len(pairs)} pairs', caplog)
|
||||||
assert exchange._klines
|
assert exchange._klines
|
||||||
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
assert exchange._api_async.fetch_ohlcv.call_count == 4
|
||||||
|
exchange._api_async.fetch_ohlcv.reset_mock()
|
||||||
for pair in pairs:
|
for pair in pairs:
|
||||||
assert isinstance(exchange.klines(pair), DataFrame)
|
assert isinstance(exchange.klines(pair), DataFrame)
|
||||||
assert len(exchange.klines(pair)) > 0
|
assert len(exchange.klines(pair)) > 0
|
||||||
@ -1653,7 +1669,7 @@ def test_refresh_latest_ohlcv(mocker, default_conf, caplog) -> None:
|
|||||||
res = exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')])
|
res = exchange.refresh_latest_ohlcv([('IOTA/ETH', '5m'), ('XRP/ETH', '5m')])
|
||||||
assert len(res) == len(pairs)
|
assert len(res) == len(pairs)
|
||||||
|
|
||||||
assert exchange._api_async.fetch_ohlcv.call_count == 2
|
assert exchange._api_async.fetch_ohlcv.call_count == 0
|
||||||
assert log_has(f"Using cached candle (OHLCV) data for pair {pairs[0][0]}, "
|
assert log_has(f"Using cached candle (OHLCV) data for pair {pairs[0][0]}, "
|
||||||
f"timeframe {pairs[0][1]} ...",
|
f"timeframe {pairs[0][1]} ...",
|
||||||
caplog)
|
caplog)
|
||||||
|
Loading…
Reference in New Issue
Block a user