Merge pull request #6294 from xataxxx/health

/health api and telegram commands to return last processing time
This commit is contained in:
Matthias 2022-02-02 19:53:45 +01:00 committed by GitHub
commit a733a74dd9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 8 deletions

View File

@ -100,6 +100,8 @@ class FreqtradeBot(LoggingMixin):
self._exit_lock = Lock() self._exit_lock = Lock()
LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe)) LoggingMixin.__init__(self, logger, timeframe_to_seconds(self.strategy.timeframe))
self.last_process = datetime(1970, 1, 1, tzinfo=timezone.utc)
def notify_status(self, msg: str) -> None: def notify_status(self, msg: str) -> None:
""" """
Public method for users of this class (worker, etc.) to send notifications Public method for users of this class (worker, etc.) to send notifications
@ -187,6 +189,7 @@ class FreqtradeBot(LoggingMixin):
self.enter_positions() self.enter_positions()
Trade.commit() Trade.commit()
self.last_process = datetime.now(timezone.utc)
def process_stopped(self) -> None: def process_stopped(self) -> None:
""" """

View File

@ -384,3 +384,8 @@ class BacktestResponse(BaseModel):
class SysInfo(BaseModel): class SysInfo(BaseModel):
cpu_pct: List[float] cpu_pct: List[float]
ram_pct: float ram_pct: float
class Health(BaseModel):
last_process: datetime
last_process_ts: int

View File

@ -14,12 +14,12 @@ from freqtrade.rpc import RPC
from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload, from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload,
BlacklistResponse, Count, Daily, BlacklistResponse, Count, Daily,
DeleteLockRequest, DeleteTrade, ForceBuyPayload, DeleteLockRequest, DeleteTrade, ForceBuyPayload,
ForceBuyResponse, ForceSellPayload, Locks, Logs, ForceBuyResponse, ForceSellPayload, Health, Locks,
OpenTradeSchema, PairHistory, PerformanceEntry, Logs, OpenTradeSchema, PairHistory,
Ping, PlotConfig, Profit, ResultMsg, ShowConfig, PerformanceEntry, Ping, PlotConfig, Profit,
Stats, StatusMsg, StrategyListResponse, ResultMsg, ShowConfig, Stats, StatusMsg,
StrategyResponse, SysInfo, Version, StrategyListResponse, StrategyResponse, SysInfo,
WhitelistResponse) Version, WhitelistResponse)
from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional
from freqtrade.rpc.rpc import RPCException from freqtrade.rpc.rpc import RPCException
@ -291,3 +291,8 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option
@router.get('/sysinfo', response_model=SysInfo, tags=['info']) @router.get('/sysinfo', response_model=SysInfo, tags=['info'])
def sysinfo(): def sysinfo():
return RPC._rpc_sysinfo() return RPC._rpc_sysinfo()
@router.get('/health', response_model=Health, tags=['info'])
def health(rpc: RPC = Depends(get_rpc)):
return rpc._health()

View File

@ -10,6 +10,7 @@ from typing import Any, Dict, List, Optional, Tuple, Union
import arrow import arrow
import psutil import psutil
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from dateutil.tz import tzlocal
from numpy import NAN, inf, int64, mean from numpy import NAN, inf, int64, mean
from pandas import DataFrame from pandas import DataFrame
@ -1038,3 +1039,11 @@ class RPC:
"cpu_pct": psutil.cpu_percent(interval=1, percpu=True), "cpu_pct": psutil.cpu_percent(interval=1, percpu=True),
"ram_pct": psutil.virtual_memory().percent "ram_pct": psutil.virtual_memory().percent
} }
def _health(self) -> Dict[str, Union[str, int]]:
last_p = self._freqtrade.last_process
return {
'last_process': str(last_p),
'last_process_loc': last_p.astimezone(tzlocal()).strftime(DATETIME_PRINT_FORMAT),
'last_process_ts': int(last_p.timestamp()),
}

View File

