mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
Simplify api backtesting by extracting the background_method
This commit is contained in:
@@ -8,6 +8,7 @@ from fastapi import APIRouter, BackgroundTasks, Depends
|
|||||||
from fastapi.exceptions import HTTPException
|
from fastapi.exceptions import HTTPException
|
||||||
|
|
||||||
from freqtrade.configuration.config_validation import validate_config_consistency
|
from freqtrade.configuration.config_validation import validate_config_consistency
|
||||||
|
from freqtrade.constants import Config
|
||||||
from freqtrade.data.btanalysis import get_backtest_resultlist, load_and_merge_backtest_result
|
from freqtrade.data.btanalysis import get_backtest_resultlist, load_and_merge_backtest_result
|
||||||
from freqtrade.enums import BacktestState
|
from freqtrade.enums import BacktestState
|
||||||
from freqtrade.exceptions import DependencyException, OperationalException
|
from freqtrade.exceptions import DependencyException, OperationalException
|
||||||
@@ -26,8 +27,80 @@ logger = logging.getLogger(__name__)
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
def __run_backtest_bg(btconfig: Config):
|
||||||
|
from freqtrade.optimize.optimize_reports import generate_backtest_stats, store_backtest_stats
|
||||||
|
from freqtrade.resolvers import StrategyResolver
|
||||||
|
asyncio.set_event_loop(asyncio.new_event_loop())
|
||||||
|
try:
|
||||||
|
# Reload strategy
|
||||||
|
lastconfig = ApiBG.bt['last_config']
|
||||||
|
strat = StrategyResolver.load_strategy(btconfig)
|
||||||
|
validate_config_consistency(btconfig)
|
||||||
|
|
||||||
|
if (
|
||||||
|
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
|
||||||
|
ApiBG.bt['bt'] = Backtesting(btconfig)
|
||||||
|
ApiBG.bt['bt'].load_bt_data_detail()
|
||||||
|
else:
|
||||||
|
ApiBG.bt['bt'].config = btconfig
|
||||||
|
ApiBG.bt['bt'].init_backtest()
|
||||||
|
# Only reload data if timeframe changed.
|
||||||
|
if (
|
||||||
|
not ApiBG.bt['data']
|
||||||
|
or not ApiBG.bt['timerange']
|
||||||
|
or lastconfig.get('timeframe') != strat.timeframe
|
||||||
|
or lastconfig.get('timerange') != btconfig['timerange']
|
||||||
|
):
|
||||||
|
ApiBG.bt['data'], ApiBG.bt['timerange'] = ApiBG.bt[
|
||||||
|
'bt'].load_bt_data()
|
||||||
|
|
||||||
|
lastconfig['timerange'] = btconfig['timerange']
|
||||||
|
lastconfig['timeframe'] = strat.timeframe
|
||||||
|
lastconfig['protections'] = btconfig.get('protections', [])
|
||||||
|
lastconfig['enable_protections'] = btconfig.get('enable_protections')
|
||||||
|
lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet')
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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 = ApiBG.bt['bt'].backtest_one_strategy(
|
||||||
|
strat, ApiBG.bt['data'], ApiBG.bt['timerange'])
|
||||||
|
|
||||||
|
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'], ApiBG.bt['bt'].results,
|
||||||
|
datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.info("Backtest finished.")
|
||||||
|
|
||||||
|
except (Exception, OperationalException, DependencyException) as e:
|
||||||
|
logger.exception(f"Backtesting caused an error: {e}")
|
||||||
|
ApiBG.bt['bt_error'] = str(e)
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
ApiBG.bgtask_running = False
|
||||||
|
|
||||||
|
|
||||||
@router.post('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest'])
|
@router.post('/backtest', response_model=BacktestResponse, tags=['webserver', 'backtest'])
|
||||||
async def api_start_backtest( # noqa: C901
|
async def api_start_backtest(
|
||||||
bt_settings: BacktestRequest, background_tasks: BackgroundTasks,
|
bt_settings: BacktestRequest, background_tasks: BackgroundTasks,
|
||||||
config=Depends(get_config), ws_mode=Depends(is_webserver_mode)):
|
config=Depends(get_config), ws_mode=Depends(is_webserver_mode)):
|
||||||
ApiBG.bt['bt_error'] = None
|
ApiBG.bt['bt_error'] = None
|
||||||
@@ -56,79 +129,8 @@ async def api_start_backtest( # noqa: C901
|
|||||||
|
|
||||||
# Start backtesting
|
# Start backtesting
|
||||||
# Initialize backtesting object
|
# Initialize backtesting object
|
||||||
def run_backtest():
|
|
||||||
from freqtrade.optimize.optimize_reports import (generate_backtest_stats,
|
|
||||||
store_backtest_stats)
|
|
||||||
from freqtrade.resolvers import StrategyResolver
|
|
||||||
asyncio.set_event_loop(asyncio.new_event_loop())
|
|
||||||
try:
|
|
||||||
# Reload strategy
|
|
||||||
lastconfig = ApiBG.bt['last_config']
|
|
||||||
strat = StrategyResolver.load_strategy(btconfig)
|
|
||||||
validate_config_consistency(btconfig)
|
|
||||||
|
|
||||||
if (
|
background_tasks.add_task(__run_backtest_bg, btconfig=btconfig)
|
||||||
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
|
|
||||||
ApiBG.bt['bt'] = Backtesting(btconfig)
|
|
||||||
ApiBG.bt['bt'].load_bt_data_detail()
|
|
||||||
else:
|
|
||||||
ApiBG.bt['bt'].config = btconfig
|
|
||||||
ApiBG.bt['bt'].init_backtest()
|
|
||||||
# Only reload data if timeframe changed.
|
|
||||||
if (
|
|
||||||
not ApiBG.bt['data']
|
|
||||||
or not ApiBG.bt['timerange']
|
|
||||||
or lastconfig.get('timeframe') != strat.timeframe
|
|
||||||
or lastconfig.get('timerange') != btconfig['timerange']
|
|
||||||
):
|
|
||||||
ApiBG.bt['data'], ApiBG.bt['timerange'] = ApiBG.bt[
|
|
||||||
'bt'].load_bt_data()
|
|
||||||
|
|
||||||
lastconfig['timerange'] = btconfig['timerange']
|
|
||||||
lastconfig['timeframe'] = strat.timeframe
|
|
||||||
lastconfig['protections'] = btconfig.get('protections', [])
|
|
||||||
lastconfig['enable_protections'] = btconfig.get('enable_protections')
|
|
||||||
lastconfig['dry_run_wallet'] = btconfig.get('dry_run_wallet')
|
|
||||||
|
|
||||||
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()
|
|
||||||
|
|
||||||
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 = ApiBG.bt['bt'].backtest_one_strategy(
|
|
||||||
strat, ApiBG.bt['data'], ApiBG.bt['timerange'])
|
|
||||||
|
|
||||||
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'], ApiBG.bt['bt'].results,
|
|
||||||
datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info("Backtest finished.")
|
|
||||||
|
|
||||||
except (Exception, OperationalException, DependencyException) as e:
|
|
||||||
logger.exception(f"Backtesting caused an error: {e}")
|
|
||||||
ApiBG.bt['bt_error'] = str(e)
|
|
||||||
pass
|
|
||||||
finally:
|
|
||||||
ApiBG.bgtask_running = False
|
|
||||||
|
|
||||||
background_tasks.add_task(run_backtest)
|
|
||||||
ApiBG.bgtask_running = True
|
ApiBG.bgtask_running = True
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user