From 5b9c03e7e2ac6bf23e8c7406d9e549c8f680499e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 09:15:52 +0200 Subject: [PATCH 01/14] fix: don't filter min duration on > 0 closes #11838 --- freqtrade/optimize/optimize_reports/optimize_reports.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index 4242d7a9e..51f58e070 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -347,7 +347,7 @@ def generate_trading_stats(results: DataFrame) -> dict[str, Any]: else timedelta() ) winner_holding_min = ( - timedelta(minutes=round(winning_duration[winning_duration > 0].min())) + timedelta(minutes=round(winning_duration.min())) if not winning_duration.empty else timedelta() ) @@ -362,7 +362,7 @@ def generate_trading_stats(results: DataFrame) -> dict[str, Any]: else timedelta() ) loser_holding_min = ( - timedelta(minutes=round(losing_duration[losing_duration > 0].min())) + timedelta(minutes=round(losing_duration.min())) if not losing_duration.empty else timedelta() ) From 28064d013cac9765190137920d1ec33ecee156e9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 09:36:33 +0200 Subject: [PATCH 02/14] feat: add ccxt_sync_config to json schema --- build_helpers/schema.json | 6 +++++- freqtrade/config_schema/config_schema.py | 12 +++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/build_helpers/schema.json b/build_helpers/schema.json index a7323ddfa..b40465f26 100644 --- a/build_helpers/schema.json +++ b/build_helpers/schema.json @@ -1247,7 +1247,11 @@ "type": "object" }, "ccxt_async_config": { - "description": "CCXT asynchronous configuration settings.", + "description": "CCXT asynchronous configuration settings.Usually ccxt_config should be used instead.", + "type": "object" + }, + "ccxt_sync_config": { + "description": "CCXT synchronous configuration settings. Usually ccxt_config should be used instead.", "type": "object" } }, diff --git a/freqtrade/config_schema/config_schema.py b/freqtrade/config_schema/config_schema.py index 9c91d81f7..ce71e8f78 100644 --- a/freqtrade/config_schema/config_schema.py +++ b/freqtrade/config_schema/config_schema.py @@ -913,7 +913,17 @@ CONF_SCHEMA = { }, "ccxt_config": {"description": "CCXT configuration settings.", "type": "object"}, "ccxt_async_config": { - "description": "CCXT asynchronous configuration settings.", + "description": ( + "CCXT asynchronous configuration settings." + "Usually ccxt_config should be used instead." + ), + "type": "object", + }, + "ccxt_sync_config": { + "description": ( + "CCXT synchronous configuration settings. " + "Usually ccxt_config should be used instead." + ), "type": "object", }, }, From ae671c736e0c93741da6d824dcf202a82c58d4a3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 09:52:58 +0200 Subject: [PATCH 03/14] feat: don't lowercase ccxt_config keys closes #11852 --- freqtrade/configuration/environment_vars.py | 16 ++++++++++++++-- tests/test_configuration.py | 7 ++++++- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/freqtrade/configuration/environment_vars.py b/freqtrade/configuration/environment_vars.py index 8a825f59b..e5eb031e7 100644 --- a/freqtrade/configuration/environment_vars.py +++ b/freqtrade/configuration/environment_vars.py @@ -43,15 +43,27 @@ def _flat_vars_to_nested_dict(env_dict: dict[str, Any], prefix: str) -> dict[str :return: Nested dict based on available and relevant variables. """ no_convert = ["CHAT_ID", "PASSWORD"] + ccxt_config_keys = ["ccxt_config", "ccxt_sync_config", "ccxt_async_config"] relevant_vars: dict[str, Any] = {} for env_var, val in sorted(env_dict.items()): if env_var.startswith(prefix): logger.info(f"Loading variable '{env_var}'") key = env_var.replace(prefix, "") - for k in reversed(key.split("__")): + key_parts = key.split("__") + logger.info("Key parts: %s", key_parts) + + # Check if any ccxt config key is in the key parts + preserve_case = key_parts[0].lower() == "exchange" and any( + ccxt_key in [part.lower() for part in key_parts] for ccxt_key in ccxt_config_keys + ) + + for i, k in enumerate(reversed(key_parts)): + # Preserve case for the final key if ccxt config is involved + key_name = k if preserve_case and i == 0 else k.lower() + val = { - k.lower(): ( + key_name: ( _get_var_typed(val) if not isinstance(val, dict) and k not in no_convert else val diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 6c54ad350..32ece7b4e 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1477,7 +1477,9 @@ def test_flat_vars_to_nested_dict(caplog): test_args = { "FREQTRADE__EXCHANGE__SOME_SETTING": "true", "FREQTRADE__EXCHANGE__SOME_FALSE_SETTING": "false", - "FREQTRADE__EXCHANGE__CONFIG__whatever": "sometime", + "FREQTRADE__EXCHANGE__CONFIG__whatEver": "sometime", # Lowercased + # Preserve case for ccxt_config + "FREQTRADE__EXCHANGE__CCXT_CONFIG__httpsProxy": "something", "FREQTRADE__EXIT_PRICING__PRICE_SIDE": "bid", "FREQTRADE__EXIT_PRICING__cccc": "500", "FREQTRADE__STAKE_AMOUNT": "200.05", @@ -1500,6 +1502,9 @@ def test_flat_vars_to_nested_dict(caplog): "config": { "whatever": "sometime", }, + "ccxt_config": { + "httpsProxy": "something", + }, "some_setting": True, "some_false_setting": False, "pair_whitelist": ["BTC/USDT", "ETH/USDT"], From 0e1d3e279c6105e1b42824e8bf4306564f4d645c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 10:02:52 +0200 Subject: [PATCH 04/14] chore: attempt CI workaround for ta-lib build failure --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 263ad5db7..bf11b8bab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -65,6 +65,8 @@ jobs: export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include + echo "numpy<2.3.0" > constraints.txt + export PIP_CONSTRAINTS=${HOME}/constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . @@ -228,6 +230,8 @@ jobs: export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include + echo "numpy<2.3.0" > constraints.txt + export PIP_CONSTRAINTS=${HOME}/constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . From 37d03a5b44abe60b8dd529ed6783304130b53444 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 10:04:04 +0200 Subject: [PATCH 05/14] chore: proper naming for environment var --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bf11b8bab..a216cc77a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include echo "numpy<2.3.0" > constraints.txt - export PIP_CONSTRAINTS=${HOME}/constraints.txt + export PIP_CONSTRAINT=${HOME}/constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . @@ -231,7 +231,7 @@ jobs: export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include echo "numpy<2.3.0" > constraints.txt - export PIP_CONSTRAINTS=${HOME}/constraints.txt + export PIP_CONSTRAINT=${HOME}/constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . From 69cca13d15303f6d7e81da4a94a2fd3e89551360 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 10:05:56 +0200 Subject: [PATCH 06/14] chore: use UV_BUILD_CONSTRAINT --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a216cc77a..b90b47515 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include echo "numpy<2.3.0" > constraints.txt - export PIP_CONSTRAINT=${HOME}/constraints.txt + export UV_BUILD_CONSTRAINT=${HOME}/constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . @@ -231,7 +231,7 @@ jobs: export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include echo "numpy<2.3.0" > constraints.txt - export PIP_CONSTRAINT=${HOME}/constraints.txt + export UV_BUILD_CONSTRAINT=${HOME}/constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . From 286f385f994df736a7d2dbc0a403622e25ea90e9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 10:07:01 +0200 Subject: [PATCH 07/14] chore(ci): use proper directory --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b90b47515..054602560 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -66,7 +66,7 @@ jobs: export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include echo "numpy<2.3.0" > constraints.txt - export UV_BUILD_CONSTRAINT=${HOME}/constraints.txt + export UV_BUILD_CONSTRAINT=constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . @@ -231,7 +231,7 @@ jobs: export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include echo "numpy<2.3.0" > constraints.txt - export UV_BUILD_CONSTRAINT=${HOME}/constraints.txt + export UV_BUILD_CONSTRAINT=constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . From 9d46fd21dc6a4c10d9a83eb81be9330c642d17a0 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 10:17:14 +0200 Subject: [PATCH 08/14] chore(ci): clean up after pip install --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 054602560..a2082b075 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,6 +70,7 @@ jobs: uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . + rm constraints.txt - name: Check for version alignment run: | @@ -235,6 +236,7 @@ jobs: uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . + rm constraints.txt - name: Tests run: | From 11f339f80267244660682d9ff1dcd08bc0aac578 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 10:25:41 +0200 Subject: [PATCH 09/14] chore(ci): UV workaround for build-online --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a2082b075..257890ae3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -486,9 +486,13 @@ jobs: export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include + echo "numpy<2.3.0" > constraints.txt + export UV_BUILD_CONSTRAINT=constraints.txt uv pip install -r requirements-dev.txt uv pip install -e ft_client/ uv pip install -e . + rm constraints.txt + - name: Tests incl. ccxt compatibility tests env: From b874ceaf2f4875bd5885802d41188f6babd2263a Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 11:11:15 +0200 Subject: [PATCH 10/14] chore: improved "pins" for numpy --- Dockerfile | 2 +- build_helpers/install_windows.ps1 | 2 +- docker/Dockerfile.armhf | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index edc35b474..064637b8a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -35,7 +35,7 @@ ENV LD_LIBRARY_PATH /usr/local/lib # Install dependencies COPY --chown=ftuser:ftuser requirements.txt requirements-hyperopt.txt /freqtrade/ USER ftuser -RUN pip install --user --no-cache-dir "numpy<3.0" \ +RUN pip install --user --no-cache-dir "numpy<2.3.0" \ && pip install --user --no-cache-dir -r requirements-hyperopt.txt # Copy dependencies to runtime-image diff --git a/build_helpers/install_windows.ps1 b/build_helpers/install_windows.ps1 index 3cdbbb3f6..99f4de025 100644 --- a/build_helpers/install_windows.ps1 +++ b/build_helpers/install_windows.ps1 @@ -3,7 +3,7 @@ python -m pip install --upgrade pip python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" -pip install -U wheel "numpy<3" +pip install -U wheel "numpy<2.3" pip install --only-binary ta-lib --find-links=build_helpers\ ta-lib pip install -r requirements-dev.txt diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf index 3c705180c..6167c0d07 100644 --- a/docker/Dockerfile.armhf +++ b/docker/Dockerfile.armhf @@ -34,7 +34,7 @@ COPY build_helpers/* /tmp/ # Install dependencies COPY --chown=ftuser:ftuser requirements.txt /freqtrade/ USER ftuser -RUN pip install --user --no-cache-dir "numpy<3" \ +RUN pip install --user --no-cache-dir "numpy<2.3.0" \ && pip install --user --no-index --find-links /tmp/ pyarrow TA-Lib \ && pip install --user --no-cache-dir -r requirements.txt From e0f147e0764db1c5177ff1fe0b57392e6caf0018 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 12:42:26 +0200 Subject: [PATCH 11/14] feat: add "--notes" to backtest command closes #11847 --- freqtrade/commands/arguments.py | 1 + freqtrade/commands/cli_options.py | 5 +++++ freqtrade/configuration/configuration.py | 1 + freqtrade/optimize/backtesting.py | 6 +++++- freqtrade/optimize/optimize_reports/optimize_reports.py | 3 +++ 5 files changed, 15 insertions(+), 1 deletion(-) diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index 52beb3dfa..d134920d0 100755 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -57,6 +57,7 @@ ARGS_BACKTEST = [ "backtest_breakdown", "backtest_cache", "freqai_backtest_live_models", + "backtest_notes", ] ARGS_HYPEROPT = [ diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index c5d0ad49b..669f68f5a 100755 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -204,6 +204,11 @@ AVAILABLE_CLI_OPTIONS = { help="Export backtest results (default: trades).", choices=constants.EXPORT_OPTIONS, ), + "backtest_notes": Arg( + "--notes", + help="Add notes to the backtest results.", + metavar="TEXT", + ), "exportfilename": Arg( "--export-filename", "--backtest-filename", diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 264d114b8..e1e62e778 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -310,6 +310,7 @@ class Configuration: ("backtest_cache", "Parameter --cache={} detected ..."), ("disableparamexport", "Parameter --disableparamexport detected: {} ..."), ("freqai_backtest_live_models", "Parameter --freqai-backtest-live-models detected ..."), + ("backtest_notes", "Parameter --notes detected: {} ..."), ] self._args_to_config_loop(config, configurations) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 2d58c461a..ed247e20d 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -1822,7 +1822,11 @@ class Backtesting: # Update old results with new ones. if len(self.all_bt_content) > 0: results = generate_backtest_stats( - data, self.all_bt_content, min_date=min_date, max_date=max_date + data, + self.all_bt_content, + min_date=min_date, + max_date=max_date, + notes=self.config.get("backtest_notes"), ) if self.results: self.results["metadata"].update(results["metadata"]) diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index 51f58e070..d35e96605 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -669,6 +669,7 @@ def generate_backtest_stats( all_results: dict[str, BacktestContentType], min_date: datetime, max_date: datetime, + notes: str | None = None, ) -> BacktestResultType: """ :param btdata: Backtest data @@ -694,6 +695,8 @@ def generate_backtest_stats( "backtest_start_ts": int(min_date.timestamp()), "backtest_end_ts": int(max_date.timestamp()), } + if notes: + metadata[strategy]["notes"] = notes result["strategy"][strategy] = strat_stats strategy_results = generate_strategy_comparison(bt_stats=result["strategy"]) From ba5f771e65b304f98162773328ba487bc28cff48 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 12:46:25 +0200 Subject: [PATCH 12/14] chore: update documentation command --- docs/commands/backtesting.md | 3 ++- freqtrade/commands/arguments.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/commands/backtesting.md b/docs/commands/backtesting.md index c54d9e40c..8e09c245d 100644 --- a/docs/commands/backtesting.md +++ b/docs/commands/backtesting.md @@ -17,7 +17,7 @@ usage: freqtrade backtesting [-h] [-v] [--no-color] [--logfile FILE] [-V] [--export-filename PATH] [--breakdown {day,week,month,year} [{day,week,month,year} ...]] [--cache {none,day,week,month}] - [--freqai-backtest-live-models] + [--freqai-backtest-live-models] [--notes TEXT] options: -h, --help show this help message and exit @@ -73,6 +73,7 @@ options: age (default: day). --freqai-backtest-live-models Run backtest with ready models. + --notes TEXT Add notes to the backtest results. Common arguments: -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index d134920d0..df5775183 100755 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -251,7 +251,7 @@ ARGS_STRATEGY_UPDATER = ["strategy_list", "strategy_path", "recursive_strategy_s ARGS_LOOKAHEAD_ANALYSIS = [ a for a in ARGS_BACKTEST - if a not in ("position_stacking", "backtest_cache", "backtest_breakdown") + if a not in ("position_stacking", "backtest_cache", "backtest_breakdown", "backtest_notes") ] + ["minimum_trade_amount", "targeted_trade_amount", "lookahead_analysis_exportfilename"] ARGS_RECURSIVE_ANALYSIS = ["timeframe", "timerange", "dataformat_ohlcv", "pairs", "startup_candle"] From 0e68de0c6df5c25c211fe643692a17251a5ac860 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 12:51:38 +0200 Subject: [PATCH 13/14] feat: document --notes usage --- docs/backtesting.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/backtesting.md b/docs/backtesting.md index 4d91b3ca6..924edc2ac 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -5,6 +5,8 @@ This page explains how to validate your strategy performance by using Backtestin Backtesting requires historic data to be available. To learn how to get data for the pairs and exchange you're interested in, head over to the [Data Downloading](data-download.md) section of the documentation. +Backtesting is also available in [webserver mode](freq-ui.md#backtesting), which allows you to run backtests via the web interface. + ## Backtesting command reference --8<-- "commands/backtesting.md" @@ -435,6 +437,10 @@ To save time, by default backtest will reuse a cached result from within the las To further analyze your backtest results, freqtrade will export the trades to file by default. You can then load the trades to perform further analysis as shown in the [data analysis](strategy_analysis_example.md#load-backtest-results-to-pandas-dataframe) backtesting section. +Also, you can use freqtrade in [webserver mode](freq-ui.md#backtesting) to visualize the backtest results in a web interface. +This mode also allows you to load existing backtest results, so you can analyze them without running the backtest again. +For this mode - `--notes ""` can be used to add notes to the backtest results, which will be shown in the web interface. + ### Backtest output file The output file freqtrade produces is a zip file containing the following files: From 7fb7c43e45b53da4a087de887f5b865394f012ed Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 8 Jun 2025 12:52:06 +0200 Subject: [PATCH 14/14] chore: pip_constraint fix for dockerfile --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 064637b8a..ec7dca342 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,6 +36,8 @@ ENV LD_LIBRARY_PATH /usr/local/lib COPY --chown=ftuser:ftuser requirements.txt requirements-hyperopt.txt /freqtrade/ USER ftuser RUN pip install --user --no-cache-dir "numpy<2.3.0" \ + && echo "numpy<2.3.0" > /tmp/constraints.txt \ + && export PIP_CONSTRAINT=/tmp/constraints.txt \ && pip install --user --no-cache-dir -r requirements-hyperopt.txt # Copy dependencies to runtime-image