From 10a0bf386b835f2590abfa4435e304cee374901e Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:46:23 +0200 Subject: [PATCH 01/15] ci: Don't test against 3.10 --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4372a8775..3a7f514c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: matrix: os: [ "ubuntu-22.04", "ubuntu-24.04" ] - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 @@ -160,7 +160,7 @@ jobs: strategy: matrix: os: [ "macos-14", "macos-15" ] - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 @@ -288,7 +288,7 @@ jobs: strategy: matrix: os: [ windows-latest ] - python-version: ["3.10", "3.11", "3.12", "3.13"] + python-version: ["3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 From 7c84059b3a552a4b5589fc639b4d4692a675c452 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:46:40 +0200 Subject: [PATCH 02/15] chore: drop support for pytohn 3.10 in setup scripts --- setup.ps1 | 7 ++----- setup.sh | 8 ++++---- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/setup.ps1 b/setup.ps1 index 0bf0542cd..39c9b6665 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -153,16 +153,13 @@ function Find-PythonExecutable { "python3.13", "python3.12", "python3.11", - "python3.10", "python3", "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python313\python.exe", "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python312\python.exe", "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python311\python.exe", - "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python310\python.exe", "C:\Python313\python.exe", "C:\Python312\python.exe", "C:\Python311\python.exe", - "C:\Python310\python.exe" ) @@ -178,10 +175,10 @@ function Main { "Starting the operations..." | Out-File $LogFilePath -Append "Current directory: $(Get-Location)" | Out-File $LogFilePath -Append - # Exit on lower versions than Python 3.10 or when Python executable not found + # Exit on lower versions than Python 3.11 or when Python executable not found $PythonExecutable = Find-PythonExecutable if ($null -eq $PythonExecutable) { - Write-Log "No suitable Python executable found. Please ensure that Python 3.10 or higher is installed and available in the system PATH." -Level 'ERROR' + Write-Log "No suitable Python executable found. Please ensure that Python 3.11 or higher is installed and available in the system PATH." -Level 'ERROR' Exit 1 } diff --git a/setup.sh b/setup.sh index 4e877b19d..a5c73f945 100755 --- a/setup.sh +++ b/setup.sh @@ -25,7 +25,7 @@ function check_installed_python() { exit 2 fi - for v in 13 12 11 10 + for v in 13 12 11 do PYTHON="python3.${v}" which $PYTHON @@ -36,7 +36,7 @@ function check_installed_python() { fi done - echo "No usable python found. Please make sure to have python3.10 or newer installed." + echo "No usable python found. Please make sure to have python3.11 or newer installed." exit 1 } @@ -257,7 +257,7 @@ function install() { install_redhat else echo "This script does not support your OS." - echo "If you have Python version 3.10 - 3.13, pip, virtualenv, ta-lib you can continue." + echo "If you have Python version 3.11 - 3.13, pip, virtualenv, ta-lib you can continue." echo "Wait 10 seconds to continue the next install steps or use ctrl+c to interrupt this shell." sleep 10 fi @@ -284,7 +284,7 @@ function help() { echo " -p,--plot Install dependencies for Plotting scripts." } -# Verify if 3.10+ is installed +# Verify if 3.11+ is installed check_installed_python case $* in From b49dafc41242b434986d7bfc0636c96fca48bb1a Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:47:40 +0200 Subject: [PATCH 03/15] docs: update to 3.11+ version requirement --- README.md | 4 ++-- docs/index.md | 2 +- docs/installation.md | 8 ++++---- docs/windows_installation.md | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index f56b44887..7fa99d54b 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Please find the complete documentation on the [freqtrade website](https://www.fr ## Features -- [x] **Based on Python 3.10+**: For botting on any operating system - Windows, macOS and Linux. +- [x] **Based on Python 3.11+**: For botting on any operating system - Windows, macOS and Linux. - [x] **Persistence**: Persistence is achieved through sqlite. - [x] **Dry-run**: Run the bot without paying money. - [x] **Backtesting**: Run a simulation of your buy/sell strategy. @@ -219,7 +219,7 @@ To run this bot we recommend you a cloud instance with a minimum of: ### Software requirements -- [Python >= 3.10](http://docs.python-guide.org/en/latest/starting/installation/) +- [Python >= 3.11](http://docs.python-guide.org/en/latest/starting/installation/) - [pip](https://pip.pypa.io/en/stable/installing/) - [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) - [TA-Lib](https://ta-lib.github.io/ta-lib-python/) diff --git a/docs/index.md b/docs/index.md index 962130a3c..839a32f58 100644 --- a/docs/index.md +++ b/docs/index.md @@ -87,7 +87,7 @@ To run this bot we recommend you a linux cloud instance with a minimum of: Alternatively -- Python 3.10+ +- Python 3.11+ - pip (pip3) - git - TA-Lib diff --git a/docs/installation.md b/docs/installation.md index b835498d0..564b87dfb 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -24,7 +24,7 @@ The easiest way to install and run Freqtrade is to clone the bot Github reposito The `stable` branch contains the code of the last release (done usually once per month on an approximately one week old snapshot of the `develop` branch to prevent packaging bugs, so potentially it's more stable). !!! Note - Python3.10 or higher and the corresponding `pip` are assumed to be available. The install-script will warn you and stop if that's not the case. `git` is also needed to clone the Freqtrade repository. + Python3.11 or higher and the corresponding `pip` are assumed to be available. The install-script will warn you and stop if that's not the case. `git` is also needed to clone the Freqtrade repository. Also, python headers (`python-dev` / `python-devel`) must be available for the installation to complete successfully. !!! Warning "Up-to-date clock" @@ -42,7 +42,7 @@ These requirements apply to both [Script Installation](#script-installation) and ### Install guide -* [Python >= 3.10](http://docs.python-guide.org/en/latest/starting/installation/) +* [Python >= 3.11](http://docs.python-guide.org/en/latest/starting/installation/) * [pip](https://pip.pypa.io/en/stable/installing/) * [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [virtualenv](https://virtualenv.pypa.io/en/stable/installation.html) (Recommended) @@ -54,7 +54,7 @@ We've included/collected install instructions for Ubuntu, MacOS, and Windows. Th OS Specific steps are listed first, the common section below is necessary for all systems. !!! Note - Python3.10 or higher and the corresponding pip are assumed to be available. + Python3.11 or higher and the corresponding pip are assumed to be available. === "Debian/Ubuntu" #### Install necessary dependencies @@ -179,7 +179,7 @@ You can as well update, configure and reset the codebase of your bot with `./scr ** --install ** With this option, the script will install the bot and most dependencies: -You will need to have git and python3.10+ installed beforehand for this to work. +You will need to have git and python3.11+ installed beforehand for this to work. * Mandatory software as: `ta-lib` * Setup your virtualenv under `.venv/` diff --git a/docs/windows_installation.md b/docs/windows_installation.md index 936124b05..35a3d4441 100644 --- a/docs/windows_installation.md +++ b/docs/windows_installation.md @@ -5,7 +5,7 @@ We **strongly** recommend that Windows users use [Docker](docker_quickstart.md) If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu instructions should work. Otherwise, please follow the instructions below. -All instructions assume that python 3.10+ is installed and available. +All instructions assume that python 3.11+ is installed and available. ## Clone the git repository @@ -42,7 +42,7 @@ cd freqtrade Install ta-lib according to the [ta-lib documentation](https://github.com/TA-Lib/ta-lib-python#windows). -As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), Freqtrade provides these dependencies (in the binary wheel format) for the latest 3 Python versions (3.10, 3.11, 3.12 and 3.13) and for 64bit Windows. +As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), Freqtrade provides these dependencies (in the binary wheel format) for the latest 3 Python versions (3.11, 3.12 and 3.13) and for 64bit Windows. These Wheels are also used by CI running on windows, and are therefore tested together with freqtrade. Other versions must be downloaded from the above link. From e1f26aeb4e9016888e70f06baa51d69ecd6f8aba Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:48:29 +0200 Subject: [PATCH 04/15] chore!: drop support for python 3.10 --- freqtrade/__main__.py | 2 +- freqtrade/main.py | 4 ++-- ft_client/pyproject.toml | 3 +-- pyproject.toml | 3 +-- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/freqtrade/__main__.py b/freqtrade/__main__.py index caa26ce0a..06dc49bbf 100755 --- a/freqtrade/__main__.py +++ b/freqtrade/__main__.py @@ -3,7 +3,7 @@ __main__.py for Freqtrade To launch Freqtrade as a module -> python -m freqtrade (with Python >= 3.10) +> python -m freqtrade (with Python >= 3.11) """ from freqtrade import main diff --git a/freqtrade/main.py b/freqtrade/main.py index d4aab3468..fd142452a 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -10,8 +10,8 @@ from typing import Any # check min. python version -if sys.version_info < (3, 10): # pragma: no cover # noqa: UP036 - sys.exit("Freqtrade requires Python version >= 3.10") +if sys.version_info < (3, 11): # pragma: no cover # noqa: UP036 + sys.exit("Freqtrade requires Python version >= 3.11") from freqtrade import __version__ from freqtrade.commands import Arguments diff --git a/ft_client/pyproject.toml b/ft_client/pyproject.toml index d69dad49d..f41331b26 100644 --- a/ft_client/pyproject.toml +++ b/ft_client/pyproject.toml @@ -13,14 +13,13 @@ authors = [ description = "Freqtrade - Client scripts" readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.11" license = {text = "GPLv3"} # license = "GPLv3" classifiers = [ "Environment :: Console", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", diff --git a/pyproject.toml b/pyproject.toml index df6048cb6..dac02870a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,13 +13,12 @@ authors = [ description = "Freqtrade - Crypto Trading Bot" readme = "README.md" -requires-python = ">=3.10" +requires-python = ">=3.11" license = {text = "GPLv3"} classifiers = [ "Environment :: Console", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", From 5f59e1435e4832908b2141ceccda6841d2edaeb5 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:50:55 +0200 Subject: [PATCH 05/15] chore: bump known 3.11+ dependencies --- requirements-hyperopt.txt | 2 +- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 3c5dd3fd8..36acef376 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -2,7 +2,7 @@ -r requirements.txt # Required for hyperopt -scipy==1.15.3 +scipy==1.16.0 scikit-learn==1.7.0 filelock==3.18.0 optuna==4.4.0 diff --git a/requirements.txt b/requirements.txt index eb3ce455a..36020dd63 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -numpy==2.2.6 +numpy==2.3.1 pandas==2.3.0 bottleneck==1.5.0 numexpr==2.11.0 From 37cc949d94278ce87379721ddd36607699a5b511 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:56:42 +0200 Subject: [PATCH 06/15] refactor: Update utc to UTC --- freqtrade/configuration/timerange.py | 9 ++++----- freqtrade/data/btanalysis/bt_fileutils.py | 4 ++-- freqtrade/data/dataprovider.py | 16 ++++++++-------- .../data/history/datahandlers/idatahandler.py | 10 +++++----- freqtrade/exchange/binance.py | 4 ++-- freqtrade/exchange/bitpanda.py | 4 ++-- freqtrade/exchange/exchange.py | 12 ++++++------ freqtrade/exchange/exchange_utils.py | 4 ++-- freqtrade/exchange/exchange_utils_timeframe.py | 6 +++--- freqtrade/freqai/data_drawer.py | 4 ++-- freqtrade/freqai/data_kitchen.py | 8 ++++---- freqtrade/freqai/freqai_interface.py | 4 ++-- freqtrade/freqai/utils.py | 4 ++-- freqtrade/rpc/api_server/ws/channel.py | 4 ++-- freqtrade/rpc/rpc.py | 6 +++--- 15 files changed, 49 insertions(+), 50 deletions(-) diff --git a/freqtrade/configuration/timerange.py b/freqtrade/configuration/timerange.py index 30860cd63..7cf629c38 100644 --- a/freqtrade/configuration/timerange.py +++ b/freqtrade/configuration/timerange.py @@ -4,9 +4,8 @@ This module contains the argument manager class import logging import re -from datetime import datetime, timezone - -from typing_extensions import Self +from datetime import UTC, datetime +from typing import Self from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.exceptions import ConfigurationError @@ -152,7 +151,7 @@ class TimeRange: if stype[0] == "date" and len(starts) == 8: start = int( datetime.strptime(starts, "%Y%m%d") - .replace(tzinfo=timezone.utc) + .replace(tzinfo=UTC) .timestamp() ) elif len(starts) == 13: @@ -165,7 +164,7 @@ class TimeRange: if stype[1] == "date" and len(stops) == 8: stop = int( datetime.strptime(stops, "%Y%m%d") - .replace(tzinfo=timezone.utc) + .replace(tzinfo=UTC) .timestamp() ) elif len(stops) == 13: diff --git a/freqtrade/data/btanalysis/bt_fileutils.py b/freqtrade/data/btanalysis/bt_fileutils.py index 50de33974..d6957b792 100644 --- a/freqtrade/data/btanalysis/bt_fileutils.py +++ b/freqtrade/data/btanalysis/bt_fileutils.py @@ -5,7 +5,7 @@ Helpers when analyzing backtest data import logging import zipfile from copy import copy -from datetime import datetime, timezone +from datetime import UTC, datetime from io import BytesIO, StringIO from pathlib import Path from typing import Any, Literal @@ -324,7 +324,7 @@ def find_existing_backtest_stats( if min_backtest_date is not None: backtest_date = strategy_metadata["backtest_start_time"] - backtest_date = datetime.fromtimestamp(backtest_date, tz=timezone.utc) + backtest_date = datetime.fromtimestamp(backtest_date, tz=UTC) if backtest_date < min_backtest_date: # Do not use a cached result for this strategy as first result is too old. del run_ids[strategy_name] diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index d5e7937c5..ed1da4ca2 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -7,7 +7,7 @@ Common Interface for bot and strategy to access data. import logging from collections import deque -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any from pandas import DataFrame, Timedelta, Timestamp, to_timedelta @@ -98,7 +98,7 @@ class DataProvider: :param candle_type: Any of the enum CandleType (must match trading mode!) """ pair_key = (pair, timeframe, candle_type) - self.__cached_pairs[pair_key] = (dataframe, datetime.now(timezone.utc)) + self.__cached_pairs[pair_key] = (dataframe, datetime.now(UTC)) # For multiple producers we will want to merge the pairlists instead of overwriting def _set_producer_pairs(self, pairlist: list[str], producer_name: str = "default"): @@ -131,7 +131,7 @@ class DataProvider: "data": { "key": pair_key, "df": dataframe.tail(1), - "la": datetime.now(timezone.utc), + "la": datetime.now(UTC), }, } self.__rpc.send_msg(msg) @@ -164,7 +164,7 @@ class DataProvider: if producer_name not in self.__producer_pairs_df: self.__producer_pairs_df[producer_name] = {} - _last_analyzed = datetime.now(timezone.utc) if not last_analyzed else last_analyzed + _last_analyzed = datetime.now(UTC) if not last_analyzed else last_analyzed self.__producer_pairs_df[producer_name][pair_key] = (dataframe, _last_analyzed) logger.debug(f"External DataFrame for {pair_key} from {producer_name} added.") @@ -275,12 +275,12 @@ class DataProvider: # If we have no data from this Producer yet if producer_name not in self.__producer_pairs_df: # We don't have this data yet, return empty DataFrame and datetime (01-01-1970) - return (DataFrame(), datetime.fromtimestamp(0, tz=timezone.utc)) + return (DataFrame(), datetime.fromtimestamp(0, tz=UTC)) # If we do have data from that Producer, but no data on this pair_key if pair_key not in self.__producer_pairs_df[producer_name]: # We don't have this data yet, return empty DataFrame and datetime (01-01-1970) - return (DataFrame(), datetime.fromtimestamp(0, tz=timezone.utc)) + return (DataFrame(), datetime.fromtimestamp(0, tz=UTC)) # We have it, return this data df, la = self.__producer_pairs_df[producer_name][pair_key] @@ -396,10 +396,10 @@ class DataProvider: if (max_index := self.__slice_index.get(pair)) is not None: df = df.iloc[max(0, max_index - MAX_DATAFRAME_CANDLES) : max_index] else: - return (DataFrame(), datetime.fromtimestamp(0, tz=timezone.utc)) + return (DataFrame(), datetime.fromtimestamp(0, tz=UTC)) return df, date else: - return (DataFrame(), datetime.fromtimestamp(0, tz=timezone.utc)) + return (DataFrame(), datetime.fromtimestamp(0, tz=UTC)) @property def runmode(self) -> RunMode: diff --git a/freqtrade/data/history/datahandlers/idatahandler.py b/freqtrade/data/history/datahandlers/idatahandler.py index e368c1d41..67ae386c6 100644 --- a/freqtrade/data/history/datahandlers/idatahandler.py +++ b/freqtrade/data/history/datahandlers/idatahandler.py @@ -8,7 +8,7 @@ import logging import re from abc import ABC, abstractmethod from copy import deepcopy -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from pandas import DataFrame, to_datetime @@ -118,8 +118,8 @@ class IDataHandler(ABC): df = self._ohlcv_load(pair, timeframe, None, candle_type) if df.empty: return ( - datetime.fromtimestamp(0, tz=timezone.utc), - datetime.fromtimestamp(0, tz=timezone.utc), + datetime.fromtimestamp(0, tz=UTC), + datetime.fromtimestamp(0, tz=UTC), 0, ) return df.iloc[0]["date"].to_pydatetime(), df.iloc[-1]["date"].to_pydatetime(), len(df) @@ -201,8 +201,8 @@ class IDataHandler(ABC): df = self._trades_load(pair, trading_mode) if df.empty: return ( - datetime.fromtimestamp(0, tz=timezone.utc), - datetime.fromtimestamp(0, tz=timezone.utc), + datetime.fromtimestamp(0, tz=UTC), + datetime.fromtimestamp(0, tz=UTC), 0, ) return ( diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 73c718e5e..c9ed83b50 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -1,7 +1,7 @@ """Binance exchange subclass""" import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path import ccxt @@ -160,7 +160,7 @@ class Binance(Exchange): since_ms = x[3][0][0] logger.info( f"Candle-data for {pair} available starting with " - f"{datetime.fromtimestamp(since_ms // 1000, tz=timezone.utc).isoformat()}." + f"{datetime.fromtimestamp(since_ms // 1000, tz=UTC).isoformat()}." ) if until_ms and since_ms >= until_ms: logger.warning( diff --git a/freqtrade/exchange/bitpanda.py b/freqtrade/exchange/bitpanda.py index 05c5af6ac..2f88e2fc8 100644 --- a/freqtrade/exchange/bitpanda.py +++ b/freqtrade/exchange/bitpanda.py @@ -1,7 +1,7 @@ """Bitpanda exchange subclass""" import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from freqtrade.exchange import Exchange @@ -34,5 +34,5 @@ class Bitpanda(Exchange): :param pair: Pair the order is for :param since: datetime object of the order creation time. Assumes object is in UTC. """ - params = {"to": int(datetime.now(timezone.utc).timestamp() * 1000)} + params = {"to": int(datetime.now(UTC).timestamp() * 1000)} return super().get_trades_for_order(order_id, pair, since, params) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index d95f649d0..8373e0a63 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -9,7 +9,7 @@ import logging import signal from collections.abc import Coroutine, Generator from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from math import floor, isnan from threading import Lock from typing import Any, Literal, TypeGuard, TypeVar @@ -655,7 +655,7 @@ class Exchange: if isinstance(markets, Exception): raise markets return None - except asyncio.TimeoutError as e: + except TimeoutError as e: logger.warning("Could not load markets. Reason: %s", e) raise TemporaryError from e @@ -2220,7 +2220,7 @@ class Exchange: _params = params if params else {} my_trades = self._api.fetch_my_trades( pair, - int((since.replace(tzinfo=timezone.utc).timestamp() - 5) * 1000), + int((since.replace(tzinfo=UTC).timestamp() - 5) * 1000), params=_params, ) matched_trades = [trade for trade in my_trades if trade["order"] == order_id] @@ -3347,7 +3347,7 @@ class Exchange: if not filename.parent.is_dir(): filename.parent.mkdir(parents=True) data = { - "updated": datetime.now(timezone.utc), + "updated": datetime.now(UTC), "data": tiers, } file_dump_json(filename, data) @@ -3369,7 +3369,7 @@ class Exchange: updated = tiers.get("updated") if updated: updated_dt = parser.parse(updated) - if updated_dt < datetime.now(timezone.utc) - cache_time: + if updated_dt < datetime.now(UTC) - cache_time: logger.info("Cached leverage tiers are outdated. Will update.") return None return tiers.get("data") @@ -3584,7 +3584,7 @@ class Exchange: mark_price_type = CandleType.from_string(self._ft_has["mark_ohlcv_price"]) if not close_date: - close_date = datetime.now(timezone.utc) + close_date = datetime.now(UTC) since_ms = dt_ts(timeframe_to_prev_date(timeframe, open_date)) mark_comb: PairWithTimeframe = (pair, timeframe, mark_price_type) diff --git a/freqtrade/exchange/exchange_utils.py b/freqtrade/exchange/exchange_utils.py index ad92033fb..a400fa123 100644 --- a/freqtrade/exchange/exchange_utils.py +++ b/freqtrade/exchange/exchange_utils.py @@ -3,7 +3,7 @@ Exchange support utils """ import inspect -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from math import ceil, floor, isnan from typing import Any @@ -148,7 +148,7 @@ def date_minus_candles(timeframe: str, candle_count: int, date: datetime | None """ if not date: - date = datetime.now(timezone.utc) + date = datetime.now(UTC) tf_min = timeframe_to_minutes(timeframe) new_date = timeframe_to_prev_date(timeframe, date) - timedelta(minutes=tf_min * candle_count) diff --git a/freqtrade/exchange/exchange_utils_timeframe.py b/freqtrade/exchange/exchange_utils_timeframe.py index 494df58f2..1897edf3b 100644 --- a/freqtrade/exchange/exchange_utils_timeframe.py +++ b/freqtrade/exchange/exchange_utils_timeframe.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime import ccxt from ccxt import ROUND_DOWN, ROUND_UP @@ -59,7 +59,7 @@ def timeframe_to_prev_date(timeframe: str, date: datetime | None = None) -> date :returns: date of previous candle (with utc timezone) """ if not date: - date = datetime.now(timezone.utc) + date = datetime.now(UTC) new_timestamp = ccxt.Exchange.round_timeframe(timeframe, dt_ts(date), ROUND_DOWN) // 1000 return dt_from_ts(new_timestamp) @@ -73,6 +73,6 @@ def timeframe_to_next_date(timeframe: str, date: datetime | None = None) -> date :returns: date of next candle (with utc timezone) """ if not date: - date = datetime.now(timezone.utc) + date = datetime.now(UTC) new_timestamp = ccxt.Exchange.round_timeframe(timeframe, dt_ts(date), ROUND_UP) // 1000 return dt_from_ts(new_timestamp) diff --git a/freqtrade/freqai/data_drawer.py b/freqtrade/freqai/data_drawer.py index cf3e828bf..fef0ab73a 100644 --- a/freqtrade/freqai/data_drawer.py +++ b/freqtrade/freqai/data_drawer.py @@ -5,7 +5,7 @@ import re import shutil import threading import warnings -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path from typing import Any, TypedDict @@ -116,7 +116,7 @@ class FreqaiDataDrawer: if metric not in self.metric_tracker[pair]: self.metric_tracker[pair][metric] = {"timestamp": [], "value": []} - timestamp = int(datetime.now(timezone.utc).timestamp()) + timestamp = int(datetime.now(UTC).timestamp()) self.metric_tracker[pair][metric]["value"].append(value) self.metric_tracker[pair][metric]["timestamp"].append(timestamp) diff --git a/freqtrade/freqai/data_kitchen.py b/freqtrade/freqai/data_kitchen.py index c03dbb276..c39343ab2 100644 --- a/freqtrade/freqai/data_kitchen.py +++ b/freqtrade/freqai/data_kitchen.py @@ -3,7 +3,7 @@ import inspect import logging import random import shutil -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from typing import Any @@ -341,7 +341,7 @@ class FreqaiDataKitchen: full_timerange = TimeRange.parse_timerange(tr) config_timerange = TimeRange.parse_timerange(self.config["timerange"]) if config_timerange.stopts == 0: - config_timerange.stopts = int(datetime.now(tz=timezone.utc).timestamp()) + config_timerange.stopts = int(datetime.now(tz=UTC).timestamp()) timerange_train = copy.deepcopy(full_timerange) timerange_backtest = copy.deepcopy(full_timerange) @@ -525,7 +525,7 @@ class FreqaiDataKitchen: :return: bool = If the model is expired or not. """ - time = datetime.now(tz=timezone.utc).timestamp() + time = datetime.now(tz=UTC).timestamp() elapsed_time = (time - trained_timestamp) / 3600 # hours max_time = self.freqai_config.get("expiration_hours", 0) if max_time > 0: @@ -536,7 +536,7 @@ class FreqaiDataKitchen: def check_if_new_training_required( self, trained_timestamp: int ) -> tuple[bool, TimeRange, TimeRange]: - time = datetime.now(tz=timezone.utc).timestamp() + time = datetime.now(tz=UTC).timestamp() trained_timerange = TimeRange() data_load_timerange = TimeRange() diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index f79568774..1c66de46f 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -3,7 +3,7 @@ import threading import time from abc import ABC, abstractmethod from collections import deque -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from typing import Any, Literal @@ -76,7 +76,7 @@ class IFreqaiModel(ABC): self.dd = FreqaiDataDrawer(Path(self.full_path), self.config) # set current candle to arbitrary historical date - self.current_candle: datetime = datetime.fromtimestamp(637887600, tz=timezone.utc) + self.current_candle: datetime = datetime.fromtimestamp(637887600, tz=UTC) self.dd.current_candle = self.current_candle self.scanning = False self.ft_params = self.freqai_info["feature_parameters"] diff --git a/freqtrade/freqai/utils.py b/freqtrade/freqai/utils.py index d2a15046d..d3aeaefe3 100644 --- a/freqtrade/freqai/utils.py +++ b/freqtrade/freqai/utils.py @@ -1,5 +1,5 @@ import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from typing import Any @@ -64,7 +64,7 @@ def get_required_data_timerange(config: Config) -> TimeRange: Used to compute the required data download time range for auto data-download in FreqAI """ - time = datetime.now(tz=timezone.utc).timestamp() + time = datetime.now(tz=UTC).timestamp() timeframes = config["freqai"]["feature_parameters"].get("include_timeframes") diff --git a/freqtrade/rpc/api_server/ws/channel.py b/freqtrade/rpc/api_server/ws/channel.py index bf5d64b6c..1a7a4bde6 100644 --- a/freqtrade/rpc/api_server/ws/channel.py +++ b/freqtrade/rpc/api_server/ws/channel.py @@ -102,7 +102,7 @@ class WebSocketChannel: self._send_times.append(total_time) self._calc_send_limit() - except asyncio.TimeoutError: + except TimeoutError: logger.info(f"Connection for {self} timed out, disconnecting") raise @@ -201,8 +201,8 @@ class WebSocketChannel: try: await task except ( + TimeoutError, asyncio.CancelledError, - asyncio.TimeoutError, WebSocketDisconnect, ConnectionClosed, RuntimeError, diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 01f649ba3..8883a11b8 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -5,7 +5,7 @@ This module contains class to define a RPC communications import logging from abc import abstractmethod from collections.abc import Generator, Sequence -from datetime import date, datetime, timedelta, timezone +from datetime import UTC, date, datetime, timedelta from typing import TYPE_CHECKING, Any import psutil @@ -375,7 +375,7 @@ class RPC: """ :param timeunit: Valid entries are 'days', 'weeks', 'months' """ - start_date = datetime.now(timezone.utc).date() + start_date = datetime.now(UTC).date() if timeunit == "weeks": # weekly start_date = start_date - timedelta(days=start_date.weekday()) # Monday @@ -1259,7 +1259,7 @@ class RPC: for lock in locks: lock.active = False - lock.lock_end_time = datetime.now(timezone.utc) + lock.lock_end_time = datetime.now(UTC) Trade.commit() From a7ee45a0bad6cfa62aee291c80aecc31c9367a23 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:58:03 +0200 Subject: [PATCH 07/15] refactor: Update timezone.utc to UTC --- .../RL/BaseReinforcementLearningModel.py | 4 +-- freqtrade/ft_types/plot_annotation_type.py | 4 +-- freqtrade/optimize/base_analysis.py | 8 +++--- .../optimize/hyperopt/hyperopt_optimizer.py | 6 ++--- freqtrade/optimize/hyperopt_tools.py | 4 +-- .../optimize_reports/optimize_reports.py | 6 ++--- freqtrade/persistence/key_value_store.py | 10 ++++---- freqtrade/persistence/pairlock.py | 6 ++--- freqtrade/persistence/pairlock_middleware.py | 14 +++++------ freqtrade/persistence/trade_model.py | 25 +++++++++---------- freqtrade/plot/plotting.py | 4 +-- freqtrade/plugins/protectionmanager.py | 6 ++--- freqtrade/plugins/protections/iprotection.py | 4 +-- 13 files changed, 50 insertions(+), 51 deletions(-) diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py index 9481f8f7e..db9fba85a 100644 --- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py +++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py @@ -3,7 +3,7 @@ import importlib import logging from abc import abstractmethod from collections.abc import Callable -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from typing import Any @@ -239,7 +239,7 @@ class BaseReinforcementLearningModel(IFreqaiModel): pair, refresh=False, side="exit", is_short=trade.is_short ) - now = datetime.now(timezone.utc).timestamp() + now = datetime.now(UTC).timestamp() trade_duration = int((now - trade.open_date_utc.timestamp()) / self.base_tf_seconds) current_profit = trade.calc_profit_ratio(current_rate) if trade.is_short: diff --git a/freqtrade/ft_types/plot_annotation_type.py b/freqtrade/ft_types/plot_annotation_type.py index b08db3c57..4da4102c4 100644 --- a/freqtrade/ft_types/plot_annotation_type.py +++ b/freqtrade/ft_types/plot_annotation_type.py @@ -1,8 +1,8 @@ from datetime import datetime -from typing import Literal +from typing import Literal, Required from pydantic import TypeAdapter -from typing_extensions import Required, TypedDict +from typing_extensions import TypedDict class AnnotationType(TypedDict, total=False): diff --git a/freqtrade/optimize/base_analysis.py b/freqtrade/optimize/base_analysis.py index a2fe53867..7e75137ec 100644 --- a/freqtrade/optimize/base_analysis.py +++ b/freqtrade/optimize/base_analysis.py @@ -1,6 +1,6 @@ import logging from copy import deepcopy -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any from pandas import DataFrame @@ -38,7 +38,7 @@ class BaseAnalysis: @staticmethod def dt_to_timestamp(dt: datetime): - timestamp = int(dt.replace(tzinfo=timezone.utc).timestamp()) + timestamp = int(dt.replace(tzinfo=UTC).timestamp()) return timestamp def fill_full_varholder(self): @@ -48,12 +48,12 @@ class BaseAnalysis: parsed_timerange = TimeRange.parse_timerange(self.local_config["timerange"]) if parsed_timerange.startdt is None: - self.full_varHolder.from_dt = datetime.fromtimestamp(0, tz=timezone.utc) + self.full_varHolder.from_dt = datetime.fromtimestamp(0, tz=UTC) else: self.full_varHolder.from_dt = parsed_timerange.startdt if parsed_timerange.stopdt is None: - self.full_varHolder.to_dt = datetime.now(timezone.utc) + self.full_varHolder.to_dt = datetime.now(UTC) else: self.full_varHolder.to_dt = parsed_timerange.stopdt diff --git a/freqtrade/optimize/hyperopt/hyperopt_optimizer.py b/freqtrade/optimize/hyperopt/hyperopt_optimizer.py index 94be5e50e..7f56dfb81 100644 --- a/freqtrade/optimize/hyperopt/hyperopt_optimizer.py +++ b/freqtrade/optimize/hyperopt/hyperopt_optimizer.py @@ -6,7 +6,7 @@ and will be sent to the hyperopt worker processes. import logging import sys import warnings -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from typing import Any @@ -273,7 +273,7 @@ class HyperOptimizer: Keep this function as optimized as possible! """ HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE) - backtest_start_time = datetime.now(timezone.utc) + backtest_start_time = datetime.now(UTC) # Apply parameters if HyperoptTools.has_space(self.config, "buy"): @@ -330,7 +330,7 @@ class HyperOptimizer: bt_results = self.backtesting.backtest( processed=processed, start_date=self.min_date, end_date=self.max_date ) - backtest_end_time = datetime.now(timezone.utc) + backtest_end_time = datetime.now(UTC) bt_results.update( { "backtest_start_time": int(backtest_start_time.timestamp()), diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 2ecc5a73f..4cb9887eb 100644 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -1,7 +1,7 @@ import logging from collections.abc import Iterator from copy import deepcopy -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from typing import Any @@ -71,7 +71,7 @@ class HyperoptTools: "strategy_name": strategy_name, "params": final_params, "ft_stratparam_v": 1, - "export_time": datetime.now(timezone.utc), + "export_time": datetime.now(UTC), } logger.info(f"Dumping parameters to {filename}") with filename.open("w") as f: diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index d35e96605..77d2c5a79 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -1,6 +1,6 @@ import logging from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Any, Literal import numpy as np @@ -652,9 +652,9 @@ def generate_strategy_stats( "max_drawdown_abs": 0.0, "max_drawdown_low": 0.0, "max_drawdown_high": 0.0, - "drawdown_start": datetime(1970, 1, 1, tzinfo=timezone.utc), + "drawdown_start": datetime(1970, 1, 1, tzinfo=UTC), "drawdown_start_ts": 0, - "drawdown_end": datetime(1970, 1, 1, tzinfo=timezone.utc), + "drawdown_end": datetime(1970, 1, 1, tzinfo=UTC), "drawdown_end_ts": 0, "csum_min": 0, "csum_max": 0, diff --git a/freqtrade/persistence/key_value_store.py b/freqtrade/persistence/key_value_store.py index 814748da6..0d5a97fa1 100644 --- a/freqtrade/persistence/key_value_store.py +++ b/freqtrade/persistence/key_value_store.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from enum import Enum from typing import ClassVar, Literal @@ -114,7 +114,7 @@ class KeyValueStore: if kv.value_type == ValueTypesEnum.STRING: return kv.string_value if kv.value_type == ValueTypesEnum.DATETIME and kv.datetime_value is not None: - return kv.datetime_value.replace(tzinfo=timezone.utc) + return kv.datetime_value.replace(tzinfo=UTC) if kv.value_type == ValueTypesEnum.FLOAT: return kv.float_value if kv.value_type == ValueTypesEnum.INT: @@ -156,7 +156,7 @@ class KeyValueStore: ) if kv is None or kv.datetime_value is None: return None - return kv.datetime_value.replace(tzinfo=timezone.utc) + return kv.datetime_value.replace(tzinfo=UTC) @staticmethod def get_float_value(key: KeyStoreKeys) -> float | None: @@ -207,5 +207,5 @@ def set_startup_time() -> None: if t is not None: KeyValueStore.store_value("bot_start_time", t.open_date_utc) else: - KeyValueStore.store_value("bot_start_time", datetime.now(timezone.utc)) - KeyValueStore.store_value("startup_time", datetime.now(timezone.utc)) + KeyValueStore.store_value("bot_start_time", datetime.now(UTC)) + KeyValueStore.store_value("startup_time", datetime.now(UTC)) diff --git a/freqtrade/persistence/pairlock.py b/freqtrade/persistence/pairlock.py index 6c98a607d..db88bd878 100644 --- a/freqtrade/persistence/pairlock.py +++ b/freqtrade/persistence/pairlock.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any, ClassVar from sqlalchemy import ScalarResult, String, or_, select @@ -69,10 +69,10 @@ class PairLock(ModelBase): "id": self.id, "pair": self.pair, "lock_time": self.lock_time.strftime(DATETIME_PRINT_FORMAT), - "lock_timestamp": int(self.lock_time.replace(tzinfo=timezone.utc).timestamp() * 1000), + "lock_timestamp": int(self.lock_time.replace(tzinfo=UTC).timestamp() * 1000), "lock_end_time": self.lock_end_time.strftime(DATETIME_PRINT_FORMAT), "lock_end_timestamp": int( - self.lock_end_time.replace(tzinfo=timezone.utc).timestamp() * 1000 + self.lock_end_time.replace(tzinfo=UTC).timestamp() * 1000 ), "reason": self.reason, "side": self.side, diff --git a/freqtrade/persistence/pairlock_middleware.py b/freqtrade/persistence/pairlock_middleware.py index 0adbcc8be..02642c463 100644 --- a/freqtrade/persistence/pairlock_middleware.py +++ b/freqtrade/persistence/pairlock_middleware.py @@ -1,6 +1,6 @@ import logging from collections.abc import Sequence -from datetime import datetime, timezone +from datetime import UTC, datetime from sqlalchemy import select @@ -52,7 +52,7 @@ class PairLocks: """ lock = PairLock( pair=pair, - lock_time=now or datetime.now(timezone.utc), + lock_time=now or datetime.now(UTC), lock_end_time=timeframe_to_next_date(PairLocks.timeframe, until), reason=reason, side=side, @@ -77,7 +77,7 @@ class PairLocks: :param side: Side get locks for, can be 'long', 'short', '*' or None """ if not now: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) if PairLocks.use_db: return PairLock.query_pair_locks(pair, now, side).all() @@ -114,7 +114,7 @@ class PairLocks: defaults to datetime.now(timezone.utc) """ if not now: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) logger.info(f"Releasing all locks for {pair}.") locks = PairLocks.get_pair_locks(pair, now, side=side) @@ -132,7 +132,7 @@ class PairLocks: defaults to datetime.now(timezone.utc) """ if not now: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) if PairLocks.use_db: # used in live modes @@ -161,7 +161,7 @@ class PairLocks: defaults to datetime.now(timezone.utc) """ if not now: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) return len(PairLocks.get_pair_locks("*", now, side)) > 0 @@ -173,7 +173,7 @@ class PairLocks: defaults to datetime.now(timezone.utc) """ if not now: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) return len(PairLocks.get_pair_locks(pair, now, side)) > 0 or PairLocks.is_global_lock( now, side diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 9c7234d35..44cd06baf 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -6,9 +6,9 @@ import logging from collections import defaultdict from collections.abc import Sequence from dataclasses import dataclass -from datetime import datetime, timezone +from datetime import UTC, datetime from math import isclose -from typing import Any, ClassVar, Optional, cast +from typing import Any, ClassVar, Optional, Self, cast from sqlalchemy import ( Enum, @@ -25,7 +25,6 @@ from sqlalchemy import ( select, ) from sqlalchemy.orm import Mapped, lazyload, mapped_column, relationship, validates -from typing_extensions import Self from freqtrade.constants import ( CANCELED_EXCHANGE_STATES, @@ -121,13 +120,13 @@ class Order(ModelBase): @property def order_date_utc(self) -> datetime: """Order-date with UTC timezoneinfo""" - return self.order_date.replace(tzinfo=timezone.utc) + return self.order_date.replace(tzinfo=UTC) @property def order_filled_utc(self) -> datetime | None: """last order-date with UTC timezoneinfo""" return ( - self.order_filled_date.replace(tzinfo=timezone.utc) if self.order_filled_date else None + self.order_filled_date.replace(tzinfo=UTC) if self.order_filled_date else None ) @property @@ -229,7 +228,7 @@ class Order(ModelBase): self.order_filled_date = dt_from_ts( safe_value_fallback(order, "lastTradeTimestamp", default_value=dt_ts()) ) - self.order_update_date = datetime.now(timezone.utc) + self.order_update_date = datetime.now(UTC) def to_ccxt_object(self, stopPriceName: str = "stopPrice") -> dict[str, Any]: order: dict[str, Any] = { @@ -286,7 +285,7 @@ class Order(ModelBase): self.order_date.strftime(DATETIME_PRINT_FORMAT) if self.order_date else None ), "order_timestamp": ( - int(self.order_date.replace(tzinfo=timezone.utc).timestamp() * 1000) + int(self.order_date.replace(tzinfo=UTC).timestamp() * 1000) if self.order_date else None ), @@ -533,7 +532,7 @@ class LocalTrade: @property def open_date_utc(self): - return self.open_date.replace(tzinfo=timezone.utc) + return self.open_date.replace(tzinfo=UTC) @property def stoploss_last_update_utc(self): @@ -543,7 +542,7 @@ class LocalTrade: @property def close_date_utc(self): - return self.close_date.replace(tzinfo=timezone.utc) if self.close_date else None + return self.close_date.replace(tzinfo=UTC) if self.close_date else None @property def entry_side(self) -> str: @@ -1056,7 +1055,7 @@ class LocalTrade: return zero open_date = self.open_date.replace(tzinfo=None) - now = (self.close_date or datetime.now(timezone.utc)).replace(tzinfo=None) + now = (self.close_date or datetime.now(UTC)).replace(tzinfo=None) sec_per_hour = FtPrecise(3600) total_seconds = FtPrecise((now - open_date).total_seconds()) hours = total_seconds / sec_per_hour or zero @@ -1572,12 +1571,12 @@ class LocalTrade: fee_close=data["fee_close"], fee_close_cost=data.get("fee_close_cost"), fee_close_currency=data.get("fee_close_currency"), - open_date=datetime.fromtimestamp(data["open_timestamp"] // 1000, tz=timezone.utc), + open_date=datetime.fromtimestamp(data["open_timestamp"] // 1000, tz=UTC), open_rate=data["open_rate"], open_rate_requested=data.get("open_rate_requested", data["open_rate"]), open_trade_value=data.get("open_trade_value"), close_date=( - datetime.fromtimestamp(data["close_timestamp"] // 1000, tz=timezone.utc) + datetime.fromtimestamp(data["close_timestamp"] // 1000, tz=UTC) if data["close_timestamp"] else None ), @@ -1622,7 +1621,7 @@ class LocalTrade: if order.get("order_date") else None, order_filled_date=( - datetime.fromtimestamp(order["order_filled_timestamp"] // 1000, tz=timezone.utc) + datetime.fromtimestamp(order["order_filled_timestamp"] // 1000, tz=UTC) if order["order_filled_timestamp"] else None ), diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 6419ab0c2..5c43ee404 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -1,5 +1,5 @@ import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path import pandas as pd @@ -638,7 +638,7 @@ def load_and_plot_trades(config: Config): exchange = ExchangeResolver.load_exchange(config) IStrategy.dp = DataProvider(config, exchange) strategy.ft_bot_start() - strategy_safe_wrapper(strategy.bot_loop_start)(current_time=datetime.now(timezone.utc)) + strategy_safe_wrapper(strategy.bot_loop_start)(current_time=datetime.now(UTC)) plot_elements = init_plotscript(config, list(exchange.markets), strategy.startup_candle_count) timerange = plot_elements["timerange"] trades = plot_elements["trades"] diff --git a/freqtrade/plugins/protectionmanager.py b/freqtrade/plugins/protectionmanager.py index 187b9ee63..0eda8422f 100644 --- a/freqtrade/plugins/protectionmanager.py +++ b/freqtrade/plugins/protectionmanager.py @@ -3,7 +3,7 @@ Protection manager class """ import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from typing import Any from freqtrade.constants import Config, LongShort @@ -49,7 +49,7 @@ class ProtectionManager: def global_stop(self, now: datetime | None = None, side: LongShort = "long") -> PairLock | None: if not now: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) result = None for protection_handler in self._protection_handlers: if protection_handler.has_global_stop: @@ -65,7 +65,7 @@ class ProtectionManager: self, pair, now: datetime | None = None, side: LongShort = "long" ) -> PairLock | None: if not now: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) result = None for protection_handler in self._protection_handlers: if protection_handler.has_local_stop: diff --git a/freqtrade/plugins/protections/iprotection.py b/freqtrade/plugins/protections/iprotection.py index 38a726dcc..8f2f51729 100644 --- a/freqtrade/plugins/protections/iprotection.py +++ b/freqtrade/plugins/protections/iprotection.py @@ -1,7 +1,7 @@ import logging from abc import ABC, abstractmethod from dataclasses import dataclass -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Any from freqtrade.constants import Config, LongShort @@ -127,7 +127,7 @@ class IProtection(LoggingMixin, ABC): max_date: datetime = max([trade.close_date for trade in trades if trade.close_date]) # coming from Database, tzinfo is not set. if max_date.tzinfo is None: - max_date = max_date.replace(tzinfo=timezone.utc) + max_date = max_date.replace(tzinfo=UTC) if self._unlock_at is not None: # unlock_at case with fixed hour of the day From 5b8b80cf59482514c0523d08ac6a59e589f57988 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:58:54 +0200 Subject: [PATCH 08/15] refactor: Update timezone.utc to UTC --- freqtrade/freqtradebot.py | 44 ++++++++++---------- freqtrade/rpc/api_server/api_auth.py | 8 ++-- freqtrade/rpc/api_server/webserver_bgwork.py | 4 +- freqtrade/rpc/external_message_consumer.py | 2 +- freqtrade/strategy/interface.py | 6 +-- freqtrade/util/datetime_helpers.py | 8 ++-- freqtrade/util/periodic_cache.py | 4 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index c5605412d..f8af2b469 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -5,7 +5,7 @@ Freqtrade is the main module of this bot. It contains the class Freqtrade() import logging import traceback from copy import deepcopy -from datetime import datetime, time, timedelta, timezone +from datetime import UTC, datetime, time, timedelta from math import isclose from threading import Lock from time import sleep @@ -266,7 +266,7 @@ class FreqtradeBot(LoggingMixin): ) strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)( - current_time=datetime.now(timezone.utc) + current_time=datetime.now(UTC) ) with self._measure_execution: @@ -296,7 +296,7 @@ class FreqtradeBot(LoggingMixin): self._schedule.run_pending() Trade.commit() self.rpc.process_msg_queue(self.dataprovider._msg_queue) - self.last_process = datetime.now(timezone.utc) + self.last_process = datetime.now(UTC) def process_stopped(self) -> None: """ @@ -421,7 +421,7 @@ class FreqtradeBot(LoggingMixin): except InvalidOrderException as e: logger.warning(f"Error updating Order {order.order_id} due to {e}.") - if order.order_date_utc - timedelta(days=5) < datetime.now(timezone.utc): + if order.order_date_utc - timedelta(days=5) < datetime.now(UTC): logger.warning( "Order is older than 5 days. Assuming order was fully cancelled." ) @@ -755,7 +755,7 @@ class FreqtradeBot(LoggingMixin): logger.debug(f"Calling adjust_trade_position for pair {trade.pair}") stake_amount, order_tag = self.strategy._adjust_trade_position_internal( trade=trade, - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), current_rate=current_entry_rate, current_profit=current_entry_profit, min_stake=min_entry_stake, @@ -916,7 +916,7 @@ class FreqtradeBot(LoggingMixin): amount=amount, rate=enter_limit_requested, time_in_force=time_in_force, - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), entry_tag=enter_tag, side=trade_side, ): @@ -987,7 +987,7 @@ class FreqtradeBot(LoggingMixin): # Fee is applied twice because we make a LIMIT_BUY and LIMIT_SELL fee = self.exchange.get_fee(symbol=pair, taker_or_maker="maker") base_currency = self.exchange.get_pair_base_currency(pair) - open_date = datetime.now(timezone.utc) + open_date = datetime.now(UTC) funding_fees = self.exchange.get_funding_fees( pair=pair, @@ -1106,7 +1106,7 @@ class FreqtradeBot(LoggingMixin): )( pair=pair, trade=trade, - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), proposed_rate=enter_limit_requested, entry_tag=entry_tag, side=trade_side, @@ -1124,7 +1124,7 @@ class FreqtradeBot(LoggingMixin): else: leverage = strategy_safe_wrapper(self.strategy.leverage, default_retval=1.0)( pair=pair, - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), current_rate=enter_limit_requested, proposed_leverage=1.0, max_leverage=max_leverage, @@ -1157,7 +1157,7 @@ class FreqtradeBot(LoggingMixin): self.strategy.custom_stake_amount, default_retval=stake_amount )( pair=pair, - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), current_rate=enter_limit_requested, proposed_stake=stake_amount, min_stake=min_stake_amount, @@ -1222,7 +1222,7 @@ class FreqtradeBot(LoggingMixin): "quote_currency": self.exchange.get_pair_quote_currency(trade.pair), "fiat_currency": self.config.get("fiat_display_currency", None), "amount": order.safe_amount_after_fee if fill else (order.safe_amount or trade.amount), - "open_date": trade.open_date_utc or datetime.now(timezone.utc), + "open_date": trade.open_date_utc or datetime.now(UTC), "current_rate": current_rate, "sub_trade": sub_trade, } @@ -1361,7 +1361,7 @@ class FreqtradeBot(LoggingMixin): exits: list[ExitCheckTuple] = self.strategy.should_exit( trade, exit_rate, - datetime.now(timezone.utc), + datetime.now(UTC), enter=enter, exit_=exit_, force_stoploss=0, @@ -1496,7 +1496,7 @@ class FreqtradeBot(LoggingMixin): if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side): # we check if the update is necessary update_beat = self.strategy.order_types.get("stoploss_on_exchange_interval", 60) - upd_req = datetime.now(timezone.utc) - timedelta(seconds=update_beat) + upd_req = datetime.now(UTC) - timedelta(seconds=update_beat) if trade.stoploss_last_update_utc and upd_req >= trade.stoploss_last_update_utc: # cancelling the current stoploss on exchange first logger.info( @@ -1584,7 +1584,7 @@ class FreqtradeBot(LoggingMixin): if fully_cancelled or ( open_order and self.strategy.ft_check_timed_out( - trade, open_order, datetime.now(timezone.utc) + trade, open_order, datetime.now(UTC) ) ): self.handle_cancel_order( @@ -1683,7 +1683,7 @@ class FreqtradeBot(LoggingMixin): trade=trade, order=order_obj, pair=trade.pair, - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), proposed_rate=proposed_rate, current_order_rate=order_obj.safe_placement_price, entry_tag=trade.enter_tag, @@ -2075,7 +2075,7 @@ class FreqtradeBot(LoggingMixin): )( pair=trade.pair, trade=trade, - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), proposed_rate=proposed_limit_rate, current_profit=current_profit, exit_tag=exit_reason, @@ -2106,7 +2106,7 @@ class FreqtradeBot(LoggingMixin): time_in_force=time_in_force, exit_reason=exit_reason, sell_reason=exit_reason, # sellreason -> compatibility - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), ) ): logger.info(f"User denied exit for {trade.pair}.") @@ -2202,7 +2202,7 @@ class FreqtradeBot(LoggingMixin): "enter_tag": trade.enter_tag, "exit_reason": trade.exit_reason, "open_date": trade.open_date_utc, - "close_date": trade.close_date_utc or datetime.now(timezone.utc), + "close_date": trade.close_date_utc or datetime.now(UTC), "stake_amount": trade.stake_amount, "stake_currency": self.config["stake_currency"], "base_currency": self.exchange.get_pair_base_currency(trade.pair), @@ -2257,7 +2257,7 @@ class FreqtradeBot(LoggingMixin): "enter_tag": trade.enter_tag, "exit_reason": trade.exit_reason, "open_date": trade.open_date, - "close_date": trade.close_date or datetime.now(timezone.utc), + "close_date": trade.close_date or datetime.now(UTC), "stake_currency": self.config["stake_currency"], "base_currency": self.exchange.get_pair_base_currency(trade.pair), "quote_currency": self.exchange.get_pair_quote_currency(trade.pair), @@ -2338,7 +2338,7 @@ class FreqtradeBot(LoggingMixin): def _update_trade_after_fill(self, trade: Trade, order: Order, send_msg: bool) -> Trade: if order.status in constants.NON_OPEN_EXCHANGE_STATES: strategy_safe_wrapper(self.strategy.order_filled, default_retval=None)( - pair=trade.pair, trade=trade, order=order, current_time=datetime.now(timezone.utc) + pair=trade.pair, trade=trade, order=order, current_time=datetime.now(UTC) ) # If a entry order was closed, force update on stoploss on exchange if order.ft_order_side == trade.entry_side: @@ -2371,7 +2371,7 @@ class FreqtradeBot(LoggingMixin): ) profit = trade.calc_profit_ratio(current_rate) self.strategy.ft_stoploss_adjust( - current_rate, trade, datetime.now(timezone.utc), profit, 0, after_fill=True + current_rate, trade, datetime.now(UTC), profit, 0, after_fill=True ) # Updating wallets when order is closed self.wallets.update() @@ -2397,7 +2397,7 @@ class FreqtradeBot(LoggingMixin): def handle_protections(self, pair: str, side: LongShort) -> None: # Lock pair for one candle to prevent immediate re-entries - self.strategy.lock_pair(pair, datetime.now(timezone.utc), reason="Auto lock", side=side) + self.strategy.lock_pair(pair, datetime.now(UTC), reason="Auto lock", side=side) prot_trig = self.protections.stop_per_pair(pair, side=side) if prot_trig: msg: RPCProtectionMsg = { diff --git a/freqtrade/rpc/api_server/api_auth.py b/freqtrade/rpc/api_server/api_auth.py index 5f63c0aa5..44ed3db57 100644 --- a/freqtrade/rpc/api_server/api_auth.py +++ b/freqtrade/rpc/api_server/api_auth.py @@ -1,6 +1,6 @@ import logging import secrets -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from typing import Any import jwt @@ -89,15 +89,15 @@ async def validate_ws_token( def create_token(data: dict, secret_key: str, token_type: str = "access") -> str: # noqa: S107 to_encode = data.copy() if token_type == "access": # noqa: S105 - expire = datetime.now(timezone.utc) + timedelta(minutes=15) + expire = datetime.now(UTC) + timedelta(minutes=15) elif token_type == "refresh": # noqa: S105 - expire = datetime.now(timezone.utc) + timedelta(days=30) + expire = datetime.now(UTC) + timedelta(days=30) else: raise ValueError() to_encode.update( { "exp": expire, - "iat": datetime.now(timezone.utc), + "iat": datetime.now(UTC), "type": token_type, } ) diff --git a/freqtrade/rpc/api_server/webserver_bgwork.py b/freqtrade/rpc/api_server/webserver_bgwork.py index 4c335a2f2..75aadb78e 100644 --- a/freqtrade/rpc/api_server/webserver_bgwork.py +++ b/freqtrade/rpc/api_server/webserver_bgwork.py @@ -1,7 +1,7 @@ -from typing import Any, Literal +from typing import Any, Literal, NotRequired from uuid import uuid4 -from typing_extensions import NotRequired, TypedDict +from typing_extensions import TypedDict from freqtrade.exchange.exchange import Exchange diff --git a/freqtrade/rpc/external_message_consumer.py b/freqtrade/rpc/external_message_consumer.py index fe22613bd..0a11fbefe 100644 --- a/freqtrade/rpc/external_message_consumer.py +++ b/freqtrade/rpc/external_message_consumer.py @@ -266,7 +266,7 @@ class ExternalMessageConsumer: except Exception as e: logger.exception(f"Error handling producer message: {e}") - except (asyncio.TimeoutError, websockets.exceptions.ConnectionClosed): + except (TimeoutError, websockets.exceptions.ConnectionClosed): # We haven't received data yet. Check the connection and continue. try: # ping diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 2186ea9b3..c863a8392 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -5,7 +5,7 @@ This module defines the interface to apply for strategies import logging from abc import ABC, abstractmethod -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from math import isinf, isnan from pandas import DataFrame @@ -1149,7 +1149,7 @@ class IStrategy(ABC, HyperStrategyMixin): manually from within the strategy, to allow an easy way to unlock pairs. :param pair: Unlock pair to allow trading again """ - PairLocks.unlock_pair(pair, datetime.now(timezone.utc)) + PairLocks.unlock_pair(pair, datetime.now(UTC)) def unlock_reason(self, reason: str) -> None: """ @@ -1158,7 +1158,7 @@ class IStrategy(ABC, HyperStrategyMixin): manually from within the strategy, to allow an easy way to unlock pairs. :param reason: Unlock pairs to allow trading again """ - PairLocks.unlock_reason(reason, datetime.now(timezone.utc)) + PairLocks.unlock_reason(reason, datetime.now(UTC)) def is_pair_locked( self, pair: str, *, candle_date: datetime | None = None, side: str = "*" diff --git a/freqtrade/util/datetime_helpers.py b/freqtrade/util/datetime_helpers.py index 03ee3b6d9..e2390105d 100644 --- a/freqtrade/util/datetime_helpers.py +++ b/freqtrade/util/datetime_helpers.py @@ -1,5 +1,5 @@ import re -from datetime import datetime, timezone +from datetime import UTC, datetime from time import time import humanize @@ -9,7 +9,7 @@ from freqtrade.constants import DATETIME_PRINT_FORMAT def dt_now() -> datetime: """Return the current datetime in UTC.""" - return datetime.now(timezone.utc) + return datetime.now(UTC) def dt_utc( @@ -22,7 +22,7 @@ def dt_utc( microsecond: int = 0, ) -> datetime: """Return a datetime in UTC.""" - return datetime(year, month, day, hour, minute, second, microsecond, tzinfo=timezone.utc) + return datetime(year, month, day, hour, minute, second, microsecond, tzinfo=UTC) def dt_ts(dt: datetime | None = None) -> int: @@ -68,7 +68,7 @@ def dt_from_ts(timestamp: float) -> datetime: if timestamp > 1e10: # Timezone in ms - convert to seconds timestamp /= 1000 - return datetime.fromtimestamp(timestamp, tz=timezone.utc) + return datetime.fromtimestamp(timestamp, tz=UTC) def shorten_date(_date: str) -> str: diff --git a/freqtrade/util/periodic_cache.py b/freqtrade/util/periodic_cache.py index 4f7405d2b..33322fd55 100644 --- a/freqtrade/util/periodic_cache.py +++ b/freqtrade/util/periodic_cache.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from cachetools import TTLCache @@ -11,7 +11,7 @@ class PeriodicCache(TTLCache): def __init__(self, maxsize, ttl, getsizeof=None): def local_timer(): - ts = datetime.now(timezone.utc).timestamp() + ts = datetime.now(UTC).timestamp() offset = ts % ttl return ts - offset From 79f0271720288fc50e2c381e6927646103ca6ec1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 08:59:15 +0200 Subject: [PATCH 09/15] refactor: Asyncio timeoutError update --- scripts/ws_client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/ws_client.py b/scripts/ws_client.py index 46dd2da10..233a7828a 100755 --- a/scripts/ws_client.py +++ b/scripts/ws_client.py @@ -234,7 +234,7 @@ async def create_client( await protocol.on_message(ws, name, message) - except (asyncio.TimeoutError, websockets.exceptions.WebSocketException): + except (TimeoutError, websockets.exceptions.WebSocketException): # Try pinging try: pong = await ws.ping() @@ -244,7 +244,7 @@ async def create_client( continue - except asyncio.TimeoutError: + except TimeoutError: logger.error(f"Ping timed out, retrying in {sleep_time}s") await asyncio.sleep(sleep_time) From deb8bde07801550bb9c4b82cb961cf217c84d930 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 09:01:13 +0200 Subject: [PATCH 10/15] refactor: tests - update timezone.utc to UTC --- tests/conftest.py | 4 +-- tests/conftest_trades.py | 24 ++++++------- tests/conftest_trades_usdt.py | 22 ++++++------ tests/data/test_btanalysis.py | 30 ++++++++-------- tests/data/test_datahandler.py | 16 ++++----- tests/data/test_dataprovider.py | 10 +++--- tests/data/test_historic_precision.py | 20 +++++------ tests/data/test_trade_converter_kraken.py | 6 ++-- tests/exchange/test_bybit.py | 6 ++-- tests/exchange/test_exchange.py | 32 ++++++++--------- tests/exchange/test_exchange_utils.py | 42 +++++++++++------------ tests/exchange/test_gate.py | 4 +-- tests/exchange/test_hyperliquid.py | 4 +-- tests/exchange/test_okx.py | 12 +++---- tests/exchange_online/test_ccxt_compat.py | 16 ++++----- tests/freqai/test_freqai_backtesting.py | 12 +++---- tests/freqai/test_freqai_datakitchen.py | 6 ++-- tests/optimize/test_backtesting.py | 16 ++++----- tests/test_timerange.py | 8 ++--- 19 files changed, 145 insertions(+), 145 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 7703ffa5e..fefb9bad2 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,7 +4,7 @@ import logging import platform import re from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path from unittest.mock import MagicMock, Mock, PropertyMock @@ -126,7 +126,7 @@ def get_args(args): def generate_trades_history(n_rows, start_date: datetime | None = None, days=5): np.random.seed(42) if not start_date: - start_date = datetime(2020, 1, 1, tzinfo=timezone.utc) + start_date = datetime(2020, 1, 1, tzinfo=UTC) # Generate random data end_date = start_date + timedelta(days=days) diff --git a/tests/conftest_trades.py b/tests/conftest_trades.py index f493a9302..072dfa60e 100644 --- a/tests/conftest_trades.py +++ b/tests/conftest_trades.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from freqtrade.persistence.models import Order, Trade @@ -43,7 +43,7 @@ def mock_trade_1(fee, is_short: bool): fee_open=fee.return_value, fee_close=fee.return_value, is_open=True, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=17), + open_date=datetime.now(tz=UTC) - timedelta(minutes=17), open_rate=0.123, exchange="binance", strategy="StrategyTestV3", @@ -106,8 +106,8 @@ def mock_trade_2(fee, is_short: bool): timeframe=5, enter_tag="TEST1", exit_reason="sell_signal", - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), - close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), + open_date=datetime.now(tz=UTC) - timedelta(minutes=20), + close_date=datetime.now(tz=UTC) - timedelta(minutes=2), is_short=is_short, ) o = Order.parse_from_ccxt_object(mock_order_2(is_short), "ETC/BTC", entry_side(is_short)) @@ -168,8 +168,8 @@ def mock_trade_3(fee, is_short: bool): strategy="StrategyTestV3", timeframe=5, exit_reason="roi", - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), - close_date=datetime.now(tz=timezone.utc), + open_date=datetime.now(tz=UTC) - timedelta(minutes=20), + close_date=datetime.now(tz=UTC), is_short=is_short, ) o = Order.parse_from_ccxt_object(mock_order_3(is_short), "XRP/BTC", entry_side(is_short)) @@ -205,7 +205,7 @@ def mock_trade_4(fee, is_short: bool): amount_requested=124.0, fee_open=fee.return_value, fee_close=fee.return_value, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=14), + open_date=datetime.now(tz=UTC) - timedelta(minutes=14), is_open=True, open_rate=0.123, exchange="binance", @@ -260,7 +260,7 @@ def mock_trade_5(fee, is_short: bool): amount_requested=124.0, fee_open=fee.return_value, fee_close=fee.return_value, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=12), + open_date=datetime.now(tz=UTC) - timedelta(minutes=12), is_open=True, open_rate=0.123, exchange="binance", @@ -316,7 +316,7 @@ def mock_trade_6(fee, is_short: bool): stake_amount=0.001, amount=2.0, amount_requested=2.0, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=5), + open_date=datetime.now(tz=UTC) - timedelta(minutes=5), fee_open=fee.return_value, fee_close=fee.return_value, is_open=True, @@ -410,7 +410,7 @@ def short_trade(fee): strategy="DefaultStrategy", timeframe=5, exit_reason="sell_signal", - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), + open_date=datetime.now(tz=UTC) - timedelta(minutes=20), # close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), is_short=True, ) @@ -500,8 +500,8 @@ def leverage_trade(fee): strategy="DefaultStrategy", timeframe=5, exit_reason="sell_signal", - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=300), - close_date=datetime.now(tz=timezone.utc), + open_date=datetime.now(tz=UTC) - timedelta(minutes=300), + close_date=datetime.now(tz=UTC), interest_rate=0.0005, ) o = Order.parse_from_ccxt_object(leverage_order(), "DOGE/BTC", "sell") diff --git a/tests/conftest_trades_usdt.py b/tests/conftest_trades_usdt.py index 3a547cce2..462f0dfc6 100644 --- a/tests/conftest_trades_usdt.py +++ b/tests/conftest_trades_usdt.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from freqtrade.persistence.models import Order, Trade @@ -55,8 +55,8 @@ def mock_trade_usdt_1(fee, is_short: bool): stake_amount=20.0, amount=2.0, amount_requested=2.0, - open_date=datetime.now(tz=timezone.utc) - timedelta(days=2, minutes=20), - close_date=datetime.now(tz=timezone.utc) - timedelta(days=2, minutes=5), + open_date=datetime.now(tz=UTC) - timedelta(days=2, minutes=20), + close_date=datetime.now(tz=UTC) - timedelta(days=2, minutes=5), fee_open=fee.return_value, fee_close=fee.return_value, is_open=False, @@ -127,8 +127,8 @@ def mock_trade_usdt_2(fee, is_short: bool): timeframe=5, enter_tag="TEST1", exit_reason="exit_signal", - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), - close_date=datetime.now(tz=timezone.utc) - timedelta(minutes=2), + open_date=datetime.now(tz=UTC) - timedelta(minutes=20), + close_date=datetime.now(tz=UTC) - timedelta(minutes=2), is_short=is_short, ) o = Order.parse_from_ccxt_object(mock_order_usdt_2(is_short), "NEO/USDT", entry_side(is_short)) @@ -190,8 +190,8 @@ def mock_trade_usdt_3(fee, is_short: bool): timeframe=5, enter_tag="TEST3", exit_reason="roi", - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=20), - close_date=datetime.now(tz=timezone.utc), + open_date=datetime.now(tz=UTC) - timedelta(minutes=20), + close_date=datetime.now(tz=UTC), is_short=is_short, ) o = Order.parse_from_ccxt_object(mock_order_usdt_3(is_short), "XRP/USDT", entry_side(is_short)) @@ -228,7 +228,7 @@ def mock_trade_usdt_4(fee, is_short: bool): amount_requested=10.01, fee_open=fee.return_value, fee_close=fee.return_value, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=14), + open_date=datetime.now(tz=UTC) - timedelta(minutes=14), is_open=True, open_rate=2.0, exchange="binance", @@ -280,7 +280,7 @@ def mock_trade_usdt_5(fee, is_short: bool): amount_requested=10.01, fee_open=fee.return_value, fee_close=fee.return_value, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=12), + open_date=datetime.now(tz=UTC) - timedelta(minutes=12), is_open=True, open_rate=2.0, exchange="binance", @@ -332,7 +332,7 @@ def mock_trade_usdt_6(fee, is_short: bool): stake_amount=20.0, amount=2.0, amount_requested=2.0, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=5), + open_date=datetime.now(tz=UTC) - timedelta(minutes=5), fee_open=fee.return_value, fee_close=fee.return_value, is_open=True, @@ -374,7 +374,7 @@ def mock_trade_usdt_7(fee, is_short: bool): fee_open=fee.return_value, fee_close=fee.return_value, is_open=True, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=17), + open_date=datetime.now(tz=UTC) - timedelta(minutes=17), open_rate=2.0, exchange="binance", strategy="StrategyTestV2", diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index 6531c6e2e..dd85fb8bb 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path from unittest.mock import MagicMock from zipfile import ZipFile @@ -182,19 +182,19 @@ def test_extract_trades_of_period(testdatadir): "profit_abs": [0.0, 1, -2, -5], "open_date": to_datetime( [ - datetime(2017, 11, 13, 15, 40, 0, tzinfo=timezone.utc), - datetime(2017, 11, 14, 9, 41, 0, tzinfo=timezone.utc), - datetime(2017, 11, 14, 14, 20, 0, tzinfo=timezone.utc), - datetime(2017, 11, 15, 3, 40, 0, tzinfo=timezone.utc), + datetime(2017, 11, 13, 15, 40, 0, tzinfo=UTC), + datetime(2017, 11, 14, 9, 41, 0, tzinfo=UTC), + datetime(2017, 11, 14, 14, 20, 0, tzinfo=UTC), + datetime(2017, 11, 15, 3, 40, 0, tzinfo=UTC), ], utc=True, ), "close_date": to_datetime( [ - datetime(2017, 11, 13, 16, 40, 0, tzinfo=timezone.utc), - datetime(2017, 11, 14, 10, 41, 0, tzinfo=timezone.utc), - datetime(2017, 11, 14, 15, 25, 0, tzinfo=timezone.utc), - datetime(2017, 11, 15, 3, 55, 0, tzinfo=timezone.utc), + datetime(2017, 11, 13, 16, 40, 0, tzinfo=UTC), + datetime(2017, 11, 14, 10, 41, 0, tzinfo=UTC), + datetime(2017, 11, 14, 15, 25, 0, tzinfo=UTC), + datetime(2017, 11, 15, 3, 55, 0, tzinfo=UTC), ], utc=True, ), @@ -203,10 +203,10 @@ def test_extract_trades_of_period(testdatadir): trades1 = extract_trades_of_period(data, trades) # First and last trade are dropped as they are out of range assert len(trades1) == 2 - assert trades1.iloc[0].open_date == datetime(2017, 11, 14, 9, 41, 0, tzinfo=timezone.utc) - assert trades1.iloc[0].close_date == datetime(2017, 11, 14, 10, 41, 0, tzinfo=timezone.utc) - assert trades1.iloc[-1].open_date == datetime(2017, 11, 14, 14, 20, 0, tzinfo=timezone.utc) - assert trades1.iloc[-1].close_date == datetime(2017, 11, 14, 15, 25, 0, tzinfo=timezone.utc) + assert trades1.iloc[0].open_date == datetime(2017, 11, 14, 9, 41, 0, tzinfo=UTC) + assert trades1.iloc[0].close_date == datetime(2017, 11, 14, 10, 41, 0, tzinfo=UTC) + assert trades1.iloc[-1].open_date == datetime(2017, 11, 14, 14, 20, 0, tzinfo=UTC) + assert trades1.iloc[-1].close_date == datetime(2017, 11, 14, 15, 25, 0, tzinfo=UTC) def test_analyze_trade_parallelism(testdatadir): @@ -293,7 +293,7 @@ def test_combined_dataframes_with_rel_mean(testdatadir): pairs = ["ETH/BTC", "ADA/BTC"] data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") df = combined_dataframes_with_rel_mean( - data, datetime(2018, 1, 12, tzinfo=timezone.utc), datetime(2018, 1, 28, tzinfo=timezone.utc) + data, datetime(2018, 1, 12, tzinfo=UTC), datetime(2018, 1, 28, tzinfo=UTC) ) assert isinstance(df, DataFrame) assert "ETH/BTC" not in df.columns @@ -596,7 +596,7 @@ def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, r [1000, 500, 1000, 11000, 10000] # absolute results [1000, 50%, 0%, 0%, ~9%] # Relative drawdowns """ - init_date = datetime(2020, 1, 1, tzinfo=timezone.utc) + init_date = datetime(2020, 1, 1, tzinfo=UTC) dates = [init_date + timedelta(days=i) for i in range(len(profits))] df = DataFrame(zip(profits, dates, strict=False), columns=["profit_abs", "open_date"]) # sort by profit and reset index diff --git a/tests/data/test_datahandler.py b/tests/data/test_datahandler.py index 1cff8b54a..98c7c65ba 100644 --- a/tests/data/test_datahandler.py +++ b/tests/data/test_datahandler.py @@ -1,7 +1,7 @@ # pragma pylint: disable=missing-docstring, protected-access, C0103 import re -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from unittest.mock import MagicMock @@ -165,19 +165,19 @@ def test_datahandler_ohlcv_data_min_max(testdatadir): # Empty pair min_max = dh.ohlcv_data_min_max("UNITTEST/BTC", "8m", "spot") assert len(min_max) == 3 - assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc) + assert min_max[0] == datetime.fromtimestamp(0, tz=UTC) assert min_max[0] == min_max[1] # Empty pair2 min_max = dh.ohlcv_data_min_max("NOPAIR/XXX", "41m", "spot") assert len(min_max) == 3 - assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc) + assert min_max[0] == datetime.fromtimestamp(0, tz=UTC) assert min_max[0] == min_max[1] # Existing pair ... min_max = dh.ohlcv_data_min_max("UNITTEST/BTC", "1m", "spot") assert len(min_max) == 3 - assert min_max[0] == datetime(2017, 11, 4, 23, 2, tzinfo=timezone.utc) - assert min_max[1] == datetime(2017, 11, 14, 22, 59, tzinfo=timezone.utc) + assert min_max[0] == datetime(2017, 11, 4, 23, 2, tzinfo=UTC) + assert min_max[1] == datetime(2017, 11, 14, 22, 59, tzinfo=UTC) def test_datahandler__check_empty_df(testdatadir, caplog): @@ -467,14 +467,14 @@ def test_datahandler_trades_data_min_max(testdatadir): # Empty pair min_max = dh.trades_data_min_max("NADA/ETH", TradingMode.SPOT) assert len(min_max) == 3 - assert min_max[0] == datetime.fromtimestamp(0, tz=timezone.utc) + assert min_max[0] == datetime.fromtimestamp(0, tz=UTC) assert min_max[0] == min_max[1] # Existing pair ... min_max = dh.trades_data_min_max("XRP/ETH", TradingMode.SPOT) assert len(min_max) == 3 - assert min_max[0] == datetime(2019, 10, 11, 0, 0, 11, 620000, tzinfo=timezone.utc) - assert min_max[1] == datetime(2019, 10, 13, 11, 19, 28, 844000, tzinfo=timezone.utc) + assert min_max[0] == datetime(2019, 10, 11, 0, 0, 11, 620000, tzinfo=UTC) + assert min_max[1] == datetime(2019, 10, 13, 11, 19, 28, 844000, tzinfo=UTC) def test_gethandlerclass(): diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index c2adb84bb..27aa2f17d 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import MagicMock import pytest @@ -222,8 +222,8 @@ def test_get_producer_df(default_conf): timeframe = default_conf["timeframe"] candle_type = CandleType.SPOT - empty_la = datetime.fromtimestamp(0, tz=timezone.utc) - now = datetime.now(timezone.utc) + empty_la = datetime.fromtimestamp(0, tz=UTC) + now = datetime.now(UTC) # no data has been added, any request should return an empty dataframe dataframe, la = dataprovider.get_producer_df(pair, timeframe, candle_type) @@ -404,7 +404,7 @@ def test_get_analyzed_dataframe(mocker, default_conf, ohlcv_history): dataframe, time = dp.get_analyzed_dataframe("NOTHING/BTC", timeframe) assert dataframe.empty assert isinstance(time, datetime) - assert time == datetime(1970, 1, 1, tzinfo=timezone.utc) + assert time == datetime(1970, 1, 1, tzinfo=UTC) # Test backtest mode default_conf["runmode"] = RunMode.BACKTEST @@ -478,7 +478,7 @@ def test_dp__add_external_df(default_conf_usdt): default_conf_usdt["timeframe"] = timeframe dp = DataProvider(default_conf_usdt, None) df = generate_test_data(timeframe, 24, "2022-01-01 00:00:00+00:00") - last_analyzed = datetime.now(timezone.utc) + last_analyzed = datetime.now(UTC) res = dp._add_external_df("ETH/USDT", df, last_analyzed, timeframe, CandleType.SPOT) assert res[0] is False diff --git a/tests/data/test_historic_precision.py b/tests/data/test_historic_precision.py index 472218b8b..b1af6d191 100644 --- a/tests/data/test_historic_precision.py +++ b/tests/data/test_historic_precision.py @@ -1,6 +1,6 @@ # pragma pylint: disable=missing-docstring, C0103 -from datetime import timezone +from datetime import UTC import pandas as pd from numpy import nan @@ -16,15 +16,15 @@ def test_get_tick_size_over_time(): # Create test dataframe with different levels of precision data = { "date": [ - Timestamp("2020-01-01 00:00:00", tz=timezone.utc), - Timestamp("2020-01-02 00:00:00", tz=timezone.utc), - Timestamp("2020-01-03 00:00:00", tz=timezone.utc), - Timestamp("2020-01-15 00:00:00", tz=timezone.utc), - Timestamp("2020-01-16 00:00:00", tz=timezone.utc), - Timestamp("2020-01-31 00:00:00", tz=timezone.utc), - Timestamp("2020-02-01 00:00:00", tz=timezone.utc), - Timestamp("2020-02-15 00:00:00", tz=timezone.utc), - Timestamp("2020-03-15 00:00:00", tz=timezone.utc), + Timestamp("2020-01-01 00:00:00", tz=UTC), + Timestamp("2020-01-02 00:00:00", tz=UTC), + Timestamp("2020-01-03 00:00:00", tz=UTC), + Timestamp("2020-01-15 00:00:00", tz=UTC), + Timestamp("2020-01-16 00:00:00", tz=UTC), + Timestamp("2020-01-31 00:00:00", tz=UTC), + Timestamp("2020-02-01 00:00:00", tz=UTC), + Timestamp("2020-02-15 00:00:00", tz=UTC), + Timestamp("2020-03-15 00:00:00", tz=UTC), ], "open": [1.23456, 1.234, 1.23, 1.2, 1.23456, 1.234, 2.3456, 2.34, 2.34], "high": [1.23457, 1.235, 1.24, 1.3, 1.23456, 1.235, 2.3457, 2.34, 2.34], diff --git a/tests/data/test_trade_converter_kraken.py b/tests/data/test_trade_converter_kraken.py index d8d20fd88..bfdb2c0f3 100644 --- a/tests/data/test_trade_converter_kraken.py +++ b/tests/data/test_trade_converter_kraken.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from shutil import copytree from unittest.mock import PropertyMock @@ -50,10 +50,10 @@ def test_import_kraken_trades_from_csv(testdatadir, tmp_path, caplog, default_co assert len(trades) == 340 assert trades["date"].min().to_pydatetime() == datetime( - 2023, 1, 1, 0, 3, 56, tzinfo=timezone.utc + 2023, 1, 1, 0, 3, 56, tzinfo=UTC ) assert trades["date"].max().to_pydatetime() == datetime( - 2023, 1, 2, 23, 17, 3, tzinfo=timezone.utc + 2023, 1, 2, 23, 17, 3, tzinfo=UTC ) # ID is not filled assert len(trades.loc[trades["id"] != ""]) == 0 diff --git a/tests/exchange/test_bybit.py b/tests/exchange/test_bybit.py index ece7b3785..511d8bab4 100644 --- a/tests/exchange/test_bybit.py +++ b/tests/exchange/test_bybit.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from unittest.mock import MagicMock import pytest @@ -73,7 +73,7 @@ async def test_bybit_fetch_funding_rate(default_conf, mocker): def test_bybit_get_funding_fees(default_conf, mocker): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) exchange = get_patched_exchange(mocker, default_conf, exchange="bybit") exchange._fetch_and_calculate_funding_fees = MagicMock() exchange.get_funding_fees("BTC/USDT:USDT", 1, False, now) @@ -117,7 +117,7 @@ def test_bybit_fetch_orders(default_conf, mocker, limit_order): return True mocker.patch(f"{EXMS}.exchange_has", side_effect=exchange_has) - start_time = datetime.now(timezone.utc) - timedelta(days=20) + start_time = datetime.now(UTC) - timedelta(days=20) exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bybit") # Not available in dry-run diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 2f70f79c1..f4c493e78 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1,7 +1,7 @@ import copy import logging from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from random import randint from unittest.mock import MagicMock, Mock, PropertyMock, patch @@ -1728,7 +1728,7 @@ def test_fetch_orders(default_conf, mocker, exchange_name, limit_order): api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]]) mocker.patch(f"{EXMS}.exchange_has", return_value=True) - start_time = datetime.now(timezone.utc) - timedelta(days=20) + start_time = datetime.now(UTC) - timedelta(days=20) expected = 1 if exchange_name == "bybit": expected = 3 @@ -2106,7 +2106,7 @@ def test___now_is_time_to_refresh(default_conf, mocker, exchange_name, time_mach exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name) pair = "BTC/USDT" candle_type = CandleType.SPOT - start_dt = datetime(2023, 12, 1, 0, 10, 0, tzinfo=timezone.utc) + start_dt = datetime(2023, 12, 1, 0, 10, 0, tzinfo=UTC) time_machine.move_to(start_dt, tick=False) assert (pair, "5m", candle_type) not in exchange._pairs_last_refresh_time @@ -2129,7 +2129,7 @@ def test___now_is_time_to_refresh(default_conf, mocker, exchange_name, time_mach assert exchange._now_is_time_to_refresh(pair, "5m", candle_type) is True # Test with 1d data - start_day_dt = datetime(2023, 12, 1, 0, 0, 0, tzinfo=timezone.utc) + start_day_dt = datetime(2023, 12, 1, 0, 0, 0, tzinfo=UTC) last_closed_candle_1d = dt_ts(start_day_dt - timedelta(days=1)) exchange._pairs_last_refresh_time[(pair, "1d", candle_type)] = last_closed_candle_1d @@ -2197,7 +2197,7 @@ def test_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name, candle_ async def test__async_get_historic_ohlcv(default_conf, mocker, caplog, exchange_name, candle_type): ohlcv = [ [ - int((datetime.now(timezone.utc).timestamp() - 1000) * 1000), + int((datetime.now(UTC).timestamp() - 1000) * 1000), 1, # open 2, # high 3, # low @@ -2501,7 +2501,7 @@ def test_refresh_latest_trades( @pytest.mark.parametrize("candle_type", [CandleType.FUTURES, CandleType.MARK, CandleType.SPOT]) def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_machine) -> None: - start = datetime(2021, 8, 1, 0, 0, 0, 0, tzinfo=timezone.utc) + start = datetime(2021, 8, 1, 0, 0, 0, 0, tzinfo=UTC) ohlcv = generate_test_data_raw("1h", 100, start.strftime("%Y-%m-%d")) time_machine.move_to(start + timedelta(hours=99, minutes=30)) @@ -2595,7 +2595,7 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach def test_refresh_ohlcv_with_cache(mocker, default_conf, time_machine) -> None: - start = datetime(2021, 8, 1, 0, 0, 0, 0, tzinfo=timezone.utc) + start = datetime(2021, 8, 1, 0, 0, 0, 0, tzinfo=UTC) ohlcv = generate_test_data_raw("1h", 100, start.strftime("%Y-%m-%d")) time_machine.move_to(start, tick=False) pairs = [ @@ -2903,7 +2903,7 @@ def test_get_entry_rate( mocker, default_conf, caplog, side, ask, bid, last, last_ab, expected, time_machine ) -> None: caplog.set_level(logging.DEBUG) - start_dt = datetime(2023, 12, 1, 0, 10, 0, tzinfo=timezone.utc) + start_dt = datetime(2023, 12, 1, 0, 10, 0, tzinfo=UTC) time_machine.move_to(start_dt, tick=False) if last_ab is None: del default_conf["entry_pricing"]["price_last_balance"] @@ -2940,7 +2940,7 @@ def test_get_exit_rate( default_conf, mocker, caplog, side, bid, ask, last, last_ab, expected, time_machine ) -> None: caplog.set_level(logging.DEBUG) - start_dt = datetime(2023, 12, 1, 0, 10, 0, tzinfo=timezone.utc) + start_dt = datetime(2023, 12, 1, 0, 10, 0, tzinfo=UTC) time_machine.move_to(start_dt, tick=False) default_conf["exit_pricing"]["price_side"] = side @@ -4019,7 +4019,7 @@ def test_get_trades_for_order(default_conf, mocker, exchange_name, trading_mode, assert api_mock.fetch_my_trades.call_args[0][1] == 1525478395000 assert ( api_mock.fetch_my_trades.call_args[0][1] - == int(since.replace(tzinfo=timezone.utc).timestamp() - 5) * 1000 + == int(since.replace(tzinfo=UTC).timestamp() - 5) * 1000 ) ccxt_exceptionhandlers( @@ -4785,7 +4785,7 @@ def test_calculate_backoff(retrycount, max_retries, expected): @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_get_funding_fees(default_conf_usdt, mocker, exchange_name, caplog): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) default_conf_usdt["trading_mode"] = "futures" default_conf_usdt["margin_mode"] = "isolated" exchange = get_patched_exchange(mocker, default_conf_usdt, exchange=exchange_name) @@ -5002,8 +5002,8 @@ def test_calculate_funding_fees( ): exchange = get_patched_exchange(mocker, default_conf) kraken = get_patched_exchange(mocker, default_conf, exchange="kraken") - prior_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc) - timedelta(hours=1)) - trade_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc)) + prior_date = timeframe_to_prev_date("1h", datetime.now(UTC) - timedelta(hours=1)) + trade_date = timeframe_to_prev_date("1h", datetime.now(UTC)) funding_rates = DataFrame( [ {"date": prior_date, "open": funding_rate}, # Line not used. @@ -5072,9 +5072,9 @@ def test_combine_funding_and_mark( futures_funding_rate, ): exchange = get_patched_exchange(mocker, default_conf) - prior2_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc) - timedelta(hours=2)) - prior_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc) - timedelta(hours=1)) - trade_date = timeframe_to_prev_date("1h", datetime.now(timezone.utc)) + prior2_date = timeframe_to_prev_date("1h", datetime.now(UTC) - timedelta(hours=2)) + prior_date = timeframe_to_prev_date("1h", datetime.now(UTC) - timedelta(hours=1)) + trade_date = timeframe_to_prev_date("1h", datetime.now(UTC)) funding_rates = DataFrame( [ {"date": prior2_date, "open": funding_rate}, diff --git a/tests/exchange/test_exchange_utils.py b/tests/exchange/test_exchange_utils.py index 313a2db49..47ad7cccb 100644 --- a/tests/exchange/test_exchange_utils.py +++ b/tests/exchange/test_exchange_utils.py @@ -1,5 +1,5 @@ # pragma pylint: disable=missing-docstring, protected-access, invalid-name -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from math import isnan, nan import pytest @@ -117,7 +117,7 @@ def test_check_exchange(default_conf, caplog) -> None: def test_date_minus_candles(): - date = datetime(2019, 8, 12, 13, 25, 0, tzinfo=timezone.utc) + date = datetime(2019, 8, 12, 13, 25, 0, tzinfo=UTC) assert date_minus_candles("5m", 3, date) == date - timedelta(minutes=15) assert date_minus_candles("5m", 5, date) == date - timedelta(minutes=25) @@ -167,59 +167,59 @@ def test_timeframe_to_resample_freq(timeframe, expected): def test_timeframe_to_prev_date(): # 2019-08-12 13:22:08 - date = datetime.fromtimestamp(1565616128, tz=timezone.utc) + date = datetime.fromtimestamp(1565616128, tz=UTC) tf_list = [ # 5m -> 2019-08-12 13:20:00 - ("5m", datetime(2019, 8, 12, 13, 20, 0, tzinfo=timezone.utc)), + ("5m", datetime(2019, 8, 12, 13, 20, 0, tzinfo=UTC)), # 10m -> 2019-08-12 13:20:00 - ("10m", datetime(2019, 8, 12, 13, 20, 0, tzinfo=timezone.utc)), + ("10m", datetime(2019, 8, 12, 13, 20, 0, tzinfo=UTC)), # 1h -> 2019-08-12 13:00:00 - ("1h", datetime(2019, 8, 12, 13, 00, 0, tzinfo=timezone.utc)), + ("1h", datetime(2019, 8, 12, 13, 00, 0, tzinfo=UTC)), # 2h -> 2019-08-12 12:00:00 - ("2h", datetime(2019, 8, 12, 12, 00, 0, tzinfo=timezone.utc)), + ("2h", datetime(2019, 8, 12, 12, 00, 0, tzinfo=UTC)), # 4h -> 2019-08-12 12:00:00 - ("4h", datetime(2019, 8, 12, 12, 00, 0, tzinfo=timezone.utc)), + ("4h", datetime(2019, 8, 12, 12, 00, 0, tzinfo=UTC)), # 1d -> 2019-08-12 00:00:00 - ("1d", datetime(2019, 8, 12, 00, 00, 0, tzinfo=timezone.utc)), + ("1d", datetime(2019, 8, 12, 00, 00, 0, tzinfo=UTC)), ] for interval, result in tf_list: assert timeframe_to_prev_date(interval, date) == result - date = datetime.now(tz=timezone.utc) + date = datetime.now(tz=UTC) assert timeframe_to_prev_date("5m") < date # Does not round - time = datetime(2019, 8, 12, 13, 20, 0, tzinfo=timezone.utc) + time = datetime(2019, 8, 12, 13, 20, 0, tzinfo=UTC) assert timeframe_to_prev_date("5m", time) == time - time = datetime(2019, 8, 12, 13, 0, 0, tzinfo=timezone.utc) + time = datetime(2019, 8, 12, 13, 0, 0, tzinfo=UTC) assert timeframe_to_prev_date("1h", time) == time def test_timeframe_to_next_date(): # 2019-08-12 13:22:08 - date = datetime.fromtimestamp(1565616128, tz=timezone.utc) + date = datetime.fromtimestamp(1565616128, tz=UTC) tf_list = [ # 5m -> 2019-08-12 13:25:00 - ("5m", datetime(2019, 8, 12, 13, 25, 0, tzinfo=timezone.utc)), + ("5m", datetime(2019, 8, 12, 13, 25, 0, tzinfo=UTC)), # 10m -> 2019-08-12 13:30:00 - ("10m", datetime(2019, 8, 12, 13, 30, 0, tzinfo=timezone.utc)), + ("10m", datetime(2019, 8, 12, 13, 30, 0, tzinfo=UTC)), # 1h -> 2019-08-12 14:00:00 - ("1h", datetime(2019, 8, 12, 14, 00, 0, tzinfo=timezone.utc)), + ("1h", datetime(2019, 8, 12, 14, 00, 0, tzinfo=UTC)), # 2h -> 2019-08-12 14:00:00 - ("2h", datetime(2019, 8, 12, 14, 00, 0, tzinfo=timezone.utc)), + ("2h", datetime(2019, 8, 12, 14, 00, 0, tzinfo=UTC)), # 4h -> 2019-08-12 14:00:00 - ("4h", datetime(2019, 8, 12, 16, 00, 0, tzinfo=timezone.utc)), + ("4h", datetime(2019, 8, 12, 16, 00, 0, tzinfo=UTC)), # 1d -> 2019-08-13 00:00:00 - ("1d", datetime(2019, 8, 13, 0, 0, 0, tzinfo=timezone.utc)), + ("1d", datetime(2019, 8, 13, 0, 0, 0, tzinfo=UTC)), ] for interval, result in tf_list: assert timeframe_to_next_date(interval, date) == result - date = datetime.now(tz=timezone.utc) + date = datetime.now(tz=UTC) assert timeframe_to_next_date("5m") > date - date = datetime(2019, 8, 12, 13, 30, 0, tzinfo=timezone.utc) + date = datetime(2019, 8, 12, 13, 30, 0, tzinfo=UTC) assert timeframe_to_next_date("5m", date) == date + timedelta(minutes=5) diff --git a/tests/exchange/test_gate.py b/tests/exchange/test_gate.py index 3f5ecacf6..ee3d70c84 100644 --- a/tests/exchange/test_gate.py +++ b/tests/exchange/test_gate.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import MagicMock import pytest @@ -113,7 +113,7 @@ def test_fetch_my_trades_gate(mocker, default_conf, takerormaker, rate, cost): ) exchange = get_patched_exchange(mocker, default_conf, api_mock=api_mock, exchange="gate") exchange._trading_fees = tick - trades = exchange.get_trades_for_order("22255", "ETH/USDT:USDT", datetime.now(timezone.utc)) + trades = exchange.get_trades_for_order("22255", "ETH/USDT:USDT", datetime.now(UTC)) trade = trades[0] assert trade["fee"] assert trade["fee"]["rate"] == rate diff --git a/tests/exchange/test_hyperliquid.py b/tests/exchange/test_hyperliquid.py index a169a9a73..c7924080e 100644 --- a/tests/exchange/test_hyperliquid.py +++ b/tests/exchange/test_hyperliquid.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import MagicMock, PropertyMock import pytest @@ -306,7 +306,7 @@ def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker): def test_hyperliquid_get_funding_fees(default_conf, mocker): - now = datetime.now(timezone.utc) + now = datetime.now(UTC) exchange = get_patched_exchange(mocker, default_conf, exchange="hyperliquid") exchange._fetch_and_calculate_funding_fees = MagicMock() exchange.get_funding_fees("BTC/USDC:USDC", 1, False, now) diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 1bf783daa..e2a29b1fc 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from unittest.mock import AsyncMock, MagicMock, PropertyMock import ccxt @@ -15,7 +15,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers def test_okx_ohlcv_candle_limit(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, exchange="okx") timeframes = ("1m", "5m", "1h") - start_time = int(datetime(2021, 1, 1, tzinfo=timezone.utc).timestamp() * 1000) + start_time = int(datetime(2021, 1, 1, tzinfo=UTC).timestamp() * 1000) for timeframe in timeframes: assert exchange.ohlcv_candle_limit(timeframe, CandleType.SPOT) == 300 @@ -29,7 +29,7 @@ def test_okx_ohlcv_candle_limit(default_conf, mocker): assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 100 one_call = int( ( - datetime.now(timezone.utc) + datetime.now(UTC) - timedelta(minutes=290 * timeframe_to_minutes(timeframe)) ).timestamp() * 1000 @@ -40,7 +40,7 @@ def test_okx_ohlcv_candle_limit(default_conf, mocker): one_call = int( ( - datetime.now(timezone.utc) + datetime.now(UTC) - timedelta(minutes=320 * timeframe_to_minutes(timeframe)) ).timestamp() * 1000 @@ -693,7 +693,7 @@ def test_fetch_orders_okx(default_conf, mocker, limit_order): api_mock.fetch_closed_orders = MagicMock(return_value=[limit_order["buy"]]) mocker.patch(f"{EXMS}.exchange_has", return_value=True) - start_time = datetime.now(timezone.utc) - timedelta(days=20) + start_time = datetime.now(UTC) - timedelta(days=20) exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="okx") # Not available in dry-run @@ -727,7 +727,7 @@ def test_fetch_orders_okx(default_conf, mocker, limit_order): api_mock.fetch_closed_orders.reset_mock() # regular closed_orders endpoint only has history for 7 days. - exchange.fetch_orders("mocked", datetime.now(timezone.utc) - timedelta(days=6)) + exchange.fetch_orders("mocked", datetime.now(UTC) - timedelta(days=6)) assert api_mock.fetch_orders.call_count == 0 assert api_mock.fetch_open_orders.call_count == 1 assert api_mock.fetch_closed_orders.call_count == 1 diff --git a/tests/exchange_online/test_ccxt_compat.py b/tests/exchange_online/test_ccxt_compat.py index 85de9b5ec..aa9b51713 100644 --- a/tests/exchange_online/test_ccxt_compat.py +++ b/tests/exchange_online/test_ccxt_compat.py @@ -5,7 +5,7 @@ However, these tests should give a good idea to determine if a new exchange is suitable to run with freqtrade. """ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -248,7 +248,7 @@ class TestCCXTExchange: len(exch.klines(pair_tf)) > exch.ohlcv_candle_limit(timeframe, CandleType.SPOT) * 0.90 ) # Check if last-timeframe is within the last 2 intervals - now = datetime.now(timezone.utc) - timedelta(minutes=(timeframe_to_minutes(timeframe) * 2)) + now = datetime.now(UTC) - timedelta(minutes=(timeframe_to_minutes(timeframe) * 2)) assert exch.klines(pair_tf).iloc[-1]["date"] >= timeframe_to_prev_date(timeframe, now) def test_ccxt_fetch_ohlcv_startdate(self, exchange: EXCHANGE_FIXTURE_TYPE): @@ -266,7 +266,7 @@ class TestCCXTExchange: assert isinstance(ohlcv, dict) assert len(ohlcv[pair_tf]) == len(exch.klines(pair_tf)) # Check if last-timeframe is within the last 2 intervals - now = datetime.now(timezone.utc) - timedelta(minutes=(timeframe_to_minutes(timeframe) * 2)) + now = datetime.now(UTC) - timedelta(minutes=(timeframe_to_minutes(timeframe) * 2)) assert exch.klines(pair_tf).iloc[-1]["date"] >= timeframe_to_prev_date(timeframe, now) assert exch.klines(pair_tf)["date"].astype(int).iloc[0] // 1e6 == since_ms @@ -274,7 +274,7 @@ class TestCCXTExchange: self, exchange, exchangename, pair, timeframe, candle_type, factor=0.9 ): timeframe_ms = timeframe_to_msecs(timeframe) - now = timeframe_to_prev_date(timeframe, datetime.now(timezone.utc)) + now = timeframe_to_prev_date(timeframe, datetime.now(UTC)) for offset in (360, 120, 30, 10, 5, 2): since = now - timedelta(days=offset) since_ms = int(since.timestamp() * 1000) @@ -336,7 +336,7 @@ class TestCCXTExchange: exchange, exchangename = exchange_futures pair = EXCHANGES[exchangename].get("futures_pair", EXCHANGES[exchangename]["pair"]) - since = int((datetime.now(timezone.utc) - timedelta(days=5)).timestamp() * 1000) + since = int((datetime.now(UTC) - timedelta(days=5)).timestamp() * 1000) timeframe_ff = exchange._ft_has.get( "funding_fee_timeframe", exchange._ft_has["mark_ohlcv_timeframe"] ) @@ -371,7 +371,7 @@ class TestCCXTExchange: def test_ccxt_fetch_mark_price_history(self, exchange_futures: EXCHANGE_FIXTURE_TYPE): exchange, exchangename = exchange_futures pair = EXCHANGES[exchangename].get("futures_pair", EXCHANGES[exchangename]["pair"]) - since = int((datetime.now(timezone.utc) - timedelta(days=5)).timestamp() * 1000) + since = int((datetime.now(UTC) - timedelta(days=5)).timestamp() * 1000) pair_tf = (pair, "1h", CandleType.MARK) mark_ohlcv = exchange.refresh_latest_ohlcv([pair_tf], since_ms=since, drop_incomplete=False) @@ -389,7 +389,7 @@ class TestCCXTExchange: def test_ccxt__calculate_funding_fees(self, exchange_futures: EXCHANGE_FIXTURE_TYPE): exchange, exchangename = exchange_futures pair = EXCHANGES[exchangename].get("futures_pair", EXCHANGES[exchangename]["pair"]) - since = datetime.now(timezone.utc) - timedelta(days=5) + since = datetime.now(UTC) - timedelta(days=5) funding_fee = exchange._fetch_and_calculate_funding_fees( pair, 20, is_short=False, open_date=since @@ -403,7 +403,7 @@ class TestCCXTExchange: if not (lookback := EXCHANGES[exchangename].get("trades_lookback_hours")): pytest.skip("test_fetch_trades not enabled for this exchange") pair = EXCHANGES[exchangename]["pair"] - since = int((datetime.now(timezone.utc) - timedelta(hours=lookback)).timestamp() * 1000) + since = int((datetime.now(UTC) - timedelta(hours=lookback)).timestamp() * 1000) res = exch.loop.run_until_complete(exch._async_get_trade_history(pair, since, None, None)) assert len(res) == 2 res_pair, res_trades = res diff --git a/tests/freqai/test_freqai_backtesting.py b/tests/freqai/test_freqai_backtesting.py index e689d3927..c9be6ea05 100644 --- a/tests/freqai/test_freqai_backtesting.py +++ b/tests/freqai/test_freqai_backtesting.py @@ -1,5 +1,5 @@ from copy import deepcopy -from datetime import datetime, timezone +from datetime import UTC, datetime from pathlib import Path from unittest.mock import PropertyMock @@ -28,7 +28,7 @@ from tests.freqai.conftest import get_patched_freqai_strategy def test_freqai_backtest_start_backtest_list(freqai_conf, mocker, testdatadir, caplog): patch_exchange(mocker) - now = datetime.now(timezone.utc) + now = datetime.now(UTC) mocker.patch( "freqtrade.plugins.pairlistmanager.PairListManager.whitelist", PropertyMock(return_value=["HULUMULU/USDT", "XRP/USDT"]), @@ -73,7 +73,7 @@ def test_freqai_backtest_load_data( ): patch_exchange(mocker) - now = datetime.now(timezone.utc) + now = datetime.now(UTC) mocker.patch( "freqtrade.plugins.pairlistmanager.PairListManager.whitelist", PropertyMock(return_value=["HULUMULU/USDT", "XRP/USDT"]), @@ -98,7 +98,7 @@ def test_freqai_backtest_load_data( def test_freqai_backtest_live_models_model_not_found(freqai_conf, mocker, testdatadir, caplog): patch_exchange(mocker) - now = datetime.now(timezone.utc) + now = datetime.now(UTC) mocker.patch( "freqtrade.plugins.pairlistmanager.PairListManager.whitelist", PropertyMock(return_value=["HULUMULU/USDT", "XRP/USDT"]), @@ -163,6 +163,6 @@ def test_freqai_backtest_consistent_timerange(mocker, freqai_conf): backtesting = Backtesting(deepcopy(freqai_conf)) backtesting.start() - assert gbs.call_args[1]["min_date"] == datetime(2021, 11, 20, 0, 0, tzinfo=timezone.utc) - assert gbs.call_args[1]["max_date"] == datetime(2021, 11, 21, 0, 0, tzinfo=timezone.utc) + assert gbs.call_args[1]["min_date"] == datetime(2021, 11, 20, 0, 0, tzinfo=UTC) + assert gbs.call_args[1]["max_date"] == datetime(2021, 11, 21, 0, 0, tzinfo=UTC) Backtesting.cleanup() diff --git a/tests/freqai/test_freqai_datakitchen.py b/tests/freqai/test_freqai_datakitchen.py index 2e92ae447..d974ac801 100644 --- a/tests/freqai/test_freqai_datakitchen.py +++ b/tests/freqai/test_freqai_datakitchen.py @@ -1,5 +1,5 @@ import shutil -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path from unittest.mock import MagicMock @@ -67,9 +67,9 @@ def test_split_timerange( def test_check_if_model_expired(mocker, freqai_conf): dk = get_patched_data_kitchen(mocker, freqai_conf) - now = datetime.now(tz=timezone.utc).timestamp() + now = datetime.now(tz=UTC).timestamp() assert dk.check_if_model_expired(now) is False - now = (datetime.now(tz=timezone.utc) - timedelta(hours=2)).timestamp() + now = (datetime.now(tz=UTC) - timedelta(hours=2)).timestamp() assert dk.check_if_model_expired(now) is True shutil.rmtree(Path(dk.full_path)) diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 9ea892ab8..292ee92aa 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -3,7 +3,7 @@ import random from collections import defaultdict from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path from unittest.mock import ANY, MagicMock, PropertyMock @@ -687,7 +687,7 @@ def test_backtest__check_trade_exit(default_conf, mocker) -> None: backtesting._set_strategy(backtesting.strategylist[0]) pair = "UNITTEST/BTC" row = [ - pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=55, tzinfo=timezone.utc), + pd.Timestamp(year=2020, month=1, day=1, hour=4, minute=55, tzinfo=UTC), 200, # Open 201.5, # High 195, # Low @@ -705,7 +705,7 @@ def test_backtest__check_trade_exit(default_conf, mocker) -> None: assert isinstance(trade, LocalTrade) row_sell = [ - pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=timezone.utc), + pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0, tzinfo=UTC), 200, # Open 210.5, # High 195, # Low @@ -723,7 +723,7 @@ def test_backtest__check_trade_exit(default_conf, mocker) -> None: res = backtesting._check_trade_exit(trade, row_sell, row_sell[0].to_pydatetime()) assert res is not None assert res.exit_reason == ExitType.ROI.value - assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=timezone.utc) + assert res.close_date_utc == datetime(2020, 1, 1, 5, 0, tzinfo=UTC) # Enter new trade trade = backtesting._enter_trade(pair, row=row, direction="long") @@ -928,7 +928,7 @@ def test_backtest_one_detail(default_conf_usdt, mocker, testdatadir, use_detail) assert len(t["orders"]) == 2 entryo = t["orders"][0] - entry_ts = datetime.fromtimestamp(entryo["order_filled_timestamp"] // 1000, tz=timezone.utc) + entry_ts = datetime.fromtimestamp(entryo["order_filled_timestamp"] // 1000, tz=UTC) if entry_ts > t["open_date"]: late_entry += 1 @@ -1039,7 +1039,7 @@ def test_backtest_one_detail_futures( assert len(t["orders"]) == 2 entryo = t["orders"][0] - entry_ts = datetime.fromtimestamp(entryo["order_filled_timestamp"] // 1000, tz=timezone.utc) + entry_ts = datetime.fromtimestamp(entryo["order_filled_timestamp"] // 1000, tz=UTC) if entry_ts > t["open_date"]: late_entry += 1 @@ -1121,7 +1121,7 @@ def test_backtest_one_detail_futures_funding_fees( return df def adjust_trade_position(trade, current_time, **kwargs): - if current_time > datetime(2021, 11, 18, 2, 0, 0, tzinfo=timezone.utc): + if current_time > datetime(2021, 11, 18, 2, 0, 0, tzinfo=UTC): return None return default_conf_usdt["stake_amount"] @@ -2564,7 +2564,7 @@ def test_backtest_start_multi_strat_caching( mocker.patch("freqtrade.optimize.backtesting.Backtesting.backtest", backtestmock) mocker.patch("freqtrade.optimize.backtesting.show_backtest_results", MagicMock()) - now = min_backtest_date = datetime.now(tz=timezone.utc) + now = min_backtest_date = datetime.now(tz=UTC) start_time = now - timedelta(**start_delta) + timedelta(hours=1) if cache == "none": min_backtest_date = now + timedelta(days=1) diff --git a/tests/test_timerange.py b/tests/test_timerange.py index c7471e263..c0f44f2b7 100644 --- a/tests/test_timerange.py +++ b/tests/test_timerange.py @@ -1,5 +1,5 @@ # pragma pylint: disable=missing-docstring, C0103 -from datetime import datetime, timezone +from datetime import UTC, datetime import pytest @@ -29,8 +29,8 @@ def test_parse_timerange_incorrect(): assert TimeRange("date", "date", 1231006505, 1233360000) == timerange assert isinstance(timerange.startdt, datetime) assert isinstance(timerange.stopdt, datetime) - assert timerange.startdt == datetime.fromtimestamp(1231006505, tz=timezone.utc) - assert timerange.stopdt == datetime.fromtimestamp(1233360000, tz=timezone.utc) + assert timerange.startdt == datetime.fromtimestamp(1231006505, tz=UTC) + assert timerange.stopdt == datetime.fromtimestamp(1233360000, tz=UTC) assert timerange.timerange_str == "20090103-20090131" timerange = TimeRange.parse_timerange("1231006505000-1233360000000") @@ -68,7 +68,7 @@ def test_subtract_start(): def test_adjust_start_if_necessary(): - min_date = datetime(2017, 11, 14, 21, 15, 00, tzinfo=timezone.utc) + min_date = datetime(2017, 11, 14, 21, 15, 00, tzinfo=UTC) x = TimeRange("date", "date", 1510694100, 1510780500) # Adjust by 20 candles - min_date == startts From 640cab2ca876a6206ad84db43a031562bb75deb9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 09:02:34 +0200 Subject: [PATCH 11/15] refactor: tests - update timezone.utc to UTC --- tests/persistence/test_key_value_store.py | 18 ++++----- tests/persistence/test_persistence.py | 18 ++++----- tests/persistence/test_trade_fromjson.py | 6 +-- tests/plugins/test_pairlocks.py | 10 ++--- tests/plugins/test_percentchangepairlist.py | 8 ++-- tests/plugins/test_protections.py | 14 +++---- tests/rpc/test_rpc.py | 10 ++--- tests/rpc/test_rpc_apiserver.py | 10 ++--- tests/rpc/test_rpc_emc.py | 8 ++-- tests/strategy/test_default_strategy.py | 6 +-- tests/strategy/test_interface.py | 10 ++--- tests/util/test_datetime_helpers.py | 44 ++++++++++----------- 12 files changed, 81 insertions(+), 81 deletions(-) diff --git a/tests/persistence/test_key_value_store.py b/tests/persistence/test_key_value_store.py index e69ae0220..71411513d 100644 --- a/tests/persistence/test_key_value_store.py +++ b/tests/persistence/test_key_value_store.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -8,19 +8,19 @@ from tests.conftest import create_mock_trades_usdt @pytest.mark.usefixtures("init_persistence") def test_key_value_store(time_machine): - start = datetime(2023, 1, 1, 4, tzinfo=timezone.utc) + start = datetime(2023, 1, 1, 4, tzinfo=UTC) time_machine.move_to(start, tick=False) KeyValueStore.store_value("test", "testStringValue") - KeyValueStore.store_value("test_dt", datetime.now(timezone.utc)) + KeyValueStore.store_value("test_dt", datetime.now(UTC)) KeyValueStore.store_value("test_float", 22.51) KeyValueStore.store_value("test_int", 15) assert KeyValueStore.get_value("test") == "testStringValue" assert KeyValueStore.get_value("test") == "testStringValue" assert KeyValueStore.get_string_value("test") == "testStringValue" - assert KeyValueStore.get_value("test_dt") == datetime.now(timezone.utc) - assert KeyValueStore.get_datetime_value("test_dt") == datetime.now(timezone.utc) + assert KeyValueStore.get_value("test_dt") == datetime.now(UTC) + assert KeyValueStore.get_datetime_value("test_dt") == datetime.now(UTC) assert KeyValueStore.get_string_value("test_dt") is None assert KeyValueStore.get_float_value("test_dt") is None assert KeyValueStore.get_int_value("test_dt") is None @@ -31,11 +31,11 @@ def test_key_value_store(time_machine): assert KeyValueStore.get_datetime_value("test_int") is None time_machine.move_to(start + timedelta(days=20, hours=5), tick=False) - assert KeyValueStore.get_value("test_dt") != datetime.now(timezone.utc) + assert KeyValueStore.get_value("test_dt") != datetime.now(UTC) assert KeyValueStore.get_value("test_dt") == start # Test update works - KeyValueStore.store_value("test_dt", datetime.now(timezone.utc)) - assert KeyValueStore.get_value("test_dt") == datetime.now(timezone.utc) + KeyValueStore.store_value("test_dt", datetime.now(UTC)) + assert KeyValueStore.get_value("test_dt") == datetime.now(UTC) KeyValueStore.store_value("test_float", 23.51) assert KeyValueStore.get_value("test_float") == 23.51 @@ -52,7 +52,7 @@ def test_key_value_store(time_machine): @pytest.mark.usefixtures("init_persistence") def test_set_startup_time(fee, time_machine): create_mock_trades_usdt(fee) - start = datetime.now(timezone.utc) + start = datetime.now(UTC) time_machine.move_to(start, tick=False) set_startup_time() diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index d07bd7192..5ec1a05d2 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -1,5 +1,5 @@ # pragma pylint: disable=missing-docstring, C0103 -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from types import FunctionType import pytest @@ -265,7 +265,7 @@ def test_interest(fee, exchange, is_short, lev, minutes, rate, interest, trading stake_amount=20.0, amount=30.0, open_rate=2.0, - open_date=datetime.now(timezone.utc) - timedelta(minutes=minutes), + open_date=datetime.now(UTC) - timedelta(minutes=minutes), fee_open=fee.return_value, fee_close=fee.return_value, exchange=exchange, @@ -605,7 +605,7 @@ def test_calc_open_close_trade_price( stake_amount=60.0, open_rate=2.0, amount=30.0, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), + open_date=datetime.now(tz=UTC) - timedelta(minutes=10), interest_rate=0.0005, fee_open=fee.return_value, fee_close=fee.return_value, @@ -812,7 +812,7 @@ def test_calc_open_trade_value( stake_amount=60.0, amount=30.0, open_rate=2.0, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), + open_date=datetime.now(tz=UTC) - timedelta(minutes=10), fee_open=fee_rate, fee_close=fee_rate, exchange=exchange, @@ -863,7 +863,7 @@ def test_calc_close_trade_price( stake_amount=60.0, amount=30.0, open_rate=open_rate, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), + open_date=datetime.now(tz=UTC) - timedelta(minutes=10), fee_open=fee_rate, fee_close=fee_rate, exchange=exchange, @@ -1164,7 +1164,7 @@ def test_calc_profit( stake_amount=60.0, amount=30.0 * lev, open_rate=2.0, - open_date=datetime.now(tz=timezone.utc) - timedelta(minutes=10), + open_date=datetime.now(tz=UTC) - timedelta(minutes=10), interest_rate=0.0005, exchange=exchange, is_short=is_short, @@ -1882,7 +1882,7 @@ def test_get_trades_proxy(fee, use_db, is_short): assert len(trades) == 2 assert not trades[0].is_open - opendate = datetime.now(tz=timezone.utc) - timedelta(minutes=15) + opendate = datetime.now(tz=UTC) - timedelta(minutes=15) assert len(Trade.get_trades_proxy(open_date=opendate)) == 3 @@ -1989,7 +1989,7 @@ def test_fully_canceled_entry_order_count(fee, is_short): @pytest.mark.usefixtures("init_persistence") def test_update_order_from_ccxt(caplog, time_machine): - start = datetime(2023, 1, 1, 4, tzinfo=timezone.utc) + start = datetime(2023, 1, 1, 4, tzinfo=UTC) time_machine.move_to(start, tick=False) # Most basic order return (only has orderid) @@ -2172,7 +2172,7 @@ def test_trade_truncates_string_fields(): stake_amount=20.0, amount=30.0, open_rate=2.0, - open_date=datetime.now(timezone.utc) - timedelta(minutes=20), + open_date=datetime.now(UTC) - timedelta(minutes=20), fee_open=0.001, fee_close=0.001, exchange="binance", diff --git a/tests/persistence/test_trade_fromjson.py b/tests/persistence/test_trade_fromjson.py index ff717751e..1154751bd 100644 --- a/tests/persistence/test_trade_fromjson.py +++ b/tests/persistence/test_trade_fromjson.py @@ -1,5 +1,5 @@ import json -from datetime import datetime, timezone +from datetime import UTC, datetime import pytest @@ -182,7 +182,7 @@ def test_trade_fromjson(): assert trade.id == 25 assert trade.pair == "ETH/USDT" - assert trade.open_date_utc == datetime(2022, 10, 18, 9, 12, 42, tzinfo=timezone.utc) + assert trade.open_date_utc == datetime(2022, 10, 18, 9, 12, 42, tzinfo=UTC) assert isinstance(trade.open_date, datetime) assert trade.exit_reason == "no longer good" assert trade.realized_profit == 2.76315361 @@ -192,7 +192,7 @@ def test_trade_fromjson(): assert len(trade.orders) == 5 last_o = trade.orders[-1] - assert last_o.order_filled_utc == datetime(2022, 10, 18, 9, 45, 22, tzinfo=timezone.utc) + assert last_o.order_filled_utc == datetime(2022, 10, 18, 9, 45, 22, tzinfo=UTC) assert isinstance(last_o.order_date, datetime) assert last_o.funding_fee == -0.055 diff --git a/tests/plugins/test_pairlocks.py b/tests/plugins/test_pairlocks.py index f227818b3..678355f1c 100644 --- a/tests/plugins/test_pairlocks.py +++ b/tests/plugins/test_pairlocks.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -62,7 +62,7 @@ def test_PairLocks(use_db): pair = "BTC/USDT" # Lock until 14:30 - lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc) + lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=UTC) PairLocks.lock_pair(pair, lock_time) assert not PairLocks.is_pair_locked(pair) @@ -121,15 +121,15 @@ def test_PairLocks_getlongestlock(use_db): assert PairLocks.is_pair_locked(pair) lock = PairLocks.get_pair_longest_lock(pair) - assert lock.lock_end_time.replace(tzinfo=timezone.utc) > dt_now() + timedelta(minutes=3) - assert lock.lock_end_time.replace(tzinfo=timezone.utc) < dt_now() + timedelta(minutes=14) + assert lock.lock_end_time.replace(tzinfo=UTC) > dt_now() + timedelta(minutes=3) + assert lock.lock_end_time.replace(tzinfo=UTC) < dt_now() + timedelta(minutes=14) PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=15)) assert PairLocks.is_pair_locked(pair) lock = PairLocks.get_pair_longest_lock(pair) # Must be longer than above - assert lock.lock_end_time.replace(tzinfo=timezone.utc) > dt_now() + timedelta(minutes=14) + assert lock.lock_end_time.replace(tzinfo=UTC) > dt_now() + timedelta(minutes=14) PairLocks.reset_locks() PairLocks.use_db = True diff --git a/tests/plugins/test_percentchangepairlist.py b/tests/plugins/test_percentchangepairlist.py index 7c2f16d76..7bbf92e5d 100644 --- a/tests/plugins/test_percentchangepairlist.py +++ b/tests/plugins/test_percentchangepairlist.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import MagicMock import pandas as pd @@ -141,7 +141,7 @@ def test_gen_pairlist_with_valid_change_pair_list_config(mocker, rpl_config, tic "lookback_days": 4, } ] - start = datetime(2024, 8, 1, 0, 0, 0, 0, tzinfo=timezone.utc) + start = datetime(2024, 8, 1, 0, 0, 0, 0, tzinfo=UTC) time_machine.move_to(start, tick=False) mock_ohlcv_data = { @@ -224,7 +224,7 @@ def test_filter_pairlist_with_empty_ticker(mocker, rpl_config, tickers, time_mac "lookback_days": 4, } ] - start = datetime(2024, 8, 1, 0, 0, 0, 0, tzinfo=timezone.utc) + start = datetime(2024, 8, 1, 0, 0, 0, 0, tzinfo=UTC) time_machine.move_to(start, tick=False) mock_ohlcv_data = { @@ -291,7 +291,7 @@ def test_filter_pairlist_with_max_value_set(mocker, rpl_config, tickers, time_ma } ] - start = datetime(2024, 8, 1, 0, 0, 0, 0, tzinfo=timezone.utc) + start = datetime(2024, 8, 1, 0, 0, 0, 0, tzinfo=UTC) time_machine.move_to(start, tick=False) mock_ohlcv_data = { diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index ef2c7e1d3..164fdbb08 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -1,5 +1,5 @@ import random -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest @@ -31,8 +31,8 @@ def generate_mock_trade( stake_amount=0.01, fee_open=fee, fee_close=fee, - open_date=datetime.now(timezone.utc) - timedelta(minutes=min_ago_open or 200), - close_date=datetime.now(timezone.utc) - timedelta(minutes=min_ago_close or 30), + open_date=datetime.now(UTC) - timedelta(minutes=min_ago_open or 200), + close_date=datetime.now(UTC) - timedelta(minutes=min_ago_close or 30), open_rate=open_rate, is_open=is_open, amount=0.01 / open_rate, @@ -99,9 +99,9 @@ def test_protectionmanager(mocker, default_conf): for handler in freqtrade.protections._protection_handlers: assert handler.name in AVAILABLE_PROTECTIONS if not handler.has_global_stop: - assert handler.global_stop(datetime.now(timezone.utc), "*") is None + assert handler.global_stop(datetime.now(UTC), "*") is None if not handler.has_local_stop: - assert handler.stop_per_pair("XRP/BTC", datetime.now(timezone.utc), "*") is None + assert handler.stop_per_pair("XRP/BTC", datetime.now(UTC), "*") is None @pytest.mark.parametrize( @@ -499,7 +499,7 @@ def test_CooldownPeriod_unlock_at(mocker, default_conf, fee, caplog, time_machin assert not log_has_re(message, caplog) caplog.clear() - start_dt = datetime(2024, 5, 2, 0, 30, 0, tzinfo=timezone.utc) + start_dt = datetime(2024, 5, 2, 0, 30, 0, tzinfo=UTC) time_machine.move_to(start_dt, tick=False) generate_mock_trade( @@ -527,7 +527,7 @@ def test_CooldownPeriod_unlock_at(mocker, default_conf, fee, caplog, time_machin assert not PairLocks.is_global_lock() # Force rollover to the next day. - start_dt = datetime(2024, 5, 2, 22, 00, 0, tzinfo=timezone.utc) + start_dt = datetime(2024, 5, 2, 22, 00, 0, tzinfo=UTC) time_machine.move_to(start_dt, tick=False) generate_mock_trade( "ETH/BTC", diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 54f427f66..d8fe14d1a 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -1,5 +1,5 @@ from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from unittest.mock import ANY, MagicMock, PropertyMock import pytest @@ -347,7 +347,7 @@ def test__rpc_timeunit_profit( assert day["starting_balance"] in (pytest.approx(1062.37), pytest.approx(1066.46)) assert day["fiat_value"] in (0.0,) # ensure first day is current date - assert str(days["data"][0]["date"]) == str(datetime.now(timezone.utc).date()) + assert str(days["data"][0]["date"]) == str(datetime.now(UTC).date()) # Try invalid data with pytest.raises(RPCException, match=r".*must be an integer greater than 0*"): @@ -1296,9 +1296,9 @@ def test_rpc_add_and_delete_lock(mocker, default_conf): rpc = RPC(freqtradebot) pair = "ETH/BTC" - rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=4), "", "*") - rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=5), "", "*") - rpc._rpc_add_lock(pair, datetime.now(timezone.utc) + timedelta(minutes=10), "", "*") + rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=4), "", "*") + rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=5), "", "*") + rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=10), "", "*") locks = rpc._rpc_locks() assert locks["lock_count"] == 3 diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index d0b235638..fb85e1b80 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -5,7 +5,7 @@ Unit test file for rpc/api_server.py import asyncio import logging import time -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path from unittest.mock import ANY, MagicMock, PropertyMock @@ -637,12 +637,12 @@ def test_api_locks(botclient): [ { "pair": "ETH/BTC", - "until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=4))}Z", + "until": f"{format_date(datetime.now(UTC) + timedelta(minutes=4))}Z", "reason": "randreason", }, { "pair": "XRP/BTC", - "until": f"{format_date(datetime.now(timezone.utc) + timedelta(minutes=20))}Z", + "until": f"{format_date(datetime.now(UTC) + timedelta(minutes=20))}Z", "reason": "deadbeef", }, ], @@ -711,7 +711,7 @@ def test_api_daily(botclient, mocker, ticker, fee, markets): assert len(response["data"]) == 7 assert response["stake_currency"] == "BTC" assert response["fiat_display_currency"] == "USD" - assert response["data"][0]["date"] == str(datetime.now(timezone.utc).date()) + assert response["data"][0]["date"] == str(datetime.now(UTC).date()) def test_api_weekly(botclient, mocker, ticker, fee, markets, time_machine): @@ -1695,7 +1695,7 @@ def test_api_force_entry(botclient, mocker, fee, endpoint): exchange="binance", stake_amount=1, open_rate=0.245441, - open_date=datetime.now(timezone.utc), + open_date=datetime.now(UTC), is_open=False, is_short=False, fee_close=fee.return_value, diff --git a/tests/rpc/test_rpc_emc.py b/tests/rpc/test_rpc_emc.py index 678379e68..ef4bbad5e 100644 --- a/tests/rpc/test_rpc_emc.py +++ b/tests/rpc/test_rpc_emc.py @@ -4,7 +4,7 @@ Unit test file for rpc/external_message_consumer.py import asyncio import logging -from datetime import datetime, timezone +from datetime import UTC, datetime from unittest.mock import MagicMock import pytest @@ -99,7 +99,7 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history): "data": { "key": ("BTC/USDT", "5m", "spot"), "df": ohlcv_history, - "la": datetime.now(timezone.utc), + "la": datetime.now(UTC), }, } patched_emc.handle_producer_message(test_producer, df_message) @@ -123,7 +123,7 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history): malformed_message = { "type": "analyzed_df", - "data": {"key": "BTC/USDT", "df": ohlcv_history, "la": datetime.now(timezone.utc)}, + "data": {"key": "BTC/USDT", "df": ohlcv_history, "la": datetime.now(UTC)}, } patched_emc.handle_producer_message(test_producer, malformed_message) @@ -137,7 +137,7 @@ def test_emc_handle_producer_message(patched_emc, caplog, ohlcv_history): "data": { "key": ("BTC/USDT", "5m", "spot"), "df": ohlcv_history.loc[ohlcv_history["open"] < 0], - "la": datetime.now(timezone.utc), + "la": datetime.now(UTC), }, } patched_emc.handle_producer_message(test_producer, malformed_message) diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py index 494e374c4..27153c4de 100644 --- a/tests/strategy/test_default_strategy.py +++ b/tests/strategy/test_default_strategy.py @@ -1,4 +1,4 @@ -from datetime import datetime, timezone +from datetime import UTC, datetime import pytest from pandas import DataFrame @@ -47,7 +47,7 @@ def test_strategy_test_v3(dataframe_1m, fee, is_short, side): amount=0.1, rate=20000, time_in_force="gtc", - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), side=side, entry_tag=None, ) @@ -63,7 +63,7 @@ def test_strategy_test_v3(dataframe_1m, fee, is_short, side): time_in_force="gtc", exit_reason="roi", sell_reason="roi", - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), side=side, ) is True diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index f9ae4abdc..f80efb00f 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -1,7 +1,7 @@ # pragma pylint: disable=missing-docstring, C0103 import logging import math -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta from pathlib import Path from unittest.mock import MagicMock @@ -208,7 +208,7 @@ def test_ignore_expired_candle(default_conf): strategy = StrategyResolver.load_strategy(default_conf) strategy.ignore_buying_expired_candle_after = 60 - latest_date = datetime(2020, 12, 30, 7, 0, 0, tzinfo=timezone.utc) + latest_date = datetime(2020, 12, 30, 7, 0, 0, tzinfo=UTC) # Add 1 candle length as the "latest date" defines candle open. current_time = latest_date + timedelta(seconds=80 + 300) @@ -765,7 +765,7 @@ def test_leverage_callback(default_conf, side) -> None: assert ( strategy.leverage( pair="XRP/USDT", - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), current_rate=2.2, proposed_leverage=1.0, max_leverage=5.0, @@ -780,7 +780,7 @@ def test_leverage_callback(default_conf, side) -> None: assert ( strategy.leverage( pair="XRP/USDT", - current_time=datetime.now(timezone.utc), + current_time=datetime.now(UTC), current_rate=2.2, proposed_leverage=1.0, max_leverage=5.0, @@ -897,7 +897,7 @@ def test_is_pair_locked(default_conf): pair = "BTC/USDT" # Lock until 14:30 - lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc) + lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=UTC) # Subtract 2 seconds, as locking rounds up to the next candle. strategy.lock_pair(pair, lock_time - timedelta(seconds=2)) diff --git a/tests/util/test_datetime_helpers.py b/tests/util/test_datetime_helpers.py index 258c5a0b9..0fc8cd050 100644 --- a/tests/util/test_datetime_helpers.py +++ b/tests/util/test_datetime_helpers.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta, timezone +from datetime import UTC, datetime, timedelta import pytest import time_machine @@ -21,14 +21,14 @@ from freqtrade.util.datetime_helpers import dt_humanize_delta def test_dt_now(): with time_machine.travel("2021-09-01 05:01:00 +00:00", tick=False) as t: - now = datetime.now(timezone.utc) + now = datetime.now(UTC) assert dt_now() == now assert dt_ts() == int(now.timestamp() * 1000) assert dt_ts(now) == int(now.timestamp() * 1000) t.shift(timedelta(hours=5)) assert dt_now() >= now - assert dt_now() == datetime.now(timezone.utc) + assert dt_now() == datetime.now(UTC) assert dt_ts() == int(dt_now().timestamp() * 1000) # Test with different time than now assert dt_ts(now) == int(now.timestamp() * 1000) @@ -37,42 +37,42 @@ def test_dt_now(): def test_dt_ts_def(): assert dt_ts_def(None) == 0 assert dt_ts_def(None, 123) == 123 - assert dt_ts_def(datetime(2023, 5, 5, tzinfo=timezone.utc)) == 1683244800000 - assert dt_ts_def(datetime(2023, 5, 5, tzinfo=timezone.utc), 123) == 1683244800000 + assert dt_ts_def(datetime(2023, 5, 5, tzinfo=UTC)) == 1683244800000 + assert dt_ts_def(datetime(2023, 5, 5, tzinfo=UTC), 123) == 1683244800000 def test_dt_ts_none(): assert dt_ts_none(None) is None assert dt_ts_none(None) is None - assert dt_ts_none(datetime(2023, 5, 5, tzinfo=timezone.utc)) == 1683244800000 - assert dt_ts_none(datetime(2023, 5, 5, tzinfo=timezone.utc)) == 1683244800000 + assert dt_ts_none(datetime(2023, 5, 5, tzinfo=UTC)) == 1683244800000 + assert dt_ts_none(datetime(2023, 5, 5, tzinfo=UTC)) == 1683244800000 def test_dt_utc(): - assert dt_utc(2023, 5, 5) == datetime(2023, 5, 5, tzinfo=timezone.utc) + assert dt_utc(2023, 5, 5) == datetime(2023, 5, 5, tzinfo=UTC) assert dt_utc(2023, 5, 5, 0, 0, 0, 555500) == datetime( - 2023, 5, 5, 0, 0, 0, 555500, tzinfo=timezone.utc + 2023, 5, 5, 0, 0, 0, 555500, tzinfo=UTC ) @pytest.mark.parametrize("as_ms", [True, False]) def test_dt_from_ts(as_ms): multi = 1000 if as_ms else 1 - assert dt_from_ts(1683244800.0 * multi) == datetime(2023, 5, 5, tzinfo=timezone.utc) + assert dt_from_ts(1683244800.0 * multi) == datetime(2023, 5, 5, tzinfo=UTC) assert dt_from_ts(1683244800.5555 * multi) == datetime( - 2023, 5, 5, 0, 0, 0, 555500, tzinfo=timezone.utc + 2023, 5, 5, 0, 0, 0, 555500, tzinfo=UTC ) # As int - assert dt_from_ts(1683244800 * multi) == datetime(2023, 5, 5, tzinfo=timezone.utc) + assert dt_from_ts(1683244800 * multi) == datetime(2023, 5, 5, tzinfo=UTC) # As milliseconds - assert dt_from_ts(1683244800 * multi) == datetime(2023, 5, 5, tzinfo=timezone.utc) - assert dt_from_ts(1683242400 * multi) == datetime(2023, 5, 4, 23, 20, tzinfo=timezone.utc) + assert dt_from_ts(1683244800 * multi) == datetime(2023, 5, 5, tzinfo=UTC) + assert dt_from_ts(1683242400 * multi) == datetime(2023, 5, 4, 23, 20, tzinfo=UTC) def test_dt_floor_day(): - now = datetime(2023, 9, 1, 5, 2, 3, 455555, tzinfo=timezone.utc) + now = datetime(2023, 9, 1, 5, 2, 3, 455555, tzinfo=UTC) - assert dt_floor_day(now) == datetime(2023, 9, 1, tzinfo=timezone.utc) + assert dt_floor_day(now) == datetime(2023, 9, 1, tzinfo=UTC) def test_shorten_date() -> None: @@ -95,21 +95,21 @@ def test_format_ms_time() -> None: date_in_epoch_ms = 1523383321132 date = format_ms_time(date_in_epoch_ms) assert isinstance(date, str) - res = datetime(2018, 4, 10, 18, 2, 1, tzinfo=timezone.utc) + res = datetime(2018, 4, 10, 18, 2, 1, tzinfo=UTC) assert date == res.strftime("%Y-%m-%dT%H:%M:%S") assert date == "2018-04-10T18:02:01" - res = datetime(2017, 12, 13, 8, 2, 1, tzinfo=timezone.utc) + res = datetime(2017, 12, 13, 8, 2, 1, tzinfo=UTC) # Date 2017-12-13 08:02:01 date_in_epoch_ms = 1513152121000 assert format_ms_time(date_in_epoch_ms) == res.strftime("%Y-%m-%dT%H:%M:%S") def test_format_date() -> None: - date = datetime(2023, 9, 1, 5, 2, 3, 455555, tzinfo=timezone.utc) + date = datetime(2023, 9, 1, 5, 2, 3, 455555, tzinfo=UTC) assert format_date(date) == "2023-09-01 05:02:03" assert format_date(None) == "" - date = datetime(2021, 9, 30, 22, 59, 3, 455555, tzinfo=timezone.utc) + 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) == "" @@ -119,10 +119,10 @@ def test_format_ms_time_detailed() -> None: date_in_epoch_ms = 1523383321132 date = format_ms_time_det(date_in_epoch_ms) assert isinstance(date, str) - res = datetime(2018, 4, 10, 18, 2, 1, 132145, tzinfo=timezone.utc) + res = datetime(2018, 4, 10, 18, 2, 1, 132145, tzinfo=UTC) assert date == res.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] assert date == "2018-04-10T18:02:01.132" - res = datetime(2017, 12, 13, 8, 2, 1, 512321, tzinfo=timezone.utc) + res = datetime(2017, 12, 13, 8, 2, 1, 512321, tzinfo=UTC) # Date 2017-12-13 08:02:01 date_in_epoch_ms = 1513152121512 assert format_ms_time_det(date_in_epoch_ms) == res.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] From 7b541ff958bcbb20aae49ef40eba19e4e492b3e1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 09:07:52 +0200 Subject: [PATCH 12/15] test: refactor test to use asyncio.timeout --- tests/exchange/test_exchange_ws.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/tests/exchange/test_exchange_ws.py b/tests/exchange/test_exchange_ws.py index 6fce00aee..ed96e66d0 100644 --- a/tests/exchange/test_exchange_ws.py +++ b/tests/exchange/test_exchange_ws.py @@ -74,14 +74,16 @@ async def test_exchangews_ohlcv(mocker, time_machine, caplog): await asyncio.sleep(0.1) return MagicMock() - async def wait_for_condition(condition_func, timeout=5.0, check_interval=0.01): + async def wait_for_condition(condition_func, timeout_=5.0, check_interval=0.01): """Wait for a condition to be true with timeout.""" - start_time = asyncio.get_event_loop().time() - while asyncio.get_event_loop().time() - start_time < timeout: - if condition_func(): - return True - await asyncio.sleep(check_interval) - return False + try: + async with asyncio.timeout(timeout_): + while True: + if condition_func(): + return True + await asyncio.sleep(check_interval) + except TimeoutError: + return False ccxt_object.un_watch_ohlcv_for_symbols = AsyncMock(side_effect=NotSupported) ccxt_object.watch_ohlcv = AsyncMock(side_effect=controlled_sleeper) @@ -104,7 +106,7 @@ async def test_exchangews_ohlcv(mocker, time_machine, caplog): lambda: ( len(exchange_ws._klines_watching) == 2 and len(exchange_ws._klines_scheduled) == 2 ), - timeout=2.0, + timeout_=2.0, ) assert exchange_ws._klines_watching == { @@ -117,7 +119,7 @@ async def test_exchangews_ohlcv(mocker, time_machine, caplog): } # Wait for the expected number of watch calls - await wait_for_condition(lambda: ccxt_object.watch_ohlcv.call_count >= 6, timeout=3.0) + await wait_for_condition(lambda: ccxt_object.watch_ohlcv.call_count >= 6, timeout_=3.0) assert ccxt_object.watch_ohlcv.call_count >= 6 ccxt_object.watch_ohlcv.reset_mock() @@ -126,7 +128,7 @@ async def test_exchangews_ohlcv(mocker, time_machine, caplog): # Wait for log message await wait_for_condition( - lambda: log_has_re("un_watch_ohlcv_for_symbols not supported: ", caplog), timeout=2.0 + lambda: log_has_re("un_watch_ohlcv_for_symbols not supported: ", caplog), timeout_=2.0 ) assert log_has_re("un_watch_ohlcv_for_symbols not supported: ", caplog) From d010a050d384ffd95ec7fbe6c2f71f1df3c45746 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 09:10:14 +0200 Subject: [PATCH 13/15] chore: autoformat tests --- tests/data/test_trade_converter_kraken.py | 8 ++------ tests/exchange/test_okx.py | 6 ++---- tests/util/test_datetime_helpers.py | 8 ++------ 3 files changed, 6 insertions(+), 16 deletions(-) diff --git a/tests/data/test_trade_converter_kraken.py b/tests/data/test_trade_converter_kraken.py index bfdb2c0f3..cddcf5dc0 100644 --- a/tests/data/test_trade_converter_kraken.py +++ b/tests/data/test_trade_converter_kraken.py @@ -49,12 +49,8 @@ def test_import_kraken_trades_from_csv(testdatadir, tmp_path, caplog, default_co trades = dh.trades_load("BCH_EUR", TradingMode.SPOT) assert len(trades) == 340 - assert trades["date"].min().to_pydatetime() == datetime( - 2023, 1, 1, 0, 3, 56, tzinfo=UTC - ) - assert trades["date"].max().to_pydatetime() == datetime( - 2023, 1, 2, 23, 17, 3, tzinfo=UTC - ) + assert trades["date"].min().to_pydatetime() == datetime(2023, 1, 1, 0, 3, 56, tzinfo=UTC) + assert trades["date"].max().to_pydatetime() == datetime(2023, 1, 2, 23, 17, 3, tzinfo=UTC) # ID is not filled assert len(trades.loc[trades["id"] != ""]) == 0 diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index e2a29b1fc..bf881163e 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -29,8 +29,7 @@ def test_okx_ohlcv_candle_limit(default_conf, mocker): assert exchange.ohlcv_candle_limit(timeframe, CandleType.FUNDING_RATE, start_time) == 100 one_call = int( ( - datetime.now(UTC) - - timedelta(minutes=290 * timeframe_to_minutes(timeframe)) + datetime.now(UTC) - timedelta(minutes=290 * timeframe_to_minutes(timeframe)) ).timestamp() * 1000 ) @@ -40,8 +39,7 @@ def test_okx_ohlcv_candle_limit(default_conf, mocker): one_call = int( ( - datetime.now(UTC) - - timedelta(minutes=320 * timeframe_to_minutes(timeframe)) + datetime.now(UTC) - timedelta(minutes=320 * timeframe_to_minutes(timeframe)) ).timestamp() * 1000 ) diff --git a/tests/util/test_datetime_helpers.py b/tests/util/test_datetime_helpers.py index 0fc8cd050..82dd6dbf6 100644 --- a/tests/util/test_datetime_helpers.py +++ b/tests/util/test_datetime_helpers.py @@ -50,18 +50,14 @@ def test_dt_ts_none(): def test_dt_utc(): assert dt_utc(2023, 5, 5) == datetime(2023, 5, 5, tzinfo=UTC) - assert dt_utc(2023, 5, 5, 0, 0, 0, 555500) == datetime( - 2023, 5, 5, 0, 0, 0, 555500, tzinfo=UTC - ) + assert dt_utc(2023, 5, 5, 0, 0, 0, 555500) == datetime(2023, 5, 5, 0, 0, 0, 555500, tzinfo=UTC) @pytest.mark.parametrize("as_ms", [True, False]) def test_dt_from_ts(as_ms): multi = 1000 if as_ms else 1 assert dt_from_ts(1683244800.0 * multi) == datetime(2023, 5, 5, tzinfo=UTC) - assert dt_from_ts(1683244800.5555 * multi) == datetime( - 2023, 5, 5, 0, 0, 0, 555500, tzinfo=UTC - ) + assert dt_from_ts(1683244800.5555 * multi) == datetime(2023, 5, 5, 0, 0, 0, 555500, tzinfo=UTC) # As int assert dt_from_ts(1683244800 * multi) == datetime(2023, 5, 5, tzinfo=UTC) # As milliseconds From 16defdbfdf19efadccaf062f7db8f4cd96c8fc2b Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 4 Jul 2025 09:10:30 +0200 Subject: [PATCH 14/15] chore: autoformat actual code --- freqtrade/configuration/timerange.py | 8 ++------ freqtrade/freqtradebot.py | 4 +--- freqtrade/persistence/pairlock.py | 4 +--- freqtrade/persistence/trade_model.py | 4 +--- 4 files changed, 5 insertions(+), 15 deletions(-) diff --git a/freqtrade/configuration/timerange.py b/freqtrade/configuration/timerange.py index 7cf629c38..9a2b47504 100644 --- a/freqtrade/configuration/timerange.py +++ b/freqtrade/configuration/timerange.py @@ -150,9 +150,7 @@ class TimeRange: starts = rvals[index] if stype[0] == "date" and len(starts) == 8: start = int( - datetime.strptime(starts, "%Y%m%d") - .replace(tzinfo=UTC) - .timestamp() + datetime.strptime(starts, "%Y%m%d").replace(tzinfo=UTC).timestamp() ) elif len(starts) == 13: start = int(starts) // 1000 @@ -163,9 +161,7 @@ class TimeRange: stops = rvals[index] if stype[1] == "date" and len(stops) == 8: stop = int( - datetime.strptime(stops, "%Y%m%d") - .replace(tzinfo=UTC) - .timestamp() + datetime.strptime(stops, "%Y%m%d").replace(tzinfo=UTC).timestamp() ) elif len(stops) == 13: stop = int(stops) // 1000 diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index f8af2b469..0de03a0d8 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1583,9 +1583,7 @@ class FreqtradeBot(LoggingMixin): if not_closed: if fully_cancelled or ( open_order - and self.strategy.ft_check_timed_out( - trade, open_order, datetime.now(UTC) - ) + and self.strategy.ft_check_timed_out(trade, open_order, datetime.now(UTC)) ): self.handle_cancel_order( order, open_order, trade, constants.CANCEL_REASON["TIMEOUT"] diff --git a/freqtrade/persistence/pairlock.py b/freqtrade/persistence/pairlock.py index db88bd878..eedc7286f 100644 --- a/freqtrade/persistence/pairlock.py +++ b/freqtrade/persistence/pairlock.py @@ -71,9 +71,7 @@ class PairLock(ModelBase): "lock_time": self.lock_time.strftime(DATETIME_PRINT_FORMAT), "lock_timestamp": int(self.lock_time.replace(tzinfo=UTC).timestamp() * 1000), "lock_end_time": self.lock_end_time.strftime(DATETIME_PRINT_FORMAT), - "lock_end_timestamp": int( - self.lock_end_time.replace(tzinfo=UTC).timestamp() * 1000 - ), + "lock_end_timestamp": int(self.lock_end_time.replace(tzinfo=UTC).timestamp() * 1000), "reason": self.reason, "side": self.side, "active": self.active, diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 44cd06baf..51ec4abef 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -125,9 +125,7 @@ class Order(ModelBase): @property def order_filled_utc(self) -> datetime | None: """last order-date with UTC timezoneinfo""" - return ( - self.order_filled_date.replace(tzinfo=UTC) if self.order_filled_date else None - ) + return self.order_filled_date.replace(tzinfo=UTC) if self.order_filled_date else None @property def safe_amount(self) -> float: From ac904c60c8f54253cea570f9e820156d6ef02957 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 5 Jul 2025 08:38:02 +0200 Subject: [PATCH 15/15] fix: powershell syntax error --- setup.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.ps1 b/setup.ps1 index 39c9b6665..39e8b7d02 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -159,7 +159,7 @@ function Find-PythonExecutable { "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python311\python.exe", "C:\Python313\python.exe", "C:\Python312\python.exe", - "C:\Python311\python.exe", + "C:\Python311\python.exe" )