diff --git a/freqtrade/worker.py b/freqtrade/worker.py index dea0acc44..9b81764b6 100755 --- a/freqtrade/worker.py +++ b/freqtrade/worker.py @@ -142,9 +142,14 @@ class Worker: sleep_duration = max(throttle_secs - time_passed, 0.0) logger.debug(f"Throttling with '{func.__name__}()': sleep for {sleep_duration:.2f} s, " f"last iteration took {time_passed:.2f} s.") - time.sleep(sleep_duration) + self._sleep(sleep_duration) return result + @staticmethod + def _sleep(sleep_duration: float) -> None: + """Local sleep method - to improve testability""" + time.sleep(sleep_duration) + def _process_stopped(self) -> None: self.freqtrade.process_stopped() diff --git a/tests/test_worker.py b/tests/test_worker.py index ddca9525b..8e17f7bc1 100644 --- a/tests/test_worker.py +++ b/tests/test_worker.py @@ -1,7 +1,10 @@ import logging import time +from datetime import timedelta from unittest.mock import MagicMock, PropertyMock +import time_machine + from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import State from freqtrade.worker import Worker @@ -59,13 +62,48 @@ def test_throttle(mocker, default_conf, caplog) -> None: end = time.time() assert result == 42 - assert end - start > 0.1 + assert 0.3 > end - start > 0.1 assert log_has_re(r"Throttling with 'throttled_func\(\)': sleep for \d\.\d{2} s.*", caplog) result = worker._throttle(throttled_func, throttle_secs=-1) assert result == 42 +def test_throttle_sleep_time(mocker, default_conf, caplog) -> None: + + caplog.set_level(logging.DEBUG) + worker = get_patched_worker(mocker, default_conf) + sleep_mock = mocker.patch("freqtrade.worker.Worker._sleep") + with time_machine.travel("2022-09-01 05:00:00 +00:00") as t: + def throttled_func(x=1): + t.shift(timedelta(seconds=x)) + return 42 + + assert worker._throttle(throttled_func, throttle_secs=5) == 42 + # This moves the clock by 1 second + assert sleep_mock.call_count == 1 + assert 3.8 < sleep_mock.call_args[0][0] < 4.1 + + sleep_mock.reset_mock() + # This moves the clock by 1 second + assert worker._throttle(throttled_func, throttle_secs=10) == 42 + assert sleep_mock.call_count == 1 + assert 8.8 < sleep_mock.call_args[0][0] < 9.1 + + sleep_mock.reset_mock() + # This moves the clock by 5 second, so we only throttle by 5s + assert worker._throttle(throttled_func, throttle_secs=10, x=5) == 42 + assert sleep_mock.call_count == 1 + assert 4.8 < sleep_mock.call_args[0][0] < 5.1 + + t.move_to("2022-09-01 05:01:00 +00:00") + sleep_mock.reset_mock() + # Throttle for more than 5m (1 timeframe) + assert worker._throttle(throttled_func, throttle_secs=400, x=5) == 42 + assert sleep_mock.call_count == 1 + assert 394.8 < sleep_mock.call_args[0][0] < 395.1 + + def test_throttle_with_assets(mocker, default_conf) -> None: def throttled_func(nb_assets=-1): return nb_assets