From d3b3370f239877cf9aad256d7440db85a8f8f329 Mon Sep 17 00:00:00 2001 From: gcarq Date: Sat, 11 Nov 2017 16:47:19 +0100 Subject: [PATCH] Add configurable throttle mechanism --- config.json.example | 5 ++++- freqtrade/main.py | 11 +++++------ freqtrade/misc.py | 27 +++++++++++++++++++++++++++ freqtrade/tests/test_misc.py | 20 ++++++++++++++++++++ 4 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 freqtrade/tests/test_misc.py diff --git a/config.json.example b/config.json.example index 685189087..e91b6772f 100644 --- a/config.json.example +++ b/config.json.example @@ -34,5 +34,8 @@ "token": "token", "chat_id": "chat_id" }, - "initial_state": "running" + "initial_state": "running", + "internals": { + "process_throttle_secs": 5 + } } \ No newline at end of file diff --git a/freqtrade/main.py b/freqtrade/main.py index ef25f2378..1a9990abd 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -13,7 +13,7 @@ from jsonschema import validate from freqtrade import __version__, exchange, persistence from freqtrade.analyze import get_buy_signal -from freqtrade.misc import CONF_SCHEMA, State, get_state, update_state, build_arg_parser +from freqtrade.misc import CONF_SCHEMA, State, get_state, update_state, build_arg_parser, throttle from freqtrade.persistence import Trade from freqtrade.rpc import telegram @@ -280,14 +280,14 @@ def main(): # Load and validate configuration with open(args.config) as file: _CONF = json.load(file) + if 'internals' not in _CONF: + _CONF['internals'] = {} logger.info('Validating configuration ...') validate(_CONF, CONF_SCHEMA) # Initialize all modules and start main loop init(_CONF) - old_state = get_state() - logger.info('Initial State: %s', old_state) - telegram.send_msg('*Status:* `{}`'.format(old_state.name.lower())) + old_state = None while True: new_state = get_state() # Log state transition @@ -298,8 +298,7 @@ def main(): if new_state == State.STOPPED: time.sleep(1) elif new_state == State.RUNNING: - _process() - time.sleep(5) + throttle(_process, min_secs=_CONF['internals'].get('process_throttle_secs', 5)) old_state = new_state diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 4787ef2f9..f7f6a8e42 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -1,11 +1,15 @@ import argparse import enum import logging +from typing import Any, Callable +import time from wrapt import synchronized from freqtrade import __version__ +logger = logging.getLogger(__name__) + class State(enum.Enum): RUNNING = 0 @@ -36,6 +40,23 @@ def get_state() -> State: return _STATE +def throttle(func: Callable[..., Any], min_secs: float, *args, **kwargs) -> Any: + """ + Throttles the given callable that it + takes at least `min_secs` to finish execution. + :param func: Any callable + :param min_secs: minimum execution time in seconds + :return: Any + """ + start = time.time() + result = func(*args, **kwargs) + end = time.time() + duration = max(min_secs - (end - start), 0.0) + logger.debug('Throttling %s for %.2f seconds', func.__name__, duration) + time.sleep(duration) + return result + + def build_arg_parser() -> argparse.ArgumentParser: """ Builds and returns an ArgumentParser instance """ parser = argparse.ArgumentParser( @@ -104,6 +125,12 @@ CONF_SCHEMA = { 'required': ['enabled', 'token', 'chat_id'] }, 'initial_state': {'type': 'string', 'enum': ['running', 'stopped']}, + 'internals': { + 'type': 'object', + 'properties': { + 'process_throttle_secs': {'type': 'number'} + } + } }, 'definitions': { 'exchange': { diff --git a/freqtrade/tests/test_misc.py b/freqtrade/tests/test_misc.py new file mode 100644 index 000000000..2dd8bbb27 --- /dev/null +++ b/freqtrade/tests/test_misc.py @@ -0,0 +1,20 @@ +# pragma pylint: disable=missing-docstring +import time + +from freqtrade.misc import throttle + + +def test_throttle(): + + def func(): + return 42 + + start = time.time() + result = throttle(func, 0.1) + end = time.time() + + assert result == 42 + assert end - start > 0.1 + + result = throttle(func, -1) + assert result == 42