From cd7b26717174fc7ab47e201f0fb5e5fa68f6a4ec Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 18 Nov 2025 20:29:39 +0100 Subject: [PATCH 01/41] feat: record dry-run stop_price price separately --- freqtrade/exchange/exchange.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index a2d1eb859..5a7cc7fc8 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1119,6 +1119,7 @@ class Exchange: leverage: float, params: dict | None = None, stop_loss: bool = False, + stop_price: float | None = None, ) -> CcxtOrder: now = dt_now() order_id = f"dry_run_{side}_{pair}_{now.timestamp()}" @@ -1145,7 +1146,7 @@ class Exchange: } if stop_loss: dry_order["info"] = {"stopPrice": dry_order["price"]} - dry_order[self._ft_has["stop_price_prop"]] = dry_order["price"] + dry_order[self._ft_has["stop_price_prop"]] = stop_price or dry_order["price"] # Workaround to avoid filling stoploss orders immediately dry_order["ft_order_type"] = "stoploss" orderbook: OrderBook | None = None @@ -1517,8 +1518,9 @@ class Exchange: ordertype, side, amount, - stop_price_norm, + limit_rate or stop_price_norm, stop_loss=True, + stop_price=stop_price_norm, leverage=leverage, ) return dry_order From ca7234e33f787a8805e2549ee3395dfcc14f502c Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 18 Nov 2025 20:33:04 +0100 Subject: [PATCH 02/41] test: fix dry-stop tests --- tests/exchange/test_binance.py | 3 ++- tests/exchange/test_htx.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 2c11fc2f3..59a6c41ae 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -157,7 +157,8 @@ def test_create_stoploss_order_dry_run_binance(default_conf, mocker): assert "type" in order assert order["type"] == order_type - assert order["price"] == 220 + assert order["price"] == 217.8 + assert order["stopPrice"] == 220 assert order["amount"] == 1 diff --git a/tests/exchange/test_htx.py b/tests/exchange/test_htx.py index e32d7e85e..bffbde8c0 100644 --- a/tests/exchange/test_htx.py +++ b/tests/exchange/test_htx.py @@ -123,7 +123,8 @@ def test_create_stoploss_order_dry_run_htx(default_conf, mocker): assert "type" in order assert order["type"] == order_type - assert order["price"] == 220 + assert order["price"] == 217.8 + assert order["stopPrice"] == 220 assert order["amount"] == 1 From 73b427370b879521ae2e38705e595e769d08ea04 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 19 Nov 2025 19:51:39 +0100 Subject: [PATCH 03/41] test: add tests for dry crossed stoploss --- tests/exchange/test_exchange.py | 34 ++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 63dd0be94..4edecad57 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1111,21 +1111,29 @@ def test_create_dry_run_order_fees( @pytest.mark.parametrize( - "side,limit,offset,expected", + "side,limit,offset,is_stop,expected", [ - ("buy", 46.0, 0.0, True), - ("buy", 26.0, 0.0, True), - ("buy", 25.55, 0.0, False), - ("buy", 1, 0.0, False), # Very far away - ("sell", 25.5, 0.0, True), - ("sell", 50, 0.0, False), # Very far away - ("sell", 25.58, 0.0, False), - ("sell", 25.563, 0.01, False), - ("sell", 5.563, 0.01, True), + ("buy", 46.0, 0.0, False, True), + ("buy", 46.0, 0.0, True, False), + ("buy", 26.0, 0.0, False, True), + ("buy", 26.0, 0.0, True, False), # Stop - didn't trigger + ("buy", 25.55, 0.0, False, False), + ("buy", 25.55, 0.0, True, True), # Stop - triggered + ("buy", 1, 0.0, False, False), # Very far away + ("buy", 1, 0.0, True, True), # Current price is above stop - triggered + ("sell", 25.5, 0.0, False, True), + ("sell", 50, 0.0, False, False), # Very far away + ("sell", 25.58, 0.0, False, False), + ("sell", 25.563, 0.01, False, False), + ("sell", 25.563, 0.0, True, False), # stop order - Not triggered, best bid + ("sell", 25.566, 0.0, True, True), # stop order - triggered + ("sell", 26, 0.01, True, True), # stop order - triggered + ("sell", 5.563, 0.01, False, True), + ("sell", 5.563, 0.0, True, False), # stop order - not triggered ], ) def test__dry_is_price_crossed_with_orderbook( - default_conf, mocker, order_book_l2_usd, side, limit, offset, expected + default_conf, mocker, order_book_l2_usd, side, limit, offset, is_stop, expected ): # Best bid 25.563 # Best ask 25.566 @@ -1134,14 +1142,14 @@ def test__dry_is_price_crossed_with_orderbook( exchange.fetch_l2_order_book = order_book_l2_usd orderbook = order_book_l2_usd.return_value result = exchange._dry_is_price_crossed( - "LTC/USDT", side, limit, orderbook=orderbook, offset=offset + "LTC/USDT", side, limit, orderbook=orderbook, offset=offset, is_stop=is_stop ) assert result is expected assert order_book_l2_usd.call_count == 0 # Test without passing orderbook order_book_l2_usd.reset_mock() - result = exchange._dry_is_price_crossed("LTC/USDT", side, limit, offset=offset) + result = exchange._dry_is_price_crossed("LTC/USDT", side, limit, offset=offset, is_stop=is_stop) assert result is expected From ad256367beba9e3350b4d2718794b989ef4c9276 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 19 Nov 2025 19:52:16 +0100 Subject: [PATCH 04/41] feat: dry-is-crossed should support stoploss --- freqtrade/exchange/exchange.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 5a7cc7fc8..1b2e39b0b 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1254,13 +1254,14 @@ class Exchange: limit: float, orderbook: OrderBook | None = None, offset: float = 0.0, + is_stop: bool = False, ) -> bool: if not self.exchange_has("fetchL2OrderBook"): return True if not orderbook: orderbook = self.fetch_l2_order_book(pair, 1) try: - if side == "buy": + if (side == "buy" and not is_stop) or (side == "sell" and is_stop): price = orderbook["asks"][0][0] if limit * (1 - offset) >= price: return True From c83ea0db4fe174b95de0ad605ff3918300739823 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 19 Nov 2025 19:55:39 +0100 Subject: [PATCH 05/41] chore: fix default behavior for crossed mode --- freqtrade/exchange/exchange.py | 3 ++- tests/exchange/test_exchange.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 1b2e39b0b..11e46ca53 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1257,7 +1257,8 @@ class Exchange: is_stop: bool = False, ) -> bool: if not self.exchange_has("fetchL2OrderBook"): - return True + # True unless checking a stoploss order + return not is_stop if not orderbook: orderbook = self.fetch_l2_order_book(pair, 1) try: diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 4edecad57..743f798dd 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1173,7 +1173,10 @@ def test__dry_is_price_crossed_without_orderbook_support(default_conf, mocker): exchange.fetch_l2_order_book = MagicMock() mocker.patch(f"{EXMS}.exchange_has", return_value=False) assert exchange._dry_is_price_crossed("LTC/USDT", "buy", 1.0) + assert exchange._dry_is_price_crossed("LTC/USDT", "sell", 1.0) assert exchange.fetch_l2_order_book.call_count == 0 + assert not exchange._dry_is_price_crossed("LTC/USDT", "buy", 1.0, is_stop=True) + assert not exchange._dry_is_price_crossed("LTC/USDT", "sell", 1.0, is_stop=True) @pytest.mark.parametrize( From 3543e96ec5807dfe85ed5438152319d7f2ee7c40 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 19 Nov 2025 20:04:37 +0100 Subject: [PATCH 06/41] refactor: extract dry-market order slippage from function --- freqtrade/exchange/exchange.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 11e46ca53..b34cad18a 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1164,7 +1164,11 @@ class Exchange: if dry_order["type"] == "market" and not dry_order.get("ft_order_type"): # Update market order pricing - average = self.get_dry_market_fill_price(pair, side, amount, rate, orderbook) + slippage = 0.05 + worst_rate = rate * ((1 + slippage) if side == "buy" else (1 - slippage)) + average = self.get_dry_market_fill_price( + pair, side, amount, rate, worst_rate, orderbook + ) dry_order.update( { "average": average, @@ -1204,7 +1208,13 @@ class Exchange: return dry_order def get_dry_market_fill_price( - self, pair: str, side: str, amount: float, rate: float, orderbook: OrderBook | None + self, + pair: str, + side: str, + amount: float, + rate: float, + worst_rate: float, + orderbook: OrderBook | None, ) -> float: """ Get the market order fill price based on orderbook interpolation @@ -1213,8 +1223,6 @@ class Exchange: if not orderbook: orderbook = self.fetch_l2_order_book(pair, 20) ob_type: OBLiteral = "asks" if side == "buy" else "bids" - slippage = 0.05 - max_slippage_val = rate * ((1 + slippage) if side == "buy" else (1 - slippage)) remaining_amount = amount filled_value = 0.0 @@ -1238,11 +1246,10 @@ class Exchange: forecast_avg_filled_price = max(filled_value, 0) / amount # Limit max. slippage to specified value if side == "buy": - forecast_avg_filled_price = min(forecast_avg_filled_price, max_slippage_val) + forecast_avg_filled_price = min(forecast_avg_filled_price, worst_rate) else: - forecast_avg_filled_price = max(forecast_avg_filled_price, max_slippage_val) - + forecast_avg_filled_price = max(forecast_avg_filled_price, worst_rate) return self.price_to_precision(pair, forecast_avg_filled_price) return rate From f63484d0b027e4743bfb191e93f8006fadb6d60a Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 20 Nov 2025 06:37:49 +0100 Subject: [PATCH 07/41] feat: add dry-limit check for stoploss orders --- freqtrade/exchange/exchange.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index b34cad18a..1b0946987 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1288,6 +1288,38 @@ class Exchange: """ Check dry-run limit order fill and update fee (if it filled). """ + if order["status"] != "closed" and order.get("ft_order_type") == "stoploss": + pair = order["symbol"] + if not orderbook and self.exchange_has("fetchL2OrderBook"): + orderbook = self.fetch_l2_order_book(pair, 20) + price = order[self._ft_has["stop_price_prop"]] + crossed = self._dry_is_price_crossed( + pair, order["side"], price, orderbook, is_stop=True + ) + if crossed: + average = self.get_dry_market_fill_price( + pair, + order["side"], + order["amount"], + price, + worst_rate=order["price"], + orderbook=orderbook, + ) + order.update( + { + "status": "closed", + "filled": order["amount"], + "remaining": 0, + "average": average, + "cost": order["amount"] * average, + } + ) + self.add_dry_order_fee( + pair, + order, + "taker" if immediate else "maker", + ) + return order if ( order["status"] != "closed" and order["type"] in ["limit"] From fade66afd969db4ce254c525d2db454dc26a0ec3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 21 Nov 2025 06:47:04 +0100 Subject: [PATCH 08/41] fix: ensure we always have a price when checking stops --- freqtrade/exchange/exchange.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 1b0946987..60f4d5fdd 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -104,6 +104,7 @@ from freqtrade.misc import ( deep_merge_dicts, file_dump_json, file_load_json, + safe_value_fallback, safe_value_fallback2, ) from freqtrade.util import FtTTLCache, PeriodicCache, dt_from_ts, dt_now @@ -1292,7 +1293,7 @@ class Exchange: pair = order["symbol"] if not orderbook and self.exchange_has("fetchL2OrderBook"): orderbook = self.fetch_l2_order_book(pair, 20) - price = order[self._ft_has["stop_price_prop"]] + price = safe_value_fallback(order, self._ft_has["stop_price_prop"], "price") crossed = self._dry_is_price_crossed( pair, order["side"], price, orderbook, is_stop=True ) From 1536c09df38aa0b87e5de2f00b039a5c096c4cc6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Nov 2025 12:07:13 +0100 Subject: [PATCH 09/41] test: improved test naming --- tests/exchange/test_exchange.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 743f798dd..8f878377f 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1187,7 +1187,7 @@ def test__dry_is_price_crossed_without_orderbook_support(default_conf, mocker): (False, False, "sell", 1.0, "open", None, 0, None), ], ) -def test_check_dry_limit_order_filled_parametrized( +def test_check_dry_limit_order_filled( default_conf, mocker, crossed, From ac2723c3a0ae0a89d5b1535bca39b2aff232ad48 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Nov 2025 12:23:15 +0100 Subject: [PATCH 10/41] test: add explicit test for dry stoploss order filling --- tests/exchange/test_exchange.py | 64 +++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 8f878377f..f280ec9d9 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1231,6 +1231,70 @@ def test_check_dry_limit_order_filled( assert fee_mock.call_count == expected_calls +@pytest.mark.parametrize( + "immediate,crossed,expected_status,expected_fee_type", + [ + (True, True, "closed", "taker"), + (False, True, "closed", "maker"), + (True, False, "open", None), + ], +) +def test_check_dry_limit_order_filled_stoploss( + default_conf, mocker, immediate, crossed, expected_status, expected_fee_type, order_book_l2_usd +): + exchange = get_patched_exchange(mocker, default_conf) + mocker.patch.multiple( + EXMS, + exchange_has=MagicMock(return_value=True), + _dry_is_price_crossed=MagicMock(return_value=crossed), + fetch_l2_order_book=order_book_l2_usd, + ) + average_mock = mocker.patch(f"{EXMS}.get_dry_market_fill_price", return_value=24.25) + fee_mock = mocker.patch( + f"{EXMS}.add_dry_order_fee", + autospec=True, + side_effect=lambda self, pair, dry_order, taker_or_maker: dry_order, + ) + + amount = 1.75 + order = { + "symbol": "LTC/USDT", + "status": "open", + "type": "limit", + "side": "sell", + "amount": amount, + "filled": 0.0, + "remaining": amount, + "price": 25.0, + "average": 0.0, + "cost": 0.0, + "fee": None, + "ft_order_type": "stoploss", + "stopLossPrice": 24.5, + } + + result = exchange.check_dry_limit_order_filled(order, immediate=immediate) + + assert result["status"] == expected_status + assert order_book_l2_usd.call_count == 1 + if crossed: + assert result["filled"] == amount + assert result["remaining"] == 0 + assert result["average"] == 24.25 + assert result["cost"] == pytest.approx(amount * 24.25) + assert average_mock.call_count == 1 + assert fee_mock.call_count == 1 + assert fee_mock.call_args[0][1] == "LTC/USDT" + assert fee_mock.call_args[0][3] == expected_fee_type + else: + assert result["filled"] == 0.0 + assert result["remaining"] == amount + assert result["average"] == 0.0 + + assert average_mock.call_count == 0 + assert fee_mock.call_count == 0 + + @pytest.mark.parametrize( "side,price,filled,converted", [ From 3453bdf6072651f3499e3d3b211122b16aecde8c Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 29 Nov 2025 13:10:16 +0100 Subject: [PATCH 11/41] chore: bump version to 2025.12-dev --- freqtrade/__init__.py | 2 +- ft_client/freqtrade_client/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 641600a4d..a7acc6638 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,6 +1,6 @@ """Freqtrade bot""" -__version__ = "2025.11-dev" +__version__ = "2025.12-dev" if "dev" in __version__: from pathlib import Path diff --git a/ft_client/freqtrade_client/__init__.py b/ft_client/freqtrade_client/__init__.py index 9182ce800..d9479dba0 100644 --- a/ft_client/freqtrade_client/__init__.py +++ b/ft_client/freqtrade_client/__init__.py @@ -1,7 +1,7 @@ from freqtrade_client.ft_rest_client import FtRestClient -__version__ = "2025.11-dev" +__version__ = "2025.12-dev" if "dev" in __version__: from pathlib import Path From 52c73eabeb36e0e44e7abb56e9a66626f07fff69 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 30 Nov 2025 13:25:03 +0100 Subject: [PATCH 12/41] docs: add missing space in docs --- docs/strategy-callbacks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 5853d23e1..727075215 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -634,7 +634,7 @@ class AwesomeStrategy(IStrategy): ## Custom order price rules -By default, freqtrade use the orderbook to automatically set an order price([Relevant documentation](configuration.md#prices-used-for-orders)), you also have the option to create custom order prices based on your strategy. +By default, freqtrade use the orderbook to automatically set an order price ([Relevant documentation](configuration.md#prices-used-for-orders)), you also have the option to create custom order prices based on your strategy. You can use this feature by creating a `custom_entry_price()` function in your strategy file to customize entry prices and `custom_exit_price()` for exits. From 7a5342cc93b947008a746a51941e76ddd044f6a5 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:04:40 +0000 Subject: [PATCH 13/41] chore(deps-dev): bump scipy-stubs in the scipy group Bumps the scipy group with 1 update: [scipy-stubs](https://github.com/scipy/scipy-stubs). Updates `scipy-stubs` from 1.16.3.0 to 1.16.3.1 - [Release notes](https://github.com/scipy/scipy-stubs/releases) - [Commits](https://github.com/scipy/scipy-stubs/compare/v1.16.3.0...v1.16.3.1) --- updated-dependencies: - dependency-name: scipy-stubs dependency-version: 1.16.3.1 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: scipy ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index f9386777e..55764746f 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -24,7 +24,7 @@ time-machine==3.0.0 nbconvert==7.16.6 # mypy types -scipy-stubs==1.16.3.0 # keep in sync with `scipy` in `requirements-hyperopt.txt` +scipy-stubs==1.16.3.1 # keep in sync with `scipy` in `requirements-hyperopt.txt` types-cachetools==6.2.0.20251022 types-filelock==3.2.7 types-requests==2.32.4.20250913 From 1de9f5a2fbacfc51be0b5bd7d6bd50dc239a6576 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:04:45 +0000 Subject: [PATCH 14/41] chore(deps): bump pydantic from 2.12.4 to 2.12.5 Bumps [pydantic](https://github.com/pydantic/pydantic) from 2.12.4 to 2.12.5. - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](https://github.com/pydantic/pydantic/compare/v2.12.4...v2.12.5) --- updated-dependencies: - dependency-name: pydantic dependency-version: 2.12.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 43f79ab25..4a34268d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,7 +38,7 @@ sdnotify==0.3.2 # API Server fastapi==0.121.3 -pydantic==2.12.4 +pydantic==2.12.5 uvicorn==0.38.0 pyjwt==2.10.1 aiofiles==25.1.0 From 1d0a9022510c8a079b768937887fe59c6fa43752 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:04:53 +0000 Subject: [PATCH 15/41] chore(deps-dev): bump time-machine from 3.0.0 to 3.1.0 Bumps [time-machine](https://github.com/adamchainz/time-machine) from 3.0.0 to 3.1.0. - [Changelog](https://github.com/adamchainz/time-machine/blob/main/docs/changelog.rst) - [Commits](https://github.com/adamchainz/time-machine/compare/3.0.0...3.1.0) --- updated-dependencies: - dependency-name: time-machine dependency-version: 3.1.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index f9386777e..6b676e536 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -18,7 +18,7 @@ pytest-timeout==2.4.0 pytest-xdist==3.8.0 isort==7.0.0 # For datetime mocking -time-machine==3.0.0 +time-machine==3.1.0 # Convert jupyter notebooks to markdown documents nbconvert==7.16.6 From 691a6b0a729e95a8bebe200e834ffbad03c6037b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:05:16 +0000 Subject: [PATCH 16/41] chore(deps-dev): bump ruff from 0.14.5 to 0.14.6 Bumps [ruff](https://github.com/astral-sh/ruff) from 0.14.5 to 0.14.6. - [Release notes](https://github.com/astral-sh/ruff/releases) - [Changelog](https://github.com/astral-sh/ruff/blob/main/CHANGELOG.md) - [Commits](https://github.com/astral-sh/ruff/compare/0.14.5...0.14.6) --- updated-dependencies: - dependency-name: ruff dependency-version: 0.14.6 dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index f9386777e..e71a639c0 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ -r requirements-freqai-rl.txt -r docs/requirements-docs.txt -ruff==0.14.5 +ruff==0.14.6 mypy==1.18.2 pre-commit==4.4.0 pytest==9.0.1 From 1a95b04d796c587fc72543645daba4ff6e66bfaa Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:05:23 +0000 Subject: [PATCH 17/41] chore(deps): bump xgboost from 3.1.1 to 3.1.2 Bumps [xgboost](https://github.com/dmlc/xgboost) from 3.1.1 to 3.1.2. - [Release notes](https://github.com/dmlc/xgboost/releases) - [Changelog](https://github.com/dmlc/xgboost/blob/master/NEWS.md) - [Commits](https://github.com/dmlc/xgboost/compare/v3.1.1...v3.1.2) --- updated-dependencies: - dependency-name: xgboost dependency-version: 3.1.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index d50e2de78..24bf58986 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -7,6 +7,6 @@ scikit-learn==1.7.2 joblib==1.5.2 catboost==1.2.8; 'arm' not in platform_machine lightgbm==4.6.0 -xgboost==3.1.1 +xgboost==3.1.2 tensorboard==2.20.0 datasieve==0.1.9 From 61ab49a1244461df032e71f948e3bc313b9bdc8c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:05:36 +0000 Subject: [PATCH 18/41] chore(deps): bump pymdown-extensions from 10.17.1 to 10.17.2 Bumps [pymdown-extensions](https://github.com/facelessuser/pymdown-extensions) from 10.17.1 to 10.17.2. - [Release notes](https://github.com/facelessuser/pymdown-extensions/releases) - [Commits](https://github.com/facelessuser/pymdown-extensions/compare/10.17.1...10.17.2) --- updated-dependencies: - dependency-name: pymdown-extensions dependency-version: 10.17.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 40f8e4b45..b98977366 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -2,6 +2,6 @@ markdown==3.10 mkdocs==1.6.1 mkdocs-material==9.7.0 mdx_truly_sane_lists==1.3 -pymdown-extensions==10.17.1 +pymdown-extensions==10.17.2 jinja2==3.1.6 mike==2.1.3 From 09b15827049f27db6d1c6fc5e894e3342575c67d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:06:04 +0000 Subject: [PATCH 19/41] chore(deps): bump ccxt from 4.5.20 to 4.5.22 Bumps [ccxt](https://github.com/ccxt/ccxt) from 4.5.20 to 4.5.22. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Commits](https://github.com/ccxt/ccxt/compare/v4.5.20...v4.5.22) --- updated-dependencies: - dependency-name: ccxt dependency-version: 4.5.22 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 43f79ab25..eb7aeb528 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ ft-pandas-ta==0.3.16 ta-lib==0.6.8 technical==1.5.3 -ccxt==4.5.20 +ccxt==4.5.22 cryptography==46.0.3 aiohttp==3.13.2 SQLAlchemy==2.0.44 From a13f488b8449ceba2cbfa054b32833f3df29e434 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:29:53 +0000 Subject: [PATCH 20/41] chore(deps): bump peter-evans/create-pull-request from 7.0.8 to 7.0.9 Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 7.0.8 to 7.0.9. - [Release notes](https://github.com/peter-evans/create-pull-request/releases) - [Commits](https://github.com/peter-evans/create-pull-request/compare/271a8d0340265f705b14b6d32b9829c1cb33d45e...84ae59a2cdc2258d6fa0732dd66352dddae2a412) --- updated-dependencies: - dependency-name: peter-evans/create-pull-request dependency-version: 7.0.9 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/binance-lev-tier-update.yml | 2 +- .github/workflows/pre-commit-update.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/binance-lev-tier-update.yml b/.github/workflows/binance-lev-tier-update.yml index 9ec4c3ec0..98e61aa75 100644 --- a/.github/workflows/binance-lev-tier-update.yml +++ b/.github/workflows/binance-lev-tier-update.yml @@ -34,7 +34,7 @@ jobs: run: python build_helpers/binance_update_lev_tiers.py - - uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 + - uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9 with: token: ${{ secrets.REPO_SCOPED_TOKEN }} add-paths: freqtrade/exchange/binance_leverage_tiers.json diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml index 666207901..2cefe4db6 100644 --- a/.github/workflows/pre-commit-update.yml +++ b/.github/workflows/pre-commit-update.yml @@ -28,7 +28,7 @@ jobs: - name: Run auto-update run: pre-commit autoupdate - - uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8 + - uses: peter-evans/create-pull-request@84ae59a2cdc2258d6fa0732dd66352dddae2a412 # v7.0.9 with: token: ${{ secrets.REPO_SCOPED_TOKEN }} add-paths: .pre-commit-config.yaml From 760a2b9ff713d0006a8e3a124af4caf37e9fec12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:30:05 +0000 Subject: [PATCH 21/41] chore(deps): bump zizmorcore/zizmor-action from 0.2.0 to 0.3.0 Bumps [zizmorcore/zizmor-action](https://github.com/zizmorcore/zizmor-action) from 0.2.0 to 0.3.0. - [Release notes](https://github.com/zizmorcore/zizmor-action/releases) - [Commits](https://github.com/zizmorcore/zizmor-action/compare/e673c3917a1aef3c65c972347ed84ccd013ecda4...e639db99335bc9038abc0e066dfcd72e23d26fb4) --- updated-dependencies: - dependency-name: zizmorcore/zizmor-action dependency-version: 0.3.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 0bceed8b0..1b4ac6a78 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -27,4 +27,4 @@ jobs: persist-credentials: false - name: Run zizmor 🌈 - uses: zizmorcore/zizmor-action@e673c3917a1aef3c65c972347ed84ccd013ecda4 # v0.2.0 + uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0 From 8950423418ebb80dc0d8d9652838aeb44fbbb6b9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:30:21 +0000 Subject: [PATCH 22/41] chore(deps): bump actions/checkout from 5.0.0 to 6.0.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.0 to 6.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/binance-lev-tier-update.yml | 2 +- .github/workflows/ci.yml | 16 ++++++++-------- .github/workflows/deploy-docs.yml | 2 +- .github/workflows/devcontainer-build.yml | 2 +- .github/workflows/docker-build.yml | 4 ++-- .github/workflows/docker-update-readme.yml | 2 +- .github/workflows/pre-commit-update.yml | 2 +- .github/workflows/zizmor.yml | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/binance-lev-tier-update.yml b/.github/workflows/binance-lev-tier-update.yml index 9ec4c3ec0..170509df0 100644 --- a/.github/workflows/binance-lev-tier-update.yml +++ b/.github/workflows/binance-lev-tier-update.yml @@ -15,7 +15,7 @@ jobs: environment: name: develop steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14a55ff7a..645328355 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: python-version: ["3.11", "3.12", "3.13"] steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -177,7 +177,7 @@ jobs: name: "Mypy Version Check" runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -195,7 +195,7 @@ jobs: name: "Pre-commit checks" runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -208,7 +208,7 @@ jobs: name: "Documentation build" runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -240,7 +240,7 @@ jobs: name: "Tests and Linting - Online tests" runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -320,7 +320,7 @@ jobs: with: jobs: ${{ toJSON(needs) }} - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -367,7 +367,7 @@ jobs: id-token: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -396,7 +396,7 @@ jobs: id-token: write steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index da033cfb8..f109ddf7a 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -19,7 +19,7 @@ jobs: name: Deploy Docs through mike runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: true diff --git a/.github/workflows/devcontainer-build.yml b/.github/workflows/devcontainer-build.yml index 9946af5e9..429a2bbae 100644 --- a/.github/workflows/devcontainer-build.yml +++ b/.github/workflows/devcontainer-build.yml @@ -24,7 +24,7 @@ jobs: packages: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false - name: Login to GitHub Container Registry diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index b9faa7b75..6790b3497 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -33,7 +33,7 @@ jobs: if: github.repository == 'freqtrade/freqtrade' steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false @@ -152,7 +152,7 @@ jobs: if: github.repository == 'freqtrade/freqtrade' steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false diff --git a/.github/workflows/docker-update-readme.yml b/.github/workflows/docker-update-readme.yml index a510aadf6..de2e5faee 100644 --- a/.github/workflows/docker-update-readme.yml +++ b/.github/workflows/docker-update-readme.yml @@ -11,7 +11,7 @@ jobs: dockerHubDescription: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml index 666207901..529fbef73 100644 --- a/.github/workflows/pre-commit-update.yml +++ b/.github/workflows/pre-commit-update.yml @@ -13,7 +13,7 @@ jobs: auto-update: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6.0.0 with: persist-credentials: false diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 0bceed8b0..b479fbe4f 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -22,7 +22,7 @@ jobs: # actions: read # only needed for private repos steps: - name: Checkout repository - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: persist-credentials: false From 39233ff240d150c801dea8df83600c4853bbb660 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 03:30:36 +0000 Subject: [PATCH 23/41] chore(deps): bump astral-sh/setup-uv from 7.1.2 to 7.1.4 Bumps [astral-sh/setup-uv](https://github.com/astral-sh/setup-uv) from 7.1.2 to 7.1.4. - [Release notes](https://github.com/astral-sh/setup-uv/releases) - [Commits](https://github.com/astral-sh/setup-uv/compare/85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41...1e862dfacbd1d6d858c55d9b792c756523627244) --- updated-dependencies: - dependency-name: astral-sh/setup-uv dependency-version: 7.1.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .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 14a55ff7a..8ed594d1c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install uv - uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 + uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4 with: activate-environment: true enable-cache: true @@ -250,7 +250,7 @@ jobs: python-version: "3.12" - name: Install uv - uses: astral-sh/setup-uv@85856786d1ce8acfbcc2f13a5f3fbd6b938f9f41 # v7.1.2 + uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4 with: activate-environment: true enable-cache: true From 45b1dabe0a6f57ccdef94366a703d96ceca0853c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Dec 2025 06:44:00 +0100 Subject: [PATCH 24/41] chore: bump scipy-stubs in pre-commit config --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6a578c707..d6179bbaf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,7 +31,7 @@ repos: - types-requests==2.32.4.20250913 - types-tabulate==0.9.0.20241207 - types-python-dateutil==2.9.0.20251115 - - scipy-stubs==1.16.3.0 + - scipy-stubs==1.16.3.1 - SQLAlchemy==2.0.44 # stages: [push] From dc88ea0ed92fe45dbbfbcf3fae0ac6bc0424afd0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 05:57:42 +0000 Subject: [PATCH 25/41] chore(deps-dev): bump pre-commit from 4.4.0 to 4.5.0 Bumps [pre-commit](https://github.com/pre-commit/pre-commit) from 4.4.0 to 4.5.0. - [Release notes](https://github.com/pre-commit/pre-commit/releases) - [Changelog](https://github.com/pre-commit/pre-commit/blob/main/CHANGELOG.md) - [Commits](https://github.com/pre-commit/pre-commit/compare/v4.4.0...v4.5.0) --- updated-dependencies: - dependency-name: pre-commit dependency-version: 4.5.0 dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index e71a639c0..639373f08 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ ruff==0.14.6 mypy==1.18.2 -pre-commit==4.4.0 +pre-commit==4.5.0 pytest==9.0.1 pytest-asyncio==1.3.0 pytest-cov==7.0.0 From dbde8332f7ed2003f9e4328cdcfb0bcdca8b16ff Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 06:00:27 +0000 Subject: [PATCH 26/41] chore(deps): bump fastapi from 0.121.3 to 0.122.0 Bumps [fastapi](https://github.com/fastapi/fastapi) from 0.121.3 to 0.122.0. - [Release notes](https://github.com/fastapi/fastapi/releases) - [Commits](https://github.com/fastapi/fastapi/compare/0.121.3...0.122.0) --- updated-dependencies: - dependency-name: fastapi dependency-version: 0.122.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 4a34268d4..0f7dcaa1c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,7 +37,7 @@ orjson==3.11.4 sdnotify==0.3.2 # API Server -fastapi==0.121.3 +fastapi==0.122.0 pydantic==2.12.5 uvicorn==0.38.0 pyjwt==2.10.1 From 2f990b773d00c8f402428e2e321e014f8341df1a Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Dec 2025 19:08:50 +0100 Subject: [PATCH 27/41] chore: increase dependabot cooldown to 7 days --- .github/dependabot.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 906666552..a33f75428 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,7 +2,7 @@ version: 2 updates: - package-ecosystem: docker cooldown: - default-days: 4 + default-days: 7 directories: - "/" - "/docker" @@ -16,7 +16,7 @@ updates: - package-ecosystem: devcontainers directory: "/" cooldown: - default-days: 4 + default-days: 7 schedule: interval: daily open-pull-requests-limit: 10 @@ -24,7 +24,7 @@ updates: - package-ecosystem: pip directory: "/" cooldown: - default-days: 4 + default-days: 7 exclude: - ccxt schedule: @@ -51,7 +51,7 @@ updates: - package-ecosystem: "github-actions" directory: "/" cooldown: - default-days: 4 + default-days: 7 schedule: interval: "weekly" open-pull-requests-limit: 10 From 38151423b8da9fa8d36f99b995ee141458b9647a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Dec 2025 18:48:43 +0000 Subject: [PATCH 28/41] chore(deps): bump actions/checkout from 5.0.1 to 6.0.0 Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.1 to 6.0.0. - [Release notes](https://github.com/actions/checkout/releases) - [Commits](https://github.com/actions/checkout/compare/v5.0.1...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 5c8ef875b..2835046f4 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -22,7 +22,7 @@ jobs: # actions: read # only needed for private repos steps: - name: Checkout repository - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 + uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 # v5.0.1 with: persist-credentials: false From 2b729c25277722c3a13541d5120460c84741be80 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 1 Dec 2025 20:01:43 +0100 Subject: [PATCH 29/41] test: improve integration test to have proper return value --- tests/freqtradebot/test_integration.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tests/freqtradebot/test_integration.py b/tests/freqtradebot/test_integration.py index 2cb4b6aa8..a1090104a 100644 --- a/tests/freqtradebot/test_integration.py +++ b/tests/freqtradebot/test_integration.py @@ -50,7 +50,14 @@ def test_may_execute_exit_stoploss_on_exchange_multi(default_conf, ticker, fee, stoploss_order_mock = MagicMock(side_effect=stop_orders) # Sell 3rd trade (not called for the first trade) should_sell_mock = MagicMock(side_effect=[[], [ExitCheckTuple(exit_type=ExitType.EXIT_SIGNAL)]]) - cancel_order_mock = MagicMock() + + def patch_stoploss(order_id, *args, **kwargs): + slo = stoploss_order_open.copy() + slo["id"] = order_id + slo["status"] = "canceled" + return slo + + cancel_order_mock = MagicMock(side_effect=patch_stoploss) mocker.patch.multiple( EXMS, create_stoploss=stoploss, From 659509e085be606f49b735bbb9257ca175fae4fb Mon Sep 17 00:00:00 2001 From: Freqtrade Bot <154552126+freqtrade-bot@users.noreply.github.com> Date: Tue, 2 Dec 2025 03:26:42 +0000 Subject: [PATCH 30/41] chore: update pre-commit hooks --- .pre-commit-config.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6179bbaf..7375708e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -21,7 +21,7 @@ repos: # stages: [push] - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.18.2" + rev: "v1.19.0" hooks: - id: mypy exclude: build_helpers @@ -44,7 +44,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.14.6' + rev: 'v0.14.7' hooks: - id: ruff - id: ruff-format @@ -83,6 +83,6 @@ repos: # Ensure github actions remain safe - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.16.3 + rev: v1.18.0 hooks: - id: zizmor From d7eb49d5d03b589ffc63a2d590db050d94529d2f Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 06:31:32 +0100 Subject: [PATCH 31/41] chore: align usage of actions/checkout version pin --- .github/workflows/zizmor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 2835046f4..594b6507d 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -22,7 +22,7 @@ jobs: # actions: read # only needed for private repos steps: - name: Checkout repository - uses: actions/checkout@c2d88d3ecc89a9ef08eebf45d9637801dcee7eb5 # v5.0.1 + uses: actions/checkout@v6.0.0 with: persist-credentials: false From 4e6ea1d2ba52d637eb38199ff47aa493f52af3ed Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 30 Nov 2025 14:21:14 +0100 Subject: [PATCH 32/41] feat: don't cancel stoploss early if it's not necessary --- freqtrade/freqtradebot.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 6485b5d11..89abb0915 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1063,7 +1063,16 @@ class FreqtradeBot(LoggingMixin): return True - def cancel_stoploss_on_exchange(self, trade: Trade) -> Trade: + def cancel_stoploss_on_exchange(self, trade: Trade, allow_nonblocking: bool = False) -> Trade: + """ + Cancels on exchange stoploss orders for the given trade. + :param trade: Trade for which to cancel stoploss order + :param allow_nonblocking: If True, will skip cancelling stoploss on exchange + if the exchange supports blocking stoploss orders. + """ + if allow_nonblocking and not self.exchange.get_option("stoploss_blocks_assets", True): + logger.info(f"Skipping cancelling stoploss on exchange for {trade}.") + return trade # First cancelling stoploss on exchange ... for oslo in trade.open_sl_orders: try: @@ -2088,7 +2097,7 @@ class FreqtradeBot(LoggingMixin): limit = self.get_valid_price(custom_exit_price, proposed_limit_rate) # First cancelling stoploss on exchange ... - trade = self.cancel_stoploss_on_exchange(trade) + trade = self.cancel_stoploss_on_exchange(trade, allow_nonblocking=True) order_type = ordertype or self.strategy.order_types[exit_type] if exit_check.exit_type == ExitType.EMERGENCY_EXIT: From 4b2d09925879be11c17137ebfdd230967bdddc22 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 06:14:48 +0100 Subject: [PATCH 33/41] fix: ensure stoploss on exchange is canceled once the trade closes. --- freqtrade/freqtradebot.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 89abb0915..09afd8528 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -2387,6 +2387,8 @@ class FreqtradeBot(LoggingMixin): self.strategy.ft_stoploss_adjust( current_rate, trade, datetime.now(UTC), profit, 0, after_fill=True ) + if not trade.is_open: + self.cancel_stoploss_on_exchange(trade) # Updating wallets when order is closed self.wallets.update() return trade From faf552837aeee78d3ae4fba187141c2cf19069fa Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 06:24:57 +0100 Subject: [PATCH 34/41] test: add test for stoploss cancel skips --- tests/freqtradebot/test_integration.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/freqtradebot/test_integration.py b/tests/freqtradebot/test_integration.py index a1090104a..5d3df4c3b 100644 --- a/tests/freqtradebot/test_integration.py +++ b/tests/freqtradebot/test_integration.py @@ -800,9 +800,13 @@ def test_dca_handle_similar_open_order( # Should Create a new exit order freqtrade.exchange.amount_to_contract_precision = MagicMock(return_value=2) freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-2) + msg = r"Skipping cancelling stoploss on exchange for.*" mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=False) + assert not log_has_re(msg, caplog) freqtrade.process() + assert log_has_re(msg, caplog) + trade = Trade.get_trades().first() assert trade.orders[-2].status == "closed" From a013793b2f9e091391821ce9dd82d5ca2c5bd116 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 07:08:15 +0100 Subject: [PATCH 35/41] feat: improved trade repr format (used for logs) --- freqtrade/persistence/trade_model.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index bc4d1c47e..85f072747 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -48,7 +48,7 @@ from freqtrade.leverage import interest from freqtrade.misc import safe_value_fallback from freqtrade.persistence.base import ModelBase, SessionType from freqtrade.persistence.custom_data import CustomDataWrapper, _CustomData -from freqtrade.util import FtPrecise, dt_from_ts, dt_now, dt_ts, dt_ts_none +from freqtrade.util import FtPrecise, dt_from_ts, dt_now, dt_ts, dt_ts_none, round_value logger = logging.getLogger(__name__) @@ -654,9 +654,10 @@ class LocalTrade: ) return ( - f"Trade(id={self.id}, pair={self.pair}, amount={self.amount:.8f}, " - f"is_short={self.is_short or False}, leverage={self.leverage or 1.0}, " - f"open_rate={self.open_rate:.8f}, open_since={open_since})" + f"Trade(id={self.id}, pair={self.pair}, amount={round_value(self.amount, 8)}, " + f"is_short={self.is_short or False}, " + f"leverage={round_value(self.leverage or 1.0, 1)}, " + f"open_rate={round_value(self.open_rate, 8)}, open_since={open_since})" ) def to_json(self, minified: bool = False) -> dict[str, Any]: From 0f5427f4a0a8e43094dbf5fb633224e1e23acf72 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 07:10:33 +0100 Subject: [PATCH 36/41] feat: round_value should support None --- freqtrade/util/formatters.py | 4 ++-- tests/util/test_formatters.py | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/util/formatters.py b/freqtrade/util/formatters.py index 3d7493a2a..7ca028984 100644 --- a/freqtrade/util/formatters.py +++ b/freqtrade/util/formatters.py @@ -23,7 +23,7 @@ def strip_trailing_zeros(value: str) -> str: return value.rstrip("0").rstrip(".") -def round_value(value: float, decimals: int, keep_trailing_zeros=False) -> str: +def round_value(value: float | None, decimals: int, keep_trailing_zeros=False) -> str: """ Round value to given decimals :param value: Value to be rounded @@ -31,7 +31,7 @@ def round_value(value: float, decimals: int, keep_trailing_zeros=False) -> str: :param keep_trailing_zeros: Keep trailing zeros "222.200" vs. "222.2" :return: Rounded value as string """ - if isnan(value): + if value is None or isnan(value): return "N/A" val = f"{value:.{decimals}f}" if not keep_trailing_zeros: diff --git a/tests/util/test_formatters.py b/tests/util/test_formatters.py index a884c0750..a7463fdfa 100644 --- a/tests/util/test_formatters.py +++ b/tests/util/test_formatters.py @@ -57,6 +57,8 @@ def test_round_value(): assert round_value(222.2, 0, True) == "222" assert round_value(float("nan"), 0, True) == "N/A" assert round_value(float("nan"), 10, True) == "N/A" + assert round_value(None, 10, True) == "N/A" + assert round_value(None, 1, True) == "N/A" def test_format_duration(): From 9c73c8671e51bcea8d39568bc00a53f70b29af95 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 07:12:32 +0100 Subject: [PATCH 37/41] feat: improve format_date with fallback value --- freqtrade/util/datetime_helpers.py | 5 +++-- tests/util/test_datetime_helpers.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/freqtrade/util/datetime_helpers.py b/freqtrade/util/datetime_helpers.py index e2390105d..b6535db5d 100644 --- a/freqtrade/util/datetime_helpers.py +++ b/freqtrade/util/datetime_helpers.py @@ -90,15 +90,16 @@ def dt_humanize_delta(dt: datetime): return humanize.naturaltime(dt) -def format_date(date: datetime | None) -> str: +def format_date(date: datetime | None, fallback: str = "") -> str: """ Return a formatted date string. Returns an empty string if date is None. :param date: datetime to format + :param fallback: value to return if date is None """ if date: return date.strftime(DATETIME_PRINT_FORMAT) - return "" + return fallback def format_ms_time(date: int | float) -> str: diff --git a/tests/util/test_datetime_helpers.py b/tests/util/test_datetime_helpers.py index 82dd6dbf6..8ccb8ba20 100644 --- a/tests/util/test_datetime_helpers.py +++ b/tests/util/test_datetime_helpers.py @@ -108,6 +108,7 @@ def test_format_date() -> None: date = datetime(2021, 9, 30, 22, 59, 3, 455555, tzinfo=UTC) assert format_date(date) == "2021-09-30 22:59:03" assert format_date(None) == "" + assert format_date(None, "closed") == "closed" def test_format_ms_time_detailed() -> None: From 19526f1df2b613f8eac1b8d4ebaba576c9311d77 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 19:10:45 +0100 Subject: [PATCH 38/41] test: update tests to align to improved formatting --- tests/freqtradebot/test_freqtradebot.py | 26 ++++++++++++------------- tests/persistence/test_persistence.py | 20 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index e36d70c12..f8b790c44 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -2548,9 +2548,9 @@ def test_manage_open_orders_exception( caplog.clear() freqtrade.manage_open_orders() assert log_has_re( - r"Cannot query order for Trade\(id=1, pair=ADA/USDT, amount=30.00000000, " - r"is_short=False, leverage=1.0, " - r"open_rate=2.00000000, open_since=" + r"Cannot query order for Trade\(id=1, pair=ADA/USDT, amount=30, " + r"is_short=False, leverage=1, " + r"open_rate=2, open_since=" f"{open_trade_usdt.open_date.strftime('%Y-%m-%d %H:%M:%S')}" r"\) due to Traceback \(most recent call last\):\n*", caplog, @@ -3751,8 +3751,8 @@ def test_get_real_amount_quote( # Amount is reduced by "fee" assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == (amount * 0.001) assert log_has( - "Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, is_short=False," - " leverage=1.0, open_rate=0.24544100, open_since=closed), fee=0.008.", + "Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8, is_short=False," + " leverage=1, open_rate=0.245441, open_since=closed), fee=0.008.", caplog, ) @@ -3805,8 +3805,8 @@ def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mock # Amount is reduced by "fee" assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) is None assert log_has( - "Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, " - "is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed) failed: " + "Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8, " + "is_short=False, leverage=1, open_rate=0.245441, open_since=closed) failed: " "myTrade-dict empty found", caplog, ) @@ -3825,8 +3825,8 @@ def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mock 0, True, ( - "Fee for Trade Trade(id=None, pair=LTC/ETH, amount=8.00000000, is_short=False, " - "leverage=1.0, open_rate=0.24544100, open_since=closed) [buy]: 0.00094518 BNB -" + "Fee for Trade Trade(id=None, pair=LTC/ETH, amount=8, is_short=False, " + "leverage=1, open_rate=0.245441, open_since=closed) [buy]: 0.00094518 BNB -" " rate: None" ), ), @@ -3836,8 +3836,8 @@ def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mock 0.004, False, ( - "Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, " - "is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed), fee=0.004." + "Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8, " + "is_short=False, leverage=1, open_rate=0.245441, open_since=closed), fee=0.004." ), ), # invalid, no currency in from fee dict @@ -3941,8 +3941,8 @@ def test_get_real_amount_multi( assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == expected_amount assert log_has( ( - "Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, " - "is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed), " + "Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8, " + "is_short=False, leverage=1, open_rate=0.245441, open_since=closed), " f"fee={expected_amount}." ), caplog, diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index 8fcc59691..6e779137d 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -372,8 +372,8 @@ def test_borrowed(fee, is_short, lev, borrowed, trading_mode): @pytest.mark.parametrize( "is_short,open_rate,close_rate,lev,profit,trading_mode", [ - (False, 2.0, 2.2, 1.0, 0.09451372, spot), - (True, 2.2, 2.0, 3.0, 0.25894253, margin), + (False, 2, 2.2, 1, 0.09451372, spot), + (True, 2.2, 2.0, 3, 0.25894253, margin), ], ) @pytest.mark.usefixtures("init_persistence") @@ -493,8 +493,8 @@ def test_update_limit_order( assert trade.close_date is None assert log_has_re( f"LIMIT_{entry_side.upper()} has been fulfilled for " - r"Trade\(id=2, pair=ADA/USDT, amount=30.00000000, " - f"is_short={is_short}, leverage={lev}, open_rate={open_rate}0000000, " + r"Trade\(id=2, pair=ADA/USDT, amount=30, " + f"is_short={is_short}, leverage={lev}, open_rate={open_rate}, " r"open_since=.*\).", caplog, ) @@ -511,8 +511,8 @@ def test_update_limit_order( assert trade.close_date is not None assert log_has_re( f"LIMIT_{exit_side.upper()} has been fulfilled for " - r"Trade\(id=2, pair=ADA/USDT, amount=30.00000000, " - f"is_short={is_short}, leverage={lev}, open_rate={open_rate}0000000, " + r"Trade\(id=2, pair=ADA/USDT, amount=30, " + f"is_short={is_short}, leverage={lev}, open_rate={open_rate}, " r"open_since=.*\).", caplog, ) @@ -545,8 +545,8 @@ def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee, assert trade.close_date is None assert log_has_re( r"MARKET_BUY has been fulfilled for Trade\(id=1, " - r"pair=ADA/USDT, amount=30.00000000, is_short=False, leverage=1.0, " - r"open_rate=2.00000000, open_since=.*\).", + r"pair=ADA/USDT, amount=30, is_short=False, leverage=1, " + r"open_rate=2, open_since=.*\).", caplog, ) @@ -561,8 +561,8 @@ def test_update_market_order(market_buy_order_usdt, market_sell_order_usdt, fee, assert trade.close_date is not None assert log_has_re( r"MARKET_SELL has been fulfilled for Trade\(id=1, " - r"pair=ADA/USDT, amount=30.00000000, is_short=False, leverage=1.0, " - r"open_rate=2.00000000, open_since=.*\).", + r"pair=ADA/USDT, amount=30, is_short=False, leverage=1, " + r"open_rate=2, open_since=.*\).", caplog, ) From 971cb94ce47f85a451168545079f6855810f0770 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 20:24:43 +0100 Subject: [PATCH 39/41] feat: limit returned columns for merged mark/funding calc to used columns --- freqtrade/exchange/exchange.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 60f4d5fdd..717844db7 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -3784,10 +3784,11 @@ class Exchange: :param mark_rates: Dataframe containing Mark rates (Type mark_ohlcv_price) :param futures_funding_rate: Fake funding rate to use if funding_rates are not available """ + relevant_cols = ["date", "open_mark", "open_fund"] if futures_funding_rate is None: return mark_rates.merge( funding_rates, on="date", how="inner", suffixes=["_mark", "_fund"] - ) + )[relevant_cols] else: if len(funding_rates) == 0: # No funding rate candles - full fillup with fallback variable @@ -3800,7 +3801,7 @@ class Exchange: "low": "low_mark", "volume": "volume_mark", } - ) + )[relevant_cols] else: # Fill up missing funding_rate candles with fallback value @@ -3808,7 +3809,7 @@ class Exchange: funding_rates, on="date", how="left", suffixes=["_mark", "_fund"] ) combined["open_fund"] = combined["open_fund"].fillna(futures_funding_rate) - return combined + return combined[relevant_cols] def calculate_funding_fees( self, From 10e7d279f0c0339517d0409e733abfaa970033a8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 20:39:10 +0100 Subject: [PATCH 40/41] test: improve funding rate combine test --- tests/exchange/test_exchange.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index f280ec9d9..8d7e0b0d8 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -5304,6 +5304,7 @@ def test_combine_funding_and_mark( {"date": trade_date, "open": mark_price}, ] ) + # Test fallback to futures funding rate for missing funding rates df = exchange.combine_funding_and_mark(funding_rates, mark_rates, futures_funding_rate) if futures_funding_rate is not None: @@ -5331,6 +5332,33 @@ def test_combine_funding_and_mark( assert len(df) == 0 + # Test fallback to futures funding rate for middle missing funding rate + funding_rates = DataFrame( + [ + {"date": prior2_date, "open": funding_rate}, + # missing 1 hour + {"date": trade_date, "open": funding_rate}, + ], + ) + mark_rates = DataFrame( + [ + {"date": prior2_date, "open": mark_price}, + {"date": prior_date, "open": mark_price}, + {"date": trade_date, "open": mark_price}, + ] + ) + df = exchange.combine_funding_and_mark(funding_rates, mark_rates, futures_funding_rate) + + if futures_funding_rate is not None: + assert len(df) == 3 + assert df.iloc[0]["open_fund"] == funding_rate + assert df.iloc[1]["open_fund"] == futures_funding_rate + assert df.iloc[2]["open_fund"] == funding_rate + assert df["date"].to_list() == [prior2_date, prior_date, trade_date] + else: + assert len(df) == 2 + assert df["date"].to_list() == [prior2_date, trade_date] + @pytest.mark.parametrize( "exchange,rate_start,rate_end,d1,d2,amount,expected_fees", From 713746189178b7c27be8c7ef50f521b224f485d8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 2 Dec 2025 21:10:51 +0100 Subject: [PATCH 41/41] feat: improved download-data logging --- freqtrade/data/history/history_utils.py | 8 ++++++-- tests/data/test_history.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index 8f4a8d6c5..1467497d6 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -308,11 +308,15 @@ def _download_pair_history( candle_type=candle_type, until_ms=until_ms if until_ms else None, ) - logger.info(f"Downloaded data for {pair} with length {len(new_dataframe)}.") + logger.info( + f"Downloaded data for {pair}, {timeframe}, {candle_type} with length " + f"{len(new_dataframe)}." + ) else: new_dataframe = pair_candles logger.info( - f"Downloaded data for {pair} with length {len(new_dataframe)}. Parallel Method." + f"Downloaded data for {pair}, {timeframe}, {candle_type} with length " + f"{len(new_dataframe)}. Parallel Method." ) if data.empty: diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 2dc3204db..115e73192 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -896,7 +896,7 @@ def test_download_pair_history_with_pair_candles(mocker, default_conf, tmp_path, assert get_historic_ohlcv_mock.call_count == 0 # Verify the log message indicating parallel method was used (line 315-316) - assert log_has("Downloaded data for TEST/BTC with length 3. Parallel Method.", caplog) + assert log_has("Downloaded data for TEST/BTC, 5m, spot with length 3. Parallel Method.", caplog) # Verify data was stored assert data_handler_mock.ohlcv_store.call_count == 1