@ -113,7 +113,7 @@ class Telegram(RPCHandler):
r'/stopbuy$', r'/reload_config$', r'/show_config$', r'/stopbuy$', r'/reload_config$', r'/show_config$',
r'/logs$', r'/whitelist$', r'/blacklist$', r'/bl_delete$', r'/logs$', r'/whitelist$', r'/blacklist$', r'/bl_delete$',
r'/weekly$', r'/weekly \d+$', r'/monthly$', r'/monthly \d+$', r'/weekly$', r'/weekly \d+$', r'/monthly$', r'/monthly \d+$',
r'/forcebuy$', r'/edge$', r'/help$', r'/version$'] r'/forcebuy$', r'/edge$', r'/health$', r'/help$', r'/version$']
# Create keys for generation # Create keys for generation
valid_keys_print = [k.replace('$', '') for k in valid_keys] valid_keys_print = [k.replace('$', '') for k in valid_keys]
@ -173,6 +173,7 @@ class Telegram(RPCHandler):
CommandHandler(['blacklist_delete', 'bl_delete'], self._blacklist_delete), CommandHandler(['blacklist_delete', 'bl_delete'], self._blacklist_delete),
CommandHandler('logs', self._logs), CommandHandler('logs', self._logs),
CommandHandler('edge', self._edge), CommandHandler('edge', self._edge),
CommandHandler('health', self._health),
CommandHandler('help', self._help), CommandHandler('help', self._help),
CommandHandler('version', self._version), CommandHandler('version', self._version),
] ]
@ -1282,6 +1283,7 @@ class Telegram(RPCHandler):
"*/logs [limit]:* `Show latest logs - defaults to 10` \n" "*/logs [limit]:* `Show latest logs - defaults to 10` \n"
"*/count:* `Show number of active trades compared to allowed number of trades`\n" "*/count:* `Show number of active trades compared to allowed number of trades`\n"
"*/edge:* `Shows validated pairs by Edge if it is enabled` \n" "*/edge:* `Shows validated pairs by Edge if it is enabled` \n"
"*/health* `Show latest process timestamp - defaults to 1970-01-01 00:00:00` \n"
"_Statistics_\n" "_Statistics_\n"
"------------\n" "------------\n"
@ -1309,6 +1311,19 @@ class Telegram(RPCHandler):
self._send_msg(message, parse_mode=ParseMode.MARKDOWN) self._send_msg(message, parse_mode=ParseMode.MARKDOWN)
@authorized_only
def _health(self, update: Update, context: CallbackContext) -> None:
"""
Handler for /health
Shows the last process timestamp
"""
try:
health = self._rpc._health()
message = f"Last process: `{health['last_process_loc']}`"
self._send_msg(message)
except RPCException as e:
self._send_msg(str(e))
@authorized_only @authorized_only
def _version(self, update: Update, context: CallbackContext) -> None: def _version(self, update: Update, context: CallbackContext) -> None:
""" """

View File

@ -1276,3 +1276,13 @@ def test_rpc_edge_enabled(mocker, edge_conf) -> None:
assert ret[0]['Winrate'] == 0.66 assert ret[0]['Winrate'] == 0.66
assert ret[0]['Expectancy'] == 1.71 assert ret[0]['Expectancy'] == 1.71
assert ret[0]['Stoploss'] == -0.02 assert ret[0]['Stoploss'] == -0.02
def test_rpc_health(mocker, default_conf) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
result = rpc._health()
assert result['last_process'] == '1970-01-01 00:00:00+00:00'
assert result['last_process_ts'] == 0

View File

@ -1442,3 +1442,14 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir):
assert result['status'] == 'reset' assert result['status'] == 'reset'
assert not result['running'] assert not result['running']
assert result['status_msg'] == 'Backtest reset' assert result['status_msg'] == 'Backtest reset'
def test_health(botclient):
ftbot, client = botclient
rc = client_get(client, f"{BASE_URI}/health")
assert_response(rc)
ret = rc.json()
assert ret['last_process_ts'] == 0
assert ret['last_process'] == '1970-01-01T00:00:00+00:00'

View File

@ -99,7 +99,7 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
"['count'], ['locks'], ['unlock', 'delete_locks'], " "['count'], ['locks'], ['unlock', 'delete_locks'], "
"['reload_config', 'reload_conf'], ['show_config', 'show_conf'], " "['reload_config', 'reload_conf'], ['show_config', 'show_conf'], "
"['stopbuy'], ['whitelist'], ['blacklist'], ['blacklist_delete', 'bl_delete'], " "['stopbuy'], ['whitelist'], ['blacklist'], ['blacklist_delete', 'bl_delete'], "
"['logs'], ['edge'], ['help'], ['version']" "['logs'], ['edge'], ['health'], ['help'], ['version']"
"]") "]")
assert log_has(message_str, caplog) assert log_has(message_str, caplog)