mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
Extract api backtest logic from ApiServer class
This commit is contained in:
parent
fcb75560c4
commit
5316227219
|
@ -16,7 +16,7 @@ from freqtrade.misc import deep_merge_dicts
|
|||
from freqtrade.rpc.api_server.api_schemas import (BacktestHistoryEntry, BacktestRequest,
|
||||
BacktestResponse)
|
||||
from freqtrade.rpc.api_server.deps import get_config, is_webserver_mode
|
||||
from freqtrade.rpc.api_server.webserver import ApiServer
|
||||
from freqtrade.rpc.api_server.webserver_bgwork import ApiBG
|
||||
from freqtrade.rpc.rpc import RPCException
|
||||
|
||||
|
||||
|
@ -30,9 +30,9 @@ router = APIRouter()
|
|||
async def api_start_backtest( # noqa: C901
|
||||
bt_settings: BacktestRequest, background_tasks: BackgroundTasks,
|
||||
config=Depends(get_config), ws_mode=Depends(is_webserver_mode)):
|
||||
ApiServer._bt['bt_error'] = None
|
||||
ApiBG._bt['bt_error'] = None
|
||||
"""Start backtesting if not done so already"""
|
||||
if ApiServer._bgtask_running:
|
||||
if ApiBG._bgtask_running:
|
||||
raise RPCException('Bot Background task already running')
|
||||
|
||||
if ':' in bt_settings.strategy:
|
||||
|
@ -63,30 +63,30 @@ async def api_start_backtest( # noqa: C901
|
|||
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||
try:
|
||||
# Reload strategy
|
||||
lastconfig = ApiServer._bt['last_config']
|
||||
lastconfig = ApiBG._bt['last_config']
|
||||
strat = StrategyResolver.load_strategy(btconfig)
|
||||
validate_config_consistency(btconfig)
|
||||
|
||||
if (
|
||||
not ApiServer._bt['bt']
|
||||
not ApiBG._bt['bt']
|
||||
or lastconfig.get('timeframe') != strat.timeframe
|
||||
or lastconfig.get('timeframe_detail') != btconfig.get('timeframe_detail')
|
||||
or lastconfig.get('timerange') != btconfig['timerange']
|
||||
):
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
ApiServer._bt['bt'] = Backtesting(btconfig)
|
||||
ApiServer._bt['bt'].load_bt_data_detail()
|
||||
ApiBG._bt['bt'] = Backtesting(btconfig)
|
||||
ApiBG._bt['bt'].load_bt_data_detail()
|
||||
else:
|
||||
ApiServer._bt['bt'].config = btconfig
|
||||
ApiServer._bt['bt'].init_backtest()
|
||||
ApiBG._bt['bt'].config = btconfig
|
||||
ApiBG._bt['bt'].init_backtest()
|
||||
# Only reload data if timeframe changed.
|
||||
if (
|
||||
not ApiServer._bt['data']
|
||||
or not ApiServer._bt['timerange']
|
||||
not ApiBG._bt['data']
|
||||
or not ApiBG._bt['timerange']
|
||||
or lastconfig.get('timeframe') != strat.timeframe
|
||||
or lastconfig.get('timerange') != btconfig['timerange']
|
||||
):
|
||||
ApiServer._bt['data'], ApiServer._bt['timerange'] = ApiServer._bt[
|
||||
ApiBG._bt['data'], ApiBG._bt['timerange'] = ApiBG._bt[
|
||||
'bt'].load_bt_data()
|
||||
|
||||
lastconfig['timerange'] = btconfig['timerange']
|
||||
|
@ -95,27 +95,27 @@ async def api_start_backtest( # noqa: C901
|
|||
lastconfig['enable_protections'] = btconfig.get('enable_protections')
|
||||
lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet')
|
||||
|
||||
ApiServer._bt['bt'].enable_protections = btconfig.get('enable_protections', False)
|
||||
ApiServer._bt['bt'].strategylist = [strat]
|
||||
ApiServer._bt['bt'].results = {}
|
||||
ApiServer._bt['bt'].load_prior_backtest()
|
||||
ApiBG._bt['bt'].enable_protections = btconfig.get('enable_protections', False)
|
||||
ApiBG._bt['bt'].strategylist = [strat]
|
||||
ApiBG._bt['bt'].results = {}
|
||||
ApiBG._bt['bt'].load_prior_backtest()
|
||||
|
||||
ApiServer._bt['bt'].abort = False
|
||||
if (ApiServer._bt['bt'].results and
|
||||
strat.get_strategy_name() in ApiServer._bt['bt'].results['strategy']):
|
||||
ApiBG._bt['bt'].abort = False
|
||||
if (ApiBG._bt['bt'].results and
|
||||
strat.get_strategy_name() in ApiBG._bt['bt'].results['strategy']):
|
||||
# When previous result hash matches - reuse that result and skip backtesting.
|
||||
logger.info(f'Reusing result of previous backtest for {strat.get_strategy_name()}')
|
||||
else:
|
||||
min_date, max_date = ApiServer._bt['bt'].backtest_one_strategy(
|
||||
strat, ApiServer._bt['data'], ApiServer._bt['timerange'])
|
||||
min_date, max_date = ApiBG._bt['bt'].backtest_one_strategy(
|
||||
strat, ApiBG._bt['data'], ApiBG._bt['timerange'])
|
||||
|
||||
ApiServer._bt['bt'].results = generate_backtest_stats(
|
||||
ApiServer._bt['data'], ApiServer._bt['bt'].all_results,
|
||||
ApiBG._bt['bt'].results = generate_backtest_stats(
|
||||
ApiBG._bt['data'], ApiBG._bt['bt'].all_results,
|
||||
min_date=min_date, max_date=max_date)
|
||||
|
||||
if btconfig.get('export', 'none') == 'trades':
|
||||
store_backtest_stats(
|
||||
btconfig['exportfilename'], ApiServer._bt['bt'].results,
|
||||
btconfig['exportfilename'], ApiBG._bt['bt'].results,
|
||||
datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||
)
|
||||
|
||||
|
@ -123,13 +123,13 @@ async def api_start_backtest( # noqa: C901
|
|||
|
||||
except (Exception, OperationalException, DependencyException) as e:
|
||||
logger.exception(f"Backtesting caused an error: {e}")
|
||||
ApiServer._bt['bt_error'] = str(e)
|
||||
ApiBG._bt['bt_error'] = str(e)
|
||||
pass
|
||||
finally:
|
||||
ApiServer._bgtask_running = False
|
||||
ApiBG._bgtask_running = False
|
||||
|
||||
background_tasks.add_task(run_backtest)
|
||||
ApiServer._bgtask_running = True
|
||||
ApiBG._bgtask_running = True
|
||||
|
||||
return {
|
||||
"status": "running",
|
||||
|
@ -147,18 +147,18 @@ def api_get_backtest(ws_mode=Depends(is_webserver_mode)):
|
|||
Returns Result after backtesting has been ran.
|
||||
"""
|
||||
from freqtrade.persistence import LocalTrade
|
||||
if ApiServer._bgtask_running:
|
||||
if ApiBG._bgtask_running:
|
||||
return {
|
||||
"status": "running",
|
||||
"running": True,
|
||||
"step": (ApiServer._bt['bt'].progress.action if ApiServer._bt['bt']
|
||||
"step": (ApiBG._bt['bt'].progress.action if ApiBG._bt['bt']
|
||||
else str(BacktestState.STARTUP)),
|
||||
"progress": ApiServer._bt['bt'].progress.progress if ApiServer._bt['bt'] else 0,
|
||||
"progress": ApiBG._bt['bt'].progress.progress if ApiBG._bt['bt'] else 0,
|
||||
"trade_count": len(LocalTrade.trades),
|
||||
"status_msg": "Backtest running",
|
||||
}
|
||||
|
||||
if not ApiServer._bt['bt']:
|
||||
if not ApiBG._bt['bt']:
|
||||
return {
|
||||
"status": "not_started",
|
||||
"running": False,
|
||||
|
@ -166,13 +166,13 @@ def api_get_backtest(ws_mode=Depends(is_webserver_mode)):
|
|||
"progress": 0,
|
||||
"status_msg": "Backtest not yet executed"
|
||||
}
|
||||
if ApiServer._bt['bt_error']:
|
||||
if ApiBG._bt['bt_error']:
|
||||
return {
|
||||
"status": "error",
|
||||
"running": False,
|
||||
"step": "",
|
||||
"progress": 0,
|
||||
"status_msg": f"Backtest failed with {ApiServer._bt['bt_error']}"
|
||||
"status_msg": f"Backtest failed with {ApiBG._bt['bt_error']}"
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -181,14 +181,14 @@ def api_get_backtest(ws_mode=Depends(is_webserver_mode)):
|
|||
"status_msg": "Backtest ended",
|
||||
"step": "finished",
|
||||
"progress": 1,
|
||||
"backtest_result": ApiServer._bt['bt'].results,
|
||||
"backtest_result": ApiBG._bt['bt'].results,
|
||||
}
|
||||
|
||||
|
||||
@router.delete('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest'])
|
||||
def api_delete_backtest(ws_mode=Depends(is_webserver_mode)):
|
||||
"""Reset backtesting"""
|
||||
if ApiServer._bgtask_running:
|
||||
if ApiBG._bgtask_running:
|
||||
return {
|
||||
"status": "running",
|
||||
"running": True,
|
||||
|
@ -196,12 +196,12 @@ def api_delete_backtest(ws_mode=Depends(is_webserver_mode)):
|
|||
"progress": 0,
|
||||
"status_msg": "Backtest running",
|
||||
}
|
||||
if ApiServer._bt['bt']:
|
||||
ApiServer._bt['bt'].cleanup()
|
||||
del ApiServer._bt['bt']
|
||||
ApiServer._bt['bt'] = None
|
||||
del ApiServer._bt['data']
|
||||
ApiServer._bt['data'] = None
|
||||
if ApiBG._bt['bt']:
|
||||
ApiBG._bt['bt'].cleanup()
|
||||
del ApiBG._bt['bt']
|
||||
ApiBG._bt['bt'] = None
|
||||
del ApiBG._bt['data']
|
||||
ApiBG._bt['data'] = None
|
||||
logger.info("Backtesting reset")
|
||||
return {
|
||||
"status": "reset",
|
||||
|
@ -214,7 +214,7 @@ def api_delete_backtest(ws_mode=Depends(is_webserver_mode)):
|
|||
|
||||
@router.get('/backtest/abort', response_model=BacktestResponse, tags=['webserver', 'backtest'])
|
||||
def api_backtest_abort(ws_mode=Depends(is_webserver_mode)):
|
||||
if not ApiServer._bgtask_running:
|
||||
if not ApiBG._bgtask_running:
|
||||
return {
|
||||
"status": "not_running",
|
||||
"running": False,
|
||||
|
@ -222,7 +222,7 @@ def api_backtest_abort(ws_mode=Depends(is_webserver_mode)):
|
|||
"progress": 0,
|
||||
"status_msg": "Backtest ended",
|
||||
}
|
||||
ApiServer._bt['bt'].abort = True
|
||||
ApiBG._bt['bt'].abort = True
|
||||
return {
|
||||
"status": "stopping",
|
||||
"running": False,
|
||||
|
|
|
@ -6,6 +6,7 @@ from fastapi import Depends
|
|||
from freqtrade.enums import RunMode
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.persistence.models import _request_id_ctx_var
|
||||
from freqtrade.rpc.api_server.webserver_bgwork import ApiBG
|
||||
from freqtrade.rpc.rpc import RPC, RPCException
|
||||
|
||||
from .webserver import ApiServer
|
||||
|
@ -43,11 +44,11 @@ def get_api_config() -> Dict[str, Any]:
|
|||
|
||||
|
||||
def get_exchange(config=Depends(get_config)):
|
||||
if not ApiServer._exchange:
|
||||
if not ApiBG._exchange:
|
||||
from freqtrade.resolvers import ExchangeResolver
|
||||
ApiServer._exchange = ExchangeResolver.load_exchange(
|
||||
ApiBG._exchange = ExchangeResolver.load_exchange(
|
||||
config, load_leverage_tiers=False)
|
||||
return ApiServer._exchange
|
||||
return ApiBG._exchange
|
||||
|
||||
|
||||
def get_message_stream():
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import logging
|
||||
from ipaddress import IPv4Address
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
import orjson
|
||||
import uvicorn
|
||||
|
@ -36,19 +36,8 @@ class ApiServer(RPCHandler):
|
|||
__initialized = False
|
||||
|
||||
_rpc: RPC
|
||||
# Backtesting type: Backtesting
|
||||
_bt: Dict[str, Any] = {
|
||||
'bt': None,
|
||||
'data': None,
|
||||
'timerange': None,
|
||||
'last_config': {},
|
||||
'bt_error': None,
|
||||
}
|
||||
_has_rpc: bool = False
|
||||
_bgtask_running: bool = False
|
||||
_config: Config = {}
|
||||
# Exchange - only available in webserver mode.
|
||||
_exchange = None
|
||||
# websocket message stuff
|
||||
_message_stream: Optional[MessageStream] = None
|
||||
|
||||
|
@ -85,7 +74,7 @@ class ApiServer(RPCHandler):
|
|||
"""
|
||||
Attach rpc handler
|
||||
"""
|
||||
if not self._has_rpc:
|
||||
if not ApiServer._has_rpc:
|
||||
ApiServer._rpc = rpc
|
||||
ApiServer._has_rpc = True
|
||||
else:
|
||||
|
|
16
freqtrade/rpc/api_server/webserver_bgwork.py
Normal file
16
freqtrade/rpc/api_server/webserver_bgwork.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
class ApiBG():
|
||||
# Backtesting type: Backtesting
|
||||
_bt: Dict[str, Any] = {
|
||||
'bt': None,
|
||||
'data': None,
|
||||
'timerange': None,
|
||||
'last_config': {},
|
||||
'bt_error': None,
|
||||
}
|
||||
_bgtask_running: bool = False
|
||||
# Exchange - only available in webserver mode.
|
||||
_exchange = None
|
|
@ -26,6 +26,7 @@ from freqtrade.rpc import RPC
|
|||
from freqtrade.rpc.api_server import ApiServer
|
||||
from freqtrade.rpc.api_server.api_auth import create_token, get_user_from_token
|
||||
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
|
||||
from freqtrade.rpc.api_server.webserver_bgwork import ApiBG
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, EXMS, create_mock_trades, get_mock_coro,
|
||||
get_patched_freqtradebot, log_has, log_has_re, patch_get_signal)
|
||||
|
||||
|
@ -1733,7 +1734,7 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir):
|
|||
assert result['status_msg'] == 'Backtest ended'
|
||||
|
||||
# Simulate running backtest
|
||||
ApiServer._bgtask_running = True
|
||||
ApiBG._bgtask_running = True
|
||||
rc = client_get(client, f"{BASE_URI}/backtest/abort")
|
||||
assert_response(rc)
|
||||
result = rc.json()
|
||||
|
@ -1762,7 +1763,7 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir):
|
|||
result = rc.json()
|
||||
assert 'Bot Background task already running' in result['error']
|
||||
|
||||
ApiServer._bgtask_running = False
|
||||
ApiBG._bgtask_running = False
|
||||
|
||||
# Rerun backtest (should get previous result)
|
||||
rc = client_post(client, f"{BASE_URI}/backtest", data=data)
|
||||
|
|
Loading…
Reference in New Issue
Block a user