From 667d08d003d206d8c8ea15c837bbec12d61d43dd Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 23 Oct 2024 17:57:36 +0200 Subject: [PATCH] refactor: extract pairlist_api from background_tasks file --- .../rpc/api_server/api_background_tasks.py | 133 +--------------- freqtrade/rpc/api_server/api_pairlists.py | 145 ++++++++++++++++++ freqtrade/rpc/api_server/webserver.py | 6 + 3 files changed, 152 insertions(+), 132 deletions(-) create mode 100644 freqtrade/rpc/api_server/api_pairlists.py diff --git a/freqtrade/rpc/api_server/api_background_tasks.py b/freqtrade/rpc/api_server/api_background_tasks.py index 9ce6cbaa6..e7ab2cb75 100644 --- a/freqtrade/rpc/api_server/api_background_tasks.py +++ b/freqtrade/rpc/api_server/api_background_tasks.py @@ -1,22 +1,11 @@ import logging -from copy import deepcopy -from fastapi import APIRouter, BackgroundTasks, Depends +from fastapi import APIRouter from fastapi.exceptions import HTTPException -from freqtrade.constants import Config -from freqtrade.enums import CandleType -from freqtrade.exceptions import OperationalException -from freqtrade.persistence import FtNoDBContext from freqtrade.rpc.api_server.api_schemas import ( BackgroundTaskStatus, - BgJobStarted, - ExchangeModePayloadMixin, - PairListsPayload, - PairListsResponse, - WhitelistEvaluateResponse, ) -from freqtrade.rpc.api_server.deps import get_config, get_exchange from freqtrade.rpc.api_server.webserver_bgwork import ApiBG @@ -54,123 +43,3 @@ def background_job(jobid: str): "progress": job.get("progress"), "error": job.get("error", None), } - - -@router.get( - "/pairlists/available", response_model=PairListsResponse, tags=["pairlists", "webserver"] -) -def list_pairlists(config=Depends(get_config)): - from freqtrade.resolvers import PairListResolver - - pairlists = PairListResolver.search_all_objects(config, False) - pairlists = sorted(pairlists, key=lambda x: x["name"]) - - return { - "pairlists": [ - { - "name": x["name"], - "is_pairlist_generator": x["class"].is_pairlist_generator, - "params": x["class"].available_parameters(), - "description": x["class"].description(), - } - for x in pairlists - ] - } - - -def __run_pairlist(job_id: str, config_loc: Config): - try: - ApiBG.jobs[job_id]["is_running"] = True - from freqtrade.plugins.pairlistmanager import PairListManager - - with FtNoDBContext(): - exchange = get_exchange(config_loc) - pairlists = PairListManager(exchange, config_loc) - pairlists.refresh_pairlist() - ApiBG.jobs[job_id]["result"] = { - "method": pairlists.name_list, - "length": len(pairlists.whitelist), - "whitelist": pairlists.whitelist, - } - ApiBG.jobs[job_id]["status"] = "success" - except (OperationalException, Exception) as e: - logger.exception(e) - ApiBG.jobs[job_id]["error"] = str(e) - ApiBG.jobs[job_id]["status"] = "failed" - finally: - ApiBG.jobs[job_id]["is_running"] = False - ApiBG.pairlist_running = False - - -@router.post("/pairlists/evaluate", response_model=BgJobStarted, tags=["pairlists", "webserver"]) -def pairlists_evaluate( - payload: PairListsPayload, background_tasks: BackgroundTasks, config=Depends(get_config) -): - if ApiBG.pairlist_running: - raise HTTPException(status_code=400, detail="Pairlist evaluation is already running.") - - config_loc = deepcopy(config) - config_loc["stake_currency"] = payload.stake_currency - config_loc["pairlists"] = payload.pairlists - handleExchangePayload(payload, config_loc) - # TODO: overwrite blacklist? make it optional and fall back to the one in config? - # Outcome depends on the UI approach. - config_loc["exchange"]["pair_blacklist"] = payload.blacklist - # Random job id - job_id = ApiBG.get_job_id() - - ApiBG.jobs[job_id] = { - "category": "pairlist", - "status": "pending", - "progress": None, - "is_running": False, - "result": {}, - "error": None, - } - background_tasks.add_task(__run_pairlist, job_id, config_loc) - ApiBG.pairlist_running = True - - return { - "status": "Pairlist evaluation started in background.", - "job_id": job_id, - } - - -def handleExchangePayload(payload: ExchangeModePayloadMixin, config_loc: Config): - """ - Handle exchange and trading mode payload. - Updates the configuration with the payload values. - """ - if payload.exchange: - config_loc["exchange"]["name"] = payload.exchange - if payload.trading_mode: - config_loc["trading_mode"] = payload.trading_mode - config_loc["candle_type_def"] = CandleType.get_default( - config_loc.get("trading_mode", "spot") or "spot" - ) - if payload.margin_mode: - config_loc["margin_mode"] = payload.margin_mode - - -@router.get( - "/pairlists/evaluate/{jobid}", - response_model=WhitelistEvaluateResponse, - tags=["pairlists", "webserver"], -) -def pairlists_evaluate_get(jobid: str): - if not (job := ApiBG.jobs.get(jobid)): - raise HTTPException(status_code=404, detail="Job not found.") - - if job["is_running"]: - raise HTTPException(status_code=400, detail="Job not finished yet.") - - if error := job["error"]: - return { - "status": "failed", - "error": error, - } - - return { - "status": "success", - "result": job["result"], - } diff --git a/freqtrade/rpc/api_server/api_pairlists.py b/freqtrade/rpc/api_server/api_pairlists.py new file mode 100644 index 000000000..75467c28b --- /dev/null +++ b/freqtrade/rpc/api_server/api_pairlists.py @@ -0,0 +1,145 @@ +import logging +from copy import deepcopy + +from fastapi import APIRouter, BackgroundTasks, Depends +from fastapi.exceptions import HTTPException + +from freqtrade.constants import Config +from freqtrade.enums import CandleType +from freqtrade.exceptions import OperationalException +from freqtrade.persistence import FtNoDBContext +from freqtrade.rpc.api_server.api_schemas import ( + BgJobStarted, + ExchangeModePayloadMixin, + PairListsPayload, + PairListsResponse, + WhitelistEvaluateResponse, +) +from freqtrade.rpc.api_server.deps import get_config, get_exchange +from freqtrade.rpc.api_server.webserver_bgwork import ApiBG + + +logger = logging.getLogger(__name__) + +# Private API, protected by authentication and webserver_mode dependency +router = APIRouter() + + +@router.get( + "/pairlists/available", response_model=PairListsResponse, tags=["pairlists", "webserver"] +) +def list_pairlists(config=Depends(get_config)): + from freqtrade.resolvers import PairListResolver + + pairlists = PairListResolver.search_all_objects(config, False) + pairlists = sorted(pairlists, key=lambda x: x["name"]) + + return { + "pairlists": [ + { + "name": x["name"], + "is_pairlist_generator": x["class"].is_pairlist_generator, + "params": x["class"].available_parameters(), + "description": x["class"].description(), + } + for x in pairlists + ] + } + + +def __run_pairlist(job_id: str, config_loc: Config): + try: + ApiBG.jobs[job_id]["is_running"] = True + from freqtrade.plugins.pairlistmanager import PairListManager + + with FtNoDBContext(): + exchange = get_exchange(config_loc) + pairlists = PairListManager(exchange, config_loc) + pairlists.refresh_pairlist() + ApiBG.jobs[job_id]["result"] = { + "method": pairlists.name_list, + "length": len(pairlists.whitelist), + "whitelist": pairlists.whitelist, + } + ApiBG.jobs[job_id]["status"] = "success" + except (OperationalException, Exception) as e: + logger.exception(e) + ApiBG.jobs[job_id]["error"] = str(e) + ApiBG.jobs[job_id]["status"] = "failed" + finally: + ApiBG.jobs[job_id]["is_running"] = False + ApiBG.pairlist_running = False + + +@router.post("/pairlists/evaluate", response_model=BgJobStarted, tags=["pairlists", "webserver"]) +def pairlists_evaluate( + payload: PairListsPayload, background_tasks: BackgroundTasks, config=Depends(get_config) +): + if ApiBG.pairlist_running: + raise HTTPException(status_code=400, detail="Pairlist evaluation is already running.") + + config_loc = deepcopy(config) + config_loc["stake_currency"] = payload.stake_currency + config_loc["pairlists"] = payload.pairlists + handleExchangePayload(payload, config_loc) + # TODO: overwrite blacklist? make it optional and fall back to the one in config? + # Outcome depends on the UI approach. + config_loc["exchange"]["pair_blacklist"] = payload.blacklist + # Random job id + job_id = ApiBG.get_job_id() + + ApiBG.jobs[job_id] = { + "category": "pairlist", + "status": "pending", + "progress": None, + "is_running": False, + "result": {}, + "error": None, + } + background_tasks.add_task(__run_pairlist, job_id, config_loc) + ApiBG.pairlist_running = True + + return { + "status": "Pairlist evaluation started in background.", + "job_id": job_id, + } + + +def handleExchangePayload(payload: ExchangeModePayloadMixin, config_loc: Config): + """ + Handle exchange and trading mode payload. + Updates the configuration with the payload values. + """ + if payload.exchange: + config_loc["exchange"]["name"] = payload.exchange + if payload.trading_mode: + config_loc["trading_mode"] = payload.trading_mode + config_loc["candle_type_def"] = CandleType.get_default( + config_loc.get("trading_mode", "spot") or "spot" + ) + if payload.margin_mode: + config_loc["margin_mode"] = payload.margin_mode + + +@router.get( + "/pairlists/evaluate/{jobid}", + response_model=WhitelistEvaluateResponse, + tags=["pairlists", "webserver"], +) +def pairlists_evaluate_get(jobid: str): + if not (job := ApiBG.jobs.get(jobid)): + raise HTTPException(status_code=404, detail="Job not found.") + + if job["is_running"]: + raise HTTPException(status_code=400, detail="Job not finished yet.") + + if error := job["error"]: + return { + "status": "failed", + "error": error, + } + + return { + "status": "success", + "result": job["result"], + } diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index 57f321739..c96db9981 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -116,6 +116,7 @@ class ApiServer(RPCHandler): from freqtrade.rpc.api_server.api_auth import http_basic_or_jwt_token, router_login from freqtrade.rpc.api_server.api_background_tasks import router as api_bg_tasks from freqtrade.rpc.api_server.api_backtest import router as api_backtest + from freqtrade.rpc.api_server.api_pairlists import router as api_pairlists from freqtrade.rpc.api_server.api_v1 import router as api_v1 from freqtrade.rpc.api_server.api_v1 import router_public as api_v1_public from freqtrade.rpc.api_server.api_ws import router as ws_router @@ -140,6 +141,11 @@ class ApiServer(RPCHandler): prefix="/api/v1", dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], ) + app.include_router( + api_pairlists, + prefix="/api/v1", + dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], + ) app.include_router(ws_router, prefix="/api/v1") # UI Router MUST be last! app.include_router(router_ui, prefix="")