From 5dbd5c235af3fae7b06a6fd1accac099f9fe007a Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 07:21:52 +0100 Subject: [PATCH 1/7] Add endpoint for freqAI models --- freqtrade/rpc/api_server/api_schemas.py | 5 +++++ freqtrade/rpc/api_server/api_v1.py | 26 ++++++++++++++++++------- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index ada20230a..2100a6fe2 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -372,6 +372,10 @@ class StrategyListResponse(BaseModel): strategies: List[str] +class FreqAIModelListResponse(BaseModel): + freqaimodels: List[str] + + class StrategyResponse(BaseModel): strategy: str code: str @@ -419,6 +423,7 @@ class BacktestRequest(BaseModel): stake_amount: Optional[str] enable_protections: bool dry_run_wallet: Optional[float] + freqaimodel: Optional[str] class BacktestResponse(BaseModel): diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 9e4b140e4..e26df6eea 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -13,12 +13,13 @@ from freqtrade.rpc import RPC from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, BlacklistPayload, BlacklistResponse, Count, Daily, DeleteLockRequest, DeleteTrade, ForceEnterPayload, - ForceEnterResponse, ForceExitPayload, Health, - Locks, Logs, OpenTradeSchema, PairHistory, - PerformanceEntry, Ping, PlotConfig, Profit, - ResultMsg, ShowConfig, Stats, StatusMsg, - StrategyListResponse, StrategyResponse, SysInfo, - Version, WhitelistResponse) + ForceEnterResponse, ForceExitPayload, + FreqAIModelListResponse, Health, Locks, Logs, + OpenTradeSchema, PairHistory, PerformanceEntry, + Ping, PlotConfig, Profit, ResultMsg, ShowConfig, + Stats, StatusMsg, StrategyListResponse, + StrategyResponse, SysInfo, Version, + WhitelistResponse) from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException @@ -38,7 +39,8 @@ logger = logging.getLogger(__name__) # 2.17: Forceentry - leverage, partial force_exit # 2.20: Add websocket endpoints # 2.21: Add new_candle messagetype -API_VERSION = 2.21 +# 2.22: Add FreqAI to backtesting +API_VERSION = 2.22 # Public API, requires no auth. router_public = APIRouter() @@ -279,6 +281,16 @@ def get_strategy(strategy: str, config=Depends(get_config)): } +@router.get('/freqaimodels', response_model=FreqAIModelListResponse, tags=['freqai']) +def list_freqaimodels(config=Depends(get_config)): + from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver + strategies = FreqaiModelResolver.search_all_objects( + config, False) + strategies = sorted(strategies, key=lambda x: x['name']) + + return {'freqaimodels': [x['name'] for x in strategies]} + + @router.get('/available_pairs', response_model=AvailablePairs, tags=['candle data']) def list_available_pairs(timeframe: Optional[str] = None, stake_currency: Optional[str] = None, candletype: Optional[CandleType] = None, config=Depends(get_config)): From 256fac2a2b56b7c384fd330eb29828b6661f1702 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 07:23:41 +0100 Subject: [PATCH 2/7] Add test for freqaimodels endpoint --- tests/rpc/test_rpc_apiserver.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index ee067f911..74e620def 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1488,6 +1488,33 @@ def test_api_strategy(botclient): assert_response(rc, 500) +def test_api_freqaimodels(botclient, tmpdir): + ftbot, client = botclient + ftbot.config['user_data_dir'] = Path(tmpdir) + + rc = client_get(client, f"{BASE_URI}/freqaimodels") + + assert_response(rc) + + assert rc.json() == {'freqaimodels': [ + 'CatboostClassifier', + 'CatboostClassifierMultiTarget', + 'CatboostRegressor', + 'CatboostRegressorMultiTarget', + 'LightGBMClassifier', + 'LightGBMClassifierMultiTarget', + 'LightGBMRegressor', + 'LightGBMRegressorMultiTarget', + 'ReinforcementLearner', + 'ReinforcementLearner_multiproc', + 'XGBoostClassifier', + 'XGBoostRFClassifier', + 'XGBoostRFRegressor', + 'XGBoostRegressor', + 'XGBoostRegressorMultiTarget' + ]} + + def test_list_available_pairs(botclient): ftbot, client = botclient From 6d9f1fafb7b3105c96d02858adc0d30ef41d62d8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 19:20:39 +0100 Subject: [PATCH 3/7] allow backtest_cache to be provided via backtest API --- freqtrade/rpc/api_server/api_schemas.py | 1 + 1 file changed, 1 insertion(+) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 2100a6fe2..6c423c959 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -423,6 +423,7 @@ class BacktestRequest(BaseModel): stake_amount: Optional[str] enable_protections: bool dry_run_wallet: Optional[float] + backtest_cache: Optional[str] freqaimodel: Optional[str] From 07606a9e2380cc3b86ddf7753c5b05f7abeb37a3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 19:32:29 +0100 Subject: [PATCH 4/7] Simplify APi backtest config merging --- freqtrade/rpc/api_server/api_backtest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index b17636a7d..2d3da6d20 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -11,6 +11,7 @@ from freqtrade.configuration.config_validation import validate_config_consistenc from freqtrade.data.btanalysis import get_backtest_resultlist, load_and_merge_backtest_result from freqtrade.enums import BacktestState from freqtrade.exceptions import DependencyException +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 @@ -38,9 +39,8 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac 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] + + btconfig = deep_merge_dicts(settings, btconfig, allow_null_overrides=False) try: btconfig['stake_amount'] = float(btconfig['stake_amount']) except ValueError: From 70531224e61641587a22d1816530f79a824139e7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 20 Dec 2022 19:44:01 +0100 Subject: [PATCH 5/7] Allow setting identifier via UI --- freqtrade/rpc/api_server/api_backtest.py | 2 ++ freqtrade/rpc/api_server/api_schemas.py | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 2d3da6d20..4e43a63b1 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -38,6 +38,8 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac btconfig = deepcopy(config) settings = dict(bt_settings) + if 'freqai' in settings: + settings['freqai'] = dict(settings['freqai']) # Pydantic models will contain all keys, but non-provided ones are None btconfig = deep_merge_dicts(settings, btconfig, allow_null_overrides=False) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 6c423c959..17dff222d 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -414,6 +414,10 @@ class PairHistory(BaseModel): } +class BacktestFreqAIInputs(BaseModel): + identifier: str + + class BacktestRequest(BaseModel): strategy: str timeframe: Optional[str] @@ -425,6 +429,7 @@ class BacktestRequest(BaseModel): dry_run_wallet: Optional[float] backtest_cache: Optional[str] freqaimodel: Optional[str] + freqai: Optional[BacktestFreqAIInputs] class BacktestResponse(BaseModel): From 73792fd6ce81f004dfa0fbe3129f86a6e9e2c697 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 21 Dec 2022 06:28:55 +0100 Subject: [PATCH 6/7] Don't attempt to convert None to dict --- freqtrade/rpc/api_server/api_backtest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 4e43a63b1..bc2a40d91 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -38,7 +38,7 @@ async def api_start_backtest(bt_settings: BacktestRequest, background_tasks: Bac btconfig = deepcopy(config) settings = dict(bt_settings) - if 'freqai' in settings: + if settings.get('freqai', None) is not None: settings['freqai'] = dict(settings['freqai']) # Pydantic models will contain all keys, but non-provided ones are None From 524da3c7ab1e4ab77d6eb0d0a87226f278e5b870 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 23 Dec 2022 16:19:12 +0100 Subject: [PATCH 7/7] Don't actually load models to avoid random failures --- tests/rpc/test_rpc_apiserver.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 74e620def..aea8ea059 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1488,19 +1488,30 @@ def test_api_strategy(botclient): assert_response(rc, 500) -def test_api_freqaimodels(botclient, tmpdir): +def test_api_freqaimodels(botclient, tmpdir, mocker): ftbot, client = botclient ftbot.config['user_data_dir'] = Path(tmpdir) + mocker.patch( + "freqtrade.resolvers.freqaimodel_resolver.FreqaiModelResolver.search_all_objects", + return_value=[ + {'name': 'LightGBMClassifier'}, + {'name': 'LightGBMClassifierMultiTarget'}, + {'name': 'LightGBMRegressor'}, + {'name': 'LightGBMRegressorMultiTarget'}, + {'name': 'ReinforcementLearner'}, + {'name': 'ReinforcementLearner_multiproc'}, + {'name': 'XGBoostClassifier'}, + {'name': 'XGBoostRFClassifier'}, + {'name': 'XGBoostRFRegressor'}, + {'name': 'XGBoostRegressor'}, + {'name': 'XGBoostRegressorMultiTarget'}, + ]) rc = client_get(client, f"{BASE_URI}/freqaimodels") assert_response(rc) assert rc.json() == {'freqaimodels': [ - 'CatboostClassifier', - 'CatboostClassifierMultiTarget', - 'CatboostRegressor', - 'CatboostRegressorMultiTarget', 'LightGBMClassifier', 'LightGBMClassifierMultiTarget', 'LightGBMRegressor',