Add /health endpoint that returns last_process timestamp, fix issue #6009

This commit is contained in:
Reigo Reinmets 2022-01-23 21:58:46 +02:00
parent 451eca51c8
commit bf62fc9b25
7 changed files with 59 additions and 7 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.utcfromtimestamp(0.0)
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.utcnow()
def process_stopped(self) -> None: def process_stopped(self) -> None:
""" """

View File

@ -382,3 +382,7 @@ 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

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,
OpenTradeSchema, PairHistory, PerformanceEntry, Locks, 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
@ -290,3 +290,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

@ -1030,3 +1030,8 @@ 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, str]:
return {
'last_process': str(self._freqtrade.last_process)
}

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,20 @@ 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']}`"
logger.debug(message)
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

@ -1272,3 +1272,12 @@ 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)
ret = rpc._health()
assert ret['last_process'] == '1970-01-01 00:00:00'

View File

@ -1442,3 +1442,13 @@ 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'] == '1970-01-01T00:00:00'