From 2b754d2662c2a1cc4a283337de3e78b819fcccfa Mon Sep 17 00:00:00 2001 From: Anton Ermak Date: Tue, 9 Jan 2018 15:59:58 +0700 Subject: [PATCH] Unit tests, extract init_args from main, add option to README --- README.md | 2 ++ freqtrade/main.py | 23 ++++++++++++++++------- freqtrade/tests/test_main.py | 8 ++++++++ freqtrade/tests/test_watchdog.py | 18 ++++++++++++++++++ freqtrade/watchdog.py | 14 ++++++++++---- 5 files changed, 54 insertions(+), 11 deletions(-) create mode 100644 freqtrade/tests/test_watchdog.py diff --git a/README.md b/README.md index 045d6b624..f36854266 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,8 @@ optional arguments: --dry-run-db Force dry run to use a local DB "tradesv3.dry_run.sqlite" instead of memory DB. Work only if dry_run is enabled. + -w, --watchdog Run under watchdog (restart process if main loop is + stalled) ``` More details on: - [How to run the bot](https://github.com/gcarq/freqtrade/blob/develop/docs/bot-usage.md#bot-commands) diff --git a/freqtrade/main.py b/freqtrade/main.py index 3f929b0a7..d1b84fca8 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -399,12 +399,10 @@ def cleanup() -> None: exit(0) -def main(sysargv=sys.argv[1:]) -> None: - """ - Loads and validates the config and handles the main loop - :return: None - """ +def init_args(sysargv) -> Watchdog: + global _CONF + args = parse_args(sysargv, 'Simple High Frequency Trading Bot for crypto currencies') @@ -441,13 +439,24 @@ def main(sysargv=sys.argv[1:]) -> None: ) else: logger.info('Dry run is disabled. (--dry_run_db ignored)') - watchdog = Watchdog() if args.watchdog_enable: logger.info('Using watchdog to monitor process (--watchdog)') if not watchdog.start(): - return + exit(0) + + return args, watchdog + + +def main(sysargv=sys.argv[1:]) -> None: + """ + Loads and validates the config and handles the main loop + :return: None + """ + global _CONF + + args, watchdog = init_args(sysargv) try: init(_CONF) diff --git a/freqtrade/tests/test_main.py b/freqtrade/tests/test_main.py index cceb555f7..121add35a 100644 --- a/freqtrade/tests/test_main.py +++ b/freqtrade/tests/test_main.py @@ -37,6 +37,14 @@ def test_parse_args_backtesting(mocker): assert call_args.ticker_interval == 5 +def test_init_args(): + sysargv = ['--config', 'config.json.example'] + args, watchdog = main.init_args(sysargv) + assert args.config == 'config.json.example' + assert main._CONF['stake_currency'] == 'BTC' + assert watchdog.timeout == 300 + + def test_main_start_hyperopt(mocker): hyperopt_mock = mocker.patch( 'freqtrade.optimize.hyperopt.start', MagicMock()) diff --git a/freqtrade/tests/test_watchdog.py b/freqtrade/tests/test_watchdog.py new file mode 100644 index 000000000..4d1eae1da --- /dev/null +++ b/freqtrade/tests/test_watchdog.py @@ -0,0 +1,18 @@ +from freqtrade.watchdog import Watchdog + + +def test_watchdog_timeout(caplog): + watchdog = Watchdog(1) + assert(watchdog.run(0) is False) + log = ["Watchdog started", "Kill process due to timeout"] + for line in log: + assert line in caplog.text + + +def test_watchdog_kill(caplog): + watchdog = Watchdog(1) + watchdog.exit_gracefully(1, 0) + assert(watchdog.run(0) is False) + log = ["Watchdog started", "Watchdog stopped"] + for line in log: + assert line in caplog.text diff --git a/freqtrade/watchdog.py b/freqtrade/watchdog.py index b0fde770e..ead74ce5f 100644 --- a/freqtrade/watchdog.py +++ b/freqtrade/watchdog.py @@ -14,6 +14,10 @@ class Watchdog: shared_heartbeat = Value('d', 0.0) kill_signal = None + def __init__(self, timeout=WATCHDOG_TIMEOUT): + self.timeout = timeout + self.heartbeat() + def heartbeat(self) -> None: logger.debug("Heartbeat") self.shared_heartbeat.value = time.time() @@ -24,11 +28,11 @@ class Watchdog: def kill(self, pid): logger.info("Stopping pid {}".format(pid)) - os.kill(pid, signal.SIGTERM) # Better use sigint and then sigterm? - os.wait() + if pid: + os.kill(pid, signal.SIGTERM) # Better use sigint and then sigterm? + os.wait() def start(self) -> bool: - self.heartbeat() pid = os.fork() if pid != 0: # In watchdog proces, run it @@ -54,8 +58,10 @@ class Watchdog: timeout = time.time() - self.shared_heartbeat.value - if timeout > WATCHDOG_TIMEOUT: + if timeout > self.timeout: logger.warning("Kill process due to timeout: {}".format(timeout)) + if not pid: + return False self.kill(pid) new_pid = os.fork() if new_pid == 0: