From bb1d8fb54f67ccabe4ceadf6c57b1f15b9db50cb Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 21 Jan 2022 15:23:06 +0100 Subject: [PATCH 1/4] Improve message when no hyperopt fie is found closes #6266 --- freqtrade/data/btanalysis.py | 3 +++ freqtrade/optimize/hyperopt_tools.py | 1 + tests/data/test_btanalysis.py | 6 ++++++ tests/optimize/test_hyperopt_tools.py | 3 ++- 4 files changed, 12 insertions(+), 1 deletion(-) diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index 57eec6eea..16926923b 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -100,6 +100,9 @@ def get_latest_hyperopt_file(directory: Union[Path, str], predef_filename: str = if isinstance(directory, str): directory = Path(directory) if predef_filename: + if Path(predef_filename).is_absolute(): + raise OperationalException( + "--hyperopt-filename expects only the filename, not an absolute path.") return directory / predef_filename return directory / get_latest_hyperopt_filename(directory) diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 5eac47ba0..61a10c32b 100755 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -137,6 +137,7 @@ class HyperoptTools(): } if not HyperoptTools._test_hyperopt_results_exist(results_file): # No file found. + logger.warning(f"Hyperopt file {results_file} not found.") return [], 0 epochs = [] diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index eed3532f8..341645854 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -51,6 +51,12 @@ def test_get_latest_hyperopt_file(testdatadir): res = get_latest_hyperopt_file(str(testdatadir.parent)) assert res == testdatadir.parent / "hyperopt_results.pickle" + # Test with absolute path + with pytest.raises( + OperationalException, + match="--hyperopt-filename expects only the filename, not an absolute path."): + get_latest_hyperopt_file(str(testdatadir.parent), str(testdatadir.parent)) + def test_load_backtest_metadata(mocker, testdatadir): res = load_backtest_metadata(testdatadir / 'nonexistant.file.json') diff --git a/tests/optimize/test_hyperopt_tools.py b/tests/optimize/test_hyperopt_tools.py index d9a52db39..6cc0caf40 100644 --- a/tests/optimize/test_hyperopt_tools.py +++ b/tests/optimize/test_hyperopt_tools.py @@ -10,7 +10,7 @@ import rapidjson from freqtrade.constants import FTHYPT_FILEVERSION from freqtrade.exceptions import OperationalException from freqtrade.optimize.hyperopt_tools import HyperoptTools, hyperopt_serializer -from tests.conftest import log_has +from tests.conftest import log_has, log_has_re # Functions for recurrent object patching @@ -24,6 +24,7 @@ def test_save_results_saves_epochs(hyperopt, tmpdir, caplog) -> None: hyperopt.results_file = Path(tmpdir / 'ut_results.fthypt') hyperopt_epochs = HyperoptTools.load_filtered_results(hyperopt.results_file, {}) + assert log_has_re("Hyperopt file .* not found.", caplog) assert hyperopt_epochs == ([], 0) # Test writing to temp dir and reading again From a43c088448f4f95fe57caaa68ded70803f7fa368 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 22 Jan 2022 07:11:59 +0100 Subject: [PATCH 2/4] Allow @informative in webserver mode --- freqtrade/rpc/api_server/api_v1.py | 8 +++++--- freqtrade/rpc/api_server/deps.py | 10 ++++++++++ freqtrade/rpc/api_server/webserver.py | 2 ++ freqtrade/rpc/rpc.py | 4 ++-- 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 1c1ff39df..648ec6c57 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -20,7 +20,7 @@ from freqtrade.rpc.api_server.api_schemas import (AvailablePairs, Balances, Blac Stats, StatusMsg, StrategyListResponse, StrategyResponse, SysInfo, Version, WhitelistResponse) -from freqtrade.rpc.api_server.deps import get_config, get_rpc, get_rpc_optional +from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException @@ -217,12 +217,14 @@ def pair_candles(pair: str, timeframe: str, limit: Optional[int], rpc: RPC = Dep @router.get('/pair_history', response_model=PairHistory, tags=['candle data']) def pair_history(pair: str, timeframe: str, timerange: str, strategy: str, - config=Depends(get_config)): + config=Depends(get_config), exchange=Depends(get_exchange)): + # The initial call to this endpoint can be slow, as it may need to initialize + # the exchange class. config = deepcopy(config) config.update({ 'strategy': strategy, }) - return RPC._rpc_analysed_history_full(config, pair, timeframe, timerange) + return RPC._rpc_analysed_history_full(config, pair, timeframe, timerange, exchange) @router.get('/plot_config', response_model=PlotConfig, tags=['candle data']) diff --git a/freqtrade/rpc/api_server/deps.py b/freqtrade/rpc/api_server/deps.py index 16f9a78c0..b428d9c6d 100644 --- a/freqtrade/rpc/api_server/deps.py +++ b/freqtrade/rpc/api_server/deps.py @@ -1,5 +1,7 @@ from typing import Any, Dict, Iterator, Optional +from fastapi import Depends + from freqtrade.persistence import Trade from freqtrade.rpc.rpc import RPC, RPCException @@ -28,3 +30,11 @@ def get_config() -> Dict[str, Any]: def get_api_config() -> Dict[str, Any]: return ApiServer._config['api_server'] + + +def get_exchange(config=Depends(get_config)): + if not ApiServer._exchange: + from freqtrade.resolvers import ExchangeResolver + ApiServer._exchange = ExchangeResolver.load_exchange( + config['exchange']['name'], config) + return ApiServer._exchange diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index 235063191..63812f52f 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -41,6 +41,8 @@ class ApiServer(RPCHandler): _has_rpc: bool = False _bgtask_running: bool = False _config: Dict[str, Any] = {} + # Exchange - only available in webserver mode. + _exchange = None def __new__(cls, *args, **kwargs): """ diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index e969aa0a7..2232496de 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -995,7 +995,7 @@ class RPC: @staticmethod def _rpc_analysed_history_full(config, pair: str, timeframe: str, - timerange: str) -> Dict[str, Any]: + timerange: str, exchange) -> Dict[str, Any]: timerange_parsed = TimeRange.parse_timerange(timerange) _data = load_data( @@ -1010,7 +1010,7 @@ class RPC: from freqtrade.data.dataprovider import DataProvider from freqtrade.resolvers.strategy_resolver import StrategyResolver strategy = StrategyResolver.load_strategy(config) - strategy.dp = DataProvider(config, exchange=None, pairlists=None) + strategy.dp = DataProvider(config, exchange=exchange, pairlists=None) df_analyzed = strategy.analyze_ticker(_data[pair], {'pair': pair}) From 314a5448811a0723d5f941c096eac7b02747522a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 22 Jan 2022 08:08:50 +0100 Subject: [PATCH 3/4] Add Failing test for get_strategy_run_id Fails because max_open_trades is "inf" emulates behaviour of `max_open_trades=-1` when loading the configuration) --- tests/optimize/test_backtesting.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index edd0faba3..4caf4aec1 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -21,6 +21,7 @@ from freqtrade.data.dataprovider import DataProvider from freqtrade.data.history import get_timerange from freqtrade.enums import RunMode, SellType from freqtrade.exceptions import DependencyException, OperationalException +from freqtrade.misc import get_strategy_run_id from freqtrade.optimize.backtesting import Backtesting from freqtrade.persistence import LocalTrade from freqtrade.resolvers import StrategyResolver @@ -1357,3 +1358,13 @@ def test_backtest_start_multi_strat_caching(default_conf, mocker, caplog, testda for line in exists: assert log_has(line, caplog) + + +def test_get_strategy_run_id(default_conf_usdt): + default_conf_usdt.update({ + 'strategy': 'StrategyTestV2', + 'max_open_trades': float('inf') + }) + strategy = StrategyResolver.load_strategy(default_conf_usdt) + x = get_strategy_run_id(strategy) + assert isinstance(x, str) From 9ecd7400c8316469fd3ae981dc86e4732a991df7 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 22 Jan 2022 08:10:09 +0100 Subject: [PATCH 4/4] Allow NaN when calculating digests --- freqtrade/misc.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index f09e5ee47..2a27f1660 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -248,8 +248,10 @@ def get_strategy_run_id(strategy) -> str: if k in config: del config[k] + # Explicitly allow NaN values (e.g. max_open_trades). + # as it does not matter for getting the hash. digest.update(rapidjson.dumps(config, default=str, - number_mode=rapidjson.NM_NATIVE).encode('utf-8')) + number_mode=rapidjson.NM_NAN).encode('utf-8')) with open(strategy.__file__, 'rb') as fp: digest.update(fp.read()) return digest.hexdigest().lower()