From 02b84bd0188df372a1346a95e6c84b6cc2b4b2c1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 31 Dec 2020 20:02:27 +0100 Subject: [PATCH] Introduce webserver mode for fastapi --- freqtrade/commands/trade_commands.py | 10 +++++--- freqtrade/enums/runmode.py | 1 + freqtrade/rpc/api_server/webserver.py | 35 ++++++++++++++++++++++++--- freqtrade/rpc/rpc_manager.py | 7 +++--- 4 files changed, 43 insertions(+), 10 deletions(-) diff --git a/freqtrade/commands/trade_commands.py b/freqtrade/commands/trade_commands.py index 69ea5998f..ca1451778 100644 --- a/freqtrade/commands/trade_commands.py +++ b/freqtrade/commands/trade_commands.py @@ -1,7 +1,6 @@ import logging from typing import Any, Dict - logger = logging.getLogger(__name__) @@ -29,9 +28,14 @@ def start_trading(args: Dict[str, Any]) -> int: return 0 -def start_webserver(args: Dict[str, Any]) -> int: +def start_webserver(args: Dict[str, Any]) -> None: """ Main entry point for webserver mode """ + from freqtrade.rpc.api_server import ApiServer + from freqtrade.configuration import Configuration + from freqtrade.enums import RunMode - print(args) + # Initialize configuration + config = Configuration(args, RunMode.WEBSERVER).get_config() + ApiServer(config, standalone=True) diff --git a/freqtrade/enums/runmode.py b/freqtrade/enums/runmode.py index 7826d1d0c..6545aaec7 100644 --- a/freqtrade/enums/runmode.py +++ b/freqtrade/enums/runmode.py @@ -14,6 +14,7 @@ class RunMode(Enum): UTIL_EXCHANGE = "util_exchange" UTIL_NO_EXCHANGE = "util_no_exchange" PLOT = "plot" + WEBSERVER = "webserver" OTHER = "other" diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index a43d4abe6..9cbd5512a 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -8,6 +8,7 @@ from fastapi import Depends, FastAPI from fastapi.middleware.cors import CORSMiddleware from starlette.responses import JSONResponse +from freqtrade.exceptions import OperationalException from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer from freqtrade.rpc.rpc import RPC, RPCException, RPCHandler @@ -28,16 +29,30 @@ class FTJSONResponse(JSONResponse): class ApiServer(RPCHandler): + __instance = None + __initialized = False + _rpc: RPC _has_rpc: bool = False _config: Dict[str, Any] = {} - def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None: - super().__init__(rpc, config) + def __new__(cls, *args, **kwargs): + """ + This class is a singleton. + We'll only have one instance of it around. + """ + if ApiServer.__instance is None: + ApiServer.__instance = object.__new__(cls) + ApiServer.__initialized = False + return ApiServer.__instance + + def __init__(self, config: Dict[str, Any], standalone: bool = False) -> None: + if self.__initialized and (standalone or self._standalone): + return + self._standalone: bool = standalone + self._config = config self._server = None - ApiServer._rpc = rpc - ApiServer._has_rpc = True ApiServer._config = config api_config = self._config['api_server'] @@ -50,6 +65,18 @@ class ApiServer(RPCHandler): self.start_api() + def add_rpc_handler(self, rpc: RPC): + """ + Attach rpc handler + """ + if not self._rpc: + self._rpc = rpc + ApiServer._rpc = rpc + ApiServer._has_rpc = True + else: + # This should not happen assuming we didn't mess up. + raise OperationalException('RPC Handler already attached.') + def cleanup(self) -> None: """ Cleanup pending module resources """ if self._server: diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index 18ed68041..8814f70a0 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -36,15 +36,16 @@ class RPCManager: if config.get('api_server', {}).get('enabled', False): logger.info('Enabling rpc.api_server') from freqtrade.rpc.api_server import ApiServer - - self.registered_modules.append(ApiServer(self._rpc, config)) + apiserver = ApiServer(config) + apiserver.add_rpc_handler(self._rpc) + self.registered_modules.append(apiserver) def cleanup(self) -> None: """ Stops all enabled rpc modules """ logger.info('Cleaning up rpc modules ...') while self.registered_modules: mod = self.registered_modules.pop() - logger.debug('Cleaning up rpc.%s ...', mod.name) + logger.info('Cleaning up rpc.%s ...', mod.name) mod.cleanup() del mod