diff --git a/config_full.json.example b/config_full.json.example index c6b229ea3..5789e49ac 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -119,7 +119,8 @@ "initial_state": "running", "forcebuy_enable": false, "internals": { - "process_throttle_secs": 5 + "process_throttle_secs": 5, + "heartbeat_interval": 60 }, "strategy": "DefaultStrategy", "strategy_path": "user_data/strategies/" diff --git a/docs/configuration.md b/docs/configuration.md index 33c296a6a..90f2687d0 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -98,6 +98,7 @@ Mandatory parameters are marked as **Required**, which means that they are requi | `strategy` | DefaultStrategy | Defines Strategy class to use. | `strategy_path` | null | Adds an additional strategy lookup path (must be a directory). | `internals.process_throttle_secs` | 5 | **Required.** Set the process throttle. Value in second. +| `internals.heartbeat_interval` | 60 | Print heartbeat message every X seconds. Set to 0 to disable heartbeat messages. | `internals.sd_notify` | false | Enables use of the sd_notify protocol to tell systemd service manager about changes in the bot state and issue keep-alive pings. See [here](installation.md#7-optional-configure-freqtrade-as-a-systemd-service) for more details. | `logfile` | | Specify Logfile. Uses a rolling strategy of 10 files, with 1Mb per file. | `user_data_dir` | cwd()/user_data | Directory containing user data. Defaults to `./user_data/`. @@ -330,7 +331,7 @@ This configuration enables binance, as well as rate limiting to avoid bans from Optimal settings for rate limiting depend on the exchange and the size of the whitelist, so an ideal parameter will vary on many other settings. We try to provide sensible defaults per exchange where possible, if you encounter bans please make sure that `"enableRateLimit"` is enabled and increase the `"rateLimit"` parameter step by step. -#### Advanced FreqTrade Exchange configuration +#### Advanced Freqtrade Exchange configuration Advanced options can be configured using the `_ft_has_params` setting, which will override Defaults and exchange-specific behaviours. @@ -350,6 +351,13 @@ For example, to test the order type `FOK` with Kraken, and modify candle_limit t !!! Warning Please make sure to fully understand the impacts of these settings before modifying them. +#### Random notes for other exchanges + +* The Ocean (ccxt id: 'theocean') exchange uses Web3 functionality and requires web3 package to be installed: +```shell +$ pip3 install web3 +``` + ### What values can be used for fiat_display_currency? The `fiat_display_currency` configuration parameter sets the base currency to use for the diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index f6f12f7f8..a8fc6bc7e 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -6,26 +6,27 @@ import logging import traceback from datetime import datetime from math import isclose +from os import getpid from typing import Any, Dict, List, Optional, Tuple import arrow from requests.exceptions import RequestException -from freqtrade import (DependencyException, InvalidOrderException, - __version__, constants, persistence) +from freqtrade import (DependencyException, InvalidOrderException, __version__, + constants, persistence) +from freqtrade.configuration import validate_config_consistency from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge -from freqtrade.configuration import validate_config_consistency from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date from freqtrade.persistence import Trade +from freqtrade.resolvers import (ExchangeResolver, PairListResolver, + StrategyResolver) from freqtrade.rpc import RPCManager, RPCMessageType -from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver from freqtrade.state import State -from freqtrade.strategy.interface import SellType, IStrategy +from freqtrade.strategy.interface import IStrategy, SellType from freqtrade.wallets import Wallets - logger = logging.getLogger(__name__) @@ -50,6 +51,10 @@ class FreqtradeBot: # Init objects self.config = config + self._heartbeat_msg = 0 + + self.heartbeat_interval = self.config.get('internals', {}).get('heartbeat_interval', 60) + self.strategy: IStrategy = StrategyResolver(self.config).strategy # Check config consistency here since strategies can set certain options @@ -139,6 +144,11 @@ class FreqtradeBot: self.check_handle_timedout() Trade.session.flush() + if (self.heartbeat_interval + and (arrow.utcnow().timestamp - self._heartbeat_msg > self.heartbeat_interval)): + logger.info(f"Bot heartbeat. PID={getpid()}") + self._heartbeat_msg = arrow.utcnow().timestamp + def _refresh_whitelist(self, trades: List[Trade] = []) -> List[str]: """ Refresh whitelist from pairlist or edge and extend it with trades. @@ -445,7 +455,7 @@ class FreqtradeBot: try: # Create entity and execute trade if not self.create_trades(): - logger.info('Found no buy signals for whitelisted currencies. Trying again...') + logger.debug('Found no buy signals for whitelisted currencies. Trying again...') except DependencyException as exception: logger.warning('Unable to create trade: %s', exception) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index a58c12ead..30f9ba0a4 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1519,6 +1519,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog, def test_process_maybe_execute_buys(mocker, default_conf, caplog) -> None: + caplog.set_level(logging.DEBUG) freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.freqtradebot.FreqtradeBot.create_trades', MagicMock(return_value=False)) @@ -3642,3 +3643,27 @@ def test_startup_trade_reinit(default_conf, edge_conf, mocker): ftbot = get_patched_freqtradebot(mocker, edge_conf) ftbot.startup() assert reinit_mock.call_count == 0 + + +def test_process_i_am_alive(default_conf, mocker, caplog): + patch_RPCManager(mocker) + patch_exchange(mocker) + mocker.patch('freqtrade.exchange.Exchange.exchange_has', MagicMock(return_value=True)) + + ftbot = get_patched_freqtradebot(mocker, default_conf) + message = r"Bot heartbeat\. PID=.*" + ftbot.process() + assert log_has_re(message, caplog) + assert ftbot._heartbeat_msg != 0 + + caplog.clear() + # Message is not shown before interval is up + ftbot.process() + assert not log_has_re(message, caplog) + + caplog.clear() + # Set clock - 70 seconds + ftbot._heartbeat_msg -= 70 + + ftbot.process() + assert log_has_re(message, caplog)