Implement backtesting with fastapi

This commit is contained in:
Matthias 2021-01-02 15:13:32 +01:00
parent df55259737
commit 5c18c8726d
3 changed files with 113 additions and 9 deletions

View File

@ -313,3 +313,19 @@ class PairHistory(BaseModel):
json_encoders = {
datetime: lambda v: v.strftime(DATETIME_PRINT_FORMAT),
}
class BacktestRequest(BaseModel):
strategy: str
timeframe: Optional[str]
timerange: Optional[str]
max_open_trades: Optional[int]
stake_amount: Optional[int]
class BacktestResponse(BaseModel):
status: str
running: bool
status_msg: str
# TODO: Properly type backtestresult...
backtest_result: Optional[Dict[str, Any]]

View File

@ -1,8 +1,10 @@
import asyncio
import logging
from copy import deepcopy
from pathlib import Path
from typing import List, Optional
from fastapi import APIRouter, Depends
from fastapi import APIRouter, BackgroundTasks, Depends
from fastapi.exceptions import HTTPException
from freqtrade import __version__
@ -10,18 +12,21 @@ from freqtrade.constants import USERPATH_STRATEGIES
from freqtrade.data.history import get_datahandler
from freqtrade.exceptions import OperationalException
from freqtrade.rpc import RPC
from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload,
BlacklistResponse, Count, Daily,
DeleteLockRequest, DeleteTrade, ForceBuyPayload,
ForceBuyResponse, ForceSellPayload, Locks, Logs,
OpenTradeSchema, PairHistory, PerformanceEntry,
Ping, PlotConfig, Profit, ResultMsg, ShowConfig,
Stats, StatusMsg, StrategyListResponse,
StrategyResponse, Version, WhitelistResponse)
from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, BacktestRequest, BacktestResponse,
Balances, BlacklistPayload, BlacklistResponse,
Count, Daily, DeleteLockRequest, DeleteTrade,
ForceBuyPayload, ForceBuyResponse,
ForceSellPayload, Locks, Logs, OpenTradeSchema,
PairHistory, PerformanceEntry, Ping, PlotConfig,
Profit, ResultMsg, ShowConfig, Stats, StatusMsg,
StrategyListResponse, StrategyResponse, Version,
WhitelistResponse)
from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional
from freqtrade.rpc.rpc import RPCException
logger = logging.getLogger(__name__)
# Public API, requires no auth.
router_public = APIRouter()
# Private API, protected by authentication
@ -257,3 +262,84 @@ def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Option
}
return result
@router.post('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest'])
async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: BackgroundTasks,
config=Depends(get_config)):
"""Start backtesting if not done so already"""
if ApiServer._bgtask_running:
raise RPCException('Bot Background task already running')
btconfig = deepcopy(config)
settings = dict(bt_settings)
# Pydantic models will contain all keys, but non-provided ones are None
for setting in settings.keys():
if settings[setting] is not None:
btconfig[setting] = settings[setting]
# Start backtesting
# Initialize backtesting object
def run_backtest():
from freqtrade.optimize.backtesting import Backtesting
asyncio.set_event_loop(asyncio.new_event_loop())
try:
ApiServer._backtesting = Backtesting(btconfig)
ApiServer._backtesting.start()
finally:
ApiServer._bgtask_running = False
background_tasks.add_task(run_backtest)
ApiServer._bgtask_running = True
return {
"status": "running",
"running": True,
"status_msg": "Backtest started",
}
@router.get('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest'])
def api_get_backtest():
"""
Get backtesting result.
Returns Result after backtesting has been ran.
"""
if not ApiServer._backtesting:
return {
"status": "not_started",
"running": False,
"status_msg": "Backtesting not yet executed"
}
if ApiServer._bgtask_running:
return {
"status": "running",
"running": True,
"status_msg": "Backtest running",
}
return {
"status": "ended",
"running": False,
"status_msg": "Backtest ended",
"backtest_result": ApiServer._backtesting.results,
}
@router.delete('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest'])
def api_delete_backtest():
"""Reset backtesting"""
if ApiServer._bgtask_running:
return {
"status": "running",
"running": True,
"status_msg": "Backtest running",
}
if ApiServer._backtesting:
del ApiServer._backtesting
ApiServer._backtesting = None
logger.info("Backtesting reset")
return {
"status": "reset",
"running": False,
"status_msg": "Backtesting reset",
}

View File

@ -33,6 +33,8 @@ class ApiServer(RPCHandler):
__initialized = False
_rpc: RPC
# Backtesting type: Backtesting
_backtesting = None
_has_rpc: bool = False
_bgtask_running: bool = False
_config: Dict[str, Any] = {}