Unit tests, extract init_args from main, add option to README
This commit is contained in:
parent
5ba255f635
commit
2b754d2662
@ -159,6 +159,8 @@ optional arguments:
|
|||||||
--dry-run-db Force dry run to use a local DB
|
--dry-run-db Force dry run to use a local DB
|
||||||
"tradesv3.dry_run.sqlite" instead of memory DB. Work
|
"tradesv3.dry_run.sqlite" instead of memory DB. Work
|
||||||
only if dry_run is enabled.
|
only if dry_run is enabled.
|
||||||
|
-w, --watchdog Run under watchdog (restart process if main loop is
|
||||||
|
stalled)
|
||||||
```
|
```
|
||||||
More details on:
|
More details on:
|
||||||
- [How to run the bot](https://github.com/gcarq/freqtrade/blob/develop/docs/bot-usage.md#bot-commands)
|
- [How to run the bot](https://github.com/gcarq/freqtrade/blob/develop/docs/bot-usage.md#bot-commands)
|
||||||
|
@ -399,12 +399,10 @@ def cleanup() -> None:
|
|||||||
exit(0)
|
exit(0)
|
||||||
|
|
||||||
|
|
||||||
def main(sysargv=sys.argv[1:]) -> None:
|
def init_args(sysargv) -> Watchdog:
|
||||||
"""
|
|
||||||
Loads and validates the config and handles the main loop
|
|
||||||
:return: None
|
|
||||||
"""
|
|
||||||
global _CONF
|
global _CONF
|
||||||
|
|
||||||
args = parse_args(sysargv,
|
args = parse_args(sysargv,
|
||||||
'Simple High Frequency Trading Bot for crypto currencies')
|
'Simple High Frequency Trading Bot for crypto currencies')
|
||||||
|
|
||||||
@ -441,13 +439,24 @@ def main(sysargv=sys.argv[1:]) -> None:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
logger.info('Dry run is disabled. (--dry_run_db ignored)')
|
logger.info('Dry run is disabled. (--dry_run_db ignored)')
|
||||||
|
|
||||||
watchdog = Watchdog()
|
watchdog = Watchdog()
|
||||||
|
|
||||||
if args.watchdog_enable:
|
if args.watchdog_enable:
|
||||||
logger.info('Using watchdog to monitor process (--watchdog)')
|
logger.info('Using watchdog to monitor process (--watchdog)')
|
||||||
if not watchdog.start():
|
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:
|
try:
|
||||||
init(_CONF)
|
init(_CONF)
|
||||||
|
@ -37,6 +37,14 @@ def test_parse_args_backtesting(mocker):
|
|||||||
assert call_args.ticker_interval == 5
|
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):
|
def test_main_start_hyperopt(mocker):
|
||||||
hyperopt_mock = mocker.patch(
|
hyperopt_mock = mocker.patch(
|
||||||
'freqtrade.optimize.hyperopt.start', MagicMock())
|
'freqtrade.optimize.hyperopt.start', MagicMock())
|
||||||
|
18
freqtrade/tests/test_watchdog.py
Normal file
18
freqtrade/tests/test_watchdog.py
Normal file
@ -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
|
@ -14,6 +14,10 @@ class Watchdog:
|
|||||||
shared_heartbeat = Value('d', 0.0)
|
shared_heartbeat = Value('d', 0.0)
|
||||||
kill_signal = None
|
kill_signal = None
|
||||||
|
|
||||||
|
def __init__(self, timeout=WATCHDOG_TIMEOUT):
|
||||||
|
self.timeout = timeout
|
||||||
|
self.heartbeat()
|
||||||
|
|
||||||
def heartbeat(self) -> None:
|
def heartbeat(self) -> None:
|
||||||
logger.debug("Heartbeat")
|
logger.debug("Heartbeat")
|
||||||
self.shared_heartbeat.value = time.time()
|
self.shared_heartbeat.value = time.time()
|
||||||
@ -24,11 +28,11 @@ class Watchdog:
|
|||||||
|
|
||||||
def kill(self, pid):
|
def kill(self, pid):
|
||||||
logger.info("Stopping pid {}".format(pid))
|
logger.info("Stopping pid {}".format(pid))
|
||||||
os.kill(pid, signal.SIGTERM) # Better use sigint and then sigterm?
|
if pid:
|
||||||
os.wait()
|
os.kill(pid, signal.SIGTERM) # Better use sigint and then sigterm?
|
||||||
|
os.wait()
|
||||||
|
|
||||||
def start(self) -> bool:
|
def start(self) -> bool:
|
||||||
self.heartbeat()
|
|
||||||
pid = os.fork()
|
pid = os.fork()
|
||||||
if pid != 0:
|
if pid != 0:
|
||||||
# In watchdog proces, run it
|
# In watchdog proces, run it
|
||||||
@ -54,8 +58,10 @@ class Watchdog:
|
|||||||
|
|
||||||
timeout = time.time() - self.shared_heartbeat.value
|
timeout = time.time() - self.shared_heartbeat.value
|
||||||
|
|
||||||
if timeout > WATCHDOG_TIMEOUT:
|
if timeout > self.timeout:
|
||||||
logger.warning("Kill process due to timeout: {}".format(timeout))
|
logger.warning("Kill process due to timeout: {}".format(timeout))
|
||||||
|
if not pid:
|
||||||
|
return False
|
||||||
self.kill(pid)
|
self.kill(pid)
|
||||||
new_pid = os.fork()
|
new_pid = os.fork()
|
||||||
if new_pid == 0:
|
if new_pid == 0:
|
||||||
|
Loading…
Reference in New Issue
Block a